--- name: 穿搭页补全社区分段 overview: 在穿搭 tab 新增「社区」分段,浏览所有用户公开的穿搭(作者信息、拼图封面、点赞/收藏数、场景与发布时间),并新增后端社区列表接口支撑。 design: styleKeywords: - iOS HIG - 清爽克制 - 发丝分隔线 - 系统蓝 - 线性图标 fontSystem: fontFamily: PingFang SC heading: size: 32rpx weight: 600 subheading: size: 28rpx weight: 500 body: size: 26rpx weight: 400 colorSystem: primary: - "#007AFF" - "#5856D6" background: - "#F2F2F7" - "#FFFFFF" text: - "#000000" - "#3C3C43" - "#8E8E93" - "#C7C7CC" functional: - "#FF2D55" - "#34C759" - "#FF9500" - "#FF3B30" todos: - id: backend-community-api content: 用 [subagent:code-explorer] 确认后端批量查询与计数模式;实现 GET /outfit/community 接口(DTO/DAO/Service/路由)并更新接口文档 status: completed - id: frontend-community-data content: models.ts 新增社区穿搭类型;outfit.ts 实现三态分段、社区取数、相对时间与详情跳转 status: completed dependencies: - backend-community-api - id: frontend-community-ui content: outfit.wxml/wxss 实现社区分段、作者行卡片、互动数据与空态样式 status: completed dependencies: - frontend-community-data - id: verify-community content: 运行 go build 与 tsc 类型检查,核对前后端字段契约与路由注册 status: completed dependencies: - backend-community-api - frontend-community-ui --- ## 产品概述 为「纸条AI衣橱」小程序补全社区功能:在「穿搭」页现有「我的穿搭 / 已收藏」两个分段基础上,新增「社区」分段,让所有用户都能浏览其他用户公开的穿搭,并进入详情点赞、收藏,形成「公开到社区 → 他人浏览互动」的完整闭环。 ## 核心功能 - 穿搭页分段控件扩展为三态:「我的穿搭 / 已收藏 / 社区」,切换即加载对应列表并重置分页 - 社区分段以卡片流展示其他用户公开的穿搭(仅展示已公开且审核通过、状态正常的穿搭) - 社区卡片内容:作者头像与昵称(卡片顶部)、拼图式衣物封面与件数、穿搭名称、场景标签、点赞数与收藏数、相对发布时间 - 点击社区卡片进入他人视角的穿搭详情页,可点赞、收藏(复用现有详情页,不新增页面) - 社区列表支持下拉刷新、上拉加载更多、骨架屏加载态与空态提示 - 社区分段隐藏「+ 创建穿搭 / AI 推荐」入口行(浏览场景不展示管理入口) ## 视觉与交互效果 延续现有 iOS HIG 风格:分组灰底、纯白卡片、发丝分隔线、系统蓝强调色。社区卡片在现有穿搭卡片结构上顶部新增作者行(圆形头像 + 昵称 + 右侧相对时间),底部 meta 行新增点赞、收藏两组互动数据,整体与「我的穿搭」卡片流视觉统一、切换分段有平滑过渡。 ## 技术栈 - 前端:微信小程序原生 + TypeScript(沿用现有 webview 渲染与纯 WXSS 手写方案,不引入组件库) - 后端:Go + Gin + GORM/MySQL(zt-closet-backend,新增接口沿用现有分层:router → handler → service → dao) - 复用:前端 `request` 封装、`paged` 分页工具、`format.formatRelativeTime`、`logger`、`empty-state` 组件与图标库;后端 `PageRequest/PageResponse`、`normalizePage`、批量查询与批量组装模式 ## 实现方案 ### 总体策略 后端新增鉴权接口 `GET /outfit/community`:分页返回「其他用户已公开(is_public=2 且 audit_status=2 且 status=1)」的穿搭列表,聚合作者信息与点赞/收藏计数后一次性下发;前端将 `pages/outfit` 页扩展为三态分段,第三段对接新接口渲染社区卡片流,点击进入现有他人视角详情页(owner=0)。全程复用既有模式,不引入新架构、不新增页面、不改动 tabBar。 ### 关键决策与理由 1. **接口路径 `GET /outfit/community`**:与既有 `GET /outfit/:uuid` 在同一 Gin 路由树共存是安全的——项目内已有完全相同形态的先例(`/notification/unread` 与 `/notification/:uuid` 共存且线上运行正常),Gin 1.12 静态路由优先匹配。 2. **排除本人的公开穿搭**(SQL 加 `user_id != 当前用户`):贴合用户「查看其他用户公开的穿搭」的表述,且避免与「我的穿搭」分段内容重复;后续如需包含自己,仅需去掉一个 WHERE 条件。 3. **互动数据由后端批量聚合下发**(like_count / collect_count):现有穿搭列表在前端逐条请求 `/like/:uuid`(一页 20 条即 20 次请求),社区列表直接使用后端聚合值,杜绝该 N+1 问题。 4. **后端批量组装杜绝 N+1**:复用 `buildOutfitListResponses` 的批量模式(一次性批量查明细 + 衣物),作者信息用已有 `UserGetByIDs`,点赞/收藏计数新增 `GROUP BY target_id` 批量方法;单页固定约 7 条 SQL,与页大小无关。 5. **社区卡片复用现有拼图封面结构**(cover-item 循环)与卡片骨架,仅新增作者行与互动数据块,模板通过 `activeTab === 2` 条件渲染差异部分,避免大段重复。 6. **仅社区分段隐藏入口按钮行**,「我的穿搭 / 已收藏」保持现状,控制改动面。 ### 性能与可靠性 - 后端:分页 `page/page_size`(上限 50,`normalizePage` 规整),`ORDER BY id DESC` 走主键;批量计数对空列表短路,避免 `IN ()` 查询;错误统一返回现有错误码(19001 等),沿用 slog 日志风格。 - 前端:`page_size=20`、切换分段重置并丢弃过期响应(沿用现有 reqSeq 序号防乱序机制);辅助字段(相对时间)本地计算零请求;失败不打断主流程,仅记录 code 不打印敏感信息。 - 契约一致性:`created_at` 秒级时间戳(前端 `formatRelativeTime` 直接消费);`owner_avatar` 由后端 `upload.AvatarURL` 转为可访问 URL;`processed_image` 为完整 URL。 ### 实施注意事项 - 后端仓库位于 `/root/zt-closet-backend`(不在前端工作区),当前存在未提交改动(收藏/通知修复),新代码只做追加、不动无关文件。 - 后端改动需用户自行部署后,前端指向线上域名(BASE_URL)才能联调。 - 前端不改 `app.json`、不改 `outfit-detail`、不改 tabBar;`outfit.json` 已引入所需组件,无需调整。 - 复用 `isOutfitPublicVisible` 的可见性口径(公开 2 + 审核通过 2 + 状态正常 1),保证社区列表与详情页可见性判定一致。 ## 目录结构 后端 `/root/zt-closet-backend/`: ``` ├── internal/dto/outfit.go # [MODIFY] 新增社区列表 DTO:OutfitCommunityListRequest(内嵌 PageRequest)/ OutfitCommunityListResponse(内嵌 PageResponse)/ OutfitCommunityItem(含 outfit_uuid、outfit_name、outfit_desc、scene、source、created_at、items、owner_name、owner_avatar、like_count、collect_count) ├── internal/mysqldao/outfit.go # [MODIFY] 新增 OutfitListCommunity(ctx, excludeUserID, page, pageSize):查询 is_public=2 AND audit_status=2 AND status=1 AND user_id != 本人,Count + Order("id DESC") 分页,参照 OutfitListByUserID 写法 ├── internal/mysqldao/like.go # [MODIFY] 新增 LikeCountByTargets(ctx, likeType, targetIDs):SELECT target_id, COUNT(*) ... GROUP BY target_id(仅 status=1),空入参短路 ├── internal/mysqldao/collect.go # [MODIFY] 新增 CollectCountByTargets 批量计数(同上模式) ├── internal/service/outfit.go # [MODIFY] 新增 CommunityList:normalizePage → 查列表 → 批量组装(复用 OutfitItemListByOutfitIDs + ClothesGetByIDs + UserGetByIDs + upload.AvatarURL + 批量计数),空列表直接返回空数组 ├── internal/routers/api/v1/outfit.go # [MODIFY] 新增 Community handler(GetUserID → ShouldBindQuery → svc → ToResponse,与现有 List 写法一致) ├── internal/routers/router.go # [MODIFY] 鉴权组注册 auth.GET("/outfit/community", outfitHandler.Community) └── 接口文档.md # [MODIFY] 新增社区列表接口章节;修正 FAQ「没有社区广场接口」的过时说明 ``` 前端 `/root/zt-closet-wxmini-frontend/`: ``` └── miniprogram/ ├── types/models.ts # [MODIFY] 新增 CommunityOutfitItem 类型(与后端响应字段一一对应) └── pages/outfit/ ├── outfit.ts # [MODIFY] activeTab 扩展 0/1/2;fetchList 按分段选 /outfit、/collect、/outfit/community;社区项装饰(直接取 like_count/collect_count,计算相对时间 _time);onOutfitTap 社区项 owner=0;空态文案分支;日志沿用 logger ├── outfit.wxml # [MODIFY] 分段控件增加「社区」;entry-row 在社区分段隐藏;卡片新增作者行(社区分段)与收藏数、相对时间;状态标签仅非社区分段展示;空态文案分支 └── outfit.wxss # [MODIFY] 新增作者行、圆形头像(含 icon-user 占位)、昵称、时间、互动数据样式(延续现有 rpx 与色板) ``` ## 关键代码结构 后端响应契约(新增到 `internal/dto/outfit.go`): ``` type OutfitCommunityListResponse struct { PageResponse List []OutfitCommunityItem `json:"list"` } type OutfitCommunityItem struct { OutfitUUID string `json:"outfit_uuid"` OutfitName string `json:"outfit_name"` OutfitDesc string `json:"outfit_desc"` Scene string `json:"scene"` Source int8 `json:"source"` CreatedAt int64 `json:"created_at"` Items []OutfitItemResponse `json:"items"` OwnerName string `json:"owner_name"` OwnerAvatar string `json:"owner_avatar"` LikeCount int64 `json:"like_count"` CollectCount int64 `json:"collect_count"` } ``` 前端类型(新增到 `types/models.ts`): ```ts export interface CommunityOutfitItem { outfit_uuid: string outfit_name: string outfit_desc: string scene: string source: number created_at: number items: { clothes_uuid: string; processed_image: string }[] owner_name: string owner_avatar: string like_count: number collect_count: number } ``` ## 设计风格 延续项目既有 iOS HIG 视觉语言:分组灰底 #F2F2F7、纯白卡片(圆角 32rpx)、0.5px 发丝分隔线、系统蓝 #007AFF 强调色、SF Symbols 风格线性图标,清爽克制、以内容为中心,社区分段与现有分段无缝衔接。 ## 分段控件 胶囊分段由两项扩展为三项「我的穿搭 / 已收藏 / 社区」,三项等宽;选中项为白色滑块 + 系统蓝加粗文字,未选中为 #3C3C43;切换带轻量过渡,样式沿用现有 .segment 规格(高 64rpx、圆角 16rpx)。 ## 社区卡片 - 作者行(新增):左侧 56rpx 圆形头像(无头像回退浅灰底 icon-user 占位),昵称 26rpx 深色单行省略,右侧相对时间 22rpx 灰字 - 封面区:复用现有拼图 cover-item 错落摆放(最多 5 件)+ 右上角件数胶囊 - 信息区:穿搭名 32rpx 加粗单行省略,描述 26rpx 灰字最多两行 - 互动区(meta 行):左侧场景标签(tag-gray)与件数,右侧点赞(icon-heart)与收藏(icon-star)计数,图标 36rpx 灰字 22rpx,间距与现有卡片一致 ## 状态设计 加载态复用骨架屏(sk-card 呼吸动画);社区空态复用 empty-state(title「还没有公开的穿搭」,desc「把喜欢的穿搭公开到社区,让大家看到你的搭配」,无按钮);列表底部「没有更多了」提示沿用现有样式;卡片按压态用现有 active 透明度反馈。 ## Agent Extensions ### SubAgent - **code-explorer** - Purpose: 后端仓库(117 个 Go 文件)实现社区接口前,跨文件梳理 like/collect 数据模型、批量计数与批量组装模式、DTO/Handler 既有写法,确保新代码完全复用现有模式 - Expected outcome: 产出可复用的批量计数 SQL 模式与接口实现要点清单(表名、字段、既有工具函数位置),降低返工与不一致风险