Files
junhong_cmp_fiber/openspec/changes/super-admin-operation-password/design.md
huang 6caf0f6141
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
feat: 新增全局操作密码功能,将线下充值验证从登录密码改为统一操作密码
- 新增 RedisSystemOperationPasswordKey 常量函数(pkg/constants/redis.go)
- 新增 operation_password Service(Set/IsSet/Verify,bcrypt 哈希存 Redis)
- 新增 SuperAdminHandler 及两个接口:
  - POST /api/admin/super-admin/operation-password(设置/重置,仅超级管理员)
  - GET /api/admin/super-admin/operation-password/status(查询是否已设置)
- AgentRecharge.OfflinePay 操作密码验证从"查当前用户登录密码"改为"全局操作密码 Verify"
- Bootstrap/路由/文档生成器同步注册
2026-04-18 09:06:33 +08:00

3.9 KiB
Raw Blame History

Context

当前代理充值线下确认接口(OfflinePay)通过将 operation_password 与操作人自己的登录密码(account.Password)做 bcrypt 对比来实现操作密码验证。这个机制存在两个问题:

  1. 缺乏独立性:操作密码与登录密码耦合,修改登录密码即等于修改操作密码,无法独立管控。
  2. 安全边界不清:需要操作密码的敏感操作(如确认大额资金流转)应由超级管理员统一管控,而非各操作人分散管理。

设计决策:操作密码存 Redis不开新表。原因操作密码是全局单一配置没有历史版本需求Redis 的 SET/GET 完全满足需求,避免引入新表的维护成本。

Goals / Non-Goals

Goals:

  • 超级管理员可以设置/修改全局操作密码(存 Redisbcrypt 哈希)
  • 提供查询接口返回操作密码是否已设置(不暴露密码本身)
  • OfflinePay 的操作密码验证从"当前用户登录密码"改为"全局操作密码"
  • 封装 VerifyOperationPassword 工具函数,供后续接口统一复用

Non-Goals:

  • 不做操作密码的过期机制(非当前需求)
  • 不做操作密码历史记录
  • 不修改除 agent_recharge.OfflinePay 以外的其他接口(其他接口暂无此需求)

Decisions

决策1存储介质选 Redis 而非数据库新表

选择Redis SET system:operation_password <bcrypt_hash> 永久存储(无 TTL

理由全局单一配置无历史记录需求Redis 的简单 KV 足够。Key 命名遵循项目规范,定义在 pkg/constants/redis.go

备选:新建 tb_system_config 表 → 引入表管理、迁移、GORM 操作,过度设计。

决策2新接口归属路径

选择POST /api/admin/super-admin/operation-password(设置/修改)和 GET /api/admin/super-admin/operation-password/status(查询状态)。

理由/super-admin/ 路径前缀清晰表达"仅超级管理员"语义与现有路由命名惯例一致。Handler 层检查 userType == UserTypeSuperAdmin,非超级管理员返回 403。

决策3Service 层封装独立的 OperationPassword 服务

选择:新建 internal/service/operation_password/service.go,提供 Set(ctx, password)Verify(ctx, inputPassword) error 两个方法。

理由:将操作密码逻辑从 agent_recharge Service 中解耦,后续接口可直接注入复用,避免逻辑散落多处。

决策4未设置时的行为

选择:操作密码未设置时,Verify 返回"操作密码未设置,请联系超级管理员"错误,拒绝操作。

理由:不降级为登录密码(安全边界清晰)。初次上线前超级管理员需先设置操作密码。

决策5bcrypt 哈希存 Redis

选择Set 时用 bcrypt.GenerateFromPassword 哈希后存储Verify 时用 bcrypt.CompareHashAndPassword 对比。

理由:即使 Redis 被访问,也无法还原明文密码。与现有登录密码处理方式一致。

Risks / Trade-offs

[风险] 初次上线前操作密码未设置OfflinePay 接口会返回"操作密码未设置"错误。需在上线前由超级管理员设置初始操作密码。

[风险] Redis 数据丢失 → 如 Redis 数据被清空,操作密码丢失,所有需要操作密码的接口将拒绝服务。 → 缓解Redis 持久化AOF/RDB已在生产环境启用超级管理员可随时重设。

[风险] bcrypt 计算开销 → bcrypt 默认 cost=10约 100ms/次。此接口调用频率极低(管理操作),不影响系统整体性能。

Migration Plan

  1. 部署代码变更(新接口 + Redis 验证逻辑)
  2. 超级管理员登录后台,调用 POST /api/admin/super-admin/operation-password 设置初始操作密码
  3. 验证 GET /api/admin/super-admin/operation-password/status 返回已设置
  4. 验证 OfflinePay 接口使用新操作密码可正常通过