--- name: 穿搭列表卡片显示衣物拼图封面 overview: 修复「穿搭」列表卡片封面空白(只有铃铛占位):后端 OutfitItemResponse 增加 processed_image 字段并返回 CDN 图片 URL;前端 outfit 列表卡片封面改为最多 5 张衣物拼图网格,无图时回退铃铛占位。 todos: - id: backend-outfit-image content: 后端 dto/outfit.go 与 service/outfit.go 补 processed_image 组装链 status: completed - id: backend-collect-image content: 后端 collect.go 收藏列表 items 返回带图 status: completed dependencies: - backend-outfit-image - id: frontend-cover-collage content: 前端 models.ts 类型与 outfit 列表封面拼图渲染 status: completed dependencies: - backend-outfit-image - id: verify-build-lint content: 执行后端 go build 与前端 lint 验证 status: completed dependencies: - backend-outfit-image - backend-collect-image - frontend-cover-collage --- ## 产品概述 修复「穿搭」列表页卡片封面显示空白的问题:卡片封面目前只有铃铛占位图标和件数角标,应展示该穿搭的衣物平铺拼图(与穿搭详情页的拼图封面风格一致)。 ## 核心功能 - 穿搭列表卡片封面展示最多 5 件衣物的抠图平铺拼图,衣物图片缺失时仍显示铃铛占位。 - 已收藏 tab 的穿搭卡片同样展示拼图封面。 - 列表数据一次性从后端带图返回,不在前端逐件请求衣物详情,避免 N+1 网络开销。 - 详情页、AI 推荐结果页等已有页面行为保持不变。 ## 技术栈 - 后端:Go(沿用现有 Gin + GORM + COS 上传体系),复用 `upload.GetCosUrl` 将 COS key 转为可访问 CDN URL。 - 前端:微信小程序原生 TypeScript + WXML/WXSS,复用 `outfit-detail` 页已有的拼图封面样式模式。 ## 实现方案 ### 核心策略 后端在穿搭列表/详情/收藏的 `items` 响应中为每件衣物补充 `processed_image`(可访问 URL),前端列表卡片直接渲染最多 5 张拼图。选择后端带图而非前端逐件请求的理由:列表页每张卡片含多件衣物,前端 N+1 请求代价高;而后端 `buildOutfitListResponses` 本已批量查出全部衣物模型,补图片只是内存组装,无新增 SQL。 ### 后端改动(/root/zt-closet-backend,3 个文件) 1. `internal/dto/outfit.go`:`OutfitItemResponse` 增加 `ProcessedImage string \`json:"processed_image"\``(与衣物详情响应字段名保持一致)。 2. `internal/service/outfit.go`: - 新增 helper `buildOutfitItemResponse(c *mysqlmodel.Clothes) dto.OutfitItemResponse`:组装 `ClothesUUID` + `upload.GetCosUrl(c.ProcessedImage)`(空值不转换),需新增 import `pkg/upload`。 - `resolveOwnedClothes` 返回值改为 `([]int64, []dto.OutfitItemResponse, *errcode.Error)`,利用已查出的 `clothesList` 按请求顺序组装带图 items;`Create` 直接用 items 组装响应;`UpdateByUUID` 调用点以 `_` 忽略 items。 - `itemClothesUUIDs` 改造为 `itemResponses(ctx, outfitID) ([]dto.OutfitItemResponse, error)`:按 `OutfitItem` 明细顺序、`clothesID → Clothes` 映射组装带图 items;`GetByUUID` 相应更新。 - `buildOutfitListResponses`:`uuidByID` 改为 `clothesByID map[int64]mysqlmodel.Clothes`,按 `itemsByOutfit[o.ID]` 顺序组装 items。 - `buildOutfitCreateResponse(o *mysqlmodel.Outfit, items []dto.OutfitItemResponse)` 签名同步调整。 3. `internal/service/collect.go`:`CollectService.itemClothesUUIDs` 同样改为 `itemResponses` 返回带图 items,收藏列表 `/collect` 自动带图;新增 import `pkg/upload`。 ### 前端改动(/root/zt-closet-wxmini-frontend,3 个文件) 4. `miniprogram/types/models.ts`:`OutfitItem.items` 数组元素增加 `processed_image: string`。 5. `miniprogram/pages/outfit/outfit.wxml`:`.cover` 内改为拼图网格——`wx:for="{{item.items}}" wx:key="clothes_uuid" wx:if="{{index < 5}}"`,有 `processed_image` 时渲染 ``,否则铃铛占位;`items` 为空时保留居中大铃铛;`.cover-count` 件数角标保留。 6. `miniprogram/pages/outfit/outfit.wxss`:新增 `.cover-item`、`.cover-0~4`(absolute 定位 + 轻微旋转)、`.cover-img`、占位样式,尺寸适配 280rpx 高封面(约 150×190rpx 卡片),`.cover` 增加 `overflow: hidden`,风格参照 `outfit-detail.wxss` 17-67 行。 ### 兼容性与性能 - `processed_image` 为新增 JSON 字段,向后兼容;详情页 `outfit-detail.ts` 仍需 `name` 字段,保持原 `/clothes/{uuid}` 请求逻辑不变。 - 列表组装维持现有批量查询路径(无 N+1),图片转换仅内存操作;`GetCosUrl` 生成签名 URL 为纯计算,无 IO。 - 衣物图片缺失(抠图失败/未处理)时 `processed_image` 为空,前端自动回退铃铛占位,无异常展示。 ## 目录结构 ``` /root/zt-closet-backend/ ├── internal/dto/outfit.go # [MODIFY] OutfitItemResponse 增加 processed_image 字段 └── internal/service/ ├── outfit.go # [MODIFY] helper 与响应组装链带图(Create/GetByUUID/List) └── collect.go # [MODIFY] 收藏列表 items 带图 /root/zt-closet-wxmini-frontend/miniprogram/ ├── types/models.ts # [MODIFY] OutfitItem.items 元素增加 processed_image └── pages/outfit/ ├── outfit.wxml # [MODIFY] 卡片封面改为最多 5 张拼图网格 └── outfit.wxss # [MODIFY] 新增拼图封面样式,适配 280rpx 高卡片 ``` ## 验证方式 - 后端:`cd /root/zt-closet-backend && go build ./...` 编译通过。 - 前端:对 `models.ts`、`outfit.wxml`、`outfit.wxss` 执行 lint 检查无新错误。 - 手工验证:小程序「穿搭」页两个 tab 的卡片封面应显示衣物拼图;衣物无图的旧数据回退铃铛。