Files
junhong_cmp_fiber/migrations/000272_split_card_traffic_collection_failure.up.sql
邹进 94cbf2adab feat: 采集记录成功失败分表并保存网关原始数据,到期归档 csv.gz,新增记录分页查询接口
- 网关客户端新增原始数据收集器,记录最后一次尝试的明文请求、HTTP 状态、响应体与 code/msg/trace_id
- tb_card_traffic_collection 只写成功读数,新增 carrier_id 与原始数据列;新增 tb_card_traffic_collection_failure,失败不判断有无套餐并带错误详情
- 采集记录在线保留当月加最近 2 个整月,到期分区摘出、导出 csv.gz、校验行数与 SHA-256 后删除,本地保留 3 个月
- 新增采集成功/失败、卡日流量、套餐日流量详单、冲正流水、退款后处理尝试列表接口:可查单个也可查全部,分页与条件筛选
- 停复机记录支持资产标识、场景、原因、指令号筛选;单卡历史支持动作、结果、来源筛选
- 单卡采集记录支持筛选与合并失败记录;套餐日流量详单支持分页;退款列表支持后处理状态筛选
- 测试环境 api、worker 日志分目录,保留 40 个压缩备份;worker 挂载 archive 目录
- 迁移 000272、000273 需维护者在测试库执行后再部署

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:47:00 +09:00

76 lines
5.7 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.
-- 采集记录成功与失败分表,并保存网关原始请求与响应(refactor-audit-log-persistence)。
-- 1. tb_card_traffic_collection 只写成功读数;新增运营商账户冗余列与原始数据列(父表 ALTER 自动作用到各分区)。
-- 历史行 carrier_id 为 0;历史上写在本表的失败记录不迁移,随分区到期归档。
-- 2. 新增 tb_card_traffic_collection_failure:所有被采集的卡每次失败一条,按 observed_at 按月分区,带默认分区。
-- 3. 两张表在线保留 2 个月,到期月分区由应用层导出为 csv.gz 并校验后删除。
-- 注意:在已有大分区的父表上建索引会锁写入,执行时间与分区数据量相关,请在低峰执行。
ALTER TABLE tb_card_traffic_collection
ADD COLUMN IF NOT EXISTS carrier_id BIGINT NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS raw_request JSONB,
ADD COLUMN IF NOT EXISTS http_status INT,
ADD COLUMN IF NOT EXISTS gateway_code INT,
ADD COLUMN IF NOT EXISTS gateway_msg VARCHAR(500) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS gateway_trace_id VARCHAR(128) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS raw_response JSONB,
ADD COLUMN IF NOT EXISTS raw_response_text TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS raw_truncated BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS gateway_attempts INT NOT NULL DEFAULT 0;
COMMENT ON TABLE tb_card_traffic_collection IS '卡流量采集成功记录:采集时刻有套餐的卡每次成功读数一条,按月分区在线保留 2 个月,到期归档为 csv.gz';
COMMENT ON COLUMN tb_card_traffic_collection.carrier_id IS '运营商账户ID(采集时写入的冗余列,历史行为 0)';
COMMENT ON COLUMN tb_card_traffic_collection.raw_request IS '网关明文业务请求(加密前),不含签名与密钥';
COMMENT ON COLUMN tb_card_traffic_collection.raw_response IS '网关原始响应体(可解析为 JSON 且不超过 4KB 时)';
COMMENT ON COLUMN tb_card_traffic_collection.raw_response_text IS '网关原始响应体文本(无法解析为 JSON 或超过 4KB 截断时)';
COMMENT ON COLUMN tb_card_traffic_collection.gateway_attempts IS '网关客户端内部 HTTP 尝试次数,原始数据为最后一次尝试';
CREATE INDEX IF NOT EXISTS idx_card_traffic_collection_time ON tb_card_traffic_collection (observed_at DESC);
CREATE INDEX IF NOT EXISTS idx_card_traffic_collection_carrier ON tb_card_traffic_collection (carrier_id, observed_at DESC);
CREATE INDEX IF NOT EXISTS idx_card_traffic_collection_decision ON tb_card_traffic_collection (decision, observed_at DESC);
CREATE TABLE IF NOT EXISTS tb_card_traffic_collection_failure (
id BIGSERIAL,
iot_card_id BIGINT NOT NULL,
iccid VARCHAR(30) NOT NULL DEFAULT '',
carrier_id BIGINT NOT NULL DEFAULT 0,
observed_at TIMESTAMPTZ NOT NULL,
observation_id VARCHAR(128) NOT NULL DEFAULT '',
source VARCHAR(32) NOT NULL DEFAULT '',
scene VARCHAR(64) NOT NULL DEFAULT '',
result VARCHAR(32) NOT NULL,
error_category VARCHAR(30) NOT NULL DEFAULT '',
provider_code VARCHAR(30) NOT NULL DEFAULT '',
provider_message VARCHAR(500) NOT NULL DEFAULT '',
duration_ms INT NOT NULL DEFAULT 0,
raw_request JSONB,
http_status INT,
gateway_code INT,
gateway_msg VARCHAR(500) NOT NULL DEFAULT '',
gateway_trace_id VARCHAR(128) NOT NULL DEFAULT '',
raw_response JSONB,
raw_response_text TEXT NOT NULL DEFAULT '',
raw_truncated BOOLEAN NOT NULL DEFAULT FALSE,
gateway_attempts INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (id, observed_at)
) PARTITION BY RANGE (observed_at);
COMMENT ON TABLE tb_card_traffic_collection_failure IS '卡流量采集失败记录:所有被采集的卡每次失败一条,按月分区在线保留 2 个月,到期归档为 csv.gz';
COMMENT ON COLUMN tb_card_traffic_collection_failure.observation_id IS '采集 ID,等于对接日志 integration_id';
COMMENT ON COLUMN tb_card_traffic_collection_failure.result IS '失败类型:failed/rate_limited/invalid_payload/no_reading/invalid_reading';
COMMENT ON COLUMN tb_card_traffic_collection_failure.error_category IS '网关错误类别:timeout/network/http/invalid_response/upstream 等';
CREATE TABLE IF NOT EXISTS tb_card_traffic_collection_failure_202611 PARTITION OF tb_card_traffic_collection_failure
FOR VALUES FROM ('2026-11-01 00:00:00+08') TO ('2026-12-01 00:00:00+08');
CREATE TABLE IF NOT EXISTS tb_card_traffic_collection_failure_202612 PARTITION OF tb_card_traffic_collection_failure
FOR VALUES FROM ('2026-12-01 00:00:00+08') TO ('2027-01-01 00:00:00+08');
CREATE TABLE IF NOT EXISTS tb_card_traffic_collection_failure_202701 PARTITION OF tb_card_traffic_collection_failure
FOR VALUES FROM ('2027-01-01 00:00:00+08') TO ('2027-02-01 00:00:00+08');
CREATE TABLE IF NOT EXISTS tb_card_traffic_collection_failure_default PARTITION OF tb_card_traffic_collection_failure DEFAULT;
CREATE INDEX IF NOT EXISTS idx_card_traffic_failure_card_time ON tb_card_traffic_collection_failure (iot_card_id, observed_at DESC);
CREATE INDEX IF NOT EXISTS idx_card_traffic_failure_time ON tb_card_traffic_collection_failure (observed_at DESC);
CREATE INDEX IF NOT EXISTS idx_card_traffic_failure_carrier ON tb_card_traffic_collection_failure (carrier_id, observed_at DESC);
CREATE INDEX IF NOT EXISTS idx_card_traffic_failure_category ON tb_card_traffic_collection_failure (error_category, observed_at DESC);
CREATE INDEX IF NOT EXISTS idx_card_traffic_failure_observation ON tb_card_traffic_collection_failure (observation_id);