feat(资产钱包自动续费): 新增全局配置、每日扫描续购与可靠复机
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 10m15s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 10m15s
- 新增单行配置表 tb_asset_auto_renewal_config 与尝试记录表 tb_asset_auto_renewal_attempt(迁移 000229/000230) - 每日按上海自然日扫描,窗口内以同一资产钱包可用余额续购当前主套餐,资金/订单/套餐/审计同一事务闭合 - 唯一键保证每资产每日至多一次尝试,占位中断由后续扫描收敛,当日不重试 - 四类失败原因向客户与店铺各投递每日至多一条站内通知,并注册通知类型与个人客户白名单 - 续费成功后按条件经 Outbox 可靠投递复机,新增恢复扫描只查询回填,不使用即发即弃调用 - 配置读写仅超级管理员与平台账号,保存记录操作者、前后值快照并登记统一审计 - tasks 7.1–7.15 全部验证通过(本机隔离 PostgreSQL/Redis,零外部渠道调用)
This commit is contained in:
18
migrations/000229_add_asset_auto_renewal_config.down.sql
Normal file
18
migrations/000229_add_asset_auto_renewal_config.down.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
-- 回滚资产钱包自动续费全局配置表,与 up 严格成对。
|
||||
--
|
||||
-- 不可逆说明(ENG-MIG-001 例外条件):本迁移的 down 会删除 tb_asset_auto_renewal_config 与
|
||||
-- 其中的总开关、适用范围、指定套餐集合、到期前天数与配置版本。这些配置无法由数据库自身重建,
|
||||
-- 只能依据 tb_audit_event 中资产钱包自动续费配置的前后值快照人工复核,
|
||||
-- 因此 down 只在配置已无留存需求时执行。
|
||||
--
|
||||
-- 守卫:存在仍处于开启状态的配置行时直接阻断回滚,避免静默丢弃「已开启自动续费」的运营意图
|
||||
-- (关闭状态下删除只丢失关闭事实,风险可接受)。
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM tb_asset_auto_renewal_config WHERE enabled = 1 LIMIT 1) THEN
|
||||
RAISE EXCEPTION '自动续费总开关仍处于开启状态,拒绝回滚以避免静默丢弃已生效的续费配置';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DROP TABLE IF EXISTS tb_asset_auto_renewal_config;
|
||||
57
migrations/000229_add_asset_auto_renewal_config.up.sql
Normal file
57
migrations/000229_add_asset_auto_renewal_config.up.sql
Normal file
@@ -0,0 +1,57 @@
|
||||
-- 资产钱包自动续费:新增全局单行配置表 tb_asset_auto_renewal_config。
|
||||
-- 背景:运营需要「资产钱包余额充足时在最终到期前自动续购当前有效主套餐」的总开关、适用范围与
|
||||
-- 到期前天数的统一配置。既有仓库没有任何单行配置表(000225 的 H5 弹窗配置是多行加优先级),
|
||||
-- 因此本迁移新建一张被 CHECK 约束到单行(id 恒为 1)的全局配置表,并预置一行默认关闭。
|
||||
--
|
||||
-- 设计选择:
|
||||
-- 1. 主键恒为 1:本能力只有一份全局生效配置,不按店铺、企业或个人客户分范围。单行约束由
|
||||
-- CHECK (id = 1) 与预置行共同保证,应用层保存走同一行的行锁串行化,不新增第二行。
|
||||
-- 2. enabled 默认 0 且严格 0/1:既有「启停新增语义使用 0=禁用、1=启用」的口径(ENG-STATE-001)。
|
||||
-- 默认关闭,保证迁移上线后不会立即产生续购订单。
|
||||
-- 3. scope 只允许 all/specified;两种范围的集合互斥关系由 CHECK 兜住:
|
||||
-- specified 时 package_ids 必须非空,all 时必须为空数组。半配置(指定范围但集合为空)会被
|
||||
-- 解释为「谁都不符合范围」而静默不续购,属于必须由 Schema 兜住的风险。
|
||||
-- 4. days_before_expiry 限制 1..90:与规格的「正整数,上限 90」一致,0 天窗口会让提前续购失去意义。
|
||||
-- 5. config_version 是配置版本而非乐观锁:保存事务内自增,供尝试记录冻结快照追溯;
|
||||
-- 并发保护用单行事务锁(SELECT ... FOR UPDATE),不用 version 条件更新(ENG-CONC-001 面向余额与状态机)。
|
||||
-- 6. creator/updater 记录维护账号 ID,系统写入为 0;本表没有软删列,配置行不删除。
|
||||
-- 7. 不使用数据库外键,package_ids 中的套餐由应用层显式校验。
|
||||
|
||||
CREATE TABLE tb_asset_auto_renewal_config (
|
||||
id BIGINT PRIMARY KEY,
|
||||
enabled SMALLINT NOT NULL DEFAULT 0,
|
||||
scope VARCHAR(16) NOT NULL DEFAULT 'all',
|
||||
package_ids JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
days_before_expiry INTEGER NOT NULL DEFAULT 15,
|
||||
config_version BIGINT NOT NULL DEFAULT 1,
|
||||
creator BIGINT NOT NULL DEFAULT 0,
|
||||
updater BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT ck_asset_auto_renewal_config_singleton CHECK (id = 1),
|
||||
CONSTRAINT ck_asset_auto_renewal_config_enabled CHECK (enabled IN (0, 1)),
|
||||
CONSTRAINT ck_asset_auto_renewal_config_scope CHECK (scope IN ('all', 'specified')),
|
||||
CONSTRAINT ck_asset_auto_renewal_config_package_ids CHECK (jsonb_typeof(package_ids) = 'array'),
|
||||
CONSTRAINT ck_asset_auto_renewal_config_scope_packages CHECK (
|
||||
(scope = 'specified' AND jsonb_array_length(package_ids) >= 1)
|
||||
OR (scope = 'all' AND jsonb_array_length(package_ids) = 0)
|
||||
),
|
||||
CONSTRAINT ck_asset_auto_renewal_config_window CHECK (days_before_expiry BETWEEN 1 AND 90),
|
||||
CONSTRAINT ck_asset_auto_renewal_config_version CHECK (config_version >= 1)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE tb_asset_auto_renewal_config IS '资产钱包自动续费全局配置,主键恒为 1 的单行表,只影响后续扫描';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_config.id IS '主键,恒为 1(CHECK 约束保证单行)';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_config.enabled IS '总开关 0-关闭 1-开启;关闭后不再创建新的尝试与订单,既有尝试记录与通知保留';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_config.scope IS '适用范围 all-全部主套餐 specified-指定主套餐';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_config.package_ids IS '指定主套餐商品ID集合(jsonb 数组);scope=specified 时非空,scope=all 时为空数组';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_config.days_before_expiry IS '统一到期前天数(正整数,1-90),触发窗口为最终到期剩余天数闭区间 0 至该值';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_config.config_version IS '配置版本,每次保存事务内自增;尝试记录只保留触发时版本快照,不重算';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_config.creator IS '创建人账号ID,系统写入为 0';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_config.updater IS '最近更新人账号ID,系统写入为 0';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_config.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_config.updated_at IS '最近更新时间';
|
||||
|
||||
-- 预置唯一一行且默认关闭:读取侧按主键 1 读取,缺失即视为部署异常。
|
||||
INSERT INTO tb_asset_auto_renewal_config (id, enabled, scope, package_ids, days_before_expiry, config_version)
|
||||
VALUES (1, 0, 'all', '[]'::jsonb, 15, 1);
|
||||
28
migrations/000230_add_asset_auto_renewal_attempt.down.sql
Normal file
28
migrations/000230_add_asset_auto_renewal_attempt.down.sql
Normal file
@@ -0,0 +1,28 @@
|
||||
-- 回滚资产钱包自动续费尝试记录表,与 up 严格成对。
|
||||
-- 删除顺序与 up 的创建顺序严格倒序:先删索引与唯一键,再删表(表内 CHECK 约束随表一并移除,
|
||||
-- 不留残余约束与索引)。
|
||||
--
|
||||
-- 不可逆说明(ENG-MIG-001 例外条件):本迁移的 down 会删除 tb_asset_auto_renewal_attempt 与
|
||||
-- 其中的每日尝试、资金扣款、续费订单与复机结果快照。已发生的续费事实仍可通过 tb_order、
|
||||
-- tb_payment、tb_asset_wallet_transaction、tb_package_usage 与 tb_audit_event 复核,
|
||||
-- 但「该资产当日是否已尝试/被跳过/复机结果」无法由数据库自身重建,
|
||||
-- 因此 down 只在上述尝试事实已无留存需求时执行。
|
||||
--
|
||||
-- 守卫:存在仍处于处理中(占位后未收敛)的尝试行时直接阻断回滚,避免在未收敛状态下删除记录,
|
||||
-- 使次日扫描无法把中断的尝试收敛为终态。
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM tb_asset_auto_renewal_attempt WHERE deleted_at IS NULL AND status = 1 LIMIT 1) THEN
|
||||
RAISE EXCEPTION '存在仍处于处理中的自动续费尝试记录,拒绝回滚以避免丢失未收敛的尝试事实';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
LOCK TABLE tb_asset_auto_renewal_attempt IN ACCESS EXCLUSIVE MODE;
|
||||
|
||||
DROP INDEX IF EXISTS idx_asset_auto_renewal_attempt_asset;
|
||||
DROP INDEX IF EXISTS idx_asset_auto_renewal_attempt_resume_unresolved;
|
||||
DROP INDEX IF EXISTS idx_asset_auto_renewal_attempt_unfinished;
|
||||
DROP INDEX IF EXISTS uq_asset_auto_renewal_attempt_key;
|
||||
|
||||
DROP TABLE IF EXISTS tb_asset_auto_renewal_attempt;
|
||||
172
migrations/000230_add_asset_auto_renewal_attempt.up.sql
Normal file
172
migrations/000230_add_asset_auto_renewal_attempt.up.sql
Normal file
@@ -0,0 +1,172 @@
|
||||
-- 资产钱包自动续费:新增尝试记录表 tb_asset_auto_renewal_attempt。
|
||||
-- 背景:自动续费需要「每项资产每天至多一次尝试」的强约束,以及可追溯的资金、订单、复机与配置
|
||||
-- 快照。既有 tb_recharge_order.auto_purchase_status 只表达一个充值单的成功/失败,无法承载
|
||||
-- 「按资产按日唯一」「占位后中断可收敛」「复机结果分类」三类事实,因此新建本表。
|
||||
--
|
||||
-- 设计选择:
|
||||
-- 1. 唯一键为 (asset_type, asset_id, trigger_date) 的部分唯一索引:trigger_date 是上海自然日,
|
||||
-- 同一资产同一自然日至多一条尝试,「每天至多一次」由唯一冲突保证而不是靠应用层判断。
|
||||
-- 索引必须是部分唯一索引(WHERE deleted_at IS NULL):CREATE TABLE 的表约束不支持 WHERE 谓词,
|
||||
-- 而软删感知要求谓词使软删行不占用键位。该索引不与任何 GORM OnConflict 组合
|
||||
-- (部分唯一索引谓词未声明时 OnConflict 无法命中,见 KNOWN-ISSUE-001),
|
||||
-- 应用层以「显式插入 + 23505 识别」表达「当日已尝试」。
|
||||
-- 2. status 表达尝试生命周期:1-处理中(占位) 2-成功 3-失败 4-跳过。占位行写入后进程中断会
|
||||
-- 停在处理中,由后续扫描把「触发日期早于当日且非终态」的行按
|
||||
-- status=3 + failure_reason='interrupted' 收敛(见第 3 点),当日不重试。
|
||||
-- 生命周期状态按 ENG-STATE-001 使用整数编码。
|
||||
-- 3. failure_reason 是「尝试记录的失败归类」,只承载实际进入执行后得出的失败:
|
||||
-- insufficient_balance-余额不足 not_renewable-不可续费 order_failed-订单失败,
|
||||
-- 外加 interrupted-中断收敛(见下)。复机失败不写 failure_reason:按设计决策 11,
|
||||
-- 复机结果只落 resume_status/resume_failure_reason,避免同一事实两处表达。
|
||||
-- 规格的「失败原因枚举固定为四类」(余额不足/不可续费/订单失败/复机失败)是**通知口径**:
|
||||
-- 前三类由尝试失败产生通知,第四类由复机失败(resume_status=4)产生通知,
|
||||
-- 复用同一常量作为通知原因标识,但绝不回写 failure_reason。
|
||||
-- interrupted 是**非通知值**:占位后进程中断的记录由后续扫描收敛为
|
||||
-- status=3 + failure_reason='interrupted',表达规格要求的「收敛为中断或未知」。
|
||||
-- 该值不对应任何真实执行失败,因此通知投递判定显式排除它,收敛过程也不发送任何通知
|
||||
-- (一个从未进入资金事务的占位行发「订单失败」通知是错的)。
|
||||
-- skip_reason 取值 manual_renewed-人工已完成续购 manual_order_pending-人工订单在途。
|
||||
-- 4. resume_status 表达复机子结果:0-未评估 1-跳过 2-已投递 3-成功 4-失败 5-未知。
|
||||
-- 已投递未提交、已提交待确认与未知都属于未收敛,恢复扫描只查询已确认结果回填,绝不重复发起复机。
|
||||
-- resume_submitted_at 与 resume_anomaly_flag 照抄第 13 项既有形态:
|
||||
-- internal/domain/carrierthreshold/command.go 的 SubmissionQueryWindow/SubmissionExpired、
|
||||
-- internal/application/carrierthreshold/cycle.go:274-296 的「窗口内继续等待,超期标记异常退出自动扫描」、
|
||||
-- internal/application/carrierthreshold/lock_store.go 的 markAnomaly,
|
||||
-- 以及 migrations/000227_add_carrier_traffic_threshold.up.sql 的 anomaly_flag 列与
|
||||
-- idx_carrier_traffic_threshold_lock_unresolved 部分索引(谓词含 anomaly_flag = 0)。
|
||||
-- 本表以同样方式实现「有界查询窗口 + 超期退出自动扫描」,不新增异常记录页、不自动删除记录。
|
||||
-- 5. trigger_date 是 DATE 列:应用层与扫描的一切跨日比较必须用显式日期参数
|
||||
-- (trigger_date < ?::date),不得把 time.Time 直接与东八区零点比较——DATE 读回后是当日零点,
|
||||
-- 直接比较会因时区换算差一天。「当日不重试」与「收敛历史非终态」使用同一日期口径。
|
||||
-- 6. 资金与订单字段全部是触发时冻结的快照:wallet_transaction_id 即规格中的钱包流水号,
|
||||
-- 指向 tb_asset_wallet_transaction.id;扣款前/后余额用于人工核对资金事实。
|
||||
-- 跳过态与失败态的行这些字段保持 0/空。
|
||||
-- 7. 配置与窗口快照(config_version、window_days、final_expires_at)只记录触发时的值,
|
||||
-- 配置变更不影响已产生的尝试记录,不重算。
|
||||
-- 8. operator_type/operator_id 恒为系统任务(无人工处理入口);attempt_seq 记录该资产截至本次
|
||||
-- 尝试当日的跨日累计尝试次数。
|
||||
-- 9. 不存凭证、令牌或个人敏感信息(ENG-LOG-001);不使用数据库外键,关联由应用层显式校验。
|
||||
|
||||
CREATE TABLE tb_asset_auto_renewal_attempt (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
asset_type VARCHAR(20) NOT NULL,
|
||||
asset_id BIGINT NOT NULL,
|
||||
trigger_date DATE NOT NULL,
|
||||
status SMALLINT NOT NULL DEFAULT 1,
|
||||
failure_reason VARCHAR(32) NOT NULL DEFAULT '',
|
||||
failure_detail VARCHAR(500) NOT NULL DEFAULT '',
|
||||
skip_reason VARCHAR(32) NOT NULL DEFAULT '',
|
||||
customer_id BIGINT NOT NULL DEFAULT 0,
|
||||
shop_id BIGINT,
|
||||
config_version BIGINT NOT NULL DEFAULT 0,
|
||||
window_days INTEGER NOT NULL DEFAULT 0,
|
||||
final_expires_at TIMESTAMPTZ,
|
||||
current_usage_id BIGINT NOT NULL DEFAULT 0,
|
||||
current_package_id BIGINT NOT NULL DEFAULT 0,
|
||||
renew_package_id BIGINT NOT NULL DEFAULT 0,
|
||||
renew_price BIGINT NOT NULL DEFAULT 0,
|
||||
wallet_id BIGINT NOT NULL DEFAULT 0,
|
||||
wallet_transaction_id BIGINT NOT NULL DEFAULT 0,
|
||||
deduct_amount BIGINT NOT NULL DEFAULT 0,
|
||||
balance_before BIGINT NOT NULL DEFAULT 0,
|
||||
balance_after BIGINT NOT NULL DEFAULT 0,
|
||||
order_id BIGINT NOT NULL DEFAULT 0,
|
||||
order_no VARCHAR(64) NOT NULL DEFAULT '',
|
||||
resume_status SMALLINT NOT NULL DEFAULT 0,
|
||||
resume_submitted_at TIMESTAMPTZ,
|
||||
resume_integration_id VARCHAR(64) NOT NULL DEFAULT '',
|
||||
resume_failure_reason VARCHAR(500) NOT NULL DEFAULT '',
|
||||
resume_anomaly_flag SMALLINT NOT NULL DEFAULT 0,
|
||||
operator_type VARCHAR(32) NOT NULL DEFAULT 'system_task',
|
||||
operator_id VARCHAR(64) NOT NULL DEFAULT '',
|
||||
attempt_seq INTEGER NOT NULL DEFAULT 1,
|
||||
creator BIGINT NOT NULL DEFAULT 0,
|
||||
updater BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ,
|
||||
CONSTRAINT ck_asset_auto_renewal_attempt_asset_type CHECK (asset_type IN ('iot_card', 'device')),
|
||||
CONSTRAINT ck_asset_auto_renewal_attempt_status CHECK (status IN (1, 2, 3, 4)),
|
||||
CONSTRAINT ck_asset_auto_renewal_attempt_failure_reason CHECK (
|
||||
failure_reason IN ('', 'insufficient_balance', 'not_renewable', 'order_failed', 'interrupted')
|
||||
),
|
||||
CONSTRAINT ck_asset_auto_renewal_attempt_skip_reason CHECK (
|
||||
skip_reason IN ('', 'manual_renewed', 'manual_order_pending')
|
||||
),
|
||||
CONSTRAINT ck_asset_auto_renewal_attempt_resume_status CHECK (resume_status IN (0, 1, 2, 3, 4, 5)),
|
||||
CONSTRAINT ck_asset_auto_renewal_attempt_resume_anomaly CHECK (resume_anomaly_flag IN (0, 1)),
|
||||
CONSTRAINT ck_asset_auto_renewal_attempt_operator_type CHECK (operator_type = 'system_task'),
|
||||
CONSTRAINT ck_asset_auto_renewal_attempt_amounts CHECK (
|
||||
renew_price >= 0 AND deduct_amount >= 0 AND balance_before >= 0 AND balance_after >= 0
|
||||
),
|
||||
-- 语义列不允许出现「无因失败」或「有因成功」:状态与原因必须成对。
|
||||
-- 处理中与成功不得带任何原因;失败必须带失败归类;跳过必须带跳过原因且不得带失败归类。
|
||||
CONSTRAINT ck_asset_auto_renewal_attempt_reason_shape CHECK (
|
||||
(status IN (1, 2) AND failure_reason = '' AND skip_reason = '')
|
||||
OR (status = 3 AND failure_reason IN ('insufficient_balance', 'not_renewable', 'order_failed', 'interrupted') AND skip_reason = '')
|
||||
OR (status = 4 AND failure_reason = '' AND skip_reason IN ('manual_renewed', 'manual_order_pending'))
|
||||
)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX uq_asset_auto_renewal_attempt_key
|
||||
ON tb_asset_auto_renewal_attempt (asset_type, asset_id, trigger_date)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 终态收敛按「触发日期早于当日且仍为处理中」扫描。
|
||||
CREATE INDEX idx_asset_auto_renewal_attempt_unfinished
|
||||
ON tb_asset_auto_renewal_attempt (status, trigger_date)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
-- 复机恢复扫描只看未收敛的复机子结果:已投递未提交(事件已写但尚未执行)、
|
||||
-- 已提交待确认(进程中断)与结果未知都必须继续按只读查询收敛。
|
||||
CREATE INDEX idx_asset_auto_renewal_attempt_resume_unresolved
|
||||
ON tb_asset_auto_renewal_attempt (resume_status, resume_submitted_at)
|
||||
WHERE deleted_at IS NULL AND resume_anomaly_flag = 0
|
||||
AND (resume_status = 5 OR (resume_status = 2 AND resume_submitted_at IS NOT NULL));
|
||||
|
||||
-- 跨日尝试次数按资产累计。
|
||||
CREATE INDEX idx_asset_auto_renewal_attempt_asset ON tb_asset_auto_renewal_attempt (asset_type, asset_id);
|
||||
|
||||
COMMENT ON TABLE tb_asset_auto_renewal_attempt IS '资产钱包自动续费尝试记录,一项资产一个上海自然日至多一条,承载资金/订单/复机/配置快照';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.id IS '主键';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.asset_type IS '资产类型 iot_card-物联网卡 device-设备,与资产钱包资源类型一致';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.asset_id IS '资产ID(tb_iot_card.id 或 tb_device.id)';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.trigger_date IS '触发日期(上海自然日),与资产类型、资产ID 共同构成唯一键';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.status IS '尝试状态 1-处理中 2-成功 3-失败 4-跳过';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.failure_reason IS '尝试失败归类 insufficient_balance-余额不足 not_renewable-不可续费 order_failed-订单失败 interrupted-占位后中断收敛;成功与跳过时为空;复机失败只落 resume_status/resume_failure_reason;interrupted 不属于四类通知原因,不触发通知';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.failure_detail IS '可安全展示的失败说明,不写渠道报文、凭证或内部错误细节';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.skip_reason IS '跳过原因 manual_renewed-人工已完成续购 manual_order_pending-人工订单在途';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.customer_id IS '触发时解析到的当前个人客户ID,0-无当前个人客户';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.shop_id IS '触发时资产所属店铺ID,NULL-资产无店铺归属';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.config_version IS '触发时配置版本快照,不随配置变更重算';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.window_days IS '触发时到期前天数快照';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.final_expires_at IS '触发时最终到期时间快照';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.current_usage_id IS '触发时当前主套餐使用记录ID';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.current_package_id IS '触发时当前套餐商品ID';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.renew_package_id IS '待续购套餐商品ID(与当前套餐商品相同)';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.renew_price IS '执行时该渠道当前可售续费价(分)';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.wallet_id IS '扣款资产钱包ID;跳过与失败时为 0';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.wallet_transaction_id IS '钱包流水号(tb_asset_wallet_transaction.id);跳过与失败时为 0';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.deduct_amount IS '扣款金额(分);跳过与失败时为 0';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.balance_before IS '扣款前钱包余额(分)';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.balance_after IS '扣款后钱包余额(分)';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.order_id IS '续费订单ID;跳过与失败时为 0';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.order_no IS '续费订单号快照;跳过与失败时为空';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.resume_status IS '复机状态 0-未评估 1-跳过 2-已投递 3-成功 4-失败 5-未知;已投递与未知由恢复扫描收敛';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.resume_submitted_at IS '复机执行提交认领时刻;由消费者以「为空」条件更新取得至多一次的外部调用权,恢复扫描据此判断查询窗口';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.resume_integration_id IS '复机外部交互标识(Gateway 调用的 Integration Log 标识),便于人工核对';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.resume_failure_reason IS '可安全展示的复机失败原因,不写渠道报文原文';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.resume_anomaly_flag IS '复机异常标记 0-正常 1-查询窗口超期仍无法确认,退出自动扫描转人工核对';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.operator_type IS '操作者类型,恒为 system_task(本能力无人工处理入口)';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.operator_id IS '操作者标识,取触发时的计划任务类型';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.attempt_seq IS '跨日尝试次数(该资产截至本次触发日的累计尝试次数)';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.creator IS '创建人账号ID,系统写入为 0';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.updater IS '最近更新人账号ID,系统写入为 0';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.updated_at IS '最近更新时间';
|
||||
COMMENT ON COLUMN tb_asset_auto_renewal_attempt.deleted_at IS '软删除时间,正常流程不删除,仅数据清理使用';
|
||||
|
||||
COMMENT ON INDEX uq_asset_auto_renewal_attempt_key IS '自动续费尝试唯一键(软删感知):一项资产一个上海自然日至多一条,冲突即当日已尝试';
|
||||
COMMENT ON INDEX idx_asset_auto_renewal_attempt_unfinished IS '终态收敛扫描索引:触发日期早于当日的非终态记录';
|
||||
COMMENT ON INDEX idx_asset_auto_renewal_attempt_resume_unresolved IS '复机恢复扫描索引:已投递与未知两类未收敛复机结果';
|
||||
COMMENT ON INDEX idx_asset_auto_renewal_attempt_asset IS '跨日尝试次数按资产累计索引';
|
||||
Reference in New Issue
Block a user