This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-04-13
|
||||
@@ -0,0 +1,136 @@
|
||||
## Context
|
||||
|
||||
当前君鸿CMP系统存在两个独立的模块:
|
||||
1. **客户端实名跳转功能**(`internal/handler/app/client_realname.go`):提供实名链接获取接口
|
||||
2. **轮询系统手动触发功能**(`internal/service/polling/manual_trigger_service.go`):支持手动触发卡状态检查
|
||||
|
||||
两个模块功能完整但相互独立,导致用户主动实名时无法享受优先级检查的好处。同时手动触发功能存在技术债务需要修复。
|
||||
|
||||
**现有架构**:
|
||||
```
|
||||
客户端 → ClientRealnameHandler.GetRealnameLink() → 返回实名链接
|
||||
(独立运行,无后续动作)
|
||||
|
||||
管理员 → PollingManualTriggerHandler → ManualTriggerService.TriggerSingle()
|
||||
(仅管理员可用,客户端无法触发)
|
||||
```
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 在用户主动获取实名链接时自动触发该ICCID的实名检查优先级提升
|
||||
- 修复手动触发功能的去重机制时间不对齐问题
|
||||
- 优化手动触发的频次限制和权限检查逻辑
|
||||
- 保持现有API接口的响应时间和兼容性
|
||||
- 确保新功能的稳定性和可观测性
|
||||
|
||||
**Non-Goals:**
|
||||
- 不修改客户端实名跳转的核心流程和响应格式
|
||||
- 不改变手动触发API的对外接口
|
||||
- 不增加新的权限管理复杂性
|
||||
- 不引入新的外部依赖或中间件
|
||||
|
||||
## Decisions
|
||||
|
||||
### 决策1:异步触发设计
|
||||
**选择**:使用 goroutine 异步调用手动触发服务
|
||||
**理由**:
|
||||
- 避免影响实名链接API的响应时间(目标 < 5ms 影响)
|
||||
- 手动触发失败不应阻断主流程
|
||||
- 用户无需等待触发结果,实名链接获取成功即可
|
||||
|
||||
**实现要点(强制)**:
|
||||
- goroutine 内部必须使用 `context.WithTimeout(context.Background(), 3*time.Second)` 创建独立 context
|
||||
- **禁止**在 goroutine 内复用 `c.UserContext()`:Fiber 请求 context 在 handler 返回后即失效,goroutine 中使用会导致操作失败或 context canceled 错误
|
||||
|
||||
**备选方案**:
|
||||
- 同步调用:会影响响应时间,用户体验下降
|
||||
- 异步任务队列:过度设计,增加系统复杂性
|
||||
|
||||
### 决策2:权限适配策略
|
||||
**选择**:使用系统用户身份调用手动触发服务
|
||||
**理由**:
|
||||
- 个人客户无管理员权限,无法直接调用手动触发
|
||||
- 系统用户身份可绕过权限检查但保留操作记录
|
||||
- 通过配置管理系统用户ID,便于维护
|
||||
|
||||
**备选方案**:
|
||||
- 修改权限检查:破坏现有权限模型
|
||||
- 创建专用接口:增加代码重复
|
||||
|
||||
### 决策3:去重机制修复
|
||||
**选择**:去重key TTL调整为24小时,与日限制周期对齐
|
||||
**理由**:
|
||||
- 解决当前1小时TTL与24小时限制不匹配的问题
|
||||
- 确保同一卡在同一天内不会重复触发相同检查
|
||||
- Redis内存消耗增加可控(估计 < 10MB)
|
||||
|
||||
### 决策4:频次限制优化
|
||||
**选择**:将每日限制从100次调整为500次
|
||||
**理由**:
|
||||
- 当前100次限制过低,客户端自动触发会快速消耗额度
|
||||
- 500次既满足自动化需求,又能防止滥用
|
||||
- 保留1000张卡的单次限制不变
|
||||
|
||||
### 决策5:依赖注入方式
|
||||
**选择**:在ClientRealnameHandler中新增ManualTriggerService字段
|
||||
**理由**:
|
||||
- 遵循项目既有的结构体字段注入模式
|
||||
- 在bootstrap/services.go中统一管理依赖
|
||||
- 避免循环依赖问题
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
### 风险1:异步调用失败无感知
|
||||
**影响**:用户获取实名链接成功,但优先级触发失败,仍需等待定时轮询
|
||||
**缓解措施**:
|
||||
- 增加详细的错误日志记录,便于运维监控
|
||||
- 设置合理的超时时间,避免goroutine泄露
|
||||
- 考虑增加Prometheus指标监控异步调用成功率
|
||||
|
||||
### 风险2:Redis内存使用增加
|
||||
**影响**:去重key TTL延长至24小时,Redis内存使用量增加
|
||||
**缓解措施**:
|
||||
- 预估增加量:约10MB(假设每日1000次触发)
|
||||
- 定期清理过期key,确保Redis稳定运行
|
||||
- 监控Redis内存使用情况
|
||||
|
||||
### 风险3:频次限制调整可能被滥用
|
||||
**影响**:500次限制仍可能被恶意用户快速消耗
|
||||
**缓解措施**:
|
||||
- 保留现有的权限检查和日志记录
|
||||
- 增加基于IP的限流保护
|
||||
- 必要时可通过配置动态调整限制值
|
||||
|
||||
### 风险4:系统用户身份权限过高
|
||||
**影响**:系统用户可触发任意ICCID的检查,存在潜在安全风险
|
||||
**缓解措施**:
|
||||
- 仅在客户端实名场景使用,且ICCID已通过归属权限验证
|
||||
- 记录详细操作日志,包含原始客户ID
|
||||
- 系统用户ID通过配置管理,不硬编码
|
||||
|
||||
## Migration Plan
|
||||
|
||||
### 阶段1:代码实现和测试
|
||||
1. 修复手动触发服务的去重机制和频次限制
|
||||
2. 在ClientRealnameHandler中集成异步调用
|
||||
3. 更新依赖注入配置
|
||||
4. 编写单元测试和集成测试
|
||||
|
||||
### 阶段2:部署和监控
|
||||
1. 在测试环境验证功能正确性
|
||||
2. 配置生产环境的系统用户ID
|
||||
3. 部署到生产环境
|
||||
4. 监控异步调用成功率和系统性能
|
||||
|
||||
### 回滚策略
|
||||
- **代码回滚**:移除异步调用代码,恢复原有逻辑
|
||||
- **配置回滚**:调整频次限制和去重TTL到原有值
|
||||
- **数据清理**:清理测试期间产生的触发日志
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **系统用户ID配置方式**:是否使用环境变量还是数据库配置?
|
||||
2. **监控指标选择**:是否需要添加Prometheus指标监控?
|
||||
3. **异步调用超时时间**:设置为多少秒比较合理?(建议3秒)
|
||||
4. **是否需要熔断机制**:当手动触发服务频繁失败时是否需要暂停自动触发?
|
||||
@@ -0,0 +1,43 @@
|
||||
## Why
|
||||
|
||||
用户在C端主动获取实名跳转链接后,需要等待轮询系统的定时检查(通常30秒-5分钟间隔)才能检测到实名状态变化,导致用户实名完成后仍需等待较长时间才能正常使用卡片功能,严重影响用户体验。同时,现有轮询系统的手动触发功能存在去重机制时间不对齐、频次限制设置不合理等技术问题,需要一并修复以确保功能的稳定性和可靠性。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **新增C端实名跳转自动触发功能**:在用户获取实名链接时自动触发该ICCID的实名检查,提高检测优先级
|
||||
- **修复手动触发去重机制**:解决去重key 1小时过期与日限制按天计算的时间不对齐问题
|
||||
- **优化手动触发频次限制**:调整每日100次限制到更合理的水平(建议500次),避免正常使用受限
|
||||
- **简化权限检查逻辑**:合并冗余的权限检查函数,减少代码重复
|
||||
- **改进错误处理和日志**:增强手动触发的异常处理和追踪能力
|
||||
- **增强系统集成**:在客户端实名Handler中集成手动触发服务调用
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `client-realname-auto-trigger`: 客户端实名跳转自动触发实名检查优先级提升功能
|
||||
- `polling-manual-trigger`: 手动触发功能的 bug 修复和优化改进(为该能力补充 live spec,原 spec 仅存于 archive)
|
||||
|
||||
## Impact
|
||||
|
||||
**代码影响**:
|
||||
- `internal/handler/app/client_realname.go`: 新增手动触发服务字段和异步触发 goroutine
|
||||
- `internal/service/polling/manual_trigger_service.go`: 修复去重 TTL(1h→24h,**3处** daily limit 100→500),优化权限检查
|
||||
- `internal/bootstrap/handlers.go`: 更新 ClientRealnameHandler 依赖注入,传入 ManualTriggerService
|
||||
- `pkg/constants/redis.go`: 同步更新 RedisPollingManualDedupeKey 注释中的过期时间说明
|
||||
- `cmd/api/docs.go` / `cmd/gendocs/main.go`: 同步更新 NewClientRealnameHandler 调用参数
|
||||
|
||||
**API影响**:
|
||||
- 无新增API,现有API响应时间无变化
|
||||
- 手动触发相关API的错误处理优化
|
||||
|
||||
**数据库影响**:
|
||||
- 无表结构变更
|
||||
- `tb_polling_manual_trigger_log` 记录量可能轻微增加
|
||||
|
||||
**性能影响**:
|
||||
- 客户端实名接口增加异步调用,对响应时间影响 < 5ms
|
||||
- Redis操作增加,但通过优化去重机制整体性能提升
|
||||
|
||||
**运维影响**:
|
||||
- 实名同步延迟显著减少,用户投诉预计减少80%+
|
||||
- 手动触发功能更稳定,运维排查问题更便利
|
||||
@@ -0,0 +1,64 @@
|
||||
# Capability: 客户端实名跳转自动触发实名检查优先级提升功能
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 客户端实名跳转自动触发实名检查
|
||||
|
||||
系统 SHALL 在用户成功获取实名跳转链接时,自动触发该ICCID的实名状态检查,提高检测优先级。
|
||||
|
||||
#### Scenario: 用户主动获取实名链接时自动触发优先级检查
|
||||
|
||||
- **WHEN** 个人客户调用 `GET /api/c/v1/realname/link` 接口成功获取实名链接
|
||||
- **THEN** 系统自动将该ICCID加入实名检查的高优先级队列,无需等待定时轮询
|
||||
|
||||
#### Scenario: 自动触发失败不影响主流程
|
||||
|
||||
- **WHEN** 个人客户调用实名链接接口,但自动触发实名检查失败
|
||||
- **THEN** 系统仍正常返回实名链接,不因触发失败而阻断用户操作
|
||||
|
||||
#### Scenario: 异步处理不影响响应时间
|
||||
|
||||
- **WHEN** 个人客户调用实名链接接口
|
||||
- **THEN** 系统异步执行实名检查触发,主接口响应时间增加不超过5ms
|
||||
|
||||
### Requirement: 自动触发的权限适配
|
||||
|
||||
系统 SHALL 使用系统用户身份执行自动触发操作,绕过个人客户的权限限制。
|
||||
|
||||
#### Scenario: 个人客户无权限但能触发检查
|
||||
|
||||
- **WHEN** 个人客户获取实名链接(个人客户本身无手动触发权限)
|
||||
- **THEN** 系统使用配置的系统用户身份自动触发,成功将ICCID加入优先级队列
|
||||
|
||||
#### Scenario: 系统用户身份操作记录
|
||||
|
||||
- **WHEN** 系统使用系统用户身份执行自动触发
|
||||
- **THEN** 系统在 `tb_polling_manual_trigger_log` 表中记录操作,`triggered_by` 字段记录系统用户ID
|
||||
|
||||
### Requirement: 自动触发的错误处理和日志
|
||||
|
||||
系统 SHALL 提供完善的错误处理和日志记录机制,确保可观测性。
|
||||
|
||||
#### Scenario: 触发失败时记录详细日志
|
||||
|
||||
- **WHEN** 自动触发实名检查失败(如Redis连接失败、权限错误等)
|
||||
- **THEN** 系统记录包含客户ID、ICCID、错误原因的详细日志,级别为WARN
|
||||
|
||||
#### Scenario: 触发成功时记录操作日志
|
||||
|
||||
- **WHEN** 自动触发实名检查成功
|
||||
- **THEN** 系统记录包含客户ID、ICCID的INFO级别日志,便于运维追踪
|
||||
|
||||
### Requirement: 配置管理和灵活性
|
||||
|
||||
系统 SHALL 支持通过配置管理自动触发功能的关键参数。
|
||||
|
||||
#### Scenario: 系统用户ID配置
|
||||
|
||||
- **WHEN** 系统初始化或配置更新时
|
||||
- **THEN** 系统从环境变量 `JUNHONG_AUTO_TRIGGER_SYSTEM_USER_ID` 读取系统用户ID
|
||||
|
||||
#### Scenario: 自动触发功能开关
|
||||
|
||||
- **WHEN** 需要临时关闭自动触发功能时
|
||||
- **THEN** 系统支持通过环境变量 `JUNHONG_ENABLE_AUTO_TRIGGER` 控制功能开关(默认开启)
|
||||
@@ -0,0 +1,151 @@
|
||||
# Capability: 手动触发功能优化和Bug修复
|
||||
|
||||
> 注:`polling-manual-trigger` 能力的原始 spec 仅存于 archive(2026-02-10-polling-system-implementation),未提升到 `openspec/specs/`。本变更为该能力建立 live spec,以下内容为针对已有实现的修正与补充。
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 手动触发去重
|
||||
|
||||
系统 SHALL 对手动触发进行去重,避免重复执行,确保去重机制的时间一致性。
|
||||
|
||||
#### Scenario: 同卡重复触发
|
||||
|
||||
- **WHEN** 管理员对同一张卡连续触发两次实名检查
|
||||
- **THEN** 系统检查手动队列,如果卡ID已存在则不重复加入
|
||||
|
||||
#### Scenario: 使用 Redis Set 去重
|
||||
|
||||
- **WHEN** 批量触发 100 张卡,其中有 10 张重复
|
||||
- **THEN** 系统使用 Redis Set 临时存储卡ID,去重后再加入队列
|
||||
|
||||
#### Scenario: 手动触发与定时任务去重
|
||||
|
||||
- **WHEN** 卡A在手动队列中,同时定时队列也到期
|
||||
- **THEN** 系统优先执行手动触发,定时队列检测到卡已在执行则跳过
|
||||
|
||||
#### Scenario: 去重key TTL与日限制时间对齐
|
||||
|
||||
- **WHEN** 管理员在当天第一次触发某张卡的实名检查
|
||||
- **THEN** 系统设置去重key的TTL为24小时,与日限制周期保持一致,避免同一天内重复触发
|
||||
|
||||
#### Scenario: 去重key跨天自动清理
|
||||
|
||||
- **WHEN** 午夜12点过后,新一天开始
|
||||
- **THEN** 系统允许重新触发前一天已触发过的卡,去重key自动过期清理
|
||||
|
||||
### Requirement: 手动触发限流
|
||||
|
||||
系统 SHALL 对手动触发进行合理限流,既防止滥用又保证正常使用。
|
||||
|
||||
#### Scenario: 单次批量限制
|
||||
|
||||
- **WHEN** 管理员单次批量触发超过 1000 张卡
|
||||
- **THEN** 系统拒绝请求,提示超过单次限制
|
||||
|
||||
#### Scenario: 频率限制
|
||||
|
||||
- **WHEN** 管理员在 1 分钟内触发超过 10 次
|
||||
- **THEN** 系统拒绝请求,提示操作过于频繁,建议稍后再试
|
||||
|
||||
#### Scenario: 调整每日限制到合理水平
|
||||
|
||||
- **WHEN** 管理员当日累计触发超过 500 次(从100次调整)
|
||||
- **THEN** 系统拒绝请求,提示已达每日限制,支持正常运维需求和客户端自动触发
|
||||
|
||||
#### Scenario: 超级管理员无限制
|
||||
|
||||
- **WHEN** 超级管理员手动触发
|
||||
- **THEN** 系统不应用限流规则
|
||||
|
||||
#### Scenario: 客户端自动触发不受个人频次限制影响
|
||||
|
||||
- **WHEN** 客户端实名跳转自动触发功能调用手动触发服务
|
||||
- **THEN** 系统使用系统用户身份,不受个人用户的频次限制影响
|
||||
|
||||
### Requirement: 手动触发权限控制
|
||||
|
||||
系统 SHALL 控制手动触发的权限,支持系统用户身份的特殊权限适配。
|
||||
|
||||
#### Scenario: 普通管理员触发自己管理的卡
|
||||
|
||||
- **WHEN** 代理账号请求手动触发卡ID为 12345,该卡属于代理管理的店铺
|
||||
- **THEN** 系统验证权限通过,允许触发
|
||||
|
||||
#### Scenario: 代理账号无权触发其他店铺的卡
|
||||
|
||||
- **WHEN** 代理账号请求手动触发卡ID为 99999,该卡不属于代理管理的店铺
|
||||
- **THEN** 系统拒绝请求,返回权限不足错误
|
||||
|
||||
#### Scenario: 超级管理员可触发任意卡
|
||||
|
||||
- **WHEN** 超级管理员请求手动触发任意卡
|
||||
- **THEN** 系统跳过权限验证,允许触发
|
||||
|
||||
#### Scenario: 系统用户身份自动触发
|
||||
|
||||
- **WHEN** 客户端实名跳转功能使用系统用户身份调用手动触发
|
||||
- **THEN** 系统验证系统用户身份有效,允许触发任意ICCID的实名检查
|
||||
|
||||
#### Scenario: 企业账号禁止手动触发
|
||||
|
||||
- **WHEN** 企业账号请求手动触发卡ID为 12345,该卡属于企业
|
||||
- **THEN** 系统拒绝请求,返回无权限错误
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 优化权限检查逻辑
|
||||
|
||||
系统 SHALL 简化和优化权限检查逻辑,减少代码重复,提高可维护性。
|
||||
|
||||
#### Scenario: 统一权限检查函数
|
||||
|
||||
- **WHEN** 系统需要验证用户对卡的管理权限
|
||||
- **THEN** 系统使用统一的权限检查函数,避免在多个地方重复实现相同逻辑
|
||||
|
||||
#### Scenario: 权限检查缓存优化
|
||||
|
||||
- **WHEN** 批量操作需要检查大量卡的权限
|
||||
- **THEN** 系统使用批量权限检查,减少数据库查询次数,提高性能
|
||||
|
||||
#### Scenario: 权限检查错误信息优化
|
||||
|
||||
- **WHEN** 权限检查失败时
|
||||
- **THEN** 系统返回明确的错误信息,帮助用户了解权限不足的具体原因
|
||||
|
||||
### Requirement: 增强错误处理和日志
|
||||
|
||||
系统 SHALL 提供更完善的错误处理和日志记录,提高系统可观测性。
|
||||
|
||||
#### Scenario: 详细错误日志记录
|
||||
|
||||
- **WHEN** 手动触发过程中发生错误(Redis连接失败、权限错误等)
|
||||
- **THEN** 系统记录包含错误原因、用户ID、卡ID、上下文信息的详细日志
|
||||
|
||||
#### Scenario: 性能关键路径日志
|
||||
|
||||
- **WHEN** 手动触发操作执行时间超过预期
|
||||
- **THEN** 系统记录性能日志,包含各阶段耗时,便于性能优化
|
||||
|
||||
#### Scenario: 异常情况恢复机制
|
||||
|
||||
- **WHEN** Redis临时不可用导致手动触发失败
|
||||
- **THEN** 系统记录错误并提供恢复建议,不影响其他正常功能
|
||||
|
||||
### Requirement: 配置管理和灵活性
|
||||
|
||||
系统 SHALL 支持通过配置管理手动触发功能的关键参数,提高系统灵活性。
|
||||
|
||||
#### Scenario: 频次限制可配置
|
||||
|
||||
- **WHEN** 需要调整手动触发的频次限制时
|
||||
- **THEN** 系统支持通过配置文件或环境变量调整每日限制、单次限制等参数
|
||||
|
||||
#### Scenario: 去重TTL可配置
|
||||
|
||||
- **WHEN** 需要调整去重机制的时间窗口时
|
||||
- **THEN** 系统支持配置去重key的TTL时长,默认24小时
|
||||
|
||||
#### Scenario: 系统用户ID配置
|
||||
|
||||
- **WHEN** 系统启动或配置更新时
|
||||
- **THEN** 系统从环境变量 `JUNHONG_MANUAL_TRIGGER_SYSTEM_USER_ID` 读取系统用户ID用于客户端自动触发
|
||||
@@ -0,0 +1,52 @@
|
||||
## 1. 现状分析
|
||||
|
||||
- [x] 1.1 使用 lsp_diagnostics 检查 client_realname.go、manual_trigger_service.go 的语法和类型错误
|
||||
- [x] 1.2 分析现有 ClientRealnameHandler 和 ManualTriggerService 的依赖关系,确认无循环依赖风险
|
||||
|
||||
## 2. 配置管理和常量定义
|
||||
|
||||
- [x] 2.1 在 `pkg/config/` 中新增 `AutoTriggerSystemUserID int` 和 `EnableAutoTrigger bool`(默认 true)配置字段,通过环境变量 `JUNHONG_AUTO_TRIGGER_SYSTEM_USER_ID` 和 `JUNHONG_ENABLE_AUTO_TRIGGER` 注入
|
||||
- [x] 2.2 更新 `pkg/config/defaults/config.yaml`,添加 `auto_trigger_system_user_id` 和 `enable_auto_trigger` 默认值
|
||||
- [x] 2.3 运行 lsp_diagnostics 检查配置相关代码无错误
|
||||
|
||||
## 3. 手动触发服务 Bug 修复和优化
|
||||
|
||||
- [x] 3.1 在 `internal/service/polling/manual_trigger_service.go` 的 `TriggerSingle`、`TriggerBatch`、`TriggerByCondition` 三处将 `todayCount >= 100` 改为 `todayCount >= 500`
|
||||
- [x] 3.2 在 `internal/service/polling/manual_trigger_service.go` 的 `TriggerSingle` 中将 `s.redis.Expire(ctx, dedupeKey, time.Hour)` 改为 `s.redis.Expire(ctx, dedupeKey, 24*time.Hour)`
|
||||
- [x] 3.3 同步更新 `pkg/constants/redis.go` 中 `RedisPollingManualDedupeKey` 函数的注释,将 `// 过期时间:1小时` 改为 `// 过期时间:24小时`
|
||||
- [x] 3.4 优化 `canManageCard` 和 `canManageCards` 权限检查函数,提取公共逻辑减少重复代码
|
||||
- [x] 3.5 改进 `TriggerSingle` 的错误日志,增加 `triggeredBy`、`cardID` 等上下文字段
|
||||
- [x] 3.6 运行 lsp_diagnostics 检查 ManualTriggerService 相关代码无错误
|
||||
- [x] 3.7 使用 PostgreSQL MCP 和 Redis CLI 手动验证:触发同一张卡后 24 小时内重复触发被拒绝,24 小时后允许重复触发
|
||||
|
||||
## 4. ClientRealnameHandler 集成自动触发功能
|
||||
|
||||
- [x] 4.1 在 `ClientRealnameHandler` 结构体中新增 `manualTriggerSvc *polling.ManualTriggerService` 字段
|
||||
- [x] 4.2 修改 `NewClientRealnameHandler` 构造函数,新增 `manualTriggerSvc *polling.ManualTriggerService` 参数(可为 nil,nil 时跳过自动触发)
|
||||
- [x] 4.3 在 `GetRealnameLink` 成功返回实名链接前,若 `h.manualTriggerSvc != nil` 且配置 `EnableAutoTrigger=true`,则启动 goroutine 异步触发实名检查
|
||||
- [x] 4.4 goroutine 内部必须使用 `context.WithTimeout(context.Background(), 3*time.Second)` 创建独立 context,禁止复用 `c.UserContext()`(请求 context 在 handler 返回后失效)
|
||||
- [x] 4.5 goroutine 内部构造系统用户 context(注入 UserTypePlatform 和配置的 AutoTriggerSystemUserID),调用 `manualTriggerSvc.TriggerSingle(sysCtx, targetCard.ID, constants.TaskTypePollingRealname, systemUserID)`
|
||||
- [x] 4.6 goroutine 执行失败时记录 WARN 日志(含 customerID、ICCID、error),成功时记录 INFO 日志(含 customerID、ICCID);失败不影响主流程
|
||||
- [x] 4.7 运行 lsp_diagnostics 检查 ClientRealnameHandler 相关代码无错误
|
||||
|
||||
## 5. 依赖注入配置更新
|
||||
|
||||
- [x] 5.1 在 `internal/bootstrap/handlers.go` 中更新 `ClientRealname` handler 的初始化,传入 ManualTriggerService
|
||||
- [x] 5.2 确认 ManualTriggerService 在 bootstrap/services.go 中已初始化(检查是否已存在,若不存在则补充)
|
||||
- [x] 5.3 更新 `cmd/api/docs.go` 和 `cmd/gendocs/main.go` 中 `NewClientRealnameHandler` 的调用,补充新增的 manualTriggerSvc 参数(传 nil 即可)
|
||||
- [x] 5.4 运行 lsp_diagnostics 检查 bootstrap 和 docs.go 相关代码无编译错误
|
||||
|
||||
## 6. 手动验证
|
||||
|
||||
- [x] 6.1 使用 curl 调用 `GET /api/c/v1/realname/link` 接口,通过日志确认异步触发 goroutine 被启动
|
||||
- [x] 6.2 通过 PostgreSQL MCP 查询 `tb_polling_manual_trigger_log`,确认自动触发记录的 `triggered_by` 为配置的系统用户 ID
|
||||
- [x] 6.3 通过 Redis CLI 检查 `polling:manual:realname` 队列,确认 cardID 已写入
|
||||
- [x] 6.4 使用 PostgreSQL MCP 查询 `tb_polling_manual_trigger_log`,验证今日触发次数计数正确(不超过 500 次限制)
|
||||
- [x] 6.5 使用 Redis CLI 确认 dedup key TTL 为 24 小时(`TTL polling:manual:dedupe:realname`)
|
||||
- [x] 6.6 模拟 manualTriggerSvc 不可用场景(临时将 EnableAutoTrigger 设为 false),确认接口正常返回实名链接,日志无 panic
|
||||
|
||||
## 7. 最终验收
|
||||
|
||||
- [x] 7.1 运行 lsp_diagnostics 对所有修改过的文件做最终检查,确认无错误和警告
|
||||
- [x] 7.2 确认环境变量 `JUNHONG_AUTO_TRIGGER_SYSTEM_USER_ID` 和 `JUNHONG_ENABLE_AUTO_TRIGGER` 已在部署文档中记录
|
||||
- [x] 7.3 确认 `GetRealnameLink` handler 函数长度不超过 100 行(满足项目规范)
|
||||
64
openspec/specs/client-realname-auto-trigger/spec.md
Normal file
64
openspec/specs/client-realname-auto-trigger/spec.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Capability: 客户端实名跳转自动触发实名检查优先级提升功能
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 客户端实名跳转自动触发实名检查
|
||||
|
||||
系统 SHALL 在用户成功获取实名跳转链接时,自动触发该ICCID的实名状态检查,提高检测优先级。
|
||||
|
||||
#### Scenario: 用户主动获取实名链接时自动触发优先级检查
|
||||
|
||||
- **WHEN** 个人客户调用 `GET /api/c/v1/realname/link` 接口成功获取实名链接
|
||||
- **THEN** 系统自动将该ICCID加入实名检查的高优先级队列,无需等待定时轮询
|
||||
|
||||
#### Scenario: 自动触发失败不影响主流程
|
||||
|
||||
- **WHEN** 个人客户调用实名链接接口,但自动触发实名检查失败
|
||||
- **THEN** 系统仍正常返回实名链接,不因触发失败而阻断用户操作
|
||||
|
||||
#### Scenario: 异步处理不影响响应时间
|
||||
|
||||
- **WHEN** 个人客户调用实名链接接口
|
||||
- **THEN** 系统异步执行实名检查触发,主接口响应时间增加不超过5ms
|
||||
|
||||
### Requirement: 自动触发的权限适配
|
||||
|
||||
系统 SHALL 使用系统用户身份执行自动触发操作,绕过个人客户的权限限制。
|
||||
|
||||
#### Scenario: 个人客户无权限但能触发检查
|
||||
|
||||
- **WHEN** 个人客户获取实名链接(个人客户本身无手动触发权限)
|
||||
- **THEN** 系统使用配置的系统用户身份自动触发,成功将ICCID加入优先级队列
|
||||
|
||||
#### Scenario: 系统用户身份操作记录
|
||||
|
||||
- **WHEN** 系统使用系统用户身份执行自动触发
|
||||
- **THEN** 系统在 `tb_polling_manual_trigger_log` 表中记录操作,`triggered_by` 字段记录系统用户ID
|
||||
|
||||
### Requirement: 自动触发的错误处理和日志
|
||||
|
||||
系统 SHALL 提供完善的错误处理和日志记录机制,确保可观测性。
|
||||
|
||||
#### Scenario: 触发失败时记录详细日志
|
||||
|
||||
- **WHEN** 自动触发实名检查失败(如Redis连接失败、权限错误等)
|
||||
- **THEN** 系统记录包含客户ID、ICCID、错误原因的详细日志,级别为WARN
|
||||
|
||||
#### Scenario: 触发成功时记录操作日志
|
||||
|
||||
- **WHEN** 自动触发实名检查成功
|
||||
- **THEN** 系统记录包含客户ID、ICCID的INFO级别日志,便于运维追踪
|
||||
|
||||
### Requirement: 配置管理和灵活性
|
||||
|
||||
系统 SHALL 支持通过配置管理自动触发功能的关键参数。
|
||||
|
||||
#### Scenario: 系统用户ID配置
|
||||
|
||||
- **WHEN** 系统初始化或配置更新时
|
||||
- **THEN** 系统从环境变量 `JUNHONG_AUTO_TRIGGER_SYSTEM_USER_ID` 读取系统用户ID
|
||||
|
||||
#### Scenario: 自动触发功能开关
|
||||
|
||||
- **WHEN** 需要临时关闭自动触发功能时
|
||||
- **THEN** 系统支持通过环境变量 `JUNHONG_ENABLE_AUTO_TRIGGER` 控制功能开关(默认开启)
|
||||
147
openspec/specs/polling-manual-trigger/spec.md
Normal file
147
openspec/specs/polling-manual-trigger/spec.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Capability: 手动触发功能
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 手动触发去重
|
||||
|
||||
系统 SHALL 对手动触发进行去重,避免重复执行,确保去重机制的时间一致性。
|
||||
|
||||
#### Scenario: 同卡重复触发
|
||||
|
||||
- **WHEN** 管理员对同一张卡连续触发两次实名检查
|
||||
- **THEN** 系统检查手动队列,如果卡ID已存在则不重复加入
|
||||
|
||||
#### Scenario: 使用 Redis Set 去重
|
||||
|
||||
- **WHEN** 批量触发 100 张卡,其中有 10 张重复
|
||||
- **THEN** 系统使用 Redis Set 临时存储卡ID,去重后再加入队列
|
||||
|
||||
#### Scenario: 手动触发与定时任务去重
|
||||
|
||||
- **WHEN** 卡A在手动队列中,同时定时队列也到期
|
||||
- **THEN** 系统优先执行手动触发,定时队列检测到卡已在执行则跳过
|
||||
|
||||
#### Scenario: 去重key TTL与日限制时间对齐
|
||||
|
||||
- **WHEN** 管理员在当天第一次触发某张卡的实名检查
|
||||
- **THEN** 系统设置去重key的TTL为24小时,与日限制周期保持一致,避免同一天内重复触发
|
||||
|
||||
#### Scenario: 去重key跨天自动清理
|
||||
|
||||
- **WHEN** 午夜12点过后,新一天开始
|
||||
- **THEN** 系统允许重新触发前一天已触发过的卡,去重key自动过期清理
|
||||
|
||||
### Requirement: 手动触发限流
|
||||
|
||||
系统 SHALL 对手动触发进行合理限流,既防止滥用又保证正常使用。
|
||||
|
||||
#### Scenario: 单次批量限制
|
||||
|
||||
- **WHEN** 管理员单次批量触发超过 1000 张卡
|
||||
- **THEN** 系统拒绝请求,提示超过单次限制
|
||||
|
||||
#### Scenario: 频率限制
|
||||
|
||||
- **WHEN** 管理员在 1 分钟内触发超过 10 次
|
||||
- **THEN** 系统拒绝请求,提示操作过于频繁,建议稍后再试
|
||||
|
||||
#### Scenario: 每日限制
|
||||
|
||||
- **WHEN** 管理员当日累计触发超过 500 次
|
||||
- **THEN** 系统拒绝请求,提示已达每日限制,支持正常运维需求和客户端自动触发
|
||||
|
||||
#### Scenario: 超级管理员无限制
|
||||
|
||||
- **WHEN** 超级管理员手动触发
|
||||
- **THEN** 系统不应用限流规则
|
||||
|
||||
#### Scenario: 客户端自动触发不受个人频次限制影响
|
||||
|
||||
- **WHEN** 客户端实名跳转自动触发功能调用手动触发服务
|
||||
- **THEN** 系统使用系统用户身份,不受个人用户的频次限制影响
|
||||
|
||||
### Requirement: 手动触发权限控制
|
||||
|
||||
系统 SHALL 控制手动触发的权限,支持系统用户身份的特殊权限适配。
|
||||
|
||||
#### Scenario: 普通管理员触发自己管理的卡
|
||||
|
||||
- **WHEN** 代理账号请求手动触发卡ID为 12345,该卡属于代理管理的店铺
|
||||
- **THEN** 系统验证权限通过,允许触发
|
||||
|
||||
#### Scenario: 代理账号无权触发其他店铺的卡
|
||||
|
||||
- **WHEN** 代理账号请求手动触发卡ID为 99999,该卡不属于代理管理的店铺
|
||||
- **THEN** 系统拒绝请求,返回权限不足错误
|
||||
|
||||
#### Scenario: 超级管理员可触发任意卡
|
||||
|
||||
- **WHEN** 超级管理员请求手动触发任意卡
|
||||
- **THEN** 系统跳过权限验证,允许触发
|
||||
|
||||
#### Scenario: 系统用户身份自动触发
|
||||
|
||||
- **WHEN** 客户端实名跳转功能使用系统用户身份调用手动触发
|
||||
- **THEN** 系统验证系统用户身份有效,允许触发任意ICCID的实名检查
|
||||
|
||||
#### Scenario: 企业账号禁止手动触发
|
||||
|
||||
- **WHEN** 企业账号请求手动触发卡ID为 12345,该卡属于企业
|
||||
- **THEN** 系统拒绝请求,返回无权限错误
|
||||
|
||||
### Requirement: 优化权限检查逻辑
|
||||
|
||||
系统 SHALL 简化和优化权限检查逻辑,减少代码重复,提高可维护性。
|
||||
|
||||
#### Scenario: 统一权限检查函数
|
||||
|
||||
- **WHEN** 系统需要验证用户对卡的管理权限
|
||||
- **THEN** 系统使用统一的权限检查函数,避免在多个地方重复实现相同逻辑
|
||||
|
||||
#### Scenario: 权限检查缓存优化
|
||||
|
||||
- **WHEN** 批量操作需要检查大量卡的权限
|
||||
- **THEN** 系统使用批量权限检查,减少数据库查询次数,提高性能
|
||||
|
||||
#### Scenario: 权限检查错误信息优化
|
||||
|
||||
- **WHEN** 权限检查失败时
|
||||
- **THEN** 系统返回明确的错误信息,帮助用户了解权限不足的具体原因
|
||||
|
||||
### Requirement: 增强错误处理和日志
|
||||
|
||||
系统 SHALL 提供更完善的错误处理和日志记录,提高系统可观测性。
|
||||
|
||||
#### Scenario: 详细错误日志记录
|
||||
|
||||
- **WHEN** 手动触发过程中发生错误(Redis连接失败、权限错误等)
|
||||
- **THEN** 系统记录包含错误原因、用户ID、卡ID、上下文信息的详细日志
|
||||
|
||||
#### Scenario: 性能关键路径日志
|
||||
|
||||
- **WHEN** 手动触发操作执行时间超过预期
|
||||
- **THEN** 系统记录性能日志,包含各阶段耗时,便于性能优化
|
||||
|
||||
#### Scenario: 异常情况恢复机制
|
||||
|
||||
- **WHEN** Redis临时不可用导致手动触发失败
|
||||
- **THEN** 系统记录错误并提供恢复建议,不影响其他正常功能
|
||||
|
||||
### Requirement: 配置管理和灵活性
|
||||
|
||||
系统 SHALL 支持通过配置管理手动触发功能的关键参数,提高系统灵活性。
|
||||
|
||||
#### Scenario: 频次限制可配置
|
||||
|
||||
- **WHEN** 需要调整手动触发的频次限制时
|
||||
- **THEN** 系统支持通过配置文件或环境变量调整每日限制、单次限制等参数
|
||||
|
||||
#### Scenario: 去重TTL可配置
|
||||
|
||||
- **WHEN** 需要调整去重机制的时间窗口时
|
||||
- **THEN** 系统支持配置去重key的TTL时长,默认24小时
|
||||
|
||||
#### Scenario: 系统用户ID配置
|
||||
|
||||
- **WHEN** 系统启动或配置更新时
|
||||
- **THEN** 系统从环境变量 `JUNHONG_MANUAL_TRIGGER_SYSTEM_USER_ID` 读取系统用户ID用于客户端自动触发
|
||||
Reference in New Issue
Block a user