feat: 卡网关按运营商账户限流并记录停复机调用
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m33s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m33s
- 新增网关限流:按本系统运营商账户(卡 carrier_id)→ 限流组 → 操作限制每秒请求数,每次 HTTP 尝试计量,默认限流、仅后台人工点击/通道阈值/自动续费/手动刷新豁免;支持只统计与拦截两种模式,默认只统计 - Asynq 遇限流不计失败、固定短延迟重试;轮询任务与停复机业务重试遇限流短延迟重排 - 停机、复机不再使用网关内部重试,只发一次请求 - 网关返回结构化错误;新增上游错误分类规则表与测试接口 - 新增按月分区的停复机记录表:系统自动停复机只写记录,不再写统一审计与集成日志;普通轮询查询不再写集成日志 - 新增限流组、错误分类、停复机记录、按账户调用统计与枚举后台接口 - 迁移 000243~000246;同步 OpenSpec 提案(含后续 6 个 change)与技术方案、工程约束
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-24
|
||||
37
openspec/changes/add-stop-resume-command-queue/design.md
Normal file
37
openspec/changes/add-stop-resume-command-queue/design.md
Normal file
@@ -0,0 +1,37 @@
|
||||
## Context
|
||||
|
||||
完整设计见 `docs/engineering/轮询按运营商账户限流技术方案.md` 第三部分第 6、6.1、6.2、11.5、12 节,第 9 节停复机出口开关。
|
||||
|
||||
现状:`StopResumeService.EvaluateAndAct`(`stop_resume_service.go:113`)判断后当场调网关,`stopCardWithRetry` / `resumeCardWithRetry` 3 次、每次 sleep 2 秒;设备维度原因抢 `RedisPollingDeviceOpLockKey`(SetNX 30 秒,抢不到跳过)逐张停;`resumeAfterActivation`(4 处)、`reset_service.go:400`、`order/service.go:2196` 起 goroutine 调 `ResumeCardIfStopped`;退款 `refund/service.go:1258` 调 `ManualStopCard`,失败由 Outbox 与 `RecoverRefundPostProcessing` 反复补发;套餐到期由套餐轮询发现。第 5 个 Change 已提供账户队列、分发器、NSQ、失败告警表;第 1 个 Change 已提供限流、错误分类与停复机记录。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** 自动停复机一个出口、不风暴、不翻转、不丢;设备下各卡一致;暂停轮询的卡套餐失效照常停。
|
||||
|
||||
**Non-Goals:** 不改停复机业务规则(第 4 个 Change 已定口径);不改后台人工点击、通道阈值、自动续费的停复机;不做提前停机、停机前续费等老系统规则。
|
||||
|
||||
## Decisions
|
||||
|
||||
1. **Redis 结构**:账户 `polling:carrier:{cid}:stop_resume`(ZSET)与 `:sr_running`;卡 `polling:card:{id}:stop_resume_cmd`(Hash:动作、原因、场景、来源、版本、失败计数、首次失败、最近错误、待确认)、`:sr_claim`、`:sr_exec`、`:sr_last_call`;设备 `polling:device_eval:queue`(ZSET)、`polling:device:{id}:eval_ver`、`:eval_scenes`(Set)、`:eval_claim`、`:eval_lock`;分发锁 `polling:device_eval:dispatch_lock`。
|
||||
2. **统一判断函数**:从 `EvaluateAndAct`、`resumeSingleCard`、`resumeDeviceCards`、`ResumeCardIfStopped` 抽出"输入卡与资产数据 + 场景 → 目标状态与原因",入口与执行器共用;执行部分抽出供执行器调用一次网关。
|
||||
3. **版本号**:覆盖指令 `HINCRBY ver`;领取时随消息带版本;收尾 Lua 比较版本决定移除或保留。
|
||||
4. **单卡最小重试间隔按卡合计**:上游"卡状态变更每十分钟只允许调用 1 次"对停机与复机共同计数;领取 Lua 校验 `sr_last_call`,执行器调用前写。
|
||||
5. **结果未知在队列内闭环**:不复用现有人工核对策略(`GatewayCardCommandUnknownRecoveryStrategy` 只有人工核对);确认查询属于查状态,计入停复机所在限流组,不受单卡最小间隔约束。
|
||||
6. **设备判断**:`ZADD LT` 合并;`SADD eval_scenes`;领取带版本与场景快照;收尾 `SREM` 已处理场景并比较版本。锁内只读与写指令,毫秒级;与旧路径的 `RedisPollingDeviceOpLockKey` 互斥。
|
||||
7. **全局开关**:`polling.stop_resume_queue_enabled` 受控系统配置;所有入口通过一个"请求评估"门面按开关决定入队还是旧路径当场执行,避免分散判断。停复机与设备判断的分发不受 `scheduler_mode` 限制:开关开启后对全部账户领取(`scheduler_mode = legacy` 时套餐步骤仍在旧 handler 内,同样改为请求评估)。
|
||||
8. **退款完成语义**:请求写入成功即标记 `asset_reset`,补偿扫描只覆盖"请求未写入"的情况,不再因停机失败重放。
|
||||
9. **到期即停入口**:`package_activation_handler.go` 到期失效处理处判断无其他有效主套餐后请求评估(场景"套餐到期");接续激活路径不变。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [停复机比现在晚 1~2 秒] → 已接受;停复机先于卡任务领取并可预留 QPS。
|
||||
- [请求评估写 Redis 失败] → 记日志,由该卡下一次套餐步骤兜底;暂停轮询的卡套餐失效停机按已定口径不补偿。
|
||||
- [流量重置日集中复机] → 按账户停复机 QPS 与执行上限平滑执行,监控队列长度。
|
||||
- [开关切换中旧路径与新路径并存] → 设备锁互斥;开关关闭时队列指令保留,重新开启时执行前重新判断。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 迁移插入开关(默认关闭)与告警阈值。
|
||||
2. 测试环境部署,开关关闭时行为与现状一致;开启后按 tasks 第 10 节核对;演练开、关、再开。
|
||||
3. 生产发布后开启开关,首日监控停复机成功率、队列长度与失败告警。
|
||||
4. 回退:关闭开关。
|
||||
35
openspec/changes/add-stop-resume-command-queue/proposal.md
Normal file
35
openspec/changes/add-stop-resume-command-queue/proposal.md
Normal file
@@ -0,0 +1,35 @@
|
||||
## Why
|
||||
|
||||
自动停复机没有独立队列,在哪个任务里触发就在哪里同步调网关并 sleep 重试;激活、重置、订单后的复机在裸 goroutine 里执行,失败只打日志;退款走人工停机接口,失败后 Outbox 重投与补偿扫描反复补发(生产 2026-10-22:24 小时失败约 26.8 万次);设备下多张卡(生产约 58% 设备跨运营商账户)可能被并发的判断互相覆盖,造成翻转。另外暂停轮询的卡套餐到期、退款后无套餐也应停机,但现有设计会漏停。本 Change 是《轮询按运营商账户限流技术方案》第五部分第 3 节第 6 步。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 新增停复机指令队列:所有自动入口(套餐步骤、Outbox 三个卡观测消费者、套餐激活、流量重置、订单支付后、退款后资产处理、套餐到期、运营商账户实名管控修改)只"请求评估",由统一判断函数生成指令,进入卡所属运营商账户的停复机队列,优先级最高、先于卡任务领取。
|
||||
- 同卡只保留一条指令,新覆盖旧并递增版本号;收尾按版本号判断,执行期间生成的新指令不会被删除。
|
||||
- 执行器:执行前按规则重新判断;只调用一次上游、不 sleep 重试;单卡最小重试间隔(停机与复机合计,按账户配置);结果未知按"确认延迟 → 查状态 → 已是目标状态结束 / 否则按间隔重发"处理,不沿用现有人工核对恢复;失败按错误分类退避、告警,指令不丢弃;终态不重试。
|
||||
- 设备维度判断队列:按设备合并请求、保存并合并请求场景、版本号再判断、设备锁内只判断不调网关,为设备下各卡生成指令进各自账户队列。
|
||||
- 到期即停:套餐到期且资产无其他有效套餐时,以"套餐到期"场景直接请求评估(不经过套餐步骤)。
|
||||
- **BREAKING** 暂停轮询口径:暂停轮询的卡或设备,套餐到期、退款后无可用主套餐照常停机(只触发一次,不做补偿扫描);其他自动停复机仍不做。
|
||||
- **BREAKING** 退款后资产处理不再调用 `ManualStopCard`:改为请求评估,请求写入成功即视为资产后处理完成,停机失败由指令队列退避与告警处理,不再触发 Outbox 重投与补偿扫描。
|
||||
- **BREAKING** 激活、重置、订单后的复机去掉裸 goroutine,改为事务提交后请求评估。
|
||||
- 运营商账户实名管控修改后对受影响的卡批量请求评估(0 → 1 在线未实名,1 → 0 停机未实名),按账户 QPS 平滑执行。
|
||||
- 全局开关 `polling.stop_resume_queue_enabled`(受控系统配置,默认关闭):开启后所有账户的自动停复机经指令队列,不按账户灰度;关闭即回退为各入口当场执行。
|
||||
- 停复机方向的失败接入失败告警表;监控增加停复机成功、失败、重试与指令队列长度。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `stop-resume-command-queue`:请求评估与统一判断、指令覆盖与版本、执行器与单卡最小重试间隔、结果未知确认、失败退避与告警、设备维度判断与场景合并、自动入口统一、全局开关与回退。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `card-stop-resume`:"暂停轮询的卡不自动停复机"增加套餐到期与退款后无套餐照常停机;新增"套餐到期即停"。
|
||||
|
||||
## Impact
|
||||
|
||||
- 依赖:第 1 个(限流、错误分类、停复机记录)、第 4 个(新规则转正)、第 5 个(账户调度、NSQ、失败告警表)Change 已上线。
|
||||
- 数据库:`tb_system_config` 插入 `polling.stop_resume_queue_enabled` 与结果未知告警阈值;新的成对迁移。
|
||||
- 代码:`internal/service/iot_card/stop_resume_service.go`(`EvaluateAndAct` 只判断、生成指令;抽出执行部分)、`stop_resume_unified.go`;`internal/infrastructure/cardobservation/event.go`;`internal/service/package/activation_service.go`、`reset_service.go`、`internal/service/order/service.go`;`internal/service/refund/service.go` `stopAsset` 与 `internal/infrastructure/commissiondelivery/event.go` 补偿扫描;`internal/task/package_activation_handler.go`(到期即停);`internal/service/carrier/service.go`;分发器与 NSQ 消费者新增停复机与设备判断;`RedisPollingDeviceOpLockKey` 轮询路径替换。
|
||||
- 接口:调度参数中的停复机参数生效;`/polling-failure-alerts` 包含停复机方向;`/iot-cards/:id/polling-state` 返回当前指令;监控增加停复机指标。
|
||||
- 行为:自动停复机比现在晚约 1~2 秒;后台人工点击、通道阈值、自动续费的停复机仍直接执行。
|
||||
@@ -0,0 +1,50 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 暂停轮询的卡不自动停复机
|
||||
卡或其绑定设备暂停轮询的,系统 SHALL NOT 对该卡发起由检查结果或周期评估触发的自动停复机(流量用完、实名不满足、卡状态检查后的停复机、套餐步骤评估),也 SHALL NOT 发起套餐激活、套餐重置、充值、排队激活、实名激活、运营商账户实名管控修改等入口触发的自动复机;人工停机与人工复机照常执行。套餐失效类停机 MUST 照常执行,不看暂停轮询:套餐到期且资产无其他有效套餐、退款后资产无可用主套餐时,系统按"套餐到期"或"退款"场景请求评估并停机;该停机对暂停轮询的卡只触发一次,之后由停复机指令按失败处理重试,请求写入失败或缓存数据丢失时不做补偿扫描。请求评估与停复机指令 MUST 携带场景,执行前重新判断沿用同一场景。网关扩展状态为风险停机或已销户的卡 MUST 关闭该卡自身轮询,不论是否绑定设备:查卡状态返回该扩展状态时关闭;停复机调用直接返回命中"自动关闭轮询"规则的终态时,同样关闭并回写网关扩展状态。关闭只作用于该卡,MUST NOT 级联关闭设备与设备下其他卡。机卡分离停机等其他运营商异常状态 MUST NOT 自动关闭轮询,由人工判断。未暂停轮询的卡,不论运营商扩展状态如何,均按停机与复机条件处理,上游拒绝复机时按现有失败处理。
|
||||
|
||||
#### Scenario: 暂停轮询的卡充值后
|
||||
- **WHEN** 已被人工暂停轮询的停机卡充值并激活套餐
|
||||
- **THEN** 系统不自动复机;人工复机照常可用
|
||||
|
||||
#### Scenario: 暂停轮询的卡套餐到期
|
||||
- **WHEN** 暂停轮询的开机卡套餐到期且无其他有效套餐
|
||||
- **THEN** 系统以"套餐到期"场景生成停机指令并执行
|
||||
|
||||
#### Scenario: 暂停轮询的卡退款后
|
||||
- **WHEN** 暂停轮询的开机卡退款后资产无可用主套餐
|
||||
- **THEN** 系统以"退款"场景生成停机指令并执行,只触发一次
|
||||
|
||||
#### Scenario: 暂停轮询的卡流量用完
|
||||
- **WHEN** 暂停轮询的卡流量用完
|
||||
- **THEN** 系统不自动停机
|
||||
|
||||
#### Scenario: 恢复轮询后
|
||||
- **WHEN** 维护者为该卡恢复轮询
|
||||
- **THEN** 下一次评估起系统按停机与复机条件正常处理
|
||||
|
||||
#### Scenario: 设备卡出现机卡分离停机
|
||||
- **WHEN** 设备下的卡运营商扩展状态为机卡分离停机且未被暂停轮询
|
||||
- **THEN** 系统不自动暂停轮询,按停机与复机条件处理
|
||||
|
||||
#### Scenario: 设备下的卡被销户
|
||||
- **WHEN** 查卡状态返回绑定设备的某张卡扩展状态为已销户
|
||||
- **THEN** 系统关闭该卡轮询,设备与设备下其他卡的轮询不变
|
||||
|
||||
#### Scenario: 复机返回风险停机
|
||||
- **WHEN** 复机调用返回"当前卡为风险停机状态,仅支持风险复机"且该规则标记自动关闭轮询
|
||||
- **THEN** 系统不重试,关闭该卡轮询并回写扩展状态为风险停机
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 套餐到期即停
|
||||
|
||||
系统 SHALL 在套餐到期失效处理时,若该资产已无其他有效主套餐,直接以"套餐到期"场景请求停复机评估:独立卡当场判断并生成停机指令,设备请求设备维度判断并记录该场景;MUST NOT 依赖卡任务的套餐步骤发现过期。存在待生效的后续主套餐时,按现有到期接续流程先激活后续套餐,不发起停机。
|
||||
|
||||
#### Scenario: 独立卡到期无后续套餐
|
||||
- **WHEN** 独立卡主套餐到期且无后续主套餐
|
||||
- **THEN** 系统立即生成 `no_package` 停机指令,不等待下一次套餐检查
|
||||
|
||||
#### Scenario: 到期有后续套餐
|
||||
- **WHEN** 主套餐到期且存在待生效的后续主套餐
|
||||
- **THEN** 系统按接续流程激活后续套餐,不生成停机指令
|
||||
@@ -0,0 +1,81 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 自动停复机统一经指令队列
|
||||
|
||||
在 `polling.stop_resume_queue_enabled` 开启时,系统 SHALL 让套餐步骤、Outbox 卡观测消费者(流量增加、网络状态变化、实名变化)、套餐激活、流量重置、订单支付后、退款后资产处理、套餐到期、运营商账户实名管控修改这些自动入口只请求评估:按统一判断函数(暂停轮询 → 停机条件 → 复机条件 → 通道阈值锁)得出目标状态,需要动作时生成停复机指令并放入卡所属运营商账户的停复机队列,MUST NOT 在入口当场调用网关。请求评估本身只写共享缓存;写入失败 MUST 记日志,并由该卡下一次套餐步骤兜底。后台人工点击、通道阈值、自动续费发起的停复机 MUST 照旧直接执行。开关关闭时各入口 MUST 恢复当场执行;队列中未执行的指令保留,再次开启后继续执行,执行前重新判断。开关 MUST 全局生效,不按运营商账户灰度。
|
||||
|
||||
#### Scenario: 订单支付后复机
|
||||
- **WHEN** 客户支付后套餐生效,卡处于停机且满足复机条件
|
||||
- **THEN** 订单事务提交后系统生成复机指令,由指令执行器调用上游,支付流程不等待也不起 goroutine
|
||||
|
||||
#### Scenario: 开关关闭
|
||||
- **WHEN** 超级管理员关闭 `polling.stop_resume_queue_enabled`
|
||||
- **THEN** 各自动入口恢复当场执行,已在队列中的指令保留不删
|
||||
|
||||
### Requirement: 同卡一条指令与版本号
|
||||
|
||||
系统 SHALL 对同一张卡只保留一条停复机指令,新指令覆盖旧指令,方向相反时以最新判断为准、失败计数清零;每次生成或覆盖 MUST 递增指令版本号。执行器领取时记下版本号;收尾时版本号与领取时一致才移除指令,不一致 MUST 保留新指令,并把执行时间设为 max(当前时间, 本卡最近一次停复机调用时间 + 单卡最小重试间隔)。
|
||||
|
||||
#### Scenario: 停机执行中客户付费
|
||||
- **GIVEN** 卡 A 停机指令(版本 1)正在执行
|
||||
- **WHEN** 执行期间客户付费生成复机指令(版本 2),随后停机成功
|
||||
- **THEN** 收尾保留复机指令,在单卡最小重试间隔后执行复机
|
||||
|
||||
### Requirement: 指令执行与单卡最小重试间隔
|
||||
|
||||
系统 SHALL 优先于卡任务领取停复机指令,领取数不超过停复机所在限流组本秒剩余 QPS 与账户停复机执行上限减执行中数。同一张卡两次停复机网关调用(停机与复机合计)间隔 MUST 不小于账户配置的单卡最小重试间隔,未到间隔时推后执行时间、不发起调用。执行前系统 MUST 按指令记录的场景重新判断,已是目标状态或条件不再满足则结束指令。网关只调用一次,不 sleep 重试;成功后按现有回写更新卡状态、停机原因与网络状态序列,并写停复机记录。
|
||||
|
||||
#### Scenario: 停机后几分钟内购买套餐
|
||||
- **GIVEN** 卡 A 5 分钟前被停机,单卡最小重试间隔 10 分钟
|
||||
- **WHEN** 客户购买套餐生成复机指令
|
||||
- **THEN** 系统在停机后满 10 分钟时才调用复机,期间不调用上游
|
||||
|
||||
#### Scenario: 执行前已是目标状态
|
||||
- **WHEN** 停机指令执行前重新判断发现卡已停机
|
||||
- **THEN** 系统结束指令,不调用上游停机
|
||||
|
||||
### Requirement: 停复机结果未知的确认
|
||||
|
||||
停复机调用超时、连接失败或上游返回"调用成功""操作进行中""请求被合并"等已受理类频率限制时,系统 SHALL 置待确认标记并保留指令,在账户配置的确认延迟后再次领取:先按规则重新判断,条件不再满足则结束;否则查一次卡状态(受限流、不受单卡最小重试间隔约束),已是目标状态则按成功回写并结束;不是目标状态则清除待确认,满足单卡最小重试间隔时重新调用停复机,否则推后;查状态失败 MUST 保持待确认并按退避重查,不发起停复机。连续结果未知达到阈值时写失败告警,指令不丢弃。
|
||||
|
||||
#### Scenario: 超时后确认已停机
|
||||
- **WHEN** 停机超时,确认延迟后查状态显示已停机
|
||||
- **THEN** 系统按成功回写,结束指令,停复机记录结果为确认成功
|
||||
|
||||
#### Scenario: 查状态也失败
|
||||
- **WHEN** 确认查询失败
|
||||
- **THEN** 系统不发起停机,按退避再次查询
|
||||
|
||||
### Requirement: 停复机失败分类处理
|
||||
|
||||
系统 SHALL 按错误分类处理停复机失败:频率限制不计连续失败,按单卡最小重试间隔与上游窗口退避;终态不可操作不重试、结束指令、写告警(标记自动关闭轮询的终态按 `card-stop-resume` 关闭该卡轮询),停机方向且上游已不可用时按目标状态视为已停;已是目标状态按成功处理;账户配置错误首次即产生账户级告警;临时故障、网络超时、业务错误计入连续失败按退避重试。达到告警阈值后写失败告警,指令 MUST NOT 丢弃,按最长退避继续重试。
|
||||
|
||||
#### Scenario: 停机持续失败
|
||||
- **WHEN** 停机连续失败达到阈值
|
||||
- **THEN** 系统写停复机方向的失败告警,指令保留并按最长退避继续重试
|
||||
|
||||
### Requirement: 设备维度判断合并
|
||||
|
||||
需要设备维度判断时,系统 SHALL 不在调用方当场判断,而是请求一次设备判断:递增设备判断版本号、把请求场景加入设备的场景集合、设备加入设备判断队列(同一设备只有一个成员)。设备判断执行时 MUST 抢设备判断锁(锁内不调网关),读取最新数据按统一判断函数判断(各卡按所属运营商账户判断实名,暂停轮询按场景集合判断),需要动作时为设备下每张卡生成指令(记录触发场景)进入各卡所属账户的停复机队列。收尾时只移除本次已处理的场景;版本号变大时 MUST 用最新数据再判断一次。同一设备同一时刻只有一个判断在执行。旧路径正在对该设备执行停复机时(持现有设备锁),设备判断 MUST 推后、不并行执行。
|
||||
|
||||
#### Scenario: 跨账户设备流量用完
|
||||
- **GIVEN** 设备 D 有 3 张卡分属联通、移动、广电账户
|
||||
- **WHEN** 卡 1 流量入账使设备流量用完,同时卡 2 的消费者读到扣减前数据
|
||||
- **THEN** 两次请求合并为一次设备判断,按最新数据为 3 张卡各生成停机指令,进入各自账户队列
|
||||
|
||||
#### Scenario: 到期与普通请求合并
|
||||
- **GIVEN** 设备 D 已暂停轮询
|
||||
- **WHEN** 设备套餐到期请求与一次普通轮询请求在判断前合并
|
||||
- **THEN** 场景集合含"套餐到期",系统按套餐失效放行停机
|
||||
|
||||
### Requirement: 自动入口的改造语义
|
||||
|
||||
系统 SHALL 按以下语义改造自动入口:套餐激活、流量重置、订单支付后在业务事务提交后请求评估,不起 goroutine 当场复机;退款后资产处理在资产无可用主套餐时请求评估(设备按设备维度判断),请求写入成功即标记资产后处理完成,MUST NOT 调用人工停机接口,MUST NOT 因停机失败触发 Outbox 重投或补偿扫描;运营商账户实名管控由 0 改为 1 时对该账户在线且未实名的卡、由 1 改为 0 时对停机且未实名的卡批量请求评估(设备卡按设备),按账户 QPS 平滑执行;卡或设备恢复轮询时不另加触发,由套餐步骤随即评估。
|
||||
|
||||
#### Scenario: 退款后上游终态
|
||||
- **WHEN** 退款后资产无可用主套餐,卡在上游已销户
|
||||
- **THEN** 退款资产后处理标记完成,停机指令按终态结束并告警,不再反复补发
|
||||
|
||||
#### Scenario: 开启实名管控
|
||||
- **WHEN** 超级管理员把账户 A 实名管控由 0 改为 1
|
||||
- **THEN** 系统对 A 下在线未实名的卡请求评估,停机按 A 的停复机 QPS 逐步执行
|
||||
61
openspec/changes/add-stop-resume-command-queue/tasks.md
Normal file
61
openspec/changes/add-stop-resume-command-queue/tasks.md
Normal file
@@ -0,0 +1,61 @@
|
||||
## 0. 前置确认
|
||||
|
||||
- [ ] 0.1 确认第 1、4、5 个 Change 已上线。
|
||||
|
||||
## 1. 迁移与配置
|
||||
|
||||
- [ ] 1.1 新增成对迁移:`tb_system_config` 插入 `polling.stop_resume_queue_enabled`(默认 false)、结果未知告警阈值、停复机退避序列与告警阈值;注册受控系统配置项。
|
||||
|
||||
## 2. 统一判断与请求评估
|
||||
|
||||
- [ ] 2.1 抽出统一判断函数(暂停轮询按场景 → 停机条件 → 复机条件 → 通道阈值锁),`EvaluateAndAct`、`resumeSingleCard`、`resumeDeviceCards`、`ResumeCardIfStopped` 改为调用它。
|
||||
- [ ] 2.2 新增"请求评估(卡或设备、场景、来源)"门面:开关开启时单卡生成指令、设备请求设备判断;关闭时走旧路径当场执行。
|
||||
- [ ] 2.3 指令写入 Lua:覆盖并 `HINCRBY ver`、方向相反清零失败计数、放入卡所属账户 `stop_resume`。
|
||||
|
||||
## 3. 指令领取与执行
|
||||
|
||||
- [ ] 3.1 分发器在卡任务之前领取 `stop_resume`:清理过期 `sr_running`,领取数 = min(剩余 QPS, `sr_inflight_limit` − 执行中),校验 `sr_last_call` 推后,写 `sr_claim` 并记版本;开关开启后对全部账户领取。
|
||||
- [ ] 3.2 NSQ `polling.stop_resume.{cid}` 专用消费者:核对 token、抢 `sr_exec`、续期。
|
||||
- [ ] 3.3 执行器:按场景重新判断 → 写 `sr_last_call` → 调用一次网关(限流等待模式)→ 成功沿用现有回写与停复机记录;收尾按版本移除或保留。
|
||||
- [ ] 3.4 结果未知:置待确认,确认延迟后先判断再查状态,按规则结束、重发或推后;查询失败不重发;连续未知达阈值告警。
|
||||
- [ ] 3.5 失败分类处理:频率限制、终态(含自动关闭轮询)、已是目标状态、账户配置错误、临时故障与业务错误;停复机方向写入 `tb_polling_failure_alert`;指令不丢弃。
|
||||
|
||||
## 4. 设备维度判断
|
||||
|
||||
- [ ] 4.1 请求设备判断 Lua:`INCR eval_ver`、`SADD eval_scenes`、`ZADD LT device_eval:queue`。
|
||||
- [ ] 4.2 分发与消费:抢 `device_eval:dispatch_lock` 领取,NSQ `polling.device_eval`;消费者抢 `eval_lock`(检查旧路径设备锁),读最新数据判断,为各卡写指令(记录场景)。
|
||||
- [ ] 4.3 收尾:`SREM` 已处理场景,比较版本,变大则 score 设为当前时间再判断。
|
||||
- [ ] 4.4 轮询路径不再使用 `RedisPollingDeviceOpLockKey`;人工设备停复机保持不变。
|
||||
|
||||
## 5. 自动入口改造
|
||||
|
||||
- [ ] 5.1 套餐步骤与 Outbox 三个卡观测消费者改为请求评估,不再同步调网关、不 sleep 重试。
|
||||
- [ ] 5.2 `resumeAfterActivation`(4 处)、`reset_service.go:400`、`order/service.go:2196` 去掉 goroutine,事务提交后请求评估。
|
||||
- [ ] 5.3 `refund/service.go` `stopAsset` 改为请求评估,写入成功即标记 `asset_reset`;`RecoverRefundPostProcessing` 只补偿"请求未写入"。
|
||||
- [ ] 5.4 `package_activation_handler.go` 到期失效处:无其他有效主套餐时以"套餐到期"场景请求评估;接续激活路径不变。
|
||||
- [ ] 5.5 `carrier/service.go` 实名管控修改后按 0 → 1、1 → 0 筛选受影响卡批量请求评估(设备卡按设备)。
|
||||
|
||||
## 6. 监控与接口
|
||||
|
||||
- [ ] 6.1 `/polling-stats` 按账户增加停复机队列长度、执行中、成功、失败、重试、结果未知数。
|
||||
- [ ] 6.2 `/iot-cards/:id/polling-state` 返回当前指令(动作、原因、场景、版本、失败次数、待确认)。
|
||||
- [ ] 6.3 重新生成接口文档。
|
||||
|
||||
## 7. 编译与规范校验
|
||||
|
||||
- [ ] 7.1 `gofmt -w`;`go build ./cmd/api ./cmd/worker` 通过。
|
||||
- [ ] 7.2 `openspec validate add-stop-resume-command-queue` 与 `openspec doctor --json` 通过。
|
||||
|
||||
## 8. 测试环境验证(自动化测试为 N/A,按 ENG-TEST-001)
|
||||
|
||||
- [ ] 8.1 开关关闭:各入口行为与上线前一致。
|
||||
- [ ] 8.2 指令覆盖:停机执行中写入复机指令,收尾后复机保留并在最小间隔后执行;无覆盖时指令被移除。
|
||||
- [ ] 8.3 单卡最小重试间隔:间隔内不调用上游,score 推后到可调用时间。
|
||||
- [ ] 8.4 结果未知:超时后确认延迟查状态,已是目标状态结束、否则按间隔重发、查询失败不重发。
|
||||
- [ ] 8.5 失败分类:频率限制不计失败、终态不重试并按标记关闭轮询、已是目标状态按成功、持续失败告警且指令不丢。
|
||||
- [ ] 8.6 设备判断:判断期间新请求使版本变化后再判断;到期与普通请求合并后场景保留;跨账户设备下各卡最终状态一致。
|
||||
- [ ] 8.7 暂停轮询的卡:套餐到期与退款后停机执行一次;流量用完、激活、重置不自动停复机。
|
||||
- [ ] 8.8 退款:上游终态卡退款后不再反复补发停机;资产后处理标记完成。
|
||||
- [ ] 8.9 激活、重置、订单后复机经指令队列;流量重置日集中复机按账户 QPS 平滑。
|
||||
- [ ] 8.10 实名管控 0 → 1、1 → 0 后受影响卡被批量评估且平滑执行。
|
||||
- [ ] 8.11 开关开、关、再开:无重复停复机与漏停;旧路径设备锁持有时设备判断推后。
|
||||
Reference in New Issue
Block a user