13 KiB
13 KiB
name, overview, todos
| name | overview | todos | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| service层完整实现 | 从零实现 internal/service 全部业务逻辑(用户/衣物/穿搭/衣柜分享/收藏/点赞/通知/商品/订单退款/额度/运势/AI推荐),复用已就绪的 dao/dto/redis/pkg,并接入真实微信登录与微信支付/退款(含配置与依赖),公开内容自动审核通过,保证 service 层可独立编译。 |
|
产品概述
为"衣柜/穿搭"微信小程序后端补齐 service 业务逻辑层(internal/service),将已就绪的 DTO、MySQL DAO、Redis DAO 与 pkg 能力(大模型推荐、抠图、分类、COS、JWT、错误码)编排为完整可用的业务服务,覆盖 internal/dto 中全部请求/响应。本次仅实现 service 层(保证可独立编译),不实现 handler/router。
核心功能
- 用户与登录:微信小程序 code2session 真实登录、注册即初始化默认额度、资料修改、Token 刷新、用户信息缓存。
- 衣物管理:上传(扣额度、落 COS)+ 异步抠图/分类回填、状态查询、增删改查、列表分页。
- 穿搭管理:创建/编辑/删除、明细组装、公开设置(公开即自动审核通过并写审核日志、消耗社区分享额度)。
- 衣柜分享:部分/全量快照分享、访客详情(含过期与状态校验、访问计数)、我的分享列表、取消;创建即自动审核通过。
- 社区互动:点赞/收藏及其取消、点赞给作者发通知、点赞数/收藏数统计(Redis 优先)。
- 通知:列表、详情、标记已读、未读数(Redis 优先回落 MySQL)。
- 商品与订单:商品查询/列表;下单(真实微信 JSAPI 支付)、支付回调发放会员与额度权益、取消、订单列表;申请退款与退款回调(真实微信支付退款)。
- 额度与运势:额度概览与流水;按用户生日计算星座返回今日运势(Redis→MySQL 多级读取)。
- AI 穿搭推荐:防并发扣 AI 额度、基于衣橱与星座运势调用大模型生成推荐穿搭、幂等流水、保存到"我的搭配"。
关键约束
- 所有"我的"资源操作必须校验归属(用户/衣物/穿搭/分享/通知/订单)。
- 微信密钥仅来自配置,不硬编码;支付/退款回调必须验签解密并做幂等;额度扣减以数据库原子条件更新为准,Redis 作缓存加速。
技术栈
- 语言/框架:Go 1.27、Gin(沿用现有项目);ORM 为 GORM(含
soft_delete软删除);缓存 go-redis/v9。 - 复用能力:
internal/mysqldao(结构体 DAO +global.MysqlEngine)、internal/redisdao(包级函数,未命中返回redis.Nil)、pkg/tokenhub(大模型运势/推荐)、pkg/matting(抠图)、pkg/classification(分类)、pkg/upload+pkg/cos(落盘/上传/CDN URL)、pkg/lzcwyjwt(JWT)、pkg/errcode(业务错误码)、pkg/app(响应封装,供后续 handler 用)、global.Tracer(OTel)。 - 新增依赖:
github.com/wechatpay-apiv3/wechatpay-go(微信支付 v3 官方 SDK,用于 JSAPI 下单/回调验签解密/退款);微信登录 code2session 用标准库net/http直接请求固定域名api.weixin.qq.com(无需额外依赖)。已具备github.com/google/uuid生成各类 UUID。
实现思路
采用与 DAO 一致的结构体 Service + 构造函数风格(NewXxxService() 内部持有所需 DAO 实例),按业务域分文件放置于 internal/service,统一方法签名约定:func (s *XxxService) Method(ctx context.Context, userID int64, req *dto.XxxRequest) (*dto.XxxResponse, *errcode.Error),将底层 error 映射为语义化 *errcode.Error(DB→DatabaseError、缓存→CacheError、redis.Nil 视为未命中而非错误)。service 层不依赖 gin,保证 go build ./internal/service/... 可独立编译。
关键决策与权衡:
- 枚举常量集中化:mysqlmodel 状态/类型仅有注释、无 Go 常量。新增
internal/service/constants.go定义可读常量(状态、类型、额度类型、变更类型等),消除魔法数字,降低跨域协作出错概率。 - 额度扣减一致性:以 MySQL
QuotaIncreaseUsed(used+n<=total条件原子更新)为权威扣减,保证不超发(乐观并发);RedisQuotaRemain*/每日计数作为读取加速与限流,二者不一致时以 MySQL 为准。封装consumeQuota/refundQuota复用于衣物上传、公开分享、AI 推荐。 - 异步图片处理:
ClothesUpload同步完成校验/扣额度/落原图/建记录后立即返回,抠图+分类放入 goroutine(派生独立context、recover兜底),逐步更新image_process_status;任一步失败置状态 5 并写fail_reason且回退额度,避免阻塞请求、控制外部 RPC 时延影响。 - 支付幂等与状态机:下单/支付回调/退款回调分别用
redisdao.TryLock(KeyLockOrderCreate/KeyLockPayCallback/KeyLockRefundCallback)+OrderUpdateStatusByOrderNo(from→to)条件流转双保险,防重复回调重复发放权益。 - 多级读取:运势、未读数、点赞/收藏计数走 Redis 优先、未命中回落 MySQL 并回填,降低热点查询压力。
实现要点(防回归)
- redisdao 是包级函数,未命中返回
redis.Nil,必须errors.Is(err, redis.Nil)判断,不可当错误抛。 - mysqlmodel 均内嵌
Model,clothes表主色列名为color(非color_main),且 dtoClothesUpdateByUUIDRequest.ColorMain的 tag 为json:"color"、ClothesGetByUUIDResponse.ColorMain为json:"color_main"——映射一律以 model 字段为准,updatesmap 的 key 用真实列名color。 - dto
GoodsGetByCodeResponse.UploadQuota对应 modelGoods.CosQuota;AIRecommendQuota无对应 dto 字段(发放额度时仍需处理)。注意UserDailyUsage.CLosetShareCount原始拼写。 - 复用现有 logger(
log/slog,已接入 OTel context handler);错误日志不打印密钥/回调原文明文,避免 PII/密钥泄露。 - 归属校验统一在 service 层做(AuthZ);所有 SQL 走 DAO 参数绑定(防 SQLi 已满足);微信回调仅访问微信固定域名(无 SSRF 面)。
目录结构
zt-closet-backend/
├── configs/
│ └── config.example.yaml # [MODIFY] 新增 WechatMini(AppID/AppSecret) 与 WechatPay(AppID/MchID/APIv3Key/SerialNo/PrivateKeyPath/NotifyURL/RefundNotifyURL) 示例段
├── pkg/
│ ├── setting/
│ │ └── section.go # [MODIFY] 新增 WechatMiniSettingS、WechatPaySettingS 结构体
│ └── wechat/
│ ├── mini.go # [NEW] 微信小程序登录:Code2Session(ctx,code)->(openid,sessionKey,err),net/http 请求 jscode2session,errcode 映射
│ └── pay.go # [NEW] 微信支付客户端:初始化 wechatpay-go(加载商户私钥/证书),封装 JsapiPrepay/BuildPaySign、VerifyAndDecryptPayNotify、Refund、VerifyRefundNotify
├── global/
│ └── setting.go # [MODIFY] 新增 WechatMiniSetting、WechatPaySetting 全局变量
├── main.go # [MODIFY] setupSetting 增 ReadSection("WechatMini"/"WechatPay");新增 setupWechatPayClient 初始化支付客户端(可选,失败仅告警不 panic)
└── internal/service/
├── service.go # [NEW] 公共工具:error->errcode 映射、redis.Nil 判定、分页默认值(page/pageSize)、时间/日期工具、consumeQuota/refundQuota 额度封装
├── constants.go # [NEW] 各状态/类型 int8 常量(用户/衣物处理状态/穿搭/分享/审核/额度类型/变更类型/订单/退款/通知/点赞收藏类型等)
├── user.go # [NEW] UserService:WxLogin(真实code2session+注册+初始化额度+发Token)、InfoGetBySelf(缓存)、ProfileUpdate、RefreshToken
├── quota.go # [NEW] QuotaService:InfoGetBySelf(remain=total-used)、FlowList;ensureDefaultQuota 新用户默认额度初始化
├── horoscope.go # [NEW] HoroscopeService:由生日算星座+当日日期,Redis→MySQL 读取今日运势,回填缓存
├── clothes.go # [NEW] ClothesService:Upload(扣额度+落COS+建记录+异步抠图分类)、ProcessStatusGet、GetByUUID(CDN URL)、UpdateByUUID、DeleteByUUID、List
├── outfit.go # [NEW] OutfitService:Create/GetByUUID/Update/List/Delete/Public(公开自动审核通过+写审核日志+扣社区分享额度)
├── closet.go # [NEW] ClosetShareService:Create(自动审核通过+扣衣柜分享额度)、Detail(过期/状态校验+访问计数)、List、Cancel
├── collect.go # [NEW] CollectService:Collect(收藏/取消+Redis集合+target校验)、List(收藏穿搭列表)
├── like.go # [NEW] LikeService:LikeCreate(点赞/取消+Redis+给作者发通知)、GetByTargetID(Redis优先计数)
├── notification.go # [NEW] NotificationService:List/GetByUUID/Read/Unread;内部 sendNotification 供 like/order 复用
├── goods.go # [NEW] GoodsService:GetByCode(下架校验)、List(仅上架+类型过滤)
├── order.go # [NEW] OrderService:Create(微信JSAPI下单)、PayNotify(发放会员/额度权益+通知)、Cancel、List、RefundApply、RefundNotify
└── airecommend.go # [NEW] AIRecommendService:Create(防并发+扣AI额度+大模型推荐+幂等流水+生成待保存穿搭)、Save(置为正常)
关键代码结构
// internal/service/service.go —— service 层统一错误映射约定
// 将底层 error 归一为语义化业务错误;redis.Nil 由调用方按未命中处理,不进入此函数。
func toErr(err error, biz *errcode.Error) *errcode.Error
// 额度封装(供 clothes/outfit/closet/airecommend 复用):MySQL 原子扣减为准 + 写流水 + 同步 Redis
func (s *QuotaService) consumeQuota(ctx context.Context, userID int64, quotaType int8, n int64, changeType int8, reason, orderNo string) *errcode.Error
func (s *QuotaService) refundQuota(ctx context.Context, userID int64, quotaType int8, n int64, reason string) *errcode.Error
// pkg/wechat/pay.go —— 微信支付封装对外接口(签名以官方 SDK 实际类型为准,实现时对齐)
func InitPayClient(s *setting.WechatPaySettingS) error
func JsapiPrepay(ctx context.Context, openid, orderNo, description string, amount int64, notifyURL string) (prepayID string, err error)
func BuildMiniPayParams(prepayID string) *dto.WechatPayParams
func VerifyAndDecryptPayNotify(req *http.Request) (orderNo, transactionID string, paid bool, err error)
func Refund(ctx context.Context, orderNo, refundNo string, total, refund int64, notifyURL string) error
Agent Extensions
SubAgent
- code-explorer
- Purpose:实现阶段按需核对 DAO/Redis/pkg 的精确方法签名与字段(如 wechatpay-go 实际 API、
ClassifyResult字段到Clothes列的映射),避免签名臆测。 - Expected outcome:跨文件签名/字段映射一次性确认到位,减少编译期返工。