--- name: api-v1层完整实现 overview: 实现 internal/routers/api/v1 全部 HTTP handler 并在 router.go 完成中间件与路由接线,调用已完成的 service 层;同时修复阻断运行的中间件/启动初始化问题,形成可跑通的 RESTful 接口。 todos: - id: fix-infra content: 修复 middleware(requestid uuid 导入、JWTAuth 封禁判断反转与缓存回落 DB)、main.go 初始化 global.Tracer/CosClient 与运势任务、dto 列表字段补 form tag status: completed - id: handlers-user-basic content: 实现 user/quota/horoscope/goods 四个 handler(登录/刷新/信息/额度/运势/商品) status: completed dependencies: - fix-infra - id: handlers-clothes-outfit content: 实现 clothes(含 multipart 上传)与 outfit 两个 handler,覆盖增删改查与公开 status: completed dependencies: - fix-infra - id: handlers-community content: 实现 closet/collect/like/notification 四个 handler(分享、收藏、点赞、通知) status: completed dependencies: - fix-infra - id: handlers-order-ai content: 实现 order(含微信支付/退款回调按规范返回)与 airecommend 两个 handler status: completed dependencies: - fix-infra - id: wire-router content: 在 router.go 接线中间件链与 /api/v1 公开/鉴权路由,添加 /ping,go build 与 go vet 全量验证 status: completed dependencies: - handlers-user-basic - handlers-clothes-outfit - handlers-community - handlers-order-ai --- ## 产品概述 为"衣柜/穿搭"微信小程序后端补齐 **HTTP 接口层(api/v1)**:将已完成的 service 业务逻辑通过 Gin handler 暴露为 RESTful 接口,并完成路由与中间件接线,形成可跑通的完整后端服务。 ## 核心功能 - **用户与登录**:微信登录、刷新 Token、获取/修改个人信息 - **衣物管理**:上传(含图片处理)、状态查询、详情、修改、删除、列表 - **穿搭管理**:创建、详情、修改、删除、列表、公开设置 - **衣柜分享**:创建、详情(访客/属主)、我的分享列表、取消 - **社区互动**:点赞/取消、点赞状态与计数、收藏/取消、收藏列表 - **消息通知**:列表、详情、标记已读、未读数 - **商品与订单**:商品查询/列表、下单、订单列表、取消、退款申请、微信支付/退款回调 - **额度与运势**:额度概览与流水、今日运势 - **AI 推荐**:发起 AI 穿搭推荐、保存到我的搭配 ## 接口约定 - 统一前缀 `/api/v1`;成功返回 `{code:0,msg,data}`,失败按业务错误码返回。 - 登录、刷新 Token、商品查询、微信回调为公开接口;其余需登录鉴权。 - 微信支付/退款回调按微信规范返回受理结果。 ## 技术栈 - 语言/框架:Go 1.27 + Gin(沿用现有项目),复用 `pkg/app`(统一响应)、`pkg/errcode`(业务错误码)、`internal/middleware`(JWT/CORS/日志/追踪/RequestID)。 - 依赖注入:handler 结构体持有对应 `internal/service`,构造函数注入,与 service/dao 现有风格一致。 ## 实现思路 新增 `internal/routers/api/v1` 下按业务域拆分的 handler,并在 `router.go` 完成中间件与路由接线。handler 只负责:请求绑定与校验、path param 覆盖标识字段、从 context 取 userID、调用 service、统一响应转换;业务逻辑与归属校验(AuthZ)保持在 service 层,handler 不重复实现。 统一 handler 模式: 1. `resp := app.NewResponse(c)` 2. 绑定:JSON 用 `ShouldBindJSON`,GET query 用 `ShouldBindQuery`,失败返回 `errcode.InvalidParams.WithDetails(err.Error())` 3. REST 路径标识(uuid/code/orderNo 等)用 `c.Param(...)` 覆盖 req 对应字段 4. `middleware.GetUserID(c.Request.Context())` 取 userID 5. 调 service,错误 `resp.ToErrorResponse(e)`,成功 `resp.ToResponse(out)` ### 关键技术决策 - **同层静态路由与参数路由共存**:Gin v1.12 支持 `/notification/unread` 与 `/notification/:uuid`、`/order/refund` 与 `/order/:orderNo/cancel` 共存,无需拆分前缀,保持 URL 语义清晰。 - **微信回调独立处理**:回调不走 JWT,handler 读 `c.GetRawData()` 原始报文传 service(service 已做 V3 验签/GCM 解密),按微信规范返回 `{"code":"SUCCESS"}`(200)或 `{"code":"FAIL","message":...}`(非 200),避免微信重复回调。 - **文件上传**:`/clothes/upload` 用 `c.FormFile("file")` 取 `*multipart.FileHeader` 传 service。 ## 实现要点(防回归) 本次需一并修复阻断 api 层编译/运行的既有问题,否则接口无法跑通: - **middleware/requestid.go**:修复非法导入 `import "uuid"` → `github.com/google/uuid`,调用 `uuid.New().String()`(当前该文件导致 middleware 包无法编译)。 - **middleware/lzcwyjwt.go**: - 修复封禁判断反转:`if userInfo.Status == 1` → 应为 `!= 1` 时拒绝(1=正常),避免正常用户被拦、禁用用户放行。 - 缓存未命中(`redis.Nil`)回落 DB(`mysqldao.UserDao.UserGetByID`),成功回填 `redisdao.UserInfoSet`,仍失败再拒绝,避免缓存过期导致合法用户被锁死。 - **main.go 运行期初始化**(当前缺失,直接运行会 nil panic): - `global.Tracer = otel.Tracer(global.OtelSetting.ServiceName)`(otel 默认 no-op provider 返回合法非 nil tracer,DAO 的 `global.Tracer.Start` 依赖它)。 - `global.CosClient, _ = cos.NewCosClient()`(衣物上传 `upload.FileUploadCos` 依赖)。 - 启动运势兜底:`tokenhub.EnsureTodayFortunes()` 与 `tokenhub.StartFortuneScheduler()`。 - **dto 补 form tag**(GET query 筛选可用的最小调整):`OutfitListRequest.IsPublic/Source`、`GoodsListRequest.GoodsType`、`ClosetShareListRequest.Status` 当前仅 `json` tag,需补 `form` tag 才能 `ShouldBindQuery` 绑定。 - 不打印 token/密钥;绑定错误文本作为 details 返回可接受,不泄露内部实现。 ## 架构与数据流 请求 → Gin 中间件链(Recovery → RequestID → OTel → Slog → CORS)→ [公开路由 / JWTAuth 鉴权组] → v1 handler(绑定+取 userID)→ service(业务+AuthZ)→ dao/redis/pkg → 统一响应。 ## 目录结构 ``` zt-closet-backend/ ├── main.go # [MODIFY] 初始化 global.Tracer / global.CosClient;启动运势定时任务与启动兜底 ├── internal/ │ ├── dto/ │ │ ├── outfit.go # [MODIFY] OutfitListRequest.IsPublic/Source 补 form tag │ │ ├── goods.go # [MODIFY] GoodsListRequest.GoodsType 补 form tag │ │ └── closet.go # [MODIFY] ClosetShareListRequest.Status 补 form tag │ ├── middleware/ │ │ ├── requestid.go # [MODIFY] 修复 uuid 导入路径 │ │ └── lzcwyjwt.go # [MODIFY] 修复封禁状态判断反转 + 缓存未命中回落 DB 回填 │ └── routers/ │ ├── router.go # [MODIFY] 中间件链接线 + /api/v1 公开与鉴权路由注册 + /ping │ └── api/v1/ │ ├── user.go # [NEW] UserHandler:WxLogin/RefreshToken/InfoGetBySelf/ProfileUpdate │ ├── quota.go # [NEW] QuotaHandler:Info/FlowList │ ├── horoscope.go # [NEW] HoroscopeHandler:Today │ ├── clothes.go # [NEW] ClothesHandler:Upload/List/Get/Status/Update/Delete │ ├── outfit.go # [NEW] OutfitHandler:Create/Get/Update/Delete/List/Public │ ├── closet.go # [NEW] ClosetShareHandler:Create/Detail/List/Cancel │ ├── collect.go # [NEW] CollectHandler:Collect/List │ ├── like.go # [NEW] LikeHandler:Like/GetByTarget │ ├── notification.go # [NEW] NotificationHandler:List/Unread/Get/Read │ ├── goods.go # [NEW] GoodsHandler:GetByCode/List(公开) │ ├── order.go # [NEW] OrderHandler:Create/List/Cancel/RefundApply/PayNotify/RefundNotify │ └── airecommend.go # [NEW] AIRecommendHandler:Create/Save ``` ## 路由清单(前缀 /api/v1) 公开:POST /user/wxlogin、POST /user/refresh-token、GET /goods、GET /goods/:code、POST /order/pay/notify、POST /order/refund/notify 鉴权(JWTAuth): - GET /user/info、PUT /user/profile - GET /quota/info、GET /quota/flow - GET /horoscope/today - POST /clothes/upload、GET /clothes、GET /clothes/:uuid、GET /clothes/:uuid/status、PUT /clothes/:uuid、DELETE /clothes/:uuid - POST /outfit、GET /outfit、GET /outfit/:uuid、PUT /outfit/:uuid、DELETE /outfit/:uuid、PUT /outfit/:uuid/public - POST /closet-share、GET /closet-share、GET /closet-share/:uuid、POST /closet-share/:uuid/cancel - POST /collect、GET /collect - POST /like、GET /like/:targetUUID - GET /notification、GET /notification/unread、GET /notification/:uuid、POST /notification/:uuid/read - POST /order、GET /order、POST /order/:orderNo/cancel、POST /order/refund - POST /ai-recommend、POST /ai-recommend/save ## SubAgent - **code-explorer** - Purpose:实现阶段如需核对个别 service 方法签名或 dto 字段(如回调 body 读取、multipart 字段名、path param 与 dto 字段映射),快速跨文件确认,避免签名臆测。 - Expected outcome:handler 与 service 签名、dto 绑定一次性对齐,减少编译期返工。