Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
- 新增 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/路由/文档生成器同步注册
75 lines
3.9 KiB
Markdown
75 lines
3.9 KiB
Markdown
## Context
|
||
|
||
当前代理充值线下确认接口(`OfflinePay`)通过将 `operation_password` 与操作人自己的登录密码(`account.Password`)做 bcrypt 对比来实现操作密码验证。这个机制存在两个问题:
|
||
|
||
1. **缺乏独立性**:操作密码与登录密码耦合,修改登录密码即等于修改操作密码,无法独立管控。
|
||
2. **安全边界不清**:需要操作密码的敏感操作(如确认大额资金流转)应由超级管理员统一管控,而非各操作人分散管理。
|
||
|
||
设计决策:操作密码存 Redis,不开新表。原因:操作密码是全局单一配置,没有历史版本需求,Redis 的 SET/GET 完全满足需求,避免引入新表的维护成本。
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
- 超级管理员可以设置/修改全局操作密码(存 Redis,bcrypt 哈希)
|
||
- 提供查询接口返回操作密码是否已设置(不暴露密码本身)
|
||
- `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。
|
||
|
||
### 决策3:Service 层封装独立的 OperationPassword 服务
|
||
|
||
**选择**:新建 `internal/service/operation_password/service.go`,提供 `Set(ctx, password)` 和 `Verify(ctx, inputPassword) error` 两个方法。
|
||
|
||
**理由**:将操作密码逻辑从 `agent_recharge` Service 中解耦,后续接口可直接注入复用,避免逻辑散落多处。
|
||
|
||
### 决策4:未设置时的行为
|
||
|
||
**选择**:操作密码未设置时,`Verify` 返回"操作密码未设置,请联系超级管理员"错误,拒绝操作。
|
||
|
||
**理由**:不降级为登录密码(安全边界清晰)。初次上线前超级管理员需先设置操作密码。
|
||
|
||
### 决策5:bcrypt 哈希存 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` 接口使用新操作密码可正常通过
|