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

75 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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` 接口使用新操作密码可正常通过