- 网关客户端新增原始数据收集器,记录最后一次尝试的明文请求、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>
76 lines
5.7 KiB
SQL
76 lines
5.7 KiB
SQL
-- 采集记录成功与失败分表,并保存网关原始请求与响应(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);
|