Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
81 lines
5.4 KiB
Markdown
81 lines
5.4 KiB
Markdown
# Tasks: fix-stop-resume-lifecycle-engine
|
||
|
||
## 阶段 1:基础层修复(常量 + 停机写入)
|
||
|
||
> 优先级最高,其他所有阶段的复机逻辑都依赖 stop_reason 写入正确。
|
||
|
||
- [x] **1.1** `pkg/constants/iot.go`:新增 `StopReasonProtectPeriod = "protect_period"` 常量
|
||
- [x] **1.2** `pkg/constants/redis.go`:新增 `RedisCardResumeLockKey(cardID uint) string` 函数,Key 格式 `card:resume:lock:{cardID}`
|
||
- [x] **1.3** `internal/task/polling_handler.go`:`stopCards` 函数 DB 更新中补充 `"stop_reason": constants.StopReasonTrafficExhausted` 和 `"stopped_at": now`
|
||
- [x] **1.4** `internal/task/polling_handler.go`:`HandleProtectConsistencyCheck` 中的 `"stop_reason": "保护期一致性检查自动停机"` 替换为 `constants.StopReasonProtectPeriod`
|
||
|
||
---
|
||
|
||
## 阶段 2:ResumeCardIfStopped 核心重构
|
||
|
||
> 扩展签名 + 加未实名检查 + 加并发锁 + 加 device 支持。其他阶段依赖此函数签名。
|
||
|
||
- [x] **2.1** `internal/service/iot_card/stop_resume_service.go`:`ResumeCallback` 接口方法签名改为 `ResumeCardIfStopped(ctx context.Context, carrierType string, carrierID uint) error`
|
||
- [x] **2.2** `internal/service/iot_card/stop_resume_service.go`:`ResumeCardIfStopped` 实现:
|
||
- 参数改为 `(ctx, carrierType, carrierID)`
|
||
- `carrierType == "iot_card"` 时:读取单卡信息 → 检查实名 → 检查 stop_reason → 加 Redis 锁 → 调 Gateway → 更新 DB
|
||
- `carrierType == "device"` 时:查询设备绑定的所有已绑定卡(`bind_status=bound`) → 逐一执行 iot_card 复机逻辑(含实名检查)
|
||
- Gateway 成功但 DB 失败时记录 ERROR 日志(含卡 ID、ICCID),不返回 error 阻断流程
|
||
- [x] **2.3** `internal/service/package/activation_service.go`:更新 `ResumeCallback` 接口定义(与 2.1 一致)
|
||
- [x] **2.4** `internal/service/package/activation_service.go`:两处 `resumeCallback.ResumeCardIfStopped` 调用点更新传参(传入 `carrierType`、`carrierID`)
|
||
- [x] **2.5** 编译检查:`lsp_diagnostics` 确认无编译错误
|
||
|
||
---
|
||
|
||
## 阶段 3:购买套餐后复机(order service)
|
||
|
||
- [x] **3.1** `internal/service/order/service.go`:`Service` 结构体增加 `resumeCallback ResumeCallback` 字段(复用 `activation_service.go` 中定义的接口)
|
||
- [x] **3.2** `internal/service/order/service.go`:新增 `SetResumeCallback(callback ResumeCallback)` 方法
|
||
- [x] **3.3** `internal/service/order/service.go`:`HandlePaymentCallback` 在事务成功后(`if err != nil { return err }` 之后),根据订单的 `carrierType` 和 `carrierID` 调用 `s.resumeCallback.ResumeCardIfStopped`(检查 nil,异步 goroutine 调用)
|
||
- 仅当 `activateMainPackage` 创建了 `status=1` 的套餐时才触发(即 `!hasActiveMain` 分支)
|
||
- 传递载体信息:从 order 中读取 `IotCardID` 或 `DeviceID`
|
||
|
||
---
|
||
|
||
## 阶段 4:Depleted 套餐到期处理
|
||
|
||
- [x] **4.1** `internal/polling/package_activation_handler.go`:`findExpiredMainPackages` 查询条件改为 `WHERE status IN (1,2) AND expires_at <= now AND master_usage_id IS NULL`
|
||
- [x] **4.2** 验证:`processExpiredPackage` 的状态更新 `tx.Model(pkg).Update("status", constants.PackageUsageStatusExpired)` 对 `status=2` 的套餐同样生效(GORM 无状态前置条件,直接更新,逻辑正确无需修改)
|
||
|
||
---
|
||
|
||
## 阶段 5:流量重置后复机(reset service)
|
||
|
||
- [x] **5.1** `internal/service/package/reset_service.go`:`ResetService` 结构体增加 `resumeCallback ResumeCallback` 字段
|
||
- [x] **5.2** `internal/service/package/reset_service.go`:新增 `SetResumeCallback(callback ResumeCallback)` 方法
|
||
- [x] **5.3** `internal/service/package/reset_service.go`:`resetDailyUsageWithDB`、`resetMonthlyUsageWithDB`、`resetYearlyUsageWithDB` 三个方法在批量重置完成后,遍历被重置的套餐列表,对每个套餐的 `IotCardID` 异步调用 `resumeCallback.ResumeCardIfStopped(ctx, "iot_card", cardID)`(device 类型套餐传 `"device"` 和 `DeviceID`,nil 检查)
|
||
|
||
---
|
||
|
||
## 阶段 6:Bootstrap 注入
|
||
|
||
- [x] **6.1** `internal/bootstrap/worker_services.go`:`stopResumeService` 创建后,调用:
|
||
- `orderService.SetResumeCallback(stopResumeService)` (注意:`orderService` 已在 bootstrap 中创建)
|
||
- `resetService.SetResumeCallback(stopResumeService)`
|
||
- `usageService.SetStopResumeCallback(stopResumeService)`(已有,确认保留)
|
||
- `activationService.SetResumeCallback(stopResumeService)`(已有,确认保留)
|
||
|
||
---
|
||
|
||
## 阶段 7:知识库更新
|
||
|
||
- [x] **7.1** 更新知识库 `卡管业务整理/11-停机与复机流程.md`:在"这条链路的问题"表中标注已修复项;补充"自动停复机触发点"说明
|
||
- [x] **7.2** 更新知识库 `卡管业务整理/08-套餐使用生命周期.md`:补充"Depleted 套餐到期处理"流程节点
|
||
- [x] **7.3** 更新知识库 `修正业务/修正业务总览.md`:将本次修复的 Bug 从待修复移到已修复,新增企业设备停复机的独立追踪条目
|
||
|
||
---
|
||
|
||
## 验证清单
|
||
|
||
- [x] **V1** 编译通过:`go build ./...` 无错误
|
||
- [ ] **V2** DB 查询验证:停机卡买套餐后 `network_status=1`,`stop_reason` 清空
|
||
- [ ] **V3** DB 查询验证:流量耗尽的 3 个月套餐在模拟重置后复机
|
||
- [ ] **V4** DB 查询验证:Depleted 主套餐到期后,下一个 Pending 套餐激活为 status=1
|
||
- [ ] **V5** 日志验证:未实名卡触发复机时出现 WARN 日志而非 ERROR
|
||
- [ ] **V6** 日志验证:`stopCards` 停机后 `stop_reason=traffic_exhausted` 写入 DB
|