--- name: 收藏单条状态查询接口 overview: 参照点赞的 GET /like/:targetUUID,为收藏新增 GET /collect/:targetUUID 单条收藏状态查询接口(返回 is_collected + collect_count),补齐前端进入穿搭详情时的收藏状态初始化能力。 todos: - id: add-collect-status-api content: 新增收藏单条状态查询 DTO、Service、Handler 与路由注册 status: completed - id: sync-collect-docs content: 同步 README 与接口文档,补充 GET /collect/:targetUUID 说明 status: completed dependencies: - add-collect-status-api - id: verify-collect-build content: 执行 go build ./... 验证编译通过 status: completed dependencies: - add-collect-status-api --- ## 用户需求 收藏状态无法正确初始化——后端缺少「单条收藏状态」查询接口。点赞已有 `GET /like/:targetUUID`(返回 `is_liked` + `like_count`),收藏仅有 `POST /collect`(收藏/取消)和 `GET /collect`(我的收藏列表),前端进入穿搭详情时无法获知当前用户是否已收藏该穿搭。 ## 核心功能 参照点赞接口,为收藏补齐 `GET /collect/:targetUUID` 单条状态查询: - 返回当前用户是否已收藏(`is_collected`)与该穿搭收藏总数(`collect_count`) - Redis 优先、异常回落 MySQL,与点赞查询策略严格对称 - 校验穿搭存在性与可见性(非本人仅可查询公开可见穿搭) ## 技术栈 - Go 1.27 + Gin + GORM + go-redis,沿用现有分层(handler → service → dao / redisdao) - 复用现有收藏底层能力,不新增 DAO/Redis 方法 ## 实现方式 严格对齐点赞 `GET /like/:targetUUID` 的实现模式,新增收藏单条状态查询链路。 ### 关键决策 - **字段命名对称**:点赞 `is_liked`/`like_count`,收藏用 `is_collected`/`collect_count`。 - **路径参数命名对齐**:`targetUUID`(驼峰,与点赞一致),避免前端两套传参方式不一致。 - **回落策略一致**:是否已收藏与收藏总数均 Redis 优先;Redis 出错或计数为 0 时回落 MySQL 兜底,保证缓存冷启动/漂移时数据正确。 - **仅新增不改旧**:收藏的收藏/取消与列表逻辑保持不动,降低回归风险。 ### 数据流 `GET /collect/:targetUUID` → handler 取 `targetUUID` → service 校验穿搭(存在 + 可见性)→ Redis 查收藏状态/计数 → 异常回落 MySQL → 返回 `{is_collected, collect_count}` ## 目录结构 ``` zt-closet-backend/ ├── internal/ │ ├── dto/ │ │ └── collect.go # [MODIFY] 新增 CollectGetByTargetRequest/Response │ ├── service/ │ │ └── collect.go # [MODIFY] 新增 GetByTarget 方法 │ └── routers/ │ ├── router.go # [MODIFY] 注册 GET /collect/:targetUUID │ └── api/v1/ │ └── collect.go # [MODIFY] 新增 GetByTarget handler ├── README.md # [MODIFY] API 概览收藏小节新增接口行 └── 接口文档.md # [MODIFY] 接口总表与 5.8 节新增接口说明 ``` ## 实现细节 - DTO 定义与点赞完全对应:`CollectGetByTargetRequest{ TargetUUID string json:"target_uuid" binding:"required" }`、`CollectGetByTargetResponse{ UserIsCollected bool json:"is_collected"; CollectCount int64 json:"collect_count" }`。 - Service `GetByTarget` 内部逻辑:先 `OutfitGetByUUID`(不存在返回 `TargetNotFound`、非本人且未公开返回 `OutfitNoPermission`),再取 `targetID`,随后 `CollectIsMember`(Redis 优先,err 回落 `CollectGetByTargetAndUser` 判断 `Status == InteractStatusOn`),最后 `CollectCount`(Redis 优先,`err != nil || count == 0` 回落 `CollectCountByTarget`)。 - Handler 与路由均对齐点赞:path 参数 `targetUUID`,空值返回 `InvalidParams`;路由注册在鉴权组内,`/collect`(精确路径)与 `/collect/:targetUUID`(参数路径)在 Gin 路由树中可共存。