first commit
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
---
|
||||
name: zt-closet-manage-backend
|
||||
overview: 参考 /root/zt-closet-backend 业务服务的代码风格(Gin+GORM+Redis+viper+JWT+OTel+slog 分层架构),在 /root/zt-closet-manage-backend 从零实现管理后台后端:管理员账号体系、仪表盘统计、用户/商品/订单退款/内容审核/通知推送七大模块,与业务服务共用同一 MySQL 库。
|
||||
todos:
|
||||
- id: setup-skeleton
|
||||
content: 搭建项目骨架:go.mod 依赖裁剪、configs 模板、global、pkg/{setting,app,errcode,lzcwyjwt,tracing}、中间件链、redismodel/redisdao、main.go 初始化与优雅关闭、路由骨架与 .gitignore
|
||||
status: completed
|
||||
- id: data-layer
|
||||
content: 定义数据层:mysqlmodel 引擎与全部业务表模型、admin_users 表、AutoMigrate 与初始超级管理员 seed
|
||||
status: completed
|
||||
dependencies:
|
||||
- setup-skeleton
|
||||
- id: admin-module
|
||||
content: 实现管理员账号模块:登录/登出/刷新 token/改密、管理员 CRUD 与超级管理员权限控制
|
||||
status: completed
|
||||
dependencies:
|
||||
- data-layer
|
||||
- id: dashboard-module
|
||||
content: 实现仪表盘统计模块:用户/订单/营收/待办等核心指标聚合接口
|
||||
status: completed
|
||||
dependencies:
|
||||
- data-layer
|
||||
- id: user-module
|
||||
content: 实现用户管理模块:分页搜索/详情/封禁解封/额度与 VIP 人工调整(事务+流水)
|
||||
status: completed
|
||||
dependencies:
|
||||
- data-layer
|
||||
- id: goods-order-module
|
||||
content: 实现商品管理(CRUD、上下架)与订单退款管理(列表/详情/退款审核状态流转)
|
||||
status: completed
|
||||
dependencies:
|
||||
- data-layer
|
||||
- id: content-notify-module
|
||||
content: 实现内容审核(穿搭审核封禁、衣物管理、分享封禁)与通知推送(单用户/全量广播)
|
||||
status: completed
|
||||
dependencies:
|
||||
- data-layer
|
||||
- id: finalize
|
||||
content: 编写 README 与接口文档,执行 go mod tidy、go build、go vet 验证整体可用性
|
||||
status: completed
|
||||
dependencies:
|
||||
- admin-module
|
||||
- dashboard-module
|
||||
- user-module
|
||||
- goods-order-module
|
||||
- content-notify-module
|
||||
---
|
||||
|
||||
## 产品概述
|
||||
|
||||
为「智能衣橱」小程序业务服务(zt-closet-backend)配套的管理后台后端服务(zt-closet-manage-backend)。采用 Go 语言实现,整体代码风格、目录分层、技术栈与业务服务保持一致。管理后台与业务服务共用同一 MySQL 业务库,直接读写业务数据表;管理员账号表(admin_users)新建在同一库中。管理员通过「账号密码 + JWT 双 token」方式登录鉴权。
|
||||
|
||||
## 核心功能
|
||||
|
||||
- **管理员账号**:账号密码登录(bcrypt 加密存储)、access/refresh 双 token 签发与刷新、登出、修改自己密码;超级管理员可增删改普通管理员、禁用/启用账号;初始超级管理员由配置在启动时幂等创建;被禁用账号的 token 立即失效。
|
||||
- **仪表盘统计**:用户总数/今日新增、订单总数/各状态分布、累计营收与今日营收(已支付订单,金额单位分)、待处理退款数、上架商品数、待审核穿搭数等核心指标一次接口返回。
|
||||
- **用户管理**:用户分页列表(按昵称/手机号/OpenID/状态搜索)、用户详情(含四类额度与 VIP 信息)、封禁/解封(临时/永久,写封禁原因与期限)、额度人工调整(事务更新 user_quotas + 写 quota_flow_logs 人工调整流水)、VIP 人工调整(事务更新 users + 写 vip_flow_logs 流水)。
|
||||
- **商品管理**:商品分页列表(含下架商品)、新增、编辑、上下架、软删除;维护会员天数与四类额度权益字段,价格单位分。
|
||||
- **订单与退款管理**:订单分页列表(按状态/订单号/用户筛选)、订单详情(含支付与退款信息)、退款申请列表与审核——审核通过将退款单从「待处理」置为「退款中」(实际打款由运营在微信商户平台人工完成,业务服务回调联动状态),驳回置为「失败」并记录原因。
|
||||
- **内容审核**:公开穿搭列表与详情(含衣物明细)、审核通过/驳回(写 outfit_audit_logs)、穿搭封禁/解封;衣物分页列表与详情(按用户/处理状态筛选);衣柜分享列表、封禁/解封。
|
||||
- **通知推送**:向单个用户或全量用户发送系统通知(写 user_notifications,msg_type=3 系统、sender_id=0,并同步 Redis 未读计数)。
|
||||
|
||||
所有接口沿用业务服务约定:统一前缀 `/api/v1`、统一响应 `{code, msg, data}`、分页 `page/page_size`、金额单位分、错误码分段管理。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- 语言:Go(go.mod 现为 go 1.26.1,依赖版本全部对齐业务服务 /root/zt-closet-backend/go.mod)
|
||||
- Web 框架:Gin v1.12.0
|
||||
- ORM/数据库:GORM v1.31.2 + MySQL(含 soft_delete 软删除插件)
|
||||
- 缓存:go-redis v9(管理员 token 会话、通知未读计数)
|
||||
- 配置:spf13/viper(仅读 configs/config.yaml,不支持环境变量覆盖,与业务服务一致)
|
||||
- 鉴权:golang-jwt/v5(HS256 双 token)+ golang.org/x/crypto/bcrypt(密码哈希)
|
||||
- 可观测性:OpenTelemetry(trace/metric/log 三信号 OTLP gRPC 上报,otelgin + otelslog)+ log/slog + lumberjack(JSON 日志按大小轮转压缩,注入 trace_id/span_id)
|
||||
- 依赖裁剪:不引入 COS / 微信支付 / Tokenhub / 上传 / 迁移(golang-migrate)相关依赖
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 总体策略
|
||||
|
||||
完全复刻业务服务已验证的「handler → service → dao → model」四层架构与工程规范:`pkg/app` 统一响应、`pkg/errcode` 注册式错误码、`internal/dto` 请求响应 DTO、`internal/middleware` 中间件链、`internal/service/constants.go` 枚举常量、每层方法开启 tracing span、`global/` 全局单例。管理后台连接业务库与 Redis,读写业务表时严格遵守业务服务的枚举语义与流水口径(额度/VIP 人工调整流水、退款状态机、穿搭可见性规则等)。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph 请求链
|
||||
A[HTTP 请求] --> B[中间件链<br/>Recovery/RequestID/OTel/Slog/CORS]
|
||||
B --> C[JWTAuth 管理员鉴权]
|
||||
C --> D[Handler routers/api/v1]
|
||||
end
|
||||
D --> E[Service 业务逻辑层]
|
||||
E --> F[mysqldao GORM 数据访问]
|
||||
E --> G[redisdao 会话/计数]
|
||||
F --> H[(共享 MySQL 业务库<br/>users/goods/orders...<br/>admin_users 新建)]
|
||||
G --> I[(Redis<br/>admin token 会话/未读计数)]
|
||||
```
|
||||
|
||||
### 关键设计决策
|
||||
|
||||
1. **建表方案**:业务库的 schema_migrations 由业务服务的 golang-migrate 管理,管理后台**不引入** golang-migrate,改用启动时 GORM AutoMigrate 创建 `admin_users` 表并幂等 seed 初始超级管理员(账号密码来自配置 Admin 段),避免版本冲突。
|
||||
2. **JWT 隔离**:管理后台独立的 `pkg/lzcwyjwt`(Claims 用 `AdminID` 替代 `UserID`,函数签名风格保持一致),配置使用独立 `JWT` 段的 SecretKey/Issuer,与业务服务 token 不互通(Issuer + SecretKey 双重隔离);access token 会话写 Redis(仿业务服务 redisdao/auth.go),管理员禁用/删除立即失效。
|
||||
3. **权限控制**:`admin_users.Role`(1 超级管理员 / 2 普通管理员);管理员账号管理接口用 `AdminSuperAuth` 中间件校验角色;普通管理员禁用状态在 JWTAuth 中回查数据库拒绝(仿业务服务「Redis 失败 fail-open + DB 状态回查」策略)。
|
||||
4. **额度人工调整**:事务内 `SELECT ... FOR UPDATE` 锁 user_quotas 行 → 更新 Total(保证 total ≥ used)→ 写 quota_flow_logs(ChangeType=5 人工调整、ChangeTarget=1、ChangeNum/OldValue/NewValue/Reason,OrderNo 用流水 UUID 填充以满足 `(order_no, quota_type, change_type)` 联合唯一索引)。
|
||||
5. **VIP 人工调整**:事务内更新 users.VIPType/VIPStart/VIPExpire → 写 vip_flow_logs(ChangeType=5 人工调整、Source=4、OrderNo 用流水 UUID)。
|
||||
6. **退款审核状态机**:仅允许 `0 待处理 → 1 退款中`(通过,写审核备注)与 `0 待处理 → 3 失败`(驳回,写原因);实际微信打款由运营在商户平台手动完成,业务服务退款回调负责后续 2 成功流转与权益回收。
|
||||
7. **通知推送**:写 user_notifications(msg_type=3、sender_id=0、is_read=1)成功后 best-effort 对 Redis 未读计数自增(仿业务服务通知实现);全量广播按用户分批(每批 500)遍历插入,避免单事务过大与内存暴涨。
|
||||
8. **仪表盘统计**:对业务表做 COUNT/SUM 聚合查询(用户、订单各状态、已支付订单营收、今日新增、待处理退款、待审穿搭等),一次接口内并行/顺序聚合后组装返回。
|
||||
9. **错误码**:复用公共段(10000 参数 / 11000 鉴权 / 12000 权限 / 19000 系统),管理域新开段位:15000-15999 管理员域、16000-16999 用户管理域、17000-17999 商品域、18000-18999 订单退款域、20000-20999 内容审核域、21000-21999 通知域(独立进程,与业务服务码值无冲突)。
|
||||
|
||||
### 实现注意事项
|
||||
|
||||
- **性能**:列表一律分页(page_size ≤ 50)+ 索引字段筛选(username/wx_open_id/phone/order_no/status);用户详情一次 IN 查询四类额度避免 N+1;穿搭详情连查 outfit_items + clothes;全量广播分批插入。
|
||||
- **安全**:密码 bcrypt 哈希;初始管理员密码仅存配置(不落库明文);JWT SecretKey 启动强校验非空;所有 SQL 走 GORM 参数绑定;请求日志沿用脱敏规则(token/password 等字段打码);封禁/调整操作记录管理员 ID 与原因(CreatedBy/Reason)。
|
||||
- **日志与可观测性**:沿用业务服务 setup 顺序(setting → otel → slog → mysql → redis),ServiceName 使用 `zt-closet-manage-backend`;OTel 初始化失败降级 no-op 不阻断启动;优雅关闭含 OTel flush(独立 5s context)。
|
||||
- **blast radius**:只读业务表时一律走 soft_delete 过滤(deleted_at 为空);不修改业务服务已有代码;商品软删沿用 Model 基座;业务表结构不 AutoMigrate(避免误改业务表),仅 admin_users 由管理后台迁移。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
/root/zt-closet-manage-backend/
|
||||
├── main.go # [MODIFY] 入口:setupSetting→setupOTel→setupSlogLogger→setupMysqlEngine→setupRedisEngine→seed 管理员→路由→http.Server→优雅关闭
|
||||
├── go.mod # [MODIFY] 按业务服务裁剪依赖清单
|
||||
├── .gitignore # [NEW] 忽略 config.yaml/storage/二进制
|
||||
├── README.md # [NEW] 部署、配置、接口说明(仿业务服务风格)
|
||||
├── configs/
|
||||
│ └── config.example.yaml # [NEW] Server/Log/Mysql/Redis/OTel/JWT/Admin 段模板
|
||||
├── global/
|
||||
│ ├── setting.go # [NEW] 各配置段全局指针(Server/Log/Mysql/Redis/Otel/JWT/Admin)
|
||||
│ ├── mysql.go # [NEW] global.MysqlEngine
|
||||
│ ├── redis.go # [NEW] global.RedisEngine
|
||||
│ └── otel.go # [NEW] global.Tracer/Meter/OTelShutdown
|
||||
├── pkg/
|
||||
│ ├── app/response.go # [NEW] 统一响应 {code,msg,data}(复刻业务服务)
|
||||
│ ├── errcode/
|
||||
│ │ ├── errcode.go # [NEW] NewError 注册式错误码 + StatusCode 映射
|
||||
│ │ ├── common_code.go # [NEW] 公共段 10000-19999(复刻)
|
||||
│ │ └── admin_code.go # [NEW] 管理域 15000-21999 分段错误码
|
||||
│ ├── lzcwyjwt/lzcwyjwt.go # [NEW] Claims{AdminID,TokenType},GenerateTokens/ParseAccessToken/ParseRefreshToken
|
||||
│ ├── setting/
|
||||
│ │ ├── setting.go # [NEW] viper 仅读 configs/config.yaml
|
||||
│ │ └── section.go # [NEW] Server/Log/Mysql/Redis/Otel/JWT/Admin 配置结构体
|
||||
│ └── tracing/
|
||||
│ ├── provider.go # [NEW] OTel Provider(失败降级 no-op)
|
||||
│ ├── otel.go # [NEW] otelgin 中间件适配
|
||||
│ └── fanout.go # [NEW] slog fanout handler + OtelContextHandler
|
||||
├── internal/
|
||||
│ ├── dto/
|
||||
│ │ ├── common.go # [NEW] PageRequest/PageResponse
|
||||
│ │ ├── admin.go # [NEW] 登录/刷新/改密/管理员 CRUD DTO
|
||||
│ │ ├── dashboard.go # [NEW] 统计响应 DTO
|
||||
│ │ ├── user.go # [NEW] 用户列表/详情/封禁/额度/VIP 调整 DTO
|
||||
│ │ ├── goods.go # [NEW] 商品 CRUD DTO
|
||||
│ │ ├── order.go # [NEW] 订单/退款审核 DTO
|
||||
│ │ ├── content.go # [NEW] 穿搭审核/衣物/分享 DTO
|
||||
│ │ └── notification.go # [NEW] 通知推送 DTO
|
||||
│ ├── middleware/
|
||||
│ │ ├── requestid.go # [NEW] X-Request-ID(复刻)
|
||||
│ │ ├── otel.go # [NEW] GinOTelMiddleware(复刻)
|
||||
│ │ ├── logger.go # [NEW] GinSlogMiddleware 访问日志+脱敏(复刻)
|
||||
│ │ ├── cors.go # [NEW] CORS(复刻)
|
||||
│ │ ├── jwtauth.go # [NEW] 管理员 JWTAuth(token→Redis 会话→DB 状态回查→AdminID 注入 ctx)+ AdminSuperAuth 超管校验
|
||||
│ ├── mysqldao/
|
||||
│ │ ├── admin.go # [NEW] 管理员账号 DAO(含按用户名查询、分页)
|
||||
│ │ ├── user.go # [NEW] 用户分页/详情/封禁/额度/VIP 更新 DAO
|
||||
│ │ ├── goods.go # [NEW] 商品 CRUD DAO
|
||||
│ │ ├── order.go # [NEW] 订单分页/详情 DAO
|
||||
│ │ ├── refund.go # [NEW] 退款单分页/详情/状态流转 DAO
|
||||
│ │ ├── outfit.go # [NEW] 穿搭分页/详情/审核/封禁 + audit log DAO
|
||||
│ │ ├── clothes.go # [NEW] 衣物分页/详情 DAO
|
||||
│ │ ├── closet.go # [NEW] 分享分页/封禁 DAO
|
||||
│ │ ├── notification.go # [NEW] 通知批量插入 DAO
|
||||
│ │ └── stats.go # [NEW] 仪表盘聚合统计 DAO
|
||||
│ ├── mysqlmodel/
|
||||
│ │ ├── model.go # [NEW] Model 基座 + NewMysqlEngine(复刻)+ AutoMigrate(admin_users)
|
||||
│ │ ├── adminuser.go # [NEW] AdminUser 模型(表 admin_users)
|
||||
│ │ └── business.go # [NEW] 业务表模型:User/Goods/Order/RefundFlowLog/Outfit/OutfitItem/OutfitAuditLog/Clothes/ClosetShare/Quota/QuotaFlowLog/VIPFlowLog/UserNotification(结构与业务服务一致)
|
||||
│ ├── redisdao/
|
||||
│ │ ├── auth.go # [NEW] 管理员 token 会话存取/删除
|
||||
│ │ └── notification.go # [NEW] 通知未读计数自增
|
||||
│ ├── redismodel/redismodel.go # [NEW] NewRedisEngine + key 常量
|
||||
│ ├── routers/
|
||||
│ │ ├── router.go # [NEW] 中间件链 + /ping + 公开/鉴权路由注册
|
||||
│ │ └── api/v1/
|
||||
│ │ ├── admin.go # [NEW] 登录/刷新/登出/改密/管理员 CRUD handler
|
||||
│ │ ├── dashboard.go # [NEW] 仪表盘 handler
|
||||
│ │ ├── user.go # [NEW] 用户管理 handler
|
||||
│ │ ├── goods.go # [NEW] 商品管理 handler
|
||||
│ │ ├── order.go # [NEW] 订单/退款 handler
|
||||
│ │ ├── content.go # [NEW] 穿搭/衣物/分享 handler
|
||||
│ │ └── notification.go # [NEW] 通知推送 handler
|
||||
│ └── service/
|
||||
│ ├── service.go # [NEW] normalizePage/isNotFound 等公共函数
|
||||
│ ├── constants.go # [NEW] 管理后台枚举常量(对齐业务服务语义)
|
||||
│ ├── admin.go # [NEW] 登录鉴权/密码/管理员管理逻辑(bcrypt、seed)
|
||||
│ ├── dashboard.go # [NEW] 统计组装
|
||||
│ ├── user.go # [NEW] 用户管理(封禁/额度/VIP 调整事务+流水)
|
||||
│ ├── goods.go # [NEW] 商品业务逻辑
|
||||
│ ├── order.go # [NEW] 订单/退款审核状态机
|
||||
│ ├── content.go # [NEW] 穿搭审核/封禁、衣物、分享封禁
|
||||
│ └── notification.go # [NEW] 单用户/全量广播
|
||||
```
|
||||
|
||||
## 关键代码结构
|
||||
|
||||
```
|
||||
// internal/mysqlmodel/adminuser.go —— 管理员账号表(管理后台唯一新建表)
|
||||
type AdminUser struct {
|
||||
Model
|
||||
Username string `gorm:"column:username;uniqueIndex:uk_admin_username"` // 登录账号
|
||||
PasswordHash string `gorm:"column:password_hash"` // bcrypt 哈希
|
||||
Nickname string `gorm:"column:nickname"` // 显示名
|
||||
Role int8 `gorm:"column:role"` // 1=超级管理员 2=普通管理员
|
||||
Status int8 `gorm:"column:status"` // 1=启用 2=禁用
|
||||
LastLoginTime int64 `gorm:"column:last_login_time"` // 最近登录时间
|
||||
}
|
||||
|
||||
// pkg/lzcwyjwt/lzcwyjwt.go —— 管理员 JWT Claims(与业务服务接口风格一致,字段替换为 AdminID)
|
||||
type Claims struct {
|
||||
AdminID int64 `json:"admin_id"`
|
||||
TokenType TokenType `json:"token_type"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
func GenerateTokens(adminID int64) (accessToken, refreshToken string, err error)
|
||||
func ParseAccessToken(tokenString string) (*Claims, error)
|
||||
func ParseRefreshToken(tokenString string) (*Claims, error)
|
||||
```
|
||||
|
||||
## API 路由规划(前缀 /api/v1)
|
||||
|
||||
- 公开:`GET /ping`、`POST /admin/login`、`POST /admin/refresh-token`
|
||||
- 鉴权(JWT):`POST /admin/logout`、`PUT /admin/password`;`GET /dashboard/summary`;`GET /users`、`GET /users/:uuid`、`PUT /users/:uuid/ban`、`PUT /users/:uuid/unban`、`PUT /users/:uuid/quota`、`PUT /users/:uuid/vip`;`GET /goods`、`POST /goods`、`PUT /goods/:id`、`PUT /goods/:id/status`、`DELETE /goods/:id`;`GET /orders`、`GET /orders/:orderNo`;`GET /refunds`、`POST /refunds/:refundNo/approve`、`POST /refunds/:refundNo/reject`;`GET /outfits`、`GET /outfits/:uuid`、`POST /outfits/:uuid/audit`、`POST /outfits/:uuid/ban`、`POST /outfits/:uuid/unban`;`GET /clothes`、`GET /clothes/:uuid`;`GET /closet-shares`、`POST /closet-shares/:uuid/ban`、`POST /closet-shares/:uuid/unban`;`POST /notifications`、`POST /notifications/broadcast`
|
||||
- 超管专属:`GET /admins`、`POST /admins`、`PUT /admins/:id`、`DELETE /admins/:id`
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
name: 修复管理后台衣物图片与头像URL
|
||||
overview: 管理后台衣物列表/详情与穿搭明细返回的 processed_image 是 COS 相对 key、用户头像 avatar 同理,导致前端无法访问图片。参照业务服务 upload.GetCosUrl 逻辑,为管理后台补齐 COS 下载域名/鉴权密钥配置与 CDN 签名 URL 转换,使图片可正常展示。
|
||||
todos:
|
||||
- id: add-cos-config
|
||||
content: 新增 Cos 配置段:pkg/setting/section.go、global/setting.go、main.go 与 configs/config.example.yaml
|
||||
status: completed
|
||||
- id: add-cos-url-helper
|
||||
content: 新增 pkg/cos/cosurl.go 实现 GetCosUrl(复刻业务 MD5 CDN 签名,含空值与未配置兜底)
|
||||
status: completed
|
||||
dependencies:
|
||||
- add-cos-config
|
||||
- id: convert-clothes-images
|
||||
content: 修改 service/content.go:衣物列表/详情与穿搭详情衣物图片转可访问 URL
|
||||
status: completed
|
||||
dependencies:
|
||||
- add-cos-url-helper
|
||||
- id: convert-user-avatar
|
||||
content: 修改 service/user.go:用户头像做 http(s) 透传兼容并转 URL
|
||||
status: completed
|
||||
dependencies:
|
||||
- add-cos-url-helper
|
||||
- id: finalize-verify
|
||||
content: 更新 README 配置与安全说明,执行 go build 与 go vet 验证
|
||||
status: completed
|
||||
dependencies:
|
||||
- convert-clothes-images
|
||||
- convert-user-avatar
|
||||
---
|
||||
|
||||
## 用户需求
|
||||
|
||||
管理后台前端无法展示用户上传的衣物图片,需要修复图片地址生成逻辑。
|
||||
|
||||
## 根因
|
||||
|
||||
衣物图片上传 COS 后,数据库 `clothes.processed_image` 存的是相对 COS key(如 `{userUUID}/{userID}/{uuid}.png`),业务服务返回前会调用 `upload.GetCosUrl()` 生成带 CDN 鉴权签名的完整 URL。管理后台目前直接返回相对 key,导致前端拿到的地址无法访问。
|
||||
|
||||
用户头像存在同类问题:`users.avatar` 存的也是 COS key 或历史完整 URL,管理后台同样直接返回未转换的原始值。
|
||||
|
||||
## 修复范围
|
||||
|
||||
1. 管理后台新增 COS 下载配置(CDN 下载域名 + 下载鉴权密钥),与业务服务 Cos 段保持一致。
|
||||
2. 新增 `GetCosUrl` 工具函数,复刻业务服务 MD5 CDN 签名逻辑。
|
||||
3. 衣物列表/详情、穿搭详情中的衣物图片地址转为可访问 URL。
|
||||
4. 用户列表/详情中的头像地址做兼容转换(`http(s)` 完整 URL 透传,COS key 转 URL)。
|
||||
5. 补充配置说明与 README,确保配置为机密项、不落库、不硬编码。
|
||||
|
||||
## 技术方案
|
||||
|
||||
### 实现策略
|
||||
|
||||
完全复刻业务服务的图片 URL 生成逻辑,保持两服务共用同一 COS 时签名结果一致。仅改管理后台,不改业务服务。
|
||||
|
||||
### 关键改动
|
||||
|
||||
1. **配置层新增 Cos 段(仅下载所需字段)**
|
||||
|
||||
- `pkg/setting/section.go`:新增 `CosSettingS{CosDownloadDomain string; CdnDownloadSecretKey string}`。
|
||||
- `global/setting.go`:新增 `CosSetting *setting.CosSettingS`。
|
||||
- `main.go`:`setupSetting` 中读取 `Cos` 段。
|
||||
- `configs/config.example.yaml`:新增 `Cos:` 段,注释说明须与业务服务 Cos 段一致、属机密项。
|
||||
|
||||
2. **新增 URL 生成工具 `pkg/cos/cosurl.go`**
|
||||
复刻业务 `upload.GetCosUrl`:
|
||||
|
||||
- `ts := time.Now().Unix()`
|
||||
- `raw := CdnDownloadSecretKey + "/" + fileName + ts`
|
||||
- `sign := md5(raw)`
|
||||
- 返回 `https://{CosDownloadDomain}/{fileName}?sign={sign}&t={ts}`
|
||||
- 增加保护:`fileName == ""` 或 Cos 配置缺失/下载域名为空时,原样返回 fileName,避免未配置时 panic 或生成无效 URL。
|
||||
|
||||
3. **接入图片转换**
|
||||
|
||||
- `internal/service/content.go`:
|
||||
- `buildClothesItem` 中 `ProcessedImage` 改为 `cos.GetCosUrl(c.ProcessedImage)`。
|
||||
- `OutfitDetail` 中衣物明细 `ProcessedImage` 改为 `cos.GetCosUrl(cl.ProcessedImage)`。
|
||||
- `internal/service/user.go`:
|
||||
- `buildUserListItem` 中 `Avatar` 增加兼容转换:以 `http://`/`https://` 开头则透传,否则 `cos.GetCosUrl(u.Avatar)`;建议提取内部辅助函数 `toImageURL(key string) string` 供 avatar 复用。
|
||||
|
||||
4. **文档与验证**
|
||||
|
||||
- `README.md`:配置段说明新增 `Cos`;安全注意事项注明 `CdnDownloadSecretKey` 为机密,仅存 `configs/config.yaml`(已 gitignore),与业务服务保持一致。
|
||||
- `go build ./...` 与 `go vet ./...` 必须通过。
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
/root/zt-closet-manage-backend/
|
||||
├── configs/config.example.yaml # [MODIFY] 新增 Cos 段
|
||||
├── global/setting.go # [MODIFY] 新增 CosSetting
|
||||
├── pkg/setting/section.go # [MODIFY] 新增 CosSettingS
|
||||
├── pkg/cos/cosurl.go # [NEW] GetCosUrl 工具
|
||||
├── internal/service/content.go # [MODIFY] 衣物/穿搭图片转 URL
|
||||
├── internal/service/user.go # [MODIFY] 头像转 URL
|
||||
├── main.go # [MODIFY] setupSetting 读取 Cos 段
|
||||
└── README.md # [MODIFY] 配置与安全说明
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
- 管理后台与业务服务共用同一 COS,`Cos.CosDownloadDomain` 与 `Cos.CdnDownloadSecretKey` 必须与业务服务 `configs/config.yaml` 中的 `Cos` 段一致,否则签名校验失败。
|
||||
- 仅做 URL 转换,不改动数据库存储的相对 key;对空值、未配置做安全兜底。
|
||||
- 不引入 COS SDK,仅需 `crypto/md5`、`fmt`、`strconv`、`time` 标准库,避免额外依赖。
|
||||
@@ -0,0 +1,157 @@
|
||||
---
|
||||
name: 同步业务后端额度体系与虚拟支付改动
|
||||
overview: 将业务服务 /root/zt-closet-backend 的最新改动同步到管理后台 /root/zt-closet-manage-backend:同步商品/订单新字段与 quota_grants 额度凭证模型,并将「额度人工调整」从改 user_quotas.total 重构为发放有有效期额度凭证(quota_grants),用户详情完整展示永久/每日额度与凭证列表、支持回收。
|
||||
todos:
|
||||
- id: sync-models-constants
|
||||
content: 同步数据模型与常量:business.go 增改 Goods/Order/QuotaGrant/UserDailyUsage,constants.go 补 GoodsProduct/PayChannelVirtual/QuotaGrantSource 等
|
||||
status: completed
|
||||
- id: sync-goods-order
|
||||
content: 同步商品与订单 DTO/Service:商品 valid_days 可编辑与 product 只读展示,订单 xpay 字段展示
|
||||
status: completed
|
||||
dependencies:
|
||||
- sync-models-constants
|
||||
- id: add-quota-config
|
||||
content: 新增 Quota 配置段:pkg/setting、global、main.go 与 config.example.yaml
|
||||
status: completed
|
||||
- id: quota-dao-dto
|
||||
content: 实现 QuotaGrantDao 与 UserDailyUsage 读取,移除旧 QuotaAdjustWithFlow,重构 dto/user.go
|
||||
status: completed
|
||||
dependencies:
|
||||
- sync-models-constants
|
||||
- add-quota-config
|
||||
- id: rework-user-service
|
||||
content: 重构 service/user.go:详情完整额度计算与凭证列表、额度调整改凭证发放、新增凭证回收
|
||||
status: completed
|
||||
dependencies:
|
||||
- quota-dao-dto
|
||||
- id: router-handler-finalize
|
||||
content: 改造路由与 handler、补充错误码、更新 README 并执行 go build/vet 验证
|
||||
status: completed
|
||||
dependencies:
|
||||
- rework-user-service
|
||||
- sync-goods-order
|
||||
---
|
||||
|
||||
## 产品概述
|
||||
|
||||
业务后端(/root/zt-closet-backend)已完成额度体系重构与商品、订单字段扩展,本次任务是将管理后台(/root/zt-closet-manage-backend)同步到新语义:新增有有效期额度凭证(quota_grants)的发放与回收、用户详情完整展示永久额度与每日额度、同步商品虚拟支付字段与订单 XPay 字段,保持与业务服务共用同一 MySQL 库、同风格四层架构。
|
||||
|
||||
## 核心功能
|
||||
|
||||
- 数据模型同步:Goods 新增 valid_days(额度有效期天数)、product_id、product_status;Order 新增 xpay_product_id、xpay_platform、xpay_env;新增 QuotaGrant、UserDailyUsage 模型;user_quotas 语义更新为仅 Cos 永久额度。
|
||||
- 额度人工调整改为凭证发放:管理后台将用户额度调整从「改 user_quotas.total」改为「写 quota_grants 凭证」(source=加量包,ref_id=manual_uuid,可设有效期天数);Cos 为一次性库存,AI/社区/衣柜 为有效期内每日加量。
|
||||
- 凭证回收:支持按凭证 ID 软删除 quota_grants(含归属校验)。
|
||||
- 用户详情完整展示:Cos 永久额度(总额/已用/剩余)+ AI/社区/衣柜 每日额度(总额/当日已用/剩余)+ 该用户有效额度凭证列表;免费额度底值由管理后台新增 Quota 配置段提供(未配回落 20/3/10/5)。
|
||||
- 商品管理:valid_days 可编辑,product_id/product_status 只读展示(虚拟支付发布状态由业务侧维护)。
|
||||
- 订单管理:列表与详情展示 pay_channel=2(虚拟支付)及 xpay 平台/环境/道具 ID 字段。
|
||||
|
||||
## 技术栈
|
||||
|
||||
沿用现有管理后台技术栈与分层,不做技术栈变更:
|
||||
|
||||
- Go 1.26 + Gin + GORM + MySQL(soft_delete)+ go-redis + viper + JWT + OpenTelemetry + slog。
|
||||
- 与业务服务共用同一 MySQL 业务库与 Redis。
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 总体策略
|
||||
|
||||
严格对齐业务后端新额度语义:以「免费配置值 + 有效凭证」计算有效额度,凭证表作为可追溯、可回收的发放载体。商品/订单字段仅做模型与 DTO/Service 同步展示;额度调整与详情展示按新语义重构。
|
||||
|
||||
### 额度计算模型(管理后台侧)
|
||||
|
||||
管理后台不依赖业务 Redis 计数,直接读共享库 MySQL:
|
||||
|
||||
- Cos 永久额度:`Total = Quota配置.Cos + SUM(有效Cos凭证.amount)`,`Used = user_quotas.used + SUM(有效Cos凭证.used)`。
|
||||
- AI/社区/衣柜 每日额度:`Total = Quota配置对应项 + SUM(有效凭证.amount)`,`Used = user_daily_usages 今日对应计数`,凭证 Used 恒 0。
|
||||
- 凭证有效性:`expire_time=0` 跟随 VIP(vip_expire>now 有效),否则 `expire_time>now` 有效。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[管理员发放额度] --> B[写 quota_grants<br/>source=2 加量包<br/>ref_id=manual_uuid<br/>expire_time=now+days]
|
||||
B --> C[(quota_grants)]
|
||||
C --> D[用户详情计算]
|
||||
D --> E[Cos 永久额度<br/>免费配置 + 凭证库存]
|
||||
D --> F[AI/社区/衣柜 每日额度<br/>免费配置 + 凭证每日加量]
|
||||
G[(user_quotas)] --> E
|
||||
H[(user_daily_usages)] --> F
|
||||
I[管理员回收] -->|软删| C
|
||||
```
|
||||
|
||||
### 关键设计决策
|
||||
|
||||
1. 新增 `QuotaSettingS{Cos,AI,OutfitShare,ClosetShare *int64}` 配置段(指针类型支持显式 0,未配/负数回落 20/3/10/5,与业务 `defaultQuotaValue` 对齐),仅用于管理后台计算总额展示。
|
||||
2. 凭证发放请求改为 `{quota_type, amount, expire_days}`,`source=2 加量包`、`ref_id="manual_"+UUID`、`CreatedBy=管理员ID`;不写 quota_flow_logs(与业务广告激励模式一致)。
|
||||
3. 凭证回收为软删除,`DELETE /users/:uuid/grants/:id`,按 `id + user_id` 归属校验,防越权。
|
||||
4. 商品 product_id/product_status 只读展示(由业务虚拟支付发布流程维护),valid_days 参与创建与更新。
|
||||
5. 每日额度「当日已用」读 `user_daily_usages` 今日 StatDate,避免依赖 Redis TTL 计数。
|
||||
6. 移除旧 `QuotaAdjustWithFlow`(改 user_quotas.total + 流水)逻辑,避免遗留与新语义冲突的写路径。
|
||||
7. 错误码新开 `UserGrantNotFound=16003`。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
/root/zt-closet-manage-backend/
|
||||
├── configs/config.example.yaml # [MODIFY] 新增 Quota 配置段(Cos/AI/OutfitShare/ClosetShare)
|
||||
├── global/setting.go # [MODIFY] 新增 QuotaSetting 全局指针
|
||||
├── pkg/setting/section.go # [MODIFY] 新增 QuotaSettingS 结构体
|
||||
├── pkg/errcode/admin_code.go # [MODIFY] 新增 16003 额度凭证不存在
|
||||
├── main.go # [MODIFY] setupSetting 读取 Quota 段
|
||||
├── internal/
|
||||
│ ├── mysqlmodel/business.go # [MODIFY] Goods+3字段、Order+3字段、Quota注释、新增 QuotaGrant/UserDailyUsage
|
||||
│ ├── dto/
|
||||
│ │ ├── goods.go # [MODIFY] GoodsItem/Create/Update 增加 valid_days、product_id、product_status
|
||||
│ │ ├── order.go # [MODIFY] OrderItem 增加 xpay 字段
|
||||
│ │ └── user.go # [MODIFY] QuotaItem 语义、UserDetailResponse 增 Grants、QuotaAdjustRequest 改造、新增 UserGrantItem
|
||||
│ ├── mysqldao/
|
||||
│ │ ├── user.go # [MODIFY] 移除 QuotaAdjustWithFlow
|
||||
│ │ └── quota.go # [NEW] QuotaGrantDao(Create/ListByUserID/GetByID/SoftDelete)+ UserDailyUsageGetByUserIDAndDate
|
||||
│ ├── service/
|
||||
│ │ ├── constants.go # [MODIFY] 新增 GoodsProduct*、PayChannelVirtual、QuotaGrantSource*、XPayPlatform*
|
||||
│ │ ├── goods.go # [MODIFY] buildGoodsItem/Create/Update 处理新字段
|
||||
│ │ ├── order.go # [MODIFY] buildOrderItem 增加 xpay 字段
|
||||
│ │ └── user.go # [MODIFY] Detail 完整额度计算、QuotaAdjust 改凭证发放、新增 GrantRevoke
|
||||
│ └── routers/
|
||||
│ ├── router.go # [MODIFY] 新增 DELETE /users/:uuid/grants/:id
|
||||
│ └── api/v1/user.go # [MODIFY] 额度调整 handler 改参数、新增凭证回收 handler
|
||||
└── README.md # [MODIFY] 更新接口、配置、业务规则说明
|
||||
```
|
||||
|
||||
## 关键数据结构
|
||||
|
||||
```
|
||||
// pkg/setting/section.go
|
||||
type QuotaSettingS struct {
|
||||
Cos *int64 // 免费上传额度(永久)
|
||||
AI *int64 // 免费 AI 推荐额度(每日)
|
||||
OutfitShare *int64 // 免费社区分享额度(每日)
|
||||
ClosetShare *int64 // 免费衣柜分享额度(每日)
|
||||
}
|
||||
|
||||
// internal/mysqlmodel/business.go
|
||||
type QuotaGrant struct {
|
||||
Model
|
||||
UserID int64 `gorm:"column:user_id;index:idx_grant_user_type_expire,priority:1"`
|
||||
QuotaType int8 `gorm:"column:quota_type;index:idx_grant_user_type_expire,priority:2;uniqueIndex:uk_grant_ref_type,priority:2"`
|
||||
Amount int64 `gorm:"column:amount"`
|
||||
Used int64 `gorm:"column:used"`
|
||||
Source int8 `gorm:"column:source"`
|
||||
ExpireTime int64 `gorm:"column:expire_time;index:idx_grant_user_type_expire,priority:3"`
|
||||
RefID string `gorm:"column:ref_id;type:varchar(64);uniqueIndex:uk_grant_ref_type,priority:1"`
|
||||
}
|
||||
|
||||
// internal/dto/user.go
|
||||
type UserQuotaAdjustRequest struct {
|
||||
QuotaType int8 `json:"quota_type" binding:"required,oneof=1 2 3 4"`
|
||||
Amount int64 `json:"amount" binding:"required,min=1,max=100000000"`
|
||||
ExpireDays int64 `json:"expire_days" binding:"required,min=1,max=3650"`
|
||||
}
|
||||
```
|
||||
|
||||
## 实现注意事项
|
||||
|
||||
- 性能:用户详情一次查询 user_quotas(Cos)、quota_grants(IN 用户)、user_daily_usages(今日),避免 N+1;凭证列表数量级小,无分页压力。
|
||||
- 安全:凭证回收必须校验 `user_id` 归属;发放时校验用户存在与参数合法性;所有 SQL 走 GORM 参数绑定。
|
||||
- 语义对齐:凭证有效性判断严格复刻业务 `grantExpired`;商品只读字段不提供写入入口,避免与业务虚拟支付发布流程冲突。
|
||||
- 兼容性:仅改管理后台内部逻辑,不改业务服务代码;旧 `PUT /users/:uuid/quota` 路由保留但请求体语义变更,前端联调需同步。
|
||||
- 验证:`go build ./...` 与 `go vet ./...` 必须通过。
|
||||
@@ -0,0 +1,157 @@
|
||||
---
|
||||
name: 同步业务后端额度体系与虚拟支付改动
|
||||
overview: 将业务服务 /root/zt-closet-backend 的最新改动同步到管理后台 /root/zt-closet-manage-backend:同步商品/订单新字段与 quota_grants 额度凭证模型,并将「额度人工调整」从改 user_quotas.total 重构为发放有有效期额度凭证(quota_grants),用户详情完整展示永久/每日额度与凭证列表、支持回收。
|
||||
todos:
|
||||
- id: sync-models-constants
|
||||
content: 同步数据模型与常量:business.go 增改 Goods/Order/QuotaGrant/UserDailyUsage,constants.go 补 GoodsProduct/PayChannelVirtual/QuotaGrantSource 等
|
||||
status: completed
|
||||
- id: sync-goods-order
|
||||
content: 同步商品与订单 DTO/Service:商品 valid_days 可编辑与 product 只读展示,订单 xpay 字段展示
|
||||
status: completed
|
||||
dependencies:
|
||||
- sync-models-constants
|
||||
- id: add-quota-config
|
||||
content: 新增 Quota 配置段:pkg/setting、global、main.go 与 config.example.yaml
|
||||
status: completed
|
||||
- id: quota-dao-dto
|
||||
content: 实现 QuotaGrantDao 与 UserDailyUsage 读取,移除旧 QuotaAdjustWithFlow,重构 dto/user.go
|
||||
status: completed
|
||||
dependencies:
|
||||
- sync-models-constants
|
||||
- add-quota-config
|
||||
- id: rework-user-service
|
||||
content: 重构 service/user.go:详情完整额度计算与凭证列表、额度调整改凭证发放、新增凭证回收
|
||||
status: completed
|
||||
dependencies:
|
||||
- quota-dao-dto
|
||||
- id: router-handler-finalize
|
||||
content: 改造路由与 handler、补充错误码、更新 README 并执行 go build/vet 验证
|
||||
status: completed
|
||||
dependencies:
|
||||
- rework-user-service
|
||||
- sync-goods-order
|
||||
---
|
||||
|
||||
## 产品概述
|
||||
|
||||
业务后端(/root/zt-closet-backend)已完成额度体系重构与商品、订单字段扩展,本次任务是将管理后台(/root/zt-closet-manage-backend)同步到新语义:新增有有效期额度凭证(quota_grants)的发放与回收、用户详情完整展示永久额度与每日额度、同步商品虚拟支付字段与订单 XPay 字段,保持与业务服务共用同一 MySQL 库、同风格四层架构。
|
||||
|
||||
## 核心功能
|
||||
|
||||
- 数据模型同步:Goods 新增 valid_days(额度有效期天数)、product_id、product_status;Order 新增 xpay_product_id、xpay_platform、xpay_env;新增 QuotaGrant、UserDailyUsage 模型;user_quotas 语义更新为仅 Cos 永久额度。
|
||||
- 额度人工调整改为凭证发放:管理后台将用户额度调整从「改 user_quotas.total」改为「写 quota_grants 凭证」(source=加量包,ref_id=manual_uuid,可设有效期天数);Cos 为一次性库存,AI/社区/衣柜 为有效期内每日加量。
|
||||
- 凭证回收:支持按凭证 ID 软删除 quota_grants(含归属校验)。
|
||||
- 用户详情完整展示:Cos 永久额度(总额/已用/剩余)+ AI/社区/衣柜 每日额度(总额/当日已用/剩余)+ 该用户有效额度凭证列表;免费额度底值由管理后台新增 Quota 配置段提供(未配回落 20/3/10/5)。
|
||||
- 商品管理:valid_days 可编辑,product_id/product_status 只读展示(虚拟支付发布状态由业务侧维护)。
|
||||
- 订单管理:列表与详情展示 pay_channel=2(虚拟支付)及 xpay 平台/环境/道具 ID 字段。
|
||||
|
||||
## 技术栈
|
||||
|
||||
沿用现有管理后台技术栈与分层,不做技术栈变更:
|
||||
|
||||
- Go 1.26 + Gin + GORM + MySQL(soft_delete)+ go-redis + viper + JWT + OpenTelemetry + slog。
|
||||
- 与业务服务共用同一 MySQL 业务库与 Redis。
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 总体策略
|
||||
|
||||
严格对齐业务后端新额度语义:以「免费配置值 + 有效凭证」计算有效额度,凭证表作为可追溯、可回收的发放载体。商品/订单字段仅做模型与 DTO/Service 同步展示;额度调整与详情展示按新语义重构。
|
||||
|
||||
### 额度计算模型(管理后台侧)
|
||||
|
||||
管理后台不依赖业务 Redis 计数,直接读共享库 MySQL:
|
||||
|
||||
- Cos 永久额度:`Total = Quota配置.Cos + SUM(有效Cos凭证.amount)`,`Used = user_quotas.used + SUM(有效Cos凭证.used)`。
|
||||
- AI/社区/衣柜 每日额度:`Total = Quota配置对应项 + SUM(有效凭证.amount)`,`Used = user_daily_usages 今日对应计数`,凭证 Used 恒 0。
|
||||
- 凭证有效性:`expire_time=0` 跟随 VIP(vip_expire>now 有效),否则 `expire_time>now` 有效。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[管理员发放额度] --> B[写 quota_grants<br/>source=2 加量包<br/>ref_id=manual_uuid<br/>expire_time=now+days]
|
||||
B --> C[(quota_grants)]
|
||||
C --> D[用户详情计算]
|
||||
D --> E[Cos 永久额度<br/>免费配置 + 凭证库存]
|
||||
D --> F[AI/社区/衣柜 每日额度<br/>免费配置 + 凭证每日加量]
|
||||
G[(user_quotas)] --> E
|
||||
H[(user_daily_usages)] --> F
|
||||
I[管理员回收] -->|软删| C
|
||||
```
|
||||
|
||||
### 关键设计决策
|
||||
|
||||
1. 新增 `QuotaSettingS{Cos,AI,OutfitShare,ClosetShare *int64}` 配置段(指针类型支持显式 0,未配/负数回落 20/3/10/5,与业务 `defaultQuotaValue` 对齐),仅用于管理后台计算总额展示。
|
||||
2. 凭证发放请求改为 `{quota_type, amount, expire_days}`,`source=2 加量包`、`ref_id="manual_"+UUID`、`CreatedBy=管理员ID`;不写 quota_flow_logs(与业务广告激励模式一致)。
|
||||
3. 凭证回收为软删除,`DELETE /users/:uuid/grants/:id`,按 `id + user_id` 归属校验,防越权。
|
||||
4. 商品 product_id/product_status 只读展示(由业务虚拟支付发布流程维护),valid_days 参与创建与更新。
|
||||
5. 每日额度「当日已用」读 `user_daily_usages` 今日 StatDate,避免依赖 Redis TTL 计数。
|
||||
6. 移除旧 `QuotaAdjustWithFlow`(改 user_quotas.total + 流水)逻辑,避免遗留与新语义冲突的写路径。
|
||||
7. 错误码新开 `UserGrantNotFound=16003`。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
/root/zt-closet-manage-backend/
|
||||
├── configs/config.example.yaml # [MODIFY] 新增 Quota 配置段(Cos/AI/OutfitShare/ClosetShare)
|
||||
├── global/setting.go # [MODIFY] 新增 QuotaSetting 全局指针
|
||||
├── pkg/setting/section.go # [MODIFY] 新增 QuotaSettingS 结构体
|
||||
├── pkg/errcode/admin_code.go # [MODIFY] 新增 16003 额度凭证不存在
|
||||
├── main.go # [MODIFY] setupSetting 读取 Quota 段
|
||||
├── internal/
|
||||
│ ├── mysqlmodel/business.go # [MODIFY] Goods+3字段、Order+3字段、Quota注释、新增 QuotaGrant/UserDailyUsage
|
||||
│ ├── dto/
|
||||
│ │ ├── goods.go # [MODIFY] GoodsItem/Create/Update 增加 valid_days、product_id、product_status
|
||||
│ │ ├── order.go # [MODIFY] OrderItem 增加 xpay 字段
|
||||
│ │ └── user.go # [MODIFY] QuotaItem 语义、UserDetailResponse 增 Grants、QuotaAdjustRequest 改造、新增 UserGrantItem
|
||||
│ ├── mysqldao/
|
||||
│ │ ├── user.go # [MODIFY] 移除 QuotaAdjustWithFlow
|
||||
│ │ └── quota.go # [NEW] QuotaGrantDao(Create/ListByUserID/GetByID/SoftDelete)+ UserDailyUsageGetByUserIDAndDate
|
||||
│ ├── service/
|
||||
│ │ ├── constants.go # [MODIFY] 新增 GoodsProduct*、PayChannelVirtual、QuotaGrantSource*、XPayPlatform*
|
||||
│ │ ├── goods.go # [MODIFY] buildGoodsItem/Create/Update 处理新字段
|
||||
│ │ ├── order.go # [MODIFY] buildOrderItem 增加 xpay 字段
|
||||
│ │ └── user.go # [MODIFY] Detail 完整额度计算、QuotaAdjust 改凭证发放、新增 GrantRevoke
|
||||
│ └── routers/
|
||||
│ ├── router.go # [MODIFY] 新增 DELETE /users/:uuid/grants/:id
|
||||
│ └── api/v1/user.go # [MODIFY] 额度调整 handler 改参数、新增凭证回收 handler
|
||||
└── README.md # [MODIFY] 更新接口、配置、业务规则说明
|
||||
```
|
||||
|
||||
## 关键数据结构
|
||||
|
||||
```
|
||||
// pkg/setting/section.go
|
||||
type QuotaSettingS struct {
|
||||
Cos *int64 // 免费上传额度(永久)
|
||||
AI *int64 // 免费 AI 推荐额度(每日)
|
||||
OutfitShare *int64 // 免费社区分享额度(每日)
|
||||
ClosetShare *int64 // 免费衣柜分享额度(每日)
|
||||
}
|
||||
|
||||
// internal/mysqlmodel/business.go
|
||||
type QuotaGrant struct {
|
||||
Model
|
||||
UserID int64 `gorm:"column:user_id;index:idx_grant_user_type_expire,priority:1"`
|
||||
QuotaType int8 `gorm:"column:quota_type;index:idx_grant_user_type_expire,priority:2;uniqueIndex:uk_grant_ref_type,priority:2"`
|
||||
Amount int64 `gorm:"column:amount"`
|
||||
Used int64 `gorm:"column:used"`
|
||||
Source int8 `gorm:"column:source"`
|
||||
ExpireTime int64 `gorm:"column:expire_time;index:idx_grant_user_type_expire,priority:3"`
|
||||
RefID string `gorm:"column:ref_id;type:varchar(64);uniqueIndex:uk_grant_ref_type,priority:1"`
|
||||
}
|
||||
|
||||
// internal/dto/user.go
|
||||
type UserQuotaAdjustRequest struct {
|
||||
QuotaType int8 `json:"quota_type" binding:"required,oneof=1 2 3 4"`
|
||||
Amount int64 `json:"amount" binding:"required,min=1,max=100000000"`
|
||||
ExpireDays int64 `json:"expire_days" binding:"required,min=1,max=3650"`
|
||||
}
|
||||
```
|
||||
|
||||
## 实现注意事项
|
||||
|
||||
- 性能:用户详情一次查询 user_quotas(Cos)、quota_grants(IN 用户)、user_daily_usages(今日),避免 N+1;凭证列表数量级小,无分页压力。
|
||||
- 安全:凭证回收必须校验 `user_id` 归属;发放时校验用户存在与参数合法性;所有 SQL 走 GORM 参数绑定。
|
||||
- 语义对齐:凭证有效性判断严格复刻业务 `grantExpired`;商品只读字段不提供写入入口,避免与业务虚拟支付发布流程冲突。
|
||||
- 兼容性:仅改管理后台内部逻辑,不改业务服务代码;旧 `PUT /users/:uuid/quota` 路由保留但请求体语义变更,前端联调需同步。
|
||||
- 验证:`go build ./...` 与 `go vet ./...` 必须通过。
|
||||
Reference in New Issue
Block a user