> **Delta Spec**:本文件仅描述对 `openspec/specs/polling-stop-resume-logic/spec.md` 的变更部分。未提及的需求保持不变。 --- ### 变更:not_realname 停机范围——从设备维度缩小为单卡维度 **原行为**:`EvaluateAndAct` 在非独立卡(`is_standalone=false`)触发停机时,无论停机原因如何,均调用 `stopDeviceCards` 停机整个设备的所有在线卡。 **新行为**:仅当停机原因为 `no_package` 或 `traffic_exhausted`(设备共享资源)时,才调用 `stopDeviceCards`;当停机原因为 `not_realname` 时,调用 `stopCardWithRetry` 只停被评估的单卡。 #### Scenario: not_realname 只停单卡,不连坐已实名卡 - **GIVEN** 设备 D 下有卡 A(`real_name_status=0`,在线)和卡 B(`real_name_status=1`,在线) - **WHEN** `EvaluateAndAct` 对卡 A 执行,`checkStopReasons` 返回 `["not_realname"]` - **THEN** 调用 `stopCardWithRetry(cardA, "not_realname")`;卡 A 停机;卡 B 保持在线,`network_status` 不变 #### Scenario: no_package 仍走设备维度停机 - **GIVEN** 设备 D 下有卡 A 和卡 B 均在线,设备无有效套餐 - **WHEN** `EvaluateAndAct` 对卡 A 执行,`checkStopReasons` 返回 `["no_package"]` - **THEN** 调用 `stopDeviceCards(deviceID, "no_package")`;卡 A 和卡 B 均被停机(设备维度停机保持不变) #### Scenario: 优先级 no_package > not_realname 时仍走设备维度 - **GIVEN** 卡 A(未实名 + 无套餐),卡 B(已实名)均在线 - **WHEN** 卡 A 的 `checkStopReasons` 返回 `["no_package", "not_realname"]`,`primaryReason = "no_package"` - **THEN** `isDeviceScopeReason("no_package") = true`;调用 `stopDeviceCards`;卡 A 和卡 B 均停机 --- ### 新增:carrier_stopped——运营商侧停机原因 新增常量 `StopReasonCarrierStopped = "carrier_stopped"`,表示由运营商/Gateway 侧主动停机(非本系统发起)。 **与现有停机原因的区别**: | 原因 | 触发方 | 自动复机 | |------|--------|----------| | `no_package` | 本系统轮询 | ✅ 是 | | `traffic_exhausted` | 本系统轮询 | ✅ 是 | | `not_realname` | 本系统轮询 | ✅ 是(实名完成后) | | `manual` | 管理员手动 | ❌ 否 | | `carrier_stopped` | 运营商/Gateway | ❌ 否(需人工确认) | #### Scenario: carrier_stopped 不触发自动复机 - **GIVEN** 卡停机,`stop_reason='carrier_stopped'` - **WHEN** 下次轮询调用 `EvaluateAndAct` - **THEN** `isPollingStopReason("carrier_stopped") = false`;跳过复机逻辑;卡保持停机状态直到管理员手动处理