9.7 KiB
9.7 KiB
name, overview, todos
| name | overview | todos | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| api-v1层完整实现 | 实现 internal/routers/api/v1 全部 HTTP handler 并在 router.go 完成中间件与路由接线,调用已完成的 service 层;同时修复阻断运行的中间件/启动初始化问题,形成可跑通的 RESTful 接口。 |
|
产品概述
为"衣柜/穿搭"微信小程序后端补齐 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 模式:
resp := app.NewResponse(c)- 绑定:JSON 用
ShouldBindJSON,GET query 用ShouldBindQuery,失败返回errcode.InvalidParams.WithDetails(err.Error()) - REST 路径标识(uuid/code/orderNo 等)用
c.Param(...)覆盖 req 对应字段 middleware.GetUserID(c.Request.Context())取 userID- 调 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当前仅jsontag,需补formtag 才能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 绑定一次性对齐,减少编译期返工。