feat(db): 迁移 112 拆分履约渠道/状态/来源并收敛异常模型
- order_items 新增 fulfillment_channel / status / source / locked / decided_by - 新建 sku_channel_rules(L1 渠道规则),从 aff 商品与人工配置生成 73 条初始规则 - cost_exceptions 改为 SKU 级问题头 + cost_exception_items 明细 - 新增 finance_watermarks 管道水位与第三方卡券成本项 - 销售事实枚举扩展 third_party_voucher / unclassified
This commit is contained in:
@@ -0,0 +1,431 @@
|
||||
-- 112_fulfillment_channel_split.sql
|
||||
--
|
||||
-- 目标:把「渠道 / 状态 / 决策来源」从一列拆成三列,并把渠道决策从
|
||||
-- 「有没有任务」反推改为「配置驱动」。
|
||||
--
|
||||
-- 生产前置:
|
||||
-- · 生产迁移停在 111(2026-10-30 20:32);本迁移为 112,编号未被占用。
|
||||
-- · 生产数据:orders 438,740 / order_items 438,750 / 10.1 GB。
|
||||
-- · order_items 上任何全表 UPDATE 约 34s,超过 15s 语句超时——
|
||||
-- 所以本迁移**只做 DDL 与小表种子**,438k 行回填交给
|
||||
-- `scripts/backfill-fulfillment-channel.ts`(分批、幂等、可续跑)。
|
||||
--
|
||||
-- 本迁移不做的事:
|
||||
-- · 不删除 fulfillment_mode(等代码切换完成后由 113 删除)
|
||||
-- · 不清派生数据(由断代脚本执行)
|
||||
-- · 不改历史订单(回填脚本按断代日归档)
|
||||
--
|
||||
-- 与旧模型的关系:
|
||||
-- 旧:order_items.fulfillment_mode ∈ work_order / affiliate_dash /
|
||||
-- legacy_platform / manual(四值混渠道与兜底)
|
||||
-- 新:fulfillment_channel ∈ work_order / affiliate_dash /
|
||||
-- third_party_voucher / manual(可为空)
|
||||
-- fulfillment_status ∈ pending / assigned / archived
|
||||
-- fulfillment_source ∈ auto_rule / auto_mapping / carrier_evidence /
|
||||
-- manual_confirm / operator_assigned / cutover_archive
|
||||
--
|
||||
-- 迁移由 migrate.ts 包在单事务中执行,因此这里不使用 BEGIN/COMMIT。
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 1. 订单项:三个新列 + 锁定 + 决策人
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
ALTER TABLE order_items
|
||||
ADD COLUMN IF NOT EXISTS fulfillment_channel TEXT,
|
||||
ADD COLUMN IF NOT EXISTS fulfillment_status TEXT NOT NULL DEFAULT 'pending',
|
||||
ADD COLUMN IF NOT EXISTS fulfillment_source TEXT NOT NULL DEFAULT '',
|
||||
ADD COLUMN IF NOT EXISTS fulfillment_locked BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
ADD COLUMN IF NOT EXISTS fulfillment_decided_by BIGINT
|
||||
REFERENCES admin_users(id) ON DELETE SET NULL;
|
||||
|
||||
-- 约束:渠道与状态正交,且 pending 不得带渠道、assigned 必须有渠道。
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'chk_order_items_fulfillment_channel'
|
||||
) THEN
|
||||
ALTER TABLE order_items DROP CONSTRAINT chk_order_items_fulfillment_channel;
|
||||
END IF;
|
||||
ALTER TABLE order_items
|
||||
ADD CONSTRAINT chk_order_items_fulfillment_channel
|
||||
CHECK (
|
||||
fulfillment_channel IS NULL
|
||||
OR fulfillment_channel IN (
|
||||
'work_order', 'affiliate_dash', 'third_party_voucher', 'manual'
|
||||
)
|
||||
);
|
||||
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'chk_order_items_fulfillment_status'
|
||||
) THEN
|
||||
ALTER TABLE order_items DROP CONSTRAINT chk_order_items_fulfillment_status;
|
||||
END IF;
|
||||
ALTER TABLE order_items
|
||||
ADD CONSTRAINT chk_order_items_fulfillment_status
|
||||
CHECK (fulfillment_status IN ('pending', 'assigned', 'archived'));
|
||||
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM pg_constraint
|
||||
WHERE conname = 'chk_order_items_fulfillment_channel_status'
|
||||
) THEN
|
||||
ALTER TABLE order_items DROP CONSTRAINT chk_order_items_fulfillment_channel_status;
|
||||
END IF;
|
||||
ALTER TABLE order_items
|
||||
ADD CONSTRAINT chk_order_items_fulfillment_channel_status
|
||||
CHECK (
|
||||
(fulfillment_status = 'pending' AND fulfillment_channel IS NULL)
|
||||
OR (fulfillment_status = 'assigned' AND fulfillment_channel IS NOT NULL)
|
||||
OR fulfillment_status = 'archived'
|
||||
);
|
||||
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'chk_order_items_fulfillment_source'
|
||||
) THEN
|
||||
ALTER TABLE order_items DROP CONSTRAINT chk_order_items_fulfillment_source;
|
||||
END IF;
|
||||
ALTER TABLE order_items
|
||||
ADD CONSTRAINT chk_order_items_fulfillment_source
|
||||
CHECK (
|
||||
fulfillment_source IN (
|
||||
'', 'auto_rule', 'auto_mapping', 'carrier_evidence',
|
||||
'manual_confirm', 'operator_assigned', 'cutover_archive'
|
||||
)
|
||||
);
|
||||
END $$;
|
||||
|
||||
COMMENT ON COLUMN order_items.fulfillment_channel IS
|
||||
'履约渠道(新唯一真源):work_order 接单平台 / affiliate_dash 自动发货 / third_party_voucher 第三方卡券(仅统计)/ manual 人工;pending 时为空';
|
||||
COMMENT ON COLUMN order_items.fulfillment_status IS
|
||||
'渠道状态:pending 待分类 / assigned 已确定 / archived 断代归档';
|
||||
COMMENT ON COLUMN order_items.fulfillment_source IS
|
||||
'渠道决策来源:auto_rule 规则命中 / auto_mapping 映射命中 / carrier_evidence 历史载体证据 / manual_confirm 人工确认 / operator_assigned 运营指定 / cutover_archive 断代归档';
|
||||
COMMENT ON COLUMN order_items.fulfillment_locked IS
|
||||
'运营指定渠道后置 true:channel_assign 跳过该行,直到运营解锁';
|
||||
COMMENT ON COLUMN order_items.fulfillment_decided_by IS
|
||||
'最近一次渠道决策的后台用户;自动决策为空';
|
||||
|
||||
-- 旧列标记为废弃(113 删除)。保留是为了让过渡期代码仍可读历史值。
|
||||
COMMENT ON COLUMN order_items.fulfillment_mode IS
|
||||
'【已废弃,勿写入】旧履约方式四值枚举;读取请改用 fulfillment_channel + fulfillment_status。计划由 113 迁移删除';
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 2. 新索引:按状态与渠道取数,全部走部分索引
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- 待分类队列:只扫 pending 行。
|
||||
CREATE INDEX IF NOT EXISTS idx_order_items_fulfillment_pending_id_v2
|
||||
ON order_items (id)
|
||||
WHERE fulfillment_status = 'pending';
|
||||
|
||||
-- 渠道维度的常规读路径(列表筛选、日报汇总)。
|
||||
CREATE INDEX IF NOT EXISTS idx_order_items_fulfillment_channel_status
|
||||
ON order_items (fulfillment_channel, fulfillment_status)
|
||||
INCLUDE (id)
|
||||
WHERE fulfillment_status <> 'archived';
|
||||
|
||||
-- 商品归一:已定渠道但还没挂商品的行。
|
||||
CREATE INDEX IF NOT EXISTS idx_order_items_unmapped_id
|
||||
ON order_items (id)
|
||||
WHERE fulfillment_status = 'assigned' AND canonical_product_id IS NULL;
|
||||
|
||||
-- 成本快照:已定渠道 + 已归一 + 参与核算的渠道。
|
||||
CREATE INDEX IF NOT EXISTS idx_order_items_channel_product
|
||||
ON order_items (fulfillment_channel, canonical_product_id)
|
||||
INCLUDE (id)
|
||||
WHERE fulfillment_status = 'assigned'
|
||||
AND canonical_product_id IS NOT NULL;
|
||||
|
||||
COMMENT ON INDEX idx_order_items_fulfillment_pending_id_v2 IS
|
||||
'待分类队列:pending 订单项部分索引(2026-10-31 渠道拆分)';
|
||||
COMMENT ON INDEX idx_order_items_unmapped_id IS
|
||||
'商品归一管道:已定渠道未归一部分索引';
|
||||
COMMENT ON INDEX idx_order_items_channel_product IS
|
||||
'成本快照管道:渠道 + 商品维度覆盖索引';
|
||||
|
||||
-- 注意:旧索引 idx_order_items_fulfillment_pending_id(108)与
|
||||
-- idx_order_items_fulfillment_product(110)在 113 里删除;
|
||||
-- 过渡期保留,避免旧代码路径失速。
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 3. 渠道规则表(L1):精确键规则,覆盖自动发货 / 人工 / 卡券 / 运营覆盖
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- 设计说明:
|
||||
-- · 接单平台(work_order)不走本表:它由接单模板 + 商品匹配引擎决定,
|
||||
-- 规则引擎的结论由 channel_assign 直接写成 channel=work_order。
|
||||
-- · 本表只处理「精确键」类渠道:affiliate_dash / manual /
|
||||
-- third_party_voucher,以及运营对任意渠道的显式覆盖。
|
||||
-- · provider / shop_id 为空串表示「不限」。
|
||||
-- · 同一 SKU 命中多条同优先级但渠道不同的规则 → 不自动选,
|
||||
-- 订单落 pending 并进 routing_conflict 队列。
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sku_channel_rules (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
provider TEXT NOT NULL DEFAULT '',
|
||||
shop_id TEXT NOT NULL DEFAULT '',
|
||||
match_key_type TEXT NOT NULL DEFAULT 'sku_code',
|
||||
match_key TEXT NOT NULL,
|
||||
channel TEXT NOT NULL,
|
||||
priority INTEGER NOT NULL DEFAULT 100,
|
||||
source TEXT NOT NULL DEFAULT 'operator',
|
||||
source_ref_id BIGINT,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
note TEXT NOT NULL DEFAULT '',
|
||||
created_by_admin_user_id BIGINT REFERENCES admin_users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'chk_sku_channel_rules_key_type'
|
||||
) THEN
|
||||
ALTER TABLE sku_channel_rules
|
||||
ADD CONSTRAINT chk_sku_channel_rules_key_type
|
||||
CHECK (match_key_type IN ('sku_code', 'internal_sku', 'sku_name'));
|
||||
END IF;
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'chk_sku_channel_rules_channel'
|
||||
) THEN
|
||||
ALTER TABLE sku_channel_rules
|
||||
ADD CONSTRAINT chk_sku_channel_rules_channel
|
||||
CHECK (channel IN ('work_order', 'affiliate_dash', 'third_party_voucher', 'manual'));
|
||||
END IF;
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'chk_sku_channel_rules_source'
|
||||
) THEN
|
||||
ALTER TABLE sku_channel_rules
|
||||
ADD CONSTRAINT chk_sku_channel_rules_source
|
||||
CHECK (source IN ('operator', 'affiliate', 'manual', 'voucher', 'template'));
|
||||
END IF;
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'chk_sku_channel_rules_key'
|
||||
) THEN
|
||||
ALTER TABLE sku_channel_rules
|
||||
ADD CONSTRAINT chk_sku_channel_rules_key
|
||||
CHECK (btrim(match_key) <> '');
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 同一 (来源范围, 键类型, 键, 渠道) 只允许一条;运营覆盖用更高优先级而非重复行。
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_sku_channel_rules_key_channel
|
||||
ON sku_channel_rules (provider, shop_id, match_key_type, match_key, channel);
|
||||
|
||||
-- 解析路径:provider/shop 精确优先,空串表示不限;按优先级升序取第一条。
|
||||
CREATE INDEX IF NOT EXISTS idx_sku_channel_rules_lookup
|
||||
ON sku_channel_rules (match_key_type, match_key, enabled, priority)
|
||||
INCLUDE (provider, shop_id, channel);
|
||||
|
||||
COMMENT ON TABLE sku_channel_rules IS
|
||||
'L1 渠道路由规则:精确键(SKU / 内部 SKU / SKU 名称)→ 渠道;接单平台由模板引擎决定,不在此表';
|
||||
COMMENT ON COLUMN sku_channel_rules.priority IS
|
||||
'越小越优先:运营指定 10 / 自动发货 60 / 人工 70;同优先级多渠道=冲突';
|
||||
COMMENT ON COLUMN sku_channel_rules.source_ref_id IS
|
||||
'来源对象 id:affiliate=product_catalog.id / manual=manual_fulfillment_configs.id / 其余为空';
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 4. 从现有配置生成初始规则(小表,836 行级,可在迁移内完成)
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- 4.1 自动发货:商品目录里的 aff:<sku> 即渠道归属
|
||||
INSERT INTO sku_channel_rules (
|
||||
provider, shop_id, match_key_type, match_key, channel,
|
||||
priority, source, source_ref_id, enabled, note, created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
'', '',
|
||||
'sku_code',
|
||||
btrim(substr(p.canonical_sku, 5)),
|
||||
'affiliate_dash',
|
||||
60,
|
||||
'affiliate',
|
||||
p.id,
|
||||
TRUE,
|
||||
'由商品目录 aff: 前缀生成',
|
||||
NOW(), NOW()
|
||||
FROM product_catalog p
|
||||
WHERE p.canonical_sku LIKE 'aff:%'
|
||||
AND p.status = 'active'
|
||||
AND btrim(substr(p.canonical_sku, 5)) <> ''
|
||||
ON CONFLICT (provider, shop_id, match_key_type, match_key, channel) DO NOTHING;
|
||||
|
||||
-- 4.2 人工:已配置的人工 SKU(生产 43 条,按内部 SKU 精确匹配)
|
||||
INSERT INTO sku_channel_rules (
|
||||
provider, shop_id, match_key_type, match_key, channel,
|
||||
priority, source, source_ref_id, enabled, note, created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
'', '',
|
||||
'internal_sku',
|
||||
btrim(cfg.sku_key),
|
||||
'manual',
|
||||
70,
|
||||
'manual',
|
||||
cfg.id,
|
||||
cfg.enabled,
|
||||
'由人工履约配置生成',
|
||||
NOW(), NOW()
|
||||
FROM manual_fulfillment_configs cfg
|
||||
WHERE btrim(cfg.sku_key) <> ''
|
||||
ON CONFLICT (provider, shop_id, match_key_type, match_key, channel) DO NOTHING;
|
||||
|
||||
-- 4.3 第三方卡券:不预置,运营在「渠道规则」页按 SKU 添加(source='voucher')。
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 5. 成本项:第三方卡券采购成本(仅用于统计,无载体)
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'chk_cost_items_cost_model'
|
||||
) THEN
|
||||
ALTER TABLE cost_items
|
||||
ADD CONSTRAINT chk_cost_items_cost_model
|
||||
CHECK (cost_model IN (
|
||||
'affiliate_purchase', 'work_order_reward', 'manual_actual',
|
||||
'third_party_voucher_cost', 'platform_fee', 'payment_fee',
|
||||
'after_sales_loss', 'other'
|
||||
));
|
||||
ELSE
|
||||
ALTER TABLE cost_items DROP CONSTRAINT chk_cost_items_cost_model;
|
||||
ALTER TABLE cost_items
|
||||
ADD CONSTRAINT chk_cost_items_cost_model
|
||||
CHECK (cost_model IN (
|
||||
'affiliate_purchase', 'work_order_reward', 'manual_actual',
|
||||
'third_party_voucher_cost', 'platform_fee', 'payment_fee',
|
||||
'after_sales_loss', 'other'
|
||||
));
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
INSERT INTO cost_items (
|
||||
code, name, cost_model, pricing_mode, unit_name, currency,
|
||||
status, note, created_at, updated_at
|
||||
)
|
||||
VALUES (
|
||||
'sys_third_party_voucher_cost',
|
||||
'第三方卡券采购成本',
|
||||
'third_party_voucher_cost',
|
||||
'per_item',
|
||||
'件',
|
||||
'CNY',
|
||||
'active',
|
||||
'第三方卡券渠道:仅统计收入与采购成本,不创建履约载体、不展示给买家',
|
||||
NOW(), NOW()
|
||||
)
|
||||
ON CONFLICT (code) DO NOTHING;
|
||||
|
||||
-- 不预置 0 元占位版本:0 元会被误当成真实成本。
|
||||
-- 运营在成本中心按商品配置采购价后再生成版本。
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 6. 异常表:从「一单一条」改为「SKU 级问题头 + 明细」
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- 先摘掉旧的逐单唯一约束(subject_type, subject_id, reason)。
|
||||
DROP INDEX IF EXISTS uq_cost_exceptions_subject_reason;
|
||||
|
||||
ALTER TABLE cost_exceptions
|
||||
ADD COLUMN IF NOT EXISTS scope_key TEXT,
|
||||
ADD COLUMN IF NOT EXISTS item_count INTEGER NOT NULL DEFAULT 1,
|
||||
ADD COLUMN IF NOT EXISTS first_seen_at TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS last_seen_at TIMESTAMPTZ;
|
||||
|
||||
-- 存量行(断代脚本会整体清空)给一个可用的 scope_key,避免 NOT NULL 失败。
|
||||
UPDATE cost_exceptions
|
||||
SET scope_key = COALESCE(
|
||||
NULLIF(scope_key, ''),
|
||||
reason || '|' || COALESCE(order_item_id::text, subject_id::text)
|
||||
),
|
||||
first_seen_at = COALESCE(first_seen_at, created_at),
|
||||
last_seen_at = COALESCE(last_seen_at, created_at)
|
||||
WHERE scope_key IS NULL OR first_seen_at IS NULL OR last_seen_at IS NULL;
|
||||
|
||||
-- 只有 open / ignored 的问题头保持唯一;resolved 允许重复出现(历史留痕)。
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_cost_exceptions_scope_open
|
||||
ON cost_exceptions (scope_key)
|
||||
WHERE status <> 'resolved';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_cost_exceptions_reason_status
|
||||
ON cost_exceptions (reason, status, last_seen_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cost_exception_items (
|
||||
exception_id BIGINT NOT NULL REFERENCES cost_exceptions(id) ON DELETE CASCADE,
|
||||
order_item_id BIGINT NOT NULL REFERENCES order_items(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (exception_id, order_item_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_cost_exception_items_item
|
||||
ON cost_exception_items (order_item_id);
|
||||
|
||||
COMMENT ON TABLE cost_exceptions IS
|
||||
'成本/分类问题队列(SKU 级):scope_key 相同的订单项聚合为一条问题,明细见 cost_exception_items';
|
||||
COMMENT ON COLUMN cost_exceptions.reason IS
|
||||
'classification_missing 待分类 / routing_conflict 规则冲突 / product_mapping_missing 未归一 / cost_missing 成本缺失 / manual_config_missing 人工 SKU 未配置';
|
||||
COMMENT ON COLUMN cost_exceptions.item_count IS
|
||||
'当前牵涉的订单项数(明细去重后的计数)';
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 7. 管道水位表
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS finance_watermarks (
|
||||
pipeline TEXT PRIMARY KEY,
|
||||
last_item_id BIGINT NOT NULL DEFAULT 0,
|
||||
last_stat_date DATE,
|
||||
last_success_at TIMESTAMPTZ,
|
||||
last_error TEXT NOT NULL DEFAULT '',
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
INSERT INTO finance_watermarks (pipeline, last_item_id, last_stat_date, last_success_at)
|
||||
VALUES
|
||||
('channel_assign', 0, NULL, NULL),
|
||||
('carrier_dispatch', 0, NULL, NULL),
|
||||
('product_normalize', 0, NULL, NULL),
|
||||
('cost_snapshot', 0, NULL, NULL),
|
||||
('sales_facts', 0, NULL, NULL),
|
||||
('ledger_post', 0, NULL, NULL)
|
||||
ON CONFLICT (pipeline) DO NOTHING;
|
||||
|
||||
COMMENT ON TABLE finance_watermarks IS
|
||||
'各同步管道水位:last_item_id 用于订单项级增量,last_stat_date 用于按日聚合';
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 8. 销售事实:允许新渠道与「未分类」桶
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- 销售事实继续复用 fulfillment_mode 列承载渠道值(113 再改列名),
|
||||
-- 但必须允许第三方卡券、未分类与归档状态。
|
||||
ALTER TABLE sales_daily_facts
|
||||
DROP CONSTRAINT IF EXISTS chk_sales_daily_facts_fulfillment_mode;
|
||||
|
||||
ALTER TABLE sales_daily_facts
|
||||
ADD CONSTRAINT chk_sales_daily_facts_fulfillment_mode
|
||||
CHECK (
|
||||
fulfillment_mode IN (
|
||||
'work_order', 'affiliate_dash', 'third_party_voucher',
|
||||
'manual', 'unclassified', 'archived'
|
||||
)
|
||||
);
|
||||
|
||||
COMMENT ON COLUMN sales_daily_facts.fulfillment_mode IS
|
||||
'销售事实维度(渠道):work_order / affiliate_dash / third_party_voucher / manual / unclassified 未分类 / archived 归档';
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 9. 人工 SKU 登记表降级为派生数据
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
COMMENT ON TABLE manual_sku_registry IS
|
||||
'【派生数据】人工 SKU 扫描结果,可由 pending/manual 订单项重新生成;真源是 manual_fulfillment_configs';
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 10. 完成
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 迁移后仍需执行(按顺序):
|
||||
-- 1. scripts/backfill-fulfillment-channel.ts —— 438k 行分批回填 channel/status
|
||||
-- 2. scripts/finance-cutover-v2.ts —— 断代归档 + 派生数据清零
|
||||
-- 3. scripts/rebuild-after-cutover.ts —— 10-31 起点数据重建 + 期初建账
|
||||
Reference in New Issue
Block a user