Files
junhong_cmp_fiber/openspec/specs/polling-operations/spec.md
break aab56a6998
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 14m13s
feat(轮询优先队列): AUG26-016 卡轮询优先队列、人工入队与读侧接口,归档并同步主 Spec 与证据矩阵
新增 000228 成对迁移 tb_polling_priority_item:卡、任务类型、状态、触发类型、来源订单/套餐使用记录、
触发次数与来源集合、尝试次数、失败原因、人工原因与操作者、店铺快照与各时间列;以活动项部分唯一索引
uq_polling_priority_item_active(仅 deleted_at IS NULL AND status IN ('pending','processing') 占键位)
表达「同卡同任务类型至多一条活动项」,另有状态/时间索引与全列注释;down 守卫在存在活动项或未终态行时
拒绝回滚并给出中文原因。

新增优先轮询请求可靠事件 polling.priority.requested(载荷版本 v1、事件键前缀 prio:)与消费者:只在原
业务事务内追加、幂等键稳定;消费者按卡 × 纳入任务类型(realname/carddata/card_status/package)逐条
建项并在提交后下发执行提示,重复投递只合并触发次数、来源集合与最近触发时间,不新建行也不重复调用。
触发点为四类自动场景 purchase_activated / renewal_activated(按同载体更早套餐使用记录判定)/
queue_activated / addon_activated 与「无有效套餐」no_valid_package(仅在普通套餐轮询来源且存在待生效
套餐使用记录时追加;事件通道显式拒绝 manual_trigger);入队对象恒为卡,绑定设备资产在触发事务内冻结
在用卡快照逐卡建项,不使用设备当前卡槽口径。

轮询共享基类新增认领接缝:四个 Handler(realname/carddata/card_status/package)在并发信号量之后、调用
上游之前探测活动项——待执行条件认领、执行中且 90 秒租约未到期则跳过并延后、无活动项时行为与既有完全
等价;超租约允许相邻执行接管,尝试次数只在真正发起执行后累加,未达上限(3)回到活动态按既有间隔重排,
达上限或业务校验类失败进入失败终态并保留可安全展示原因;执行前校验卡自身与绑定设备的轮询开关。未引入
通用卡级锁与 Redis 活动标记,分片队列的出队、入队与移除路径未改动。

提示通道按任务类型独立键(polling:priority:{taskType}),与既有手动触发队列分离;调度器在同一周期内先
排空优先提示、再排空手动触发队列,提示排空不受分片背压跳过影响;未新建调度设施或异步任务类型。

新增人工优先入队与只读查询三条路由 POST /api/admin/polling-priority-items、
GET /api/admin/polling-priority-items、GET /api/admin/polling-priority-items/:id:人工入队复用既有轮询
权限判定(抽取为同包共享函数),原因必填,不受每日 500 次上限与 24 小时去重约束,重复抑制由活动项合并
承担;读侧按店铺快照下推数据范围,越权与不存在不可区分,不提供优先级分级、有效期或人工重触发入口。
新增 7 个审计动作(enqueue/claim/fail/retry/complete/dequeue/manual_denied)与资源
polling_priority_item,并按(操作者类型,来源)注册,人工侧与 Worker 侧均通过来源校验。

同步 OpenAPI 文档装配三处与路由注册;归档 Change 至
openspec/changes/archive/2026-09-17-add-priority-polling-queue/ 并同步主 Spec(新增
priority-polling-queue、polling-operations 追加单次执行互斥 Requirement 与三条路由索引)与上下文健康
证据(requirement-evidence 150 行、入口矩阵 http 403 / async 56)。

本机验证:junhong_cmp_test 与隔离 Redis DB 15,未连生产、未启动 Worker/API、未调用运营商上游;迁移
up/down/up 与 down 守卫实测(含 dirty=true 记账口径与 force 恢复),A–F 批 94 PASS、接缝 63 PASS、
提示通道 12 PASS、清理零残留 20 PASS。成功路径 Complete、真并发互斥、尝试上限第 3 次判定、HTTP 层权限
矩阵、通道阈值持锁复机边界与三类生效触发点生产集成留待测试部署验证(见
docs/verification/add-priority-polling-queue-verification.md 第 4 节)。自动化测试按项目决策为 N/A,
未新增 *_test.go。
2026-09-17 14:29:56 +08:00

148 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 轮询运营当前行为
## Purpose
描述轮询配置、人工触发、并发控制、监控、告警与数据清理的当前可观察行为。
## Requirements
### Requirement: 异步任务可观察
系统 SHALL 允许授权运营者查询轮询和清理任务的状态,并对重复手工触发执行当前防重规则。
#### Scenario: 异步任务可观察
- **GIVEN** 同类任务正在运行
- **WHEN** 再次手工触发
- **THEN** 系统拒绝、复用或排队,而不静默并发执行同一任务
### Requirement: 手工轮询任务状态
系统 SHALL 将手工轮询任务保持为待处理、处理中、已完成或已取消,并对相同运行中任务执行当前防重与并发限制;取消会立即写入已取消,但当前批量执行结束会无条件写入已完成,因此取消与后台处理竞态时已取消状态可能被覆盖,此缺陷在基线中保留。
#### Scenario: 取消运行中轮询任务
- **GIVEN** 手工轮询任务仍处于待处理或处理中
- **WHEN** 授权操作者取消任务
- **THEN** 系统先记录已取消;若后台批量处理随后结束,当前实现可能把同一任务覆盖为已完成
### Requirement: 轮询并发计数可观测
系统 SHALL 在轮询并发配置列表和详情中返回与实际限流器相同任务类型的当前计数、可用并发和使用率;重置操作 MUST 重置该同一计数。当前计数、可用并发与使用率 MUST 不因释放过期或已重置的计数而呈现负值。
#### Scenario: 查询运行中的任务计数
- **WHEN** 某轮询任务正在占用并发配额
- **THEN** 查询该任务类型的并发状态返回非零当前计数,并据此计算可用并发和使用率
#### Scenario: 重置任务计数
- **WHEN** 授权操作者重置某轮询任务类型的并发计数
- **THEN** 后续状态查询返回该任务类型的当前计数为零,且不影响其他任务类型的计数
#### Scenario: 重置后的任务完成
- **WHEN** 某任务在其并发计数已重置或过期后完成
- **THEN** 该任务类型的当前计数保持为零而不变为负数
### Requirement: 轮询并发配置覆盖实际任务
系统 SHALL 为 `realname``carddata``package``protect``card_status` 五类实际轮询任务各维护一项可查询、可更新的并发配置;`stop_start` 不得作为轮询并发配置返回或接受维护。新增的 `protect``card_status` 初始最大并发数 MUST 为 300。
#### Scenario: 查询完整配置集合
- **WHEN** 授权操作者查询轮询并发配置列表
- **THEN** 返回上述五类任务且不含 `stop_start`
#### Scenario: 维护高于一千的既有并发配置
- **WHEN** 授权操作者为已配置轮询任务提交大于 1000 的正整数最大并发数
- **THEN** 系统保存该值并使后续轮询限流读取该值
### Requirement: 轮询任务类型名称可读
系统 SHALL 在轮询并发状态中返回与任务类型一致的中文名称:实名检查、流量检查、套餐检查、保护期检查或卡状态检查。
#### Scenario: 查询卡状态并发配置
- **WHEN** 授权操作者查询 `card_status` 的并发状态
- **THEN** 返回的 `task_type_name` 为“卡状态检查”
### Requirement: 停复机遵循实际生效实名策略
系统 SHALL 在自动停机、自动复机、设备批量复机和手动停复机的实名门槛中放行行业卡、已实名卡及实际生效策略为 `none` 的卡。独立卡或无有效设备绑定的卡使用卡策略,有有效设备绑定的非独立卡使用设备策略。系统 MUST 保留真实实名状态,不得以豁免伪造已实名事实。其他策略的未实名普通卡 MUST 保持实名限制;套餐、流量、风险、保护期及非轮询停机不自动复机等既有限制保持不变。
#### Scenario: 免实名独立卡有生效套餐
- **WHEN** 未实名普通独立卡配置 `none`,存在生效套餐且流量未耗尽,当前网络在线
- **THEN** 系统不因未实名向上游请求停机
#### Scenario: 免实名卡从轮询停机恢复
- **WHEN** 上述卡当前因 `not_realname` 停机,满足其他自动复机条件
- **THEN** 系统不因未实名阻止自动复机
#### Scenario: 设备策略优先
- **WHEN** 未实名普通卡有效绑定设备,卡与设备的实名策略不同
- **THEN** 系统以设备策略判定实名豁免,卡自身的 `none` 不得覆盖设备的实名要求
#### Scenario: 行业卡与已实名卡兼容
- **WHEN** 行业卡未实名或普通卡已实名
- **THEN** 系统保留已有实名门槛放行行为
#### Scenario: 无套餐和流量耗尽不豁免
- **WHEN** 免实名卡没有生效套餐或流量已经耗尽
- **THEN** 系统仍按对应业务原因执行停机判断
#### Scenario: 策略读取失败
- **WHEN** 未实名普通卡的有效设备绑定或设备策略查询失败
- **THEN** 系统报告错误且不对该卡发起本次停复机命令,不按卡策略回退放行
### Requirement: 优先轮询项与普通轮询的单次执行互斥
系统 SHALL 在同一卡同一轮询任务类型存在优先轮询项时只允许一个执行轮次对该卡该任务类型发起上游调用待执行优先项由本轮执行认领并作为优先执行完成该轮轮询MUST NOT 另外产生第二次调用执行中且认领租约未到期的优先项使本轮执行跳过该轮次并延后MUST NOT 发起上游调用。不存在活动优先项时,普通轮询的入队、出队、移除与延后行为 MUST 与当前行为一致。优先项存在 MUST NOT 使该卡被移出普通轮询MUST NOT 改变其它轮询任务类型的执行。
#### Scenario: 待执行优先项遇到本轮执行
- **WHEN** 该卡该轮询任务类型存在待执行优先项,且该任务类型的执行轮次到达
- **THEN** 系统认领该项并完成本轮轮询,不产生额外的第二次上游调用
#### Scenario: 已领取优先项遇到本轮的另一次执行
- **WHEN** 该卡该轮询任务类型存在执行中且认领租约未到期的优先项,该任务类型又到达一次执行
- **THEN** 系统跳过该轮次并按既有间隔延后,不发起上游调用
#### Scenario: 不存在活动优先项
- **WHEN** 该卡该轮询任务类型不存在活动优先项
- **THEN** 轮询执行的排队、入队、出队与移除行为与当前行为一致
#### Scenario: 优先项结束后的普通轮询
- **WHEN** 该卡该轮询任务类型的优先项已完成或失败出队
- **THEN** 该卡继续按普通轮询间隔执行,仍存在于普通轮询中
## 可达操作索引
本节只用于入口导航,不是行为 Requirement业务义务以上述 Requirements 为准。
### 轮询配置管理
`GET /api/admin/polling-configs`(获取轮询配置列表);`POST /api/admin/polling-configs`(创建轮询配置);`DELETE /api/admin/polling-configs/{id}`(删除轮询配置);`GET /api/admin/polling-configs/{id}`(获取轮询配置详情);`PUT /api/admin/polling-configs/{id}`(更新轮询配置);`PUT /api/admin/polling-configs/{id}/status`(更新轮询配置状态);`GET /api/admin/polling-configs/enabled`(获取所有启用的配置)。
### 轮询管理-告警
`GET /api/admin/polling-alert-history/polling-alert-history`(获取轮询告警历史);`GET /api/admin/polling-alert-rules`(获取轮询告警规则列表);`POST /api/admin/polling-alert-rules`(创建轮询告警规则);`DELETE /api/admin/polling-alert-rules/{id}`(删除轮询告警规则);`GET /api/admin/polling-alert-rules/{id}`(获取轮询告警规则详情);`PUT /api/admin/polling-alert-rules/{id}`(更新轮询告警规则)。
### 轮询管理-并发控制
`GET /api/admin/polling-concurrency`(获取轮询并发配置列表);`GET /api/admin/polling-concurrency/{task_type}`(获取指定任务类型的并发配置);`PUT /api/admin/polling-concurrency/{task_type}`(更新轮询并发配置);`POST /api/admin/polling-concurrency/reset`(重置轮询并发计数)。
### 轮询管理-手动触发
`POST /api/admin/polling-manual-trigger/batch`(批量手动触发);`POST /api/admin/polling-manual-trigger/by-condition`(条件筛选触发);`POST /api/admin/polling-manual-trigger/cancel`(取消手动触发任务);`GET /api/admin/polling-manual-trigger/history`(获取手动触发历史);`POST /api/admin/polling-manual-trigger/single`(单卡手动触发);`GET /api/admin/polling-manual-trigger/status`(获取手动触发状态)。
### 轮询管理-数据清理
`GET /api/admin/data-cleanup-configs`(获取数据清理配置列表);`POST /api/admin/data-cleanup-configs`(创建数据清理配置);`DELETE /api/admin/data-cleanup-configs/{id}`(删除数据清理配置);`GET /api/admin/data-cleanup-configs/{id}`(获取数据清理配置详情);`PUT /api/admin/data-cleanup-configs/{id}`(更新数据清理配置);`GET /api/admin/data-cleanup-logs`(获取数据清理日志列表);`GET /api/admin/data-cleanup/preview`(预览待清理数据);`GET /api/admin/data-cleanup/progress`(获取数据清理进度);`POST /api/admin/data-cleanup/trigger`(手动触发数据清理)。
### 轮询管理-监控
`GET /api/admin/polling-stats`(获取轮询总览统计);`GET /api/admin/polling-stats/init-progress`(获取轮询初始化进度);`GET /api/admin/polling-stats/queues`(获取轮询队列状态);`GET /api/admin/polling-stats/tasks`(获取轮询任务统计)。
### 轮询管理-优先队列
`POST /api/admin/polling-priority-items`(人工优先入队);`GET /api/admin/polling-priority-items`(查询优先轮询项列表);`GET /api/admin/polling-priority-items/{id}`(查询优先轮询项详情)。