Files
junhong_cmp_fiber/migrations/000228_add_polling_priority_queue.down.sql
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

44 lines
2.1 KiB
PL/PgSQL
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.
-- 回滚卡轮询优先队列:删除 tb_polling_priority_item与 up 严格成对,顺序与 up 的创建顺序严格倒序。
--
-- 不可逆说明ENG-MIG-001 例外条件):本迁移的 down 会删除 tb_polling_priority_item。加急事实
-- (触发场景与来源集合、来源订单与套餐使用记录、触发次数、尝试次数、认领时间、人工原因与操作者、
-- 执行结果与失败原因只由本表承载tb_audit_event 只记录审计动作与前后值,不能还原完整队列状态,
-- 删除后无法由数据库自身重建,因此 down 只在加急事实已无留存需求时执行。
--
-- 守卫一:存在活动项(未软删且待执行/执行中)时拒绝回滚,避免在途加急任务被静默丢弃。
-- 守卫二:存在未终态行(含已软删)时拒绝回滚,避免半终态行随表一起消失、事后无从核对。
BEGIN;
DO $$
BEGIN
IF to_regclass('tb_polling_priority_item') IS NOT NULL THEN
-- 同一事务内先取排他锁再检查并删表,避免检查与删表之间写入新的加急事实。
LOCK TABLE tb_polling_priority_item IN ACCESS EXCLUSIVE MODE;
IF EXISTS (
SELECT 1 FROM tb_polling_priority_item
WHERE deleted_at IS NULL AND status IN ('pending', 'processing')
LIMIT 1
) THEN
RAISE EXCEPTION '存在卡轮询优先队列活动项,拒绝回滚以避免丢失在途加急事实与认领租约状态';
END IF;
IF EXISTS (
SELECT 1 FROM tb_polling_priority_item
WHERE status IN ('pending', 'processing')
LIMIT 1
) THEN
RAISE EXCEPTION '存在未终态的卡轮询优先队列记录(含已软删行),拒绝回滚,请人工核对后向前修复';
END IF;
END IF;
END
$$;
DROP INDEX IF EXISTS idx_polling_priority_item_shop_created;
DROP INDEX IF EXISTS idx_polling_priority_item_card;
DROP INDEX IF EXISTS idx_polling_priority_item_status_created;
DROP INDEX IF EXISTS uq_polling_priority_item_active;
DROP TABLE IF EXISTS tb_polling_priority_item;
COMMIT;