41 lines
6.8 KiB
Markdown
41 lines
6.8 KiB
Markdown
## 1. 迁移与模型
|
||
- [x] 1.1 成对迁移:`tb_carrier` 扩列 `traffic_threshold_enabled`(默认 false)、`traffic_threshold_value`(正数 numeric)、`traffic_threshold_unit`(MB/GB);新增周期阈值停机锁表,`(carrier_id, card_id, period_start)` 唯一键(软删感知)、任务状态组(stop/resume:pending/submitted/confirmed/failed/unknown)、提交认领字段(`stop_submitted_at`/`resume_submitted_at`)、`anomaly_flag`、`failure_reason`。
|
||
- [x] 1.2 锁表模型与 Store(含唯一键冲突 23505 识别、条件更新认领方法)。
|
||
|
||
## 2. 阈值配置与权限
|
||
- [x] 2.1 carrier 创建/更新 DTO 增阈值字段与校验(正数、单位 MB/GB、周期复用 `data_reset_day`)。
|
||
- [x] 2.2 字段级平台守卫:Create/Update 仅 SuperAdmin/Platform 可写阈值字段,非平台提交即拒绝;List/Get 对非平台账号不返回阈值字段;不改 carrier 其余字段既有可见性。
|
||
- [x] 2.3 阈值新增、修改、启用、停用并入既有 carrier 更新审计前后值快照;同步 `cmd/gendocs` 文档。
|
||
|
||
## 3. 达量判定
|
||
- [x] 3.1 period_start 计算(显式 Asia/Shanghai,resetDay 取卡 carrier 的 `data_reset_day`)与 GB→MB 换算常量(1 GB = 1024 MB)。
|
||
- [x] 3.2 `ApplyTrafficObservation` 事务内达量判定:`ReadingAccepted == true` 且 carrier 阈值启用时,以 `last_gateway_reading_mb` 换算比较;达量写锁 + 停机 Outbox 事件同事务。
|
||
- [x] 3.3 锁唯一键 23505 单独捕获为「该周期已处理」跳过,与流量基线 CAS 冲突分离。
|
||
|
||
## 4. 可靠停复机消费者
|
||
- [x] 4.1 停机消费者:提交认领(`stop_submitted_at IS NULL` 条件更新);卡已 offline 跳过 Gateway 直接确认;否则复用 `stopCardWithRetry`;成功后写 `network_status=offline`、`stop_reason=channel_threshold`、`stopped_at`;失败/未知保留锁与任务状态,unknown 的 Integration Log 必带 RecoveryStrategy。
|
||
- [x] 4.2 复机消费者:结构同停机(`resume_submitted_at` 认领、复用 `resumeCardWithRetry`、成功后写 online/清停因/resumed_at);新增 2 个 Outbox 事件类型与 worker 消费者注册、装配。
|
||
|
||
## 5. 周期处理与恢复 cron
|
||
- [x] 5.1 周期处理 cron(`@every 1m` + `asynq.Unique(10m)` + 无 payload):扫描 period_start 过期持锁锁行(按锁行自身 carrier 的 `data_reset_day` 判断),条件更新认领解锁,逐卡评估(有效主套餐 + 流量未耗尽 + 实名 OK + 非风险 extend + 无其他停因),全满足写复机事件,否则只解锁。
|
||
- [x] 5.2 恢复扫描 cron(同形态):扫描未决子任务(`domain.UnresolvedTaskStatuses()` = `{submitted, unknown, failed}`,见 8.1)的锁,只查询网关状态回填、绝不重复发起停复机;确认成功补写卡状态;自提交起超 30 分钟(常量)仍不可查 → `anomaly_flag` + 安全失败原因转人工,不自动删除锁。
|
||
- [x] 5.3 worker 注册两个 cron 与装配;周期处理与恢复 Handler 审计上下文固定 `ActorKind=AuditActorScheduledJob`、`Source=AuditSourceScheduler`。
|
||
|
||
## 6. 停因与复机拒绝
|
||
- [x] 6.1 新增 `StopReasonChannelThreshold = "channel_threshold"` 常量;确认不纳入 `isPollingStopReason` 与 `isDeviceScopeReason`。
|
||
- [x] 6.2 持锁拒绝覆盖四个入口(`resumeSingleCard`、`ManualStartCard`、`ForceStartCard`、`StartMachineSeparatedCard`):拒绝 + 审计 denied + 锁保留。
|
||
|
||
## 7. 验证
|
||
- [x] 7.1 隔离库验证:平台配置阈值(含 GB 换算)成功 + 审计前后值,代理/企业写被拒且读不到字段;达量触发停机、同周期重复判定唯一冲突不重复锁/不重复停机;持锁期四入口复机拒绝且锁保留;停机 unknown 保留 + RecoveryStrategy + 恢复扫描确认补写卡状态、超期 anomaly 转人工;新周期解锁、条件满足才复机、其他停因(stop_reason 非空非本停因或风险 extend)只解锁;停用后新卡不再锁、已持锁卡新周期照常解锁复机;换运营商旧锁保留、新周期按新 carrier 重置日;第 12 项套餐预警与既有 3 种停因停复机回归不变;迁移 up/down/up。
|
||
- [x] 7.2 运行 `gofmt -w`、`go build ./cmd/api ./cmd/worker`、`go run cmd/gendocs/main.go`、`openspec validate add-carrier-channel-traffic-thresholds --strict` 与 `openspec doctor --json`;自动化测试按项目决策为 N/A。
|
||
|
||
## 8. 审查修复
|
||
- [x] 8.1(B1)停机/复机调用明确失败或结果未知的子任务不退出恢复扫描:扫描谓词与超期判定改用未决集合 `{submitted, unknown, failed}`、`markTaskConfirmed` 的 expected 改为未决集合(confirmed 仍只写一次)、部分索引谓词同步为未决集合(000227,已确认生产库未应用)。
|
||
- [x] 8.2(B2)锁行引用的运营商缺失或 `data_reset_day` 非法时给出出路:周期处理按跨期语义认领解锁并同事务标记 `anomaly_flag=1` + 安全原因 + Warn 日志转人工,不写复机事件、不调运营商、不静默跳过;`ActiveLock` 维持配置缺失时不放开复机的 fail-closed 语义。
|
||
- [x] 8.3(R1)恢复 `stopCardWithRetry`/`resumeCardWithRetry` 在 Integration Log 写入失败分支的「未完成」审计,保持既有审计覆盖与返回语义不变。
|
||
- [x] 8.4(R2)按 ENG-AUDIT-001 显式登记锁行状态迁移(`unlocked`、`anomaly_flag=1`)的审计决定(不新建独立审计动作 + 承载方式),并在 design.md 补「审计决定」小节。
|
||
- [x] 8.5(R4)以 `specs/package-lifecycle/spec.md` MODIFIED delta 建模「持通道阈值锁时拒绝复机」对既有套餐生命周期自动复机路径的行为变更,并更新 proposal 的 Modified Capabilities。
|
||
- [x] 8.6 修正 design.md 与实现不符的表述(失败/未知与不可判定 carrier 的收敛路径、四入口拒绝顺序与返回、认领谓词)。
|
||
- [x] 8.7 回归验证:B1(unknown/failed 扫描与收敛、超期 anomaly)与 B2(carrier 缺失/重置日非法的解锁与异常)**用生产等价句柄(Service.s.db 为根连接池)实测**——fixture 以已提交短事务创建、逐条断言后删除并核对 0 残留(回滚事务式写法会掩蔽连接池自锁,不再用于这两条路径);R1(审计写入)用回滚事务实测;gofmt/go build/go vet/openspec validate --strict/context-health 全通过。
|
||
- [x] 8.8(B3)修复连接池自锁:新增事务句柄版 `markAnomalyInTx`(`markAnomaly` 以自身连接池委托同一实现),`processUnresolvableLock` 在事务闭包内用 `tx` 标记异常,使解锁与 anomaly 同事务提交(否则解锁已持行锁、另一条连接上的 anomaly 更新会等待该行锁直到语句超时,表现为不可判定锁行停在 locked 且周期处理 cron 每分钟空转);并完成同类自锁全面排查(结论:本 Change 仅此一处)。
|