All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m35s
- 新增不轮询清单 tb_card_polling_pause(迁移 000256 补写存量),所有改 enable_polling 的入口同事务写清单 - 卡、设备勾选批量开关轮询(单次 500,逐项结果,开启跳过风险停机与已销户)与 CSV 文件导入 - 订单套餐生效、改有效期后自动恢复轮询并按规则评估复机 - 不轮询清单查询与导出;卡、设备列表与导出增加是否轮询 - 失败告警停机被拒视图、导出与批量关闭轮询;开关轮询影响说明 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
105 lines
7.1 KiB
SQL
105 lines
7.1 KiB
SQL
-- 不轮询清单与轮询开关文件导入任务(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 '逐行结果明细';
|