Files
zt-closet-manage-backend/.codebuddy/plans/zt-closet-manage-backend_2515957e.md
T
2026-09-11 01:47:20 +08:00

19 KiB
Raw Blame History

name, overview, todos
name overview todos
zt-closet-manage-backend 参考 /root/zt-closet-backend 业务服务的代码风格(Gin+GORM+Redis+viper+JWT+OTel+slog 分层架构),在 /root/zt-closet-manage-backend 从零实现管理后台后端:管理员账号体系、仪表盘统计、用户/商品/订单退款/内容审核/通知推送七大模块,与业务服务共用同一 MySQL 库。
id content status
setup-skeleton 搭建项目骨架:go.mod 依赖裁剪、configs 模板、global、pkg/{setting,app,errcode,lzcwyjwt,tracing}、中间件链、redismodel/redisdao、main.go 初始化与优雅关闭、路由骨架与 .gitignore completed
id content status dependencies
data-layer 定义数据层:mysqlmodel 引擎与全部业务表模型、admin_users 表、AutoMigrate 与初始超级管理员 seed completed
setup-skeleton
id content status dependencies
admin-module 实现管理员账号模块:登录/登出/刷新 token/改密、管理员 CRUD 与超级管理员权限控制 completed
data-layer
id content status dependencies
dashboard-module 实现仪表盘统计模块:用户/订单/营收/待办等核心指标聚合接口 completed
data-layer
id content status dependencies
user-module 实现用户管理模块:分页搜索/详情/封禁解封/额度与 VIP 人工调整(事务+流水) completed
data-layer
id content status dependencies
goods-order-module 实现商品管理(CRUD、上下架)与订单退款管理(列表/详情/退款审核状态流转) completed
data-layer
id content status dependencies
content-notify-module 实现内容审核(穿搭审核封禁、衣物管理、分享封禁)与通知推送(单用户/全量广播) completed
data-layer
id content status dependencies
finalize 编写 README 与接口文档,执行 go mod tidy、go build、go vet 验证整体可用性 completed
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 人工调整流水、退款状态机、穿搭可见性规则等)。

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