- 指派失败改为完整补偿:取消指派(解冻打手押金 + 工单退回大厅 + 解绑现货)→ 兜底释放占用 → 摘除工单寄售标记。此前只把现货放回在库池,一旦"指派已提交、写 material 失败",工单会停在 in_progress + 押金冻结却不带寄售记录,现货还回到可匹配池,同一件货会被第二张单占用。 - 电子凭证核销失败写 consignment_fulfill_voucher_consume_failed 事件,前端按 voucherConsume.ok 弹警告并提示人工核销,不再无条件报成功(核销走快手外部接口,无法与数据库事务合并回滚)。 - 匹配规格改为 match/mismatch/unknown 三态,钻级兜底由 || 1 改 || 0:订单规格信息不全一律不进 "精确匹配",降级 partial 并在原因里列出缺失字段,消除"任意现货都 100 分精确匹配"的错发根因。 - 履约与指派在占库前做服务端规格复检:冲突 409 consignment_spec_mismatch;非 exact 必须显式 传 allowPartial(前端在部分匹配候选上先危险确认),高配充低配保留但不可误触。 - 人工解绑守卫由"状态黑名单"收窄为只拦交付中三态(与撤销白名单对齐),修复 "工单未开始 + 现货仍绑定"没有任何人工出口的问题。 - 新增 detachConsignmentMaterialFromWorkOrderWithClient,解绑回库/人工退回复库/作废同步摘掉 工单寄售标记,消除"已回库却仍显示寄卖履约"的陈旧痕迹。 - 回归测试新增 8 例(后端 410 例 / 407 通过),附迁移 079 上库前检查脚本。
136 lines
5.4 KiB
SQL
136 lines
5.4 KiB
SQL
-- 迁移 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 行。若有行,需人工决定保留哪笔冻结(多冻的部分按 deposit_release 冲正)。
|
||
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;
|
||
|
||
-- 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;
|