first commit
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
---
|
||||
name: dashboard-enrich
|
||||
overview: 前后端联动丰富运营仪表盘:后端新增趋势统计接口并扩展 summary 的「内容审核 / VIP / 商品」维度,前端引入 ECharts 用折线图、环形图等呈现。
|
||||
design:
|
||||
architecture:
|
||||
framework: vue
|
||||
styleKeywords:
|
||||
- 现代清爽
|
||||
- 渐变卡片
|
||||
- 数据可视化
|
||||
- 环形图
|
||||
fontSystem:
|
||||
fontFamily: PingFang SC
|
||||
heading:
|
||||
size: 20px
|
||||
weight: 700
|
||||
subheading:
|
||||
size: 15px
|
||||
weight: 600
|
||||
body:
|
||||
size: 13px
|
||||
weight: 400
|
||||
colorSystem:
|
||||
primary:
|
||||
- "#4F6EF7"
|
||||
- "#6A5CF0"
|
||||
- "#8B5CF6"
|
||||
background:
|
||||
- "#F5F7FB"
|
||||
- "#FFFFFF"
|
||||
text:
|
||||
- "#1F2437"
|
||||
- "#5D6685"
|
||||
- "#9AA3C0"
|
||||
functional:
|
||||
- "#22C55E"
|
||||
- "#F59E0B"
|
||||
- "#EF4444"
|
||||
- "#06B6D4"
|
||||
todos:
|
||||
- id: extend-dashboard-dto
|
||||
content: 扩展后端仪表盘 DTO(Content/VIP/Goods/Trend)
|
||||
status: completed
|
||||
- id: extend-stats-dao
|
||||
content: StatsDao 新增内容/VIP/商品/趋势统计方法
|
||||
status: completed
|
||||
- id: extend-dashboard-service
|
||||
content: DashboardService 聚合新维度并实现 Trend 方法
|
||||
status: completed
|
||||
dependencies:
|
||||
- extend-dashboard-dto
|
||||
- extend-stats-dao
|
||||
- id: add-trend-route
|
||||
content: 新增 Trend handler 并注册 dashboard/trends 路由
|
||||
status: completed
|
||||
dependencies:
|
||||
- extend-dashboard-service
|
||||
- id: setup-echarts
|
||||
content: 安装 echarts 并封装 BaseChart 通用组件
|
||||
status: completed
|
||||
- id: extend-dashboard-api
|
||||
content: 扩展前端 dashboard 类型定义与 API 函数
|
||||
status: completed
|
||||
dependencies:
|
||||
- extend-dashboard-dto
|
||||
- id: rebuild-dashboard-view
|
||||
content: 重构仪表盘页渲染趋势折线图与三个分布环形图
|
||||
status: completed
|
||||
dependencies:
|
||||
- setup-echarts
|
||||
- extend-dashboard-api
|
||||
---
|
||||
|
||||
## 产品概述
|
||||
|
||||
在现有运营仪表盘基础上进行可视化升级:保留营收、用户、订单概况与待办横幅,新增时间趋势折线图和内容审核、VIP 会员、商品分类三个分布面板,让运营人员一眼掌握核心数据动态。
|
||||
|
||||
## 核心功能
|
||||
|
||||
- 近 7/30 天可切换的趋势折线图:新增用户数、已支付订单量、营收金额(双坐标轴)
|
||||
- 内容审核概况:穿搭审核状态(待审核/通过/不通过/封禁)、衣物图片处理状态(处理中/成功/失败)、衣柜分享状态(正常/取消/过期/封禁)分布
|
||||
- VIP 会员概况:有效会员类型分布(月度/年度/体验)及 7 天内即将到期会员数
|
||||
- 商品分类统计:各类型商品数量分布与上架/下架占比
|
||||
- 保留现有 summary 各指标与页面布局,新增内容向后兼容、不影响已上线展示
|
||||
|
||||
## 技术栈
|
||||
|
||||
- 后端:Go + Gin + GORM(复用现有 StatsDao 直接聚合 MySQL 业务表,不改表结构)
|
||||
- 前端:Vue 3 + TypeScript + Element Plus + ECharts(新增 echarts 依赖)
|
||||
- 图表:ECharts 折线图 + 环形饼图
|
||||
|
||||
## 实现方案
|
||||
|
||||
后端复用现有 `DashboardService` 与 `StatsDao` 模式,将 `GET /dashboard/summary` 的响应扩展出 `content`、`vip`、`goods` 三个子结构(一次请求拿到全部概况),另新增 `GET /dashboard/trends?days=7|30` 返回按天对齐的趋势序列(无数据日期补零)。前端引入 ECharts,封装通用 `BaseChart.vue` 管理实例生命周期,在 DashboardView 内组合趋势折线图与三个环形分布图。
|
||||
|
||||
关键决策:
|
||||
|
||||
- **趋势单独一个接口**:summary 保持轻量只做概况,趋势数据按需加载并支持 7/30 天切换,避免每次都聚合全量日期序列。
|
||||
- **按天补零在 Go 端完成**:SQL 只 group by 有数据的日期,Go 端从起始日期循环补零,保证折线图 x 轴连续、时区正确。
|
||||
- **双 y 轴**:营收(元)与数量量纲不同,趋势图用左轴数量、右轴金额。
|
||||
- **向后兼容**:DashboardResponse 只增字段不改字段,前端旧字段继续渲染,新字段独立面板。
|
||||
|
||||
数据口径:
|
||||
|
||||
- 趋势新增用户:`users.created_at` 按日计数;订单量:`orders.order_status IN (2,5)` 且按 `pay_time` 计数(与营收同口径);营收:`orders.pay_amount` 求和(分)。
|
||||
- VIP 有效会员:`vip_type IN (2,3,4) AND vip_expire > now`;即将到期:`vip_expire BETWEEN now AND now+7*86400`。
|
||||
- 内容审核:穿搭按 `audit_status` 与 `status=2` 封禁数;衣物按 `image_process_status` 分组(1/2/3 归为处理中、4 成功、5 失败);分享按 `status` 分组。
|
||||
|
||||
## 架构设计
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Frontend
|
||||
DV[DashboardView.vue] --> BC[BaseChart.vue]
|
||||
DV --> API[api/dashboard.ts]
|
||||
DV --> TYPES[types/dashboard.ts]
|
||||
end
|
||||
subgraph Backend
|
||||
H[SummaryHandler / TrendHandler] --> S[DashboardService]
|
||||
S --> DAO[StatsDao]
|
||||
DAO --> M[(MySQL 业务表)]
|
||||
end
|
||||
API -->|GET /dashboard/summary| H
|
||||
API -->|GET /dashboard/trends| H
|
||||
```
|
||||
|
||||
## 关键数据结构(后端 dto/dashboard.go 扩展)
|
||||
|
||||
```
|
||||
type DashboardResponse struct {
|
||||
Users DashboardUsers `json:"users"`
|
||||
Orders DashboardOrders `json:"orders"`
|
||||
Revenue DashboardRevenue `json:"revenue"`
|
||||
Todo DashboardTodo `json:"todo"`
|
||||
Content DashboardContent `json:"content"`
|
||||
VIP DashboardVIP `json:"vip"`
|
||||
Goods DashboardGoods `json:"goods"`
|
||||
}
|
||||
|
||||
type DashboardContent struct {
|
||||
Outfit DashboardOutfitStats `json:"outfit"`
|
||||
Clothes DashboardClothesStats `json:"clothes"`
|
||||
Share DashboardShareStats `json:"share"`
|
||||
}
|
||||
|
||||
type DashboardVIP struct {
|
||||
MemberTotal int64 `json:"member_total"`
|
||||
Monthly int64 `json:"monthly"`
|
||||
Yearly int64 `json:"yearly"`
|
||||
Trial int64 `json:"trial"`
|
||||
ExpiringSoon int64 `json:"expiring_soon"`
|
||||
}
|
||||
|
||||
type DashboardGoods struct {
|
||||
Total int64 `json:"total"`
|
||||
OnShelf int64 `json:"on_shelf"`
|
||||
OffShelf int64 `json:"off_shelf"`
|
||||
ByType []GoodsTypeCount `json:"by_type"`
|
||||
}
|
||||
|
||||
type TrendPoint struct {
|
||||
Date string `json:"date"` // YYYY-MM-DD
|
||||
NewUsers int64 `json:"new_users"`
|
||||
Orders int64 `json:"orders"`
|
||||
Revenue int64 `json:"revenue"` // 分
|
||||
}
|
||||
```
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
后端 /root/zt-closet-manage-backend/
|
||||
├── internal/dto/dashboard.go # [MODIFY] 扩展 Content/VIP/Goods 结构与 Trend 请求/响应结构
|
||||
├── internal/mysqldao/stats.go # [MODIFY] 新增 ContentStatsGet/VIPStatsGet/GoodsStatsGet/TrendStatsGet
|
||||
├── internal/service/dashboard.go # [MODIFY] Summary 聚合新维度,新增 Trend 方法
|
||||
├── internal/routers/api/v1/dashboard.go # [MODIFY] 新增 Trend handler
|
||||
└── internal/routers/router.go # [MODIFY] 注册 GET /dashboard/trends
|
||||
|
||||
前端 /root/zt-closet-manage-frontend/
|
||||
├── package.json # [MODIFY] 新增 echarts 依赖
|
||||
├── src/components/BaseChart.vue # [NEW] ECharts 通用封装(init/resize/dispose/option 更新)
|
||||
├── src/types/dashboard.ts # [MODIFY] 新增 Content/VIP/Goods/Trend 类型
|
||||
├── src/api/dashboard.ts # [MODIFY] 新增 getDashboardTrends
|
||||
└── src/views/dashboard/DashboardView.vue # [MODIFY] 趋势折线图 + 内容/VIP/商品三个环形图面板
|
||||
```
|
||||
|
||||
## 实现要点
|
||||
|
||||
- DAO 趋势 SQL 使用 `DATE(FROM_UNIXTIME(created_at))` / `DATE(FROM_UNIXTIME(pay_time))` 分组,参数为秒级时间戳;返回 map[date]value 后由 service 按起始日期补零。
|
||||
- StatsDao 各新方法保持 `func (d *StatsDao) Xxx(ctx context.Context, ...) (*XxxStats, error)` 风格,`WithContext` + `span.RecordError`,与现有 stats.go 一致。
|
||||
- `TrendStatsGet` 用一次循环构造 `[]TrendPoint`,避免 N+1;`days` 参数在 handler 用 `ShouldBindQuery` 校验并限制 1~90。
|
||||
- 前端 `BaseChart.vue` 使用 `echarts.init`,`watch` option 深度更新,`ResizeObserver` 监听容器尺寸,`onBeforeUnmount` 时 `dispose` 防泄漏。
|
||||
- 趋势图折线:新增用户/订单量挂左轴,营收挂右轴(`formatMoney` 转元显示);分布图用 pie 环形,复用现有 `enumLabel` 常量映射文案。
|
||||
- 不改动已有接口字段,保证 summary 旧展示路径不回归;仅在前端 `DashboardSummary` 类型上新增可选字段并渲染新面板。
|
||||
|
||||
## 设计风格
|
||||
|
||||
延续现有管理后台的现代清爽风格,在现有渐变 hero 横幅、白底圆角卡片基础上做可视化升级,强调信息密度与层次感。趋势图采用通栏大卡片,内容审核、VIP 会员、商品分类采用三列卡片布局,图表配色与页面既有渐变色系统一。
|
||||
|
||||
## 页面布局
|
||||
|
||||
- 顶部保留渐变欢迎横幅与待办数字
|
||||
- 营收四卡片行保持不变
|
||||
- 新增通栏「数据趋势」卡片:7 天 / 30 天分段切换 + 双轴折线图
|
||||
- 新增三列分布卡片:内容审核概况、VIP 会员概况、商品分类统计,各配环形图与关键数字
|
||||
- 保留原有用户概况、订单概况面板
|
||||
|
||||
## 交互
|
||||
|
||||
- 趋势图切换 7/30 天时局部刷新,不整页加载
|
||||
- 环形图悬停显示数量与占比 tooltip
|
||||
- 卡片沿用 hover 上浮阴影,保持与现有 stat-card 一致的微动效
|
||||
@@ -0,0 +1,130 @@
|
||||
---
|
||||
name: dashboard-usage-interaction
|
||||
overview: 在运营仪表盘新增「今日消耗、COS 图数、点赞/收藏」指标:后端扩展 summary 聚合接口,前端新增展示卡片。
|
||||
todos:
|
||||
- id: add-social-models
|
||||
content: 在 business.go 中新增 Like、Collect 表模型镜像
|
||||
status: completed
|
||||
- id: extend-dto-dao
|
||||
content: 扩展 dashboard DTO 并新增 UsageStatsGet/SocialStatsGet 聚合方法
|
||||
status: completed
|
||||
dependencies:
|
||||
- add-social-models
|
||||
- id: aggregate-summary
|
||||
content: 在 DashboardService.Summary 中聚合 usage 与 social 维度
|
||||
status: completed
|
||||
dependencies:
|
||||
- extend-dto-dao
|
||||
- id: extend-frontend-types
|
||||
content: 扩展前端 dashboard 类型定义,新增 usage/social 字段
|
||||
status: completed
|
||||
dependencies:
|
||||
- extend-dto-dao
|
||||
- id: render-dashboard-metrics
|
||||
content: 在仪表盘页新增「今日消耗」「内容互动」两列指标卡片
|
||||
status: completed
|
||||
dependencies:
|
||||
- extend-frontend-types
|
||||
---
|
||||
|
||||
## 产品概述
|
||||
|
||||
在现有运营仪表盘基础上,新增「今日消耗」与「内容互动」两组共 6 个运营指标,让运营人员直观掌握每日额度消耗与社区内容互动数据。
|
||||
|
||||
## 核心功能
|
||||
|
||||
- 今日 AI 推荐消耗次数
|
||||
- 今日衣柜分享消耗次数
|
||||
- 今日社区分享消耗次数
|
||||
- COS 总图数
|
||||
- 点赞数
|
||||
- 收藏数
|
||||
|
||||
## 展示说明
|
||||
|
||||
- 在营收卡片下方、数据趋势面板上方,新增一行两列卡片:「今日消耗」展示 AI 推荐/衣柜分享/社区分享三项今日次数,「内容互动」展示 COS 图数/点赞数/收藏数三项累计值。
|
||||
- 沿用现有 stat-card / mini-stat 卡片风格与图标配色,保持页面整体视觉一致。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- 后端:Go + Gin + GORM,复用现有 `StatsDao` / `DashboardService`,不改表结构。
|
||||
- 前端:Vue 3 + TypeScript + Element Plus,复用现有卡片样式,无需新增依赖。
|
||||
|
||||
## 实现方案
|
||||
|
||||
扩展现有 `GET /dashboard/summary` 响应的 `DashboardResponse`,新增 `usage`(今日消耗)与 `social`(互动+COS)两个子结构,一次请求返回全部新增指标;前端在 `DashboardView.vue` 中新增一行两列卡片渲染。
|
||||
|
||||
数据口径(已通过业务服务源码确认):
|
||||
|
||||
| 指标 | 数据来源 | 统计口径 |
|
||||
| --- | --- | --- |
|
||||
| 今日 AI 推荐消耗次数 | `user_daily_usages` | `stat_date = 今日(YYYYMMDD)`,`SUM(ai_recommend_count)` |
|
||||
| 今日衣柜分享消耗次数 | `user_daily_usages` | `stat_date = 今日(YYYYMMDD)`,`SUM(closet_share_count)` |
|
||||
| 今日社区分享消耗次数 | `user_daily_usages` | `stat_date = 今日(YYYYMMDD)`,`SUM(outfit_share_count)` |
|
||||
| COS 总图数 | `clothes` | `processed_image != ''` 的记录数 |
|
||||
| 点赞数 | `likes` | `status = 1`(已点赞)计数 |
|
||||
| 收藏数 | `collects` | `status = 1`(已收藏)计数 |
|
||||
|
||||
|
||||
关键决策:
|
||||
|
||||
- **今日消耗合并为一次查询**:`user_daily_usages` 一行同时含三项计数字段,直接 `SUM` 三列并 `WHERE stat_date = ?`,避免三次查询。
|
||||
- **COS 总图数口径**:`clothes.processed_image` 是上传 COS 成功后回填的对象键路径,非空即代表已存图。
|
||||
- **点赞/收藏按累计总数**:用户未加「今日」前缀,与「今日消耗」形成区分,取 `status=1` 全量计数。
|
||||
- **Like/Collect 模型镜像补齐**:管理后台 `business.go` 当前缺少这两个表模型,按业务服务字段镜像新增,软删除由 `Model.DeletedAt` 自动过滤。
|
||||
|
||||
## 架构设计
|
||||
|
||||
沿用现有分层:`DashboardHandler.Summary → DashboardService.Summary → StatsDao → MySQL`。本次仅扩展 summary 聚合链路,不新增路由与 handler。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
DV[DashboardView.vue] --> API[api/dashboard.ts]
|
||||
API --> H[SummaryHandler]
|
||||
H --> S[DashboardService.Summary]
|
||||
S --> DAO[StatsDao]
|
||||
DAO --> M[(MySQL 业务表)]
|
||||
```
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
后端 /root/zt-closet-manage-backend/
|
||||
├── internal/mysqlmodel/business.go # [MODIFY] 新增 Like、Collect 表模型镜像
|
||||
├── internal/dto/dashboard.go # [MODIFY] 新增 DashboardUsage/DashboardSocial 及 DashboardResponse 字段
|
||||
├── internal/mysqldao/stats.go # [MODIFY] 新增 UsageStatsGet/SocialStatsGet
|
||||
└── internal/service/dashboard.go # [MODIFY] Summary 聚合 usage/social 维度
|
||||
|
||||
前端 /root/zt-closet-manage-frontend/
|
||||
├── src/types/dashboard.ts # [MODIFY] 新增 DashboardUsage/DashboardSocial 类型及 DashboardSummary 字段
|
||||
└── src/views/dashboard/DashboardView.vue # [MODIFY] 新增「今日消耗」「内容互动」两列卡片
|
||||
```
|
||||
|
||||
## 关键代码结构
|
||||
|
||||
```
|
||||
// dto/dashboard.go
|
||||
type DashboardUsage struct {
|
||||
AIRecommend int64 `json:"ai_recommend"` // 今日AI推荐消耗次数
|
||||
ClosetShare int64 `json:"closet_share"` // 今日衣柜分享消耗次数
|
||||
OutfitShare int64 `json:"outfit_share"` // 今日社区分享消耗次数
|
||||
}
|
||||
|
||||
type DashboardSocial struct {
|
||||
CosImageCount int64 `json:"cos_image_count"` // COS总图数
|
||||
LikeCount int64 `json:"like_count"` // 点赞数
|
||||
CollectCount int64 `json:"collect_count"` // 收藏数
|
||||
}
|
||||
|
||||
// mysqldao/stats.go
|
||||
func (d *StatsDao) UsageStatsGet(ctx context.Context, statDate int64) (*UsageStats, error)
|
||||
func (d *StatsDao) SocialStatsGet(ctx context.Context) (*SocialStats, error)
|
||||
```
|
||||
|
||||
## 实现要点
|
||||
|
||||
- `UsageStatsGet` 用单条 `Select("COALESCE(SUM(ai_recommend_count),0), COALESCE(SUM(closet_share_count),0), COALESCE(SUM(outfit_share_count),0)")` 配合 `Where("stat_date = ?", statDate)`,`Scan` 到三个字段。
|
||||
- `SocialStatsGet` 三条独立计数:`clothes.processed_image <> ''`、`likes.status = 1`、`collects.status = 1`,各自带 `span.RecordError` 与现有 DAO 风格一致。
|
||||
- `Summary` 内使用已存在的 `todayStatDate()`(service/service.go)计算今日 YYYYMMDD 整数,传给 `UsageStatsGet`。
|
||||
- 前端新增字段为可选读取(`summary?.usage` / `summary?.social`),旧后端响应未升级时不会报错;采用 6 个 `mini-stat` 数字块,分组放入两个 `panel`。
|
||||
- 不改动已有接口字段与展示路径,保证向后兼容。
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
name: 前后端逻辑对齐
|
||||
overview: 以后端最新代码为准,修复前端与后端不一致的地方,重点解决用户额度调整语义错配、额度凭证(grants)功能缺失、商品与订单字段缺失等问题。
|
||||
todos:
|
||||
- id: align-user-types-api
|
||||
content: 对齐用户额度/凭证类型并新增 revokeGrant 回收 API
|
||||
status: completed
|
||||
- id: update-user-view
|
||||
content: 更新用户页额度发放表单与详情凭证列表回收
|
||||
status: completed
|
||||
dependencies:
|
||||
- align-user-types-api
|
||||
- id: align-goods-types-view
|
||||
content: 补齐商品 valid_days 等字段并更新商品表单与展示
|
||||
status: completed
|
||||
- id: align-order-types-view
|
||||
content: 补齐订单虚拟支付字段、枚举并更新订单详情展示
|
||||
status: completed
|
||||
---
|
||||
|
||||
## 用户需求
|
||||
|
||||
后端管理代码有更新,需全量核对后端(/root/zt-closet-manage-backend)与前端(/root/zt-closet-manage-frontend)的逻辑一致性。以后端最新代码为基准,前端做适配,确保接口字段、请求语义、枚举映射完全对齐。
|
||||
|
||||
## 核对结论与变更点
|
||||
|
||||
经全量对比,登录鉴权、仪表盘、内容审核、通知、退款、响应结构与错误码均已一致,无需改动。存在以下 5 处不一致需要修复:
|
||||
|
||||
1. **用户额度调整语义错配(功能不可用)**:后端已改为「发放额度凭证」语义,请求体为 `quota_type + amount + expire_days`;前端仍使用旧的 `quota_type + new_total + reason`,导致提交必失败。
|
||||
2. **用户额度凭证(grants)功能缺失**:后端用户详情已返回 `grants` 列表,并提供 `DELETE /users/:uuid/grants/:id` 回收接口;前端类型、API、页面展示与回收操作均缺失。
|
||||
3. **商品字段缺失**:后端商品新增 `valid_days`(有效期天数,可编辑)及只读的 `product_id`、`product_status`(虚拟支付道具);前端类型、表单与列表展示均未覆盖。
|
||||
4. **订单字段缺失**:后端订单新增 `xpay_product_id`、`xpay_platform`、`xpay_env`(虚拟支付信息);前端类型与详情展示均未覆盖。
|
||||
5. **支付渠道枚举缺失**:后端支付渠道为「1=微信支付、2=微信虚拟支付」;前端 `PAY_CHANNEL` 仅定义 value=1,虚拟支付订单会显示为 `-`。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- 前端:Vue 3 + TypeScript + Element Plus + Pinia + axios(沿用现有架构,不引入新依赖)
|
||||
- 后端:Go + Gin(只读参考,不修改)
|
||||
|
||||
## 实现方案
|
||||
|
||||
以后端 `internal/dto/*.go` 的 JSON tag 与路由为唯一基准,逐项对齐前端 `src/types`、`src/api`、`src/utils/constants` 与对应 `src/views` 页面。
|
||||
|
||||
### 关键决策
|
||||
|
||||
- **额度调整语义重构**:将前端 `UserQuotaAdjustRequest` 改为 `{ quota_type, amount, expire_days }`,移除 `new_total`、`reason`。页面表单同步改为「发放数量 + 有效期天数」,提交 `amount`(1~100000000)与 `expire_days`(1~3650)。这是当前最严重的不一致,修复后功能才可用。
|
||||
- **补齐凭证管理闭环**:`UserDetailResponse` 增加 `grants: UserGrantItem[]`;`api/user.ts` 新增 `revokeGrant` 调用 `DELETE /users/:uuid/grants/:id`;详情抽屉展示凭证列表(类型/来源/数量/已用/到期/关联 ID)并增加「回收」操作,回收成功后重新拉取详情刷新。
|
||||
- **只读字段与可编辑字段分离**:商品 `product_id`、`product_status` 为只读,仅展示、不参与创建/更新提交;`valid_days` 可编辑,加入表单回显与提交。商品更新接口为「传什么改什么」的指针语义,前端编辑时仅提交可编辑字段。
|
||||
- **枚举与展示对齐**:`constants.ts` 新增 `GRANT_SOURCE`(1=会员包、2=加量包、3=广告激励),`PAY_CHANNEL` 补充 value=2(微信虚拟支付)。
|
||||
- **单位约定不变**:价格仍为「分」,前端继续用 `yuanToCents`/`formatMoney` 换算,避免引入新的金额逻辑。
|
||||
|
||||
### 实现注意事项
|
||||
|
||||
- 所有字段命名严格遵循后端 JSON tag(snake_case),禁止自创字段名。
|
||||
- 复用现有 `httpGet/httpPost/httpPut/httpDelete` 与 `enumLabel/enumTag/enumOptions/formatDate/formatText` 工具,不新增请求层逻辑。
|
||||
- 回收凭证后需重新调用 `getUserDetail` 刷新详情,避免展示陈旧数据。
|
||||
- 保持现有页面结构与组件风格,仅做字段级调整,不做无关重构。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
zt-closet-manage-frontend/
|
||||
├── src/
|
||||
│ ├── types/
|
||||
│ │ ├── user.ts # [MODIFY] UserQuotaAdjustRequest 改为 amount+expire_days;新增 UserGrantItem;UserDetailResponse 增加 grants
|
||||
│ │ ├── goods.ts # [MODIFY] GoodsItem 增加 valid_days/product_id/product_status;GoodsCreateRequest、GoodsUpdateRequest 增加 valid_days
|
||||
│ │ └── order.ts # [MODIFY] OrderItem 增加 xpay_product_id/xpay_platform/xpay_env
|
||||
│ ├── api/
|
||||
│ │ └── user.ts # [MODIFY] 新增 revokeGrant(uuid, grantId) 调用 DELETE /users/:uuid/grants/:id
|
||||
│ ├── utils/
|
||||
│ │ └── constants.ts # [MODIFY] 新增 GRANT_SOURCE 枚举;PAY_CHANNEL 补充 value=2
|
||||
│ └── views/
|
||||
│ ├── user/
|
||||
│ │ └── UserListView.vue # [MODIFY] 额度表单改为发放数量+有效期天数;详情抽屉展示 grants 并增加回收操作
|
||||
│ ├── goods/
|
||||
│ │ └── GoodsListView.vue # [MODIFY] 表单增加 valid_days;列表/详情展示 product_id、product_status
|
||||
│ └── order/
|
||||
│ └── OrderListView.vue # [MODIFY] 订单详情抽屉展示虚拟支付字段
|
||||
```
|
||||
|
||||
## 关键代码结构
|
||||
|
||||
用户域核心类型(需与后端 dto/user.go 严格对齐):
|
||||
|
||||
```ts
|
||||
// 额度发放请求(后端 UserQuotaAdjustRequest)
|
||||
export interface UserQuotaAdjustRequest {
|
||||
quota_type: number // 1=上传 2=AI 3=社区分享 4=衣柜分享
|
||||
amount: number // 发放数量,min=1
|
||||
expire_days: number // 有效期天数,min=1
|
||||
}
|
||||
|
||||
// 用户额度凭证(后端 UserGrantItem)
|
||||
export interface UserGrantItem {
|
||||
id: number
|
||||
quota_type: number
|
||||
amount: number
|
||||
used: number
|
||||
source: number // 1=会员包 2=加量包 3=广告激励
|
||||
expire_time: number // 秒,0=跟随 VIP
|
||||
ref_id: string
|
||||
created_at: number
|
||||
}
|
||||
|
||||
// 用户详情(后端 UserDetailResponse)
|
||||
export interface UserDetailResponse extends UserListItem {
|
||||
quotas: QuotaItem[]
|
||||
grants: UserGrantItem[]
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,142 @@
|
||||
---
|
||||
name: 智能衣橱管理后台前端
|
||||
overview: 基于 Vue3 + TypeScript + Vite 从零搭建「智能衣橱」管理后台前端,对接 Go 后端 /api/v1 全部接口,覆盖登录鉴权、仪表盘、用户/商品/订单退款/内容审核/通知/管理员管理 8 大模块。
|
||||
design:
|
||||
architecture:
|
||||
framework: react
|
||||
component: mui
|
||||
---
|
||||
|
||||
## 产品概述
|
||||
|
||||
基于 zt-closet-manage-backend 后端 API 契约,从零构建「智能衣橱」管理后台前端系统(Vue 3 + TypeScript + Element Plus)。系统覆盖管理员登录鉴权(JWT 双 token 自动刷新)、仪表盘统计、用户管理、商品管理、订单与退款审核、内容审核(穿搭/衣物/衣柜分享)、通知推送,以及超级管理员专属的管理员账号管理,共 9 大功能模块。
|
||||
|
||||
## 核心功能
|
||||
|
||||
- **登录与鉴权**:账号密码登录,access/refresh 双 token 管理;access 过期自动用 refresh 换新并重放原请求;登出、修改密码;按 role 区分超管/普通管理员菜单与路由权限
|
||||
- **仪表盘**:统计卡片展示用户总数/今日新增/状态分布、订单状态分布、累计与今日营收/累计退款、待办事项(待处理退款、待审核穿搭、上架商品数),金额以元展示
|
||||
- **用户管理**:分页搜索(昵称/手机号/OpenID/状态)、详情查看(含四类额度)、封禁/解封(临时按天/永久)、额度人工调整、VIP 人工调整,均带原因填写与二次确认
|
||||
- **商品管理**:分页筛选(类型/状态/关键字)、新增、编辑、上下架、软删除;商品类型含会员包/上传包/AI 包/衣柜分享包/搭配分享包,金额按分输入、按元展示
|
||||
- **订单与退款**:订单列表筛选/详情抽屉;退款单列表(状态 0-3 可显式筛选待处理)、审核通过/驳回(必填备注原因)
|
||||
- **内容审核**:穿搭列表筛选/详情(含衣物明细)、审核通过/驳回、封禁/解封;衣物列表/详情;衣柜分享列表/封禁/解封
|
||||
- **通知推送**:单用户系统通知、全量广播(返回发送人数),表单带字数限制校验
|
||||
- **管理员管理(超管专属)**:管理员列表、创建、编辑(昵称/密码/角色/状态)、删除;普通管理员无权限(后端 403,前端同时隐藏入口)
|
||||
- **统一交互规范**:所有业务错误以 body.code 判断并提示;金额统一 `分→元` 格式化;时间戳统一格式化展示;列表统一分页(page/page_size 1~50)
|
||||
|
||||
## 视觉要求
|
||||
|
||||
深色侧边栏 + 浅色内容区的经典管理后台布局;侧边栏按业务域分组菜单并支持折叠;顶部栏含面包屑、管理员信息、修改密码、登出;状态使用彩色 Tag 标签、操作按钮使用文字链接风格;表格斑马纹、hover 高亮;统计卡片带渐变图标底;弹窗表单统一 Element Plus 风格,操作均带确认与结果反馈。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **框架**:Vue 3.5(`<script setup lang="ts">` 组合式 API)+ TypeScript 6.0
|
||||
- **UI 组件库**:Element Plus(表格/表单/分页/对话框/抽屉/消息提示全覆盖)
|
||||
- **路由**:vue-router 4(含全局前置守卫鉴权与角色权限)
|
||||
- **状态管理**:Pinia(auth store 持久化 token/admin_info)
|
||||
- **HTTP**:axios 实例封装(请求拦截注入 Bearer、响应拦截统一 code 处理、11002 自动刷新重放、防并发刷新)
|
||||
- **开发代理**:Vite `server.proxy` 将 `/api` 转发 `http://localhost:8080`,规避 CORS
|
||||
- **格式化**:手写 `formatMoney`(分→元)、`formatDate`(Unix 秒→字符串)工具函数,不引入额外依赖
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 总体策略
|
||||
|
||||
按「基础骨架 → API 契约层 → 布局与鉴权 → 业务模块页」四阶段推进。所有 API 类型严格对齐后端 DTO 的 json tag;枚举以常量映射表集中管理(数值→文案→Tag 颜色);复用一套「筛选表单 + 表格 + 分页」的页面组织模式,保证 9 个列表页风格与交互一致。
|
||||
|
||||
### 关键技术决策
|
||||
|
||||
- **axios 拦截器设计**:响应 code 非 0 时统一 ElMessage 报错并 reject;HTTP 401 且 code=11002 时,以单例 Promise 防并发调用 refresh 接口(携带旧 access + refresh),成功后更新 token 并重放原请求队列;刷新失败清空会话跳转登录页。其余 401/403/500 直接提示并跳转/返回。
|
||||
- **路由权限**:静态定义全部路由,meta 标记 `requiresAuth`/`superOnly`;守卫中校验 token 与 admin_info.role,超管专属路由对普通管理员重定向到仪表盘并提示无权限。
|
||||
- **金额与时间**:所有金额字段后端为分(int64),输入框以「元」为单位编辑、提交时乘 100 取整,列表用 `formatMoney` 展示;时间戳为 Unix 秒,统一 `formatDate` 输出 `YYYY-MM-DD HH:mm:ss`,0/空值显示「—」。
|
||||
- **枚举映射**:constants.ts 集中维护 status/vip/quota/goods/order/refund/outfit/share/image 等全部枚举的 `{value,label,tagType}` 映射表,组件通过辅助函数取文案与颜色,避免散落魔法数字。
|
||||
- **页面组织模式**:列表页统一「筛选卡片区 + 表格卡片区 + 分页」三段式;写操作(封禁/调整额度/审核等)统一对话框表单 + 提交确认 + 成功后刷新当前页。
|
||||
- **性能**:列表接口后端已分页(每页最多 50 条),前端不做全量拉取;表格列数多时用固定关键列 + 详情抽屉承载次要信息;组件按路由懒加载切分 chunk。
|
||||
|
||||
## 实施细节
|
||||
|
||||
- **依赖安装**:`npm i element-plus vue-router@4 pinia axios` 及 `@element-plus/icons-vue`;main.ts 全量引入 Element Plus(管理后台规模,按需引入收益有限)并注册图标。
|
||||
- **主色定制**:通过 Element Plus CSS 变量(`--el-color-primary` 系列)覆盖为靛蓝色系,与深色侧边栏协调。
|
||||
- **退款状态筛选注意**:`/refunds?status=0` 须显式传 0(待处理),前端下拉用「-1=全部」占位转换,防止丢失。
|
||||
- **refresh-token 注意**:body 必须同时携带 `refresh_token` 与旧 `access_token`(后端强制校验)。
|
||||
- **noUncheckedIndexedAccess 已开启**:数组/对象索引访问需判空处理。
|
||||
- **业务错误提示**:后端多数业务错误 HTTP 仍为 200,必须读取 body.code 分支提示 msg;避免仅按 HTTP 状态判断。
|
||||
- **安全**:token 仅存 localStorage(本场景无 SSR);密码字段不落日志;请求日志不打印 Authorization。
|
||||
- **开发联调**:后端未启动时前端可正常渲染空态;启动后端(`go run .`,默认 8080)后经 Vite 代理联调。
|
||||
|
||||
## 架构设计
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph 前端
|
||||
A[Login 登录页] --> B[AdminLayout 主布局]
|
||||
B --> C[Router 路由 + 守卫]
|
||||
C --> D[业务页面 views]
|
||||
D --> E[api 模块层]
|
||||
E --> F[axios 实例 + 拦截器]
|
||||
F --> G[Pinia auth store]
|
||||
end
|
||||
F -->|/api 经 Vite proxy| H[Go 后端 :8080/api/v1]
|
||||
```
|
||||
|
||||
- **分层**:views(页面)→ api(接口封装,纯函数)→ request(axios 拦截器)→ store(token/管理员信息);类型集中在 types/,枚举与格式化工具在 utils/,与后端契约一一对应。
|
||||
- **数据流**:用户操作 → 页面调用 api → axios 注入 token 发请求 → 响应拦截统一处理 code/401 → 页面更新表格数据/提示结果 → auth store 变更触发路由守卫响应。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
zt-closet-manage-frontend/
|
||||
├── package.json # [MODIFY] 新增 element-plus/vue-router/pinia/axios/@element-plus/icons-vue 依赖
|
||||
├── vite.config.ts # [MODIFY] 增加 server.proxy:/api → http://localhost:8080
|
||||
├── index.html # [MODIFY] 标题改为「智能衣橱管理后台」
|
||||
├── src/
|
||||
│ ├── main.ts # [MODIFY] 挂载 router/pinia、全量引入 Element Plus 与图标、引入全局样式
|
||||
│ ├── App.vue # [MODIFY] 替换为 <router-view/> 根组件
|
||||
│ ├── assets/
|
||||
│ │ ├── main.css # [MODIFY] 全局样式:布局背景、卡片圆角阴影、滚动条、通用工具类
|
||||
│ │ └── element-theme.css # [NEW] Element Plus 主色变量覆盖(靛蓝系)+ 深色侧边栏配色变量
|
||||
│ ├── router/index.ts # [NEW] 路由表 + 全局守卫(登录态校验、superOnly 角色校验、页面标题)
|
||||
│ ├── stores/auth.ts # [NEW] Pinia auth store:token/refreshToken/adminInfo 持久化、login/logout/refresh 动作
|
||||
│ ├── utils/
|
||||
│ │ ├── request.ts # [NEW] axios 实例:注入 Bearer、code≠0 统一报错、11002 单例刷新+请求队列重放、401/403/500 处理
|
||||
│ │ ├── format.ts # [NEW] formatMoney(分→元)、formatDate(秒→YYYY-MM-DD HH:mm:ss)、formatDateTimeRange 等
|
||||
│ │ └── constants.ts # [NEW] 全量枚举映射:用户状态/VIP/额度/商品/订单/退款/穿搭/分享/图片处理/管理员角色等 → {label,tagType}
|
||||
│ ├── types/
|
||||
│ │ ├── common.ts # [NEW] ApiResponse{code,msg,data,details}、PageQuery、PageResult{total,page,page_size,list} 泛型
|
||||
│ │ ├── auth.ts # [NEW] AdminInfo、AdminLoginResponse、RefreshTokenResponse、改密请求类型
|
||||
│ │ ├── user.ts # [NEW] UserListItem/UserDetailResponse/QuotaItem/封禁/额度/VIP 调整请求类型
|
||||
│ │ ├── goods.ts # [NEW] GoodsItem/GoodsCreateRequest/GoodsUpdateRequest/状态请求类型
|
||||
│ │ ├── order.ts # [NEW] OrderItem/RefundItem/审核请求类型
|
||||
│ │ ├── content.ts # [NEW] OutfitListItem/OutfitDetailResponse/OutfitClothesItem/ClothesItem/ClosetShareItem 及审核/封禁请求
|
||||
│ │ ├── notification.ts # [NEW] 单发/广播请求与响应类型
|
||||
│ │ └── dashboard.ts # [NEW] 仪表盘聚合响应嵌套结构类型
|
||||
│ ├── api/
|
||||
│ │ ├── auth.ts # [NEW] login/refreshToken/logout/changePassword
|
||||
│ │ ├── dashboard.ts # [NEW] getSummary
|
||||
│ │ ├── user.ts # [NEW] 列表/详情/封禁/解封/额度调整/VIP 调整
|
||||
│ │ ├── goods.ts # [NEW] 列表/创建/更新/上下架/软删
|
||||
│ │ ├── order.ts # [NEW] 订单列表/详情、退款列表/通过/驳回
|
||||
│ │ ├── content.ts # [NEW] 穿搭列表/详情/审核/封禁解封、衣物列表/详情、分享列表/封禁解封
|
||||
│ │ ├── notification.ts # [NEW] 单发/广播
|
||||
│ │ └── admin.ts # [NEW] 管理员列表/创建/更新/删除
|
||||
│ ├── components/
|
||||
│ │ ├── PaginationBar.vue # [NEW] 通用分页条:v-model 页码/每页条数,联动 emit 刷新,封装 total 显示
|
||||
│ │ └── SearchFormCard.vue # [NEW] 通用筛选卡片:插槽承载筛选控件,查询/重置按钮触发 emit
|
||||
│ ├── layout/
|
||||
│ │ ├── AdminLayout.vue # [NEW] 整体布局:深色可折叠侧边栏(分组菜单、按 role 过滤超管菜单)+ 顶栏 + 内容区
|
||||
│ │ ├── SidebarMenu.vue # [NEW] 侧边栏菜单组件:递归渲染分组路由、折叠态图标提示、active 高亮
|
||||
│ │ └── HeaderBar.vue # [NEW] 顶栏:面包屑、管理员昵称下拉(修改密码对话框/登出)
|
||||
│ └── views/
|
||||
│ ├── login/LoginView.vue # [NEW] 登录页:渐变背景居中卡片、账号密码表单校验、登录后按 role 跳转
|
||||
│ ├── dashboard/DashboardView.vue # [NEW] 仪表盘:四组统计卡片(用户/订单/营收/待办)、金额元格式化、空态处理
|
||||
│ ├── user/UserListView.vue # [NEW] 用户列表:筛选/表格(状态 Tag)/详情抽屉(额度表)/封禁、解封、额度调整、VIP 调整对话框
|
||||
│ ├── goods/GoodsListView.vue # [NEW] 商品列表:筛选/表格/新增编辑对话框(分元输入转换)/上下架/软删确认
|
||||
│ ├── order/OrderListView.vue # [NEW] 订单列表:筛选/表格/详情抽屉(金额与配额快照)
|
||||
│ ├── order/RefundListView.vue # [NEW] 退款列表:状态筛选(含显式待处理)/通过、驳回对话框(必填原因)
|
||||
│ ├── review/OutfitListView.vue # [NEW] 穿搭列表:多条件筛选/审核通过驳回(驳回必填原因)/封禁解封/详情抽屉(衣物明细)
|
||||
│ ├── review/ClothesListView.vue # [NEW] 衣物列表:筛选/详情抽屉(属性字段分栏展示)
|
||||
│ ├── review/ShareListView.vue # [NEW] 衣柜分享列表:筛选/封禁解封
|
||||
│ ├── notification/NotifyView.vue # [NEW] 通知推送:单用户通知与全量广播两个表单卡片、字数限制、广播返回 sent_count 提示
|
||||
│ └── admin/AdminListView.vue # [NEW] 管理员列表(超管):筛选/表格/创建编辑对话框/删除确认/状态禁用提示
|
||||
```
|
||||
|
||||
删除模板遗留文件:`src/components/HelloWorld.vue`、`TheWelcome.vue`、`WelcomeItem.vue`、`src/assets/logo.svg`。
|
||||
Reference in New Issue
Block a user