Files
order_site/docs/寄卖系统迁移079上库前检查.sql
T
yml2213 ccb5b6f4d6 docs: 清理过时文档(以代码为准)
删除(整篇已被代码淘汰 / 重复副本 / 与仓库无关):
- cloudflared_tunnel.txt:全仓无 cloudflared 配置,个人终端记录,无任何引用
- 履约配置/工程债-lewan拆分与短链.md:文中 services/kuaishou-cloud 与 kuaishou-cloud-executor 已删除
- 履约配置/快手Cloud领取mock.md:mock:claim / mock:feifei 与 dev-mock 卡片已移除(只剩 mock:open91)
- 行业电子凭证/解决方案/技术方案/1.基础技术.md(+ 其孤立 image.png):0.基础技术.md 的 75 行截断副本
- 寄卖库存功能设计与实施方案.md:手工入库已 409 停用、收集链接表已随 079 删除、统计看板未实现
- 服务器图片迁移到阿里云OSS.md:MinIO→OSS 迁移已完成(生产 STORAGE_MODE=oss)
- pgBackRest备份方案分析.md:全仓无 pgBackRest 落地痕迹,文档自述暂缓实施
- finance-implementation-plan.md:财务模块已落地(072 迁移 / RBAC / 页面接口),已实现部分与代码重复
  (未落地的二期三期路线图可从本提交的历史版本取回)

改写为与代码一致(已下线通道不再当作现行能力):
- 履约配置/多发货平台与电子凭证整体链路.md:kuaishou-cloud / kuaishou-feifei 标注为已下线 legacy stub,
  现行自动通道 affiliate-dash、人工 manual_dispatch;核销时机与券表按实现校正
- 履约配置/{UID领取与旧单兼容, open91请求, TypeScript类型收紧}.md:lewan/feifei/cloudtentacles 表述改为
  现行 affiliate-dash 链路;normalize 具名类型 KuaishouCloudFlow/FeifeiFlow → AffiliateDashFlow
-  affiliate_dash_发货平台/affiliate-dash对接.md:§13/§14/§19/§21/§24/§25 的已删文件与已下线通道引用校正,
  §21/§22 加「当时快照 / 落地后现状」横幅,回调去重表按 013_affiliate_dash_webhook_events.sql 改写
- 91卡券/{3.异步卡密下单, 5.当前项目对接配置, 6.开发设计}.md:删掉不存在的后台入口、
  cloudtentacles/kuaishou_ct_assisted 依赖、cardsEncoding 与 .js 代码落点校正
- 行业电子凭证/技术方案.md:checklist 指向不存在的 测试用例.md → gen-test-cases.ts + 5.测试指南.md
- 履约配置/cloudtentacles发货平台.md:补「本仓库未接入该通道」说明(保持外部参考)
- 部署启动与数据库重建.md:补齐生产必需变量 APP_DOMAIN / CORS_ALLOWED_ORIGINS / STORAGE_MODE / STORAGE_OSS_*

其它:
- README.md:迁移序号范围 002~009 → 002~080
- 寄卖系统迁移079上库前检查.sql:第 2 段注释按批次 Y 的自动去重更新;新增 2.1「重复退款风险」只读排查
  (已在一次性 PostgreSQL 16 上跑通 001→080 迁移:整份脚本 exit=0,脏数据下 2/2.1 均按预期报警)
- 寄卖系统优化方案.md:删掉链路图里已下线的「客服手工录入」,去掉指向已删设计稿的引用
2026-09-17 16:57:27 +08:00

192 lines
7.9 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.
-- 迁移 079 上库前 / 上库后检查脚本(只读,可直接在生产库执行)
--
-- 迁移文件:apps/backend/src/db/migrations/079_drop_consignment_collect_links.sql
-- 已在全新空库验证:001 → 079 可一次性跑通(79 条迁移全部记录在 schema_migrations)。
-- 生产库上库前请先跑完「上库前」四段检查。
-- ============================ 上库前 ============================
-- 1. 同一张工单被多件现货同时绑定?
-- 会让唯一索引 uniq_consignment_inventories_bound_work_order 创建失败。
-- 迁移自带清理(保留工单 material 里记录的那一件,其余解绑回库),
-- 但有数据时请先确认"保留哪一件"与工单 material.consignment.inventoryId 一致。
-- 期望:0 行。
SELECT
ci.bound_work_order_id,
COUNT(*) AS bound_rows,
ARRAY_AGG(ci.id ORDER BY ci.id) AS inventory_ids,
MAX(NULLIF(wo.material_json -> 'consignment' ->> 'inventoryId', '')) AS material_inventory_id
FROM consignment_inventories ci
JOIN work_orders wo ON wo.id = ci.bound_work_order_id
WHERE ci.bound_work_order_id IS NOT NULL
AND ci.status = 'consumed'
GROUP BY ci.bound_work_order_id
HAVING COUNT(*) > 1;
-- 2. 同一提报单重复冻结了发布押金?
-- 会让唯一索引 uniq_worker_wallet_ledgers_consignment_publish_freeze 创建失败。
-- 期望:0 行。注意 079 自身会先去重再建索引——每组(打手+提报单)保留最早一条冻结流水,
-- 多余行改 payload 标记 kind=consignment_publish_superseded(移出押金口径与索引范围,
-- 原行留审计)并按 LEAST(多冻总额, 冻结池实有) 退还押金、写 consignment_publish_dedup_refund
-- 修正流水。所以有行也能建起索引;但若该提报单的押金此前已被释放/没收过,
-- 自动退款会重复退(见 2.1),必须先人工确认。
SELECT
payload_json ->> 'consignmentSubmissionId' AS submission_id,
COUNT(*) AS freeze_rows,
SUM(ABS(amount)) AS frozen_fen
FROM worker_wallet_ledgers
WHERE ledger_type = 'deposit_freeze'
AND payload_json ->> 'kind' = 'consignment_publish'
GROUP BY 1
HAVING COUNT(*) > 1;
-- 2.1(只读)重复退款风险:同提报单有多条冻结流水,且该提报单的押金已经被释放/没收过。
-- 判据:already_settled_fen > 0 时,079 的自动退款会把「已经退过的钱」再退一次——
-- 钱从同一打手其它提报单的冻结额里出,之后那张单释放/没收会因为
-- frozen < outstanding 抛 consignment_deposit_balance_inconsistent(接口 500)。
-- 命中就必须先人工处理,不要直接上库。safe_refund_should_be_fen = 应当退还的金额。
-- 期望:0 行。
WITH fr AS (
SELECT worker_id, payload_json ->> 'consignmentSubmissionId' AS sub, id, ABS(amount) AS amt
FROM worker_wallet_ledgers
WHERE ledger_type = 'deposit_freeze'
AND COALESCE(payload_json ->> 'kind', '') = 'consignment_publish'
AND COALESCE(payload_json ->> 'consignmentSubmissionId', '') <> ''
),
g AS (
SELECT
worker_id,
sub,
COUNT(*) AS freeze_rows,
SUM(amt) AS freeze_total_fen,
(ARRAY_AGG(amt ORDER BY id ASC))[1] AS kept_fen
FROM fr
GROUP BY 1, 2
),
s AS (
SELECT
worker_id,
payload_json ->> 'consignmentSubmissionId' AS sub,
COALESCE(SUM(CASE WHEN ledger_type IN ('deposit_release', 'deposit_deduction')
THEN ABS(amount) ELSE 0 END), 0) AS settled_fen
FROM worker_wallet_ledgers
WHERE COALESCE(payload_json ->> 'kind', '') = 'consignment_publish'
AND COALESCE(payload_json ->> 'consignmentSubmissionId', '') <> ''
GROUP BY 1, 2
)
SELECT
g.worker_id,
g.sub AS submission_id,
g.freeze_rows,
g.freeze_total_fen - g.kept_fen AS dup_fen,
COALESCE(s.settled_fen, 0) AS already_settled_fen,
COALESCE(w.frozen_deposit_amount, 0) AS wallet_frozen_fen,
LEAST(g.freeze_total_fen - g.kept_fen, COALESCE(w.frozen_deposit_amount, 0))
AS migration_will_refund_fen,
GREATEST(0, (g.freeze_total_fen - COALESCE(s.settled_fen, 0)) - g.kept_fen)
AS safe_refund_should_be_fen
FROM g
LEFT JOIN s ON s.worker_id = g.worker_id AND s.sub = g.sub
LEFT JOIN worker_wallets w ON w.worker_id = g.worker_id
WHERE g.freeze_rows > 1
AND COALESCE(s.settled_fen, 0) > 0
ORDER BY 1, 2;
-- 3. 待删除对象的影响面(收集链接渠道下线)。
-- 用 DO 块 + 动态 SQL,保证"表/列已被删掉"时脚本也能跑(否则会直接报错中断)。
DO $$
DECLARE
n bigint;
BEGIN
IF to_regclass('public.consignment_collect_links') IS NOT NULL THEN
EXECUTE 'SELECT COUNT(*) FROM consignment_collect_links' INTO n;
RAISE NOTICE '[3.1] 收集链接表仍存在,共 % 行;删除后不可恢复', n;
ELSE
RAISE NOTICE '[3.1] 收集链接表不存在(079 已执行或从未创建)';
END IF;
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'consignment_submissions' AND column_name = 'link_id'
) THEN
EXECUTE 'SELECT COUNT(*) FROM consignment_submissions WHERE link_id IS NOT NULL' INTO n;
RAISE NOTICE '[3.2] 提报单 link_id 仍存在,共 % 行有引用;删除后不可恢复', n;
ELSE
RAISE NOTICE '[3.2] 提报单 link_id 不存在(079 已执行或从未创建)';
END IF;
END
$$;
-- 3.3 来源枚举分布(迁移会把 collection_link 归并为 worker_portal)
SELECT source_type, COUNT(*) FROM consignment_inventories GROUP BY source_type ORDER BY 1;
-- 4. 历史遗留的"已出库但无工单绑定"现货(079 之前 worker_deposit 占库不写绑定)
-- 这些行不会随撤单自动解绑,需要运营在寄卖仓库手动【退回复库】。
SELECT COUNT(*) AS legacy_consumed_without_binding
FROM consignment_inventories
WHERE status = 'consumed' AND bound_work_order_id IS NULL;
-- ============================ 上库后 ============================
-- 5. 结构校验:新列 / 新索引 / 枚举约束是否就位
SELECT
EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'consignment_inventories' AND column_name = 'assigned_worker_id'
) AS has_assigned_worker_id,
EXISTS (
SELECT 1 FROM pg_indexes WHERE indexname = 'uniq_consignment_inventories_bound_work_order'
) AS has_bound_work_order_unique,
EXISTS (
SELECT 1 FROM pg_indexes
WHERE indexname = 'uniq_worker_wallet_ledgers_consignment_publish_freeze'
) AS has_ledger_freeze_unique,
EXISTS (
SELECT 1 FROM information_schema.tables WHERE table_name = 'consignment_collect_links'
) AS collect_links_table_still_exists,
EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'consignment_submissions' AND column_name = 'link_id'
) AS submissions_link_id_still_exists;
SELECT pg_get_constraintdef(oid) AS source_type_check
FROM pg_constraint
WHERE conname = 'consignment_inventories_source_type_check';
-- 6. 绑定一致性总览(上库后运营巡检用)
SELECT
ci.id,
ci.inventory_no,
ci.status,
ci.bound_work_order_id,
wo.status AS work_order_status,
ci.allocation_status,
ci.assigned_worker_id,
ci.worker_id AS seller_worker_id
FROM consignment_inventories ci
LEFT JOIN work_orders wo ON wo.id = ci.bound_work_order_id
WHERE ci.status = 'consumed'
ORDER BY ci.updated_at DESC
LIMIT 50;
-- 7. 发布押金挂账巡检:台账未释放金额是否超过钱包冻结额(按打手)
WITH ledger AS (
SELECT
worker_id,
COALESCE(SUM(CASE WHEN ledger_type = 'deposit_freeze' THEN ABS(amount) ELSE 0 END), 0)
- COALESCE(SUM(CASE WHEN ledger_type = 'deposit_release' THEN ABS(amount) ELSE 0 END), 0)
- COALESCE(SUM(CASE WHEN ledger_type = 'deposit_deduction' THEN ABS(amount) ELSE 0 END), 0)
AS outstanding_fen
FROM worker_wallet_ledgers
WHERE payload_json ->> 'kind' = 'consignment_publish'
GROUP BY worker_id
)
SELECT
l.worker_id,
l.outstanding_fen,
w.frozen_deposit_amount AS wallet_frozen_fen
FROM ledger l
JOIN worker_wallets w ON w.worker_id = l.worker_id
WHERE l.outstanding_fen > 0
AND l.outstanding_fen > w.frozen_deposit_amount;