Files
zt-closet-backend/.codebuddy/plans/api-v1层完整实现_e89004bd.md
2026-09-11 01:41:52 +08:00

153 lines
9.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 绑定一次性对齐,减少编译期返工。