Files
junhong_cmp_fiber/migrations/000256_add_card_polling_pause.up.sql
邹进 7ddb9312e3
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m35s
feat: 不轮询清单与批量开关轮询
- 新增不轮询清单 tb_card_polling_pause(迁移 000256 补写存量),所有改 enable_polling 的入口同事务写清单
- 卡、设备勾选批量开关轮询(单次 500,逐项结果,开启跳过风险停机与已销户)与 CSV 文件导入
- 订单套餐生效、改有效期后自动恢复轮询并按规则评估复机
- 不轮询清单查询与导出;卡、设备列表与导出增加是否轮询
- 失败告警停机被拒视图、导出与批量关闭轮询;开关轮询影响说明

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 03:07:20 +09:00

105 lines
7.1 KiB
SQL
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.
-- 不轮询清单与轮询开关文件导入任务(add-polling-pause-list-and-batch-toggle)。
-- 背景:开关轮询此前只有单资产接口与系统自动规则,没有"谁、何时、为什么关闭"的记录,也没有批量与文件导入。
--
-- 设计选择:
-- 1. tb_card_polling_pause 只做记录,调度只认卡与设备的 enable_polling,不读本表。
-- 2. 每次 enable_polling 由开启变为关闭时新增一条"已关闭"(status=1)记录;由关闭变为开启时把该记录标为"已恢复"(status=2)
-- 并写恢复来源、操作人与时间,记录保留。部分唯一索引保证同一资产只有一条"已关闭"记录。
-- 3. asset_type:iot_card / device;identifier:卡为 ICCID,设备为 IMEI(为空时取虚拟号);carrier_id 只对卡有值。
-- 4. close_source / restore_source 取值见代码常量(单资产接口、勾选批量、文件导入、风险停机或已销户自动关闭、设备级联、
-- 支付或下单生效、改有效期、停机被拒告警处理、历史数据)。
-- 5. 存量补写:当前 enable_polling = false 的卡与设备各补一条"已关闭";网关扩展状态为风险停机或已销户的卡记
-- risk_auto(风险停机或已销户自动关闭),其余记 history(历史数据);补写记录的关闭时间取资产 updated_at。
-- 6. tb_polling_status_import_task:上传 ICCID 或设备 IMEI 的 CSV,选择开启或关闭,异步逐行处理、逐行结果,部分成功不回滚。
-- 不使用数据库外键(ENG-MODEL-001)。
CREATE TABLE tb_card_polling_pause (
id BIGSERIAL PRIMARY KEY,
asset_type VARCHAR(20) NOT NULL,
asset_id BIGINT NOT NULL,
identifier VARCHAR(100) NOT NULL DEFAULT '',
carrier_id BIGINT NOT NULL DEFAULT 0,
status SMALLINT NOT NULL DEFAULT 1,
close_source VARCHAR(30) NOT NULL,
close_reason VARCHAR(500) NOT NULL DEFAULT '',
closed_by BIGINT NOT NULL DEFAULT 0,
closed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
import_task_id BIGINT NOT NULL DEFAULT 0,
restore_source VARCHAR(30) NOT NULL DEFAULT '',
restore_reason VARCHAR(500) NOT NULL DEFAULT '',
restored_by BIGINT NOT NULL DEFAULT 0,
restored_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT ck_card_polling_pause_status CHECK (status IN (1, 2)),
CONSTRAINT ck_card_polling_pause_asset_type CHECK (asset_type IN ('iot_card', 'device'))
);
CREATE UNIQUE INDEX uq_card_polling_pause_open ON tb_card_polling_pause (asset_type, asset_id) WHERE status = 1;
CREATE INDEX idx_card_polling_pause_list ON tb_card_polling_pause (status, closed_at DESC);
CREATE INDEX idx_card_polling_pause_asset ON tb_card_polling_pause (asset_type, asset_id);
CREATE INDEX idx_card_polling_pause_identifier ON tb_card_polling_pause (identifier);
COMMENT ON TABLE tb_card_polling_pause IS '不轮询清单:记录卡或设备关闭轮询的来源、原因、操作人与恢复历史;调度只认 enable_polling,不读本表';
COMMENT ON COLUMN tb_card_polling_pause.asset_type IS '资产类型:iot_card / device';
COMMENT ON COLUMN tb_card_polling_pause.asset_id IS '资产ID(卡ID或设备ID)';
COMMENT ON COLUMN tb_card_polling_pause.identifier IS '资产标识快照:卡为 ICCID,设备为 IMEI(为空时取虚拟号)';
COMMENT ON COLUMN tb_card_polling_pause.carrier_id IS '卡所属运营商账户ID(设备为 0)';
COMMENT ON COLUMN tb_card_polling_pause.status IS '状态:1-已关闭 2-已恢复';
COMMENT ON COLUMN tb_card_polling_pause.close_source IS '关闭来源';
COMMENT ON COLUMN tb_card_polling_pause.close_reason IS '关闭原因';
COMMENT ON COLUMN tb_card_polling_pause.closed_by IS '关闭操作人账号ID(系统为 0)';
COMMENT ON COLUMN tb_card_polling_pause.closed_at IS '关闭时间';
COMMENT ON COLUMN tb_card_polling_pause.import_task_id IS '文件导入任务ID(非导入为 0)';
COMMENT ON COLUMN tb_card_polling_pause.restore_source IS '恢复来源';
COMMENT ON COLUMN tb_card_polling_pause.restore_reason IS '恢复原因';
COMMENT ON COLUMN tb_card_polling_pause.restored_by IS '恢复操作人账号ID(系统为 0)';
COMMENT ON COLUMN tb_card_polling_pause.restored_at IS '恢复时间';
INSERT INTO tb_card_polling_pause (asset_type, asset_id, identifier, carrier_id, status, close_source, closed_at)
SELECT 'iot_card', c.id, c.iccid, c.carrier_id, 1,
CASE WHEN c.gateway_extend IN ('风险停机', '已销户') THEN 'risk_auto' ELSE 'history' END,
COALESCE(c.updated_at, NOW())
FROM tb_iot_card c
WHERE c.enable_polling = FALSE AND c.deleted_at IS NULL;
INSERT INTO tb_card_polling_pause (asset_type, asset_id, identifier, carrier_id, status, close_source, closed_at)
SELECT 'device', d.id, COALESCE(NULLIF(d.imei, ''), d.virtual_no), 0, 1, 'history', COALESCE(d.updated_at, NOW())
FROM tb_device d
WHERE d.enable_polling = FALSE AND d.deleted_at IS NULL;
CREATE TABLE tb_polling_status_import_task (
id BIGSERIAL PRIMARY KEY,
task_no VARCHAR(50) NOT NULL,
asset_type VARCHAR(20) NOT NULL,
enable_polling BOOLEAN NOT NULL,
reason VARCHAR(500) NOT NULL DEFAULT '',
file_name VARCHAR(255) NOT NULL DEFAULT '',
storage_key VARCHAR(500) NOT NULL,
status INTEGER NOT NULL DEFAULT 1,
total_count INTEGER NOT NULL DEFAULT 0,
success_count INTEGER NOT NULL DEFAULT 0,
skip_count INTEGER NOT NULL DEFAULT 0,
fail_count INTEGER NOT NULL DEFAULT 0,
result_items JSONB NOT NULL DEFAULT '[]',
error_message TEXT NOT NULL DEFAULT '',
creator BIGINT NOT NULL DEFAULT 0,
creator_name VARCHAR(100) NOT NULL DEFAULT '',
started_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT ck_polling_status_import_task_status CHECK (status IN (1, 2, 3, 4)),
CONSTRAINT ck_polling_status_import_task_asset_type CHECK (asset_type IN ('iot_card', 'device'))
);
CREATE UNIQUE INDEX uq_polling_status_import_task_no ON tb_polling_status_import_task (task_no);
CREATE INDEX idx_polling_status_import_task_created ON tb_polling_status_import_task (created_at DESC);
COMMENT ON TABLE tb_polling_status_import_task IS '轮询开关文件导入任务:逐行独立处理,部分成功不回滚';
COMMENT ON COLUMN tb_polling_status_import_task.asset_type IS '资产类型:iot_card(按 ICCID)/ device(按 IMEI 或虚拟号)';
COMMENT ON COLUMN tb_polling_status_import_task.enable_polling IS '目标状态:true 开启 / false 关闭';
COMMENT ON COLUMN tb_polling_status_import_task.status IS '任务状态:1-待处理 2-处理中 3-已完成 4-失败';
COMMENT ON COLUMN tb_polling_status_import_task.skip_count IS '跳过行数(已是目标状态、风险停机或已销户不能开启)';
COMMENT ON COLUMN tb_polling_status_import_task.result_items IS '逐行结果明细';