## 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 ` 永久存储(无 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` 接口使用新操作密码可正常通过