Files
order_site/docs/寄卖子系统技术分析报告.md
T
yml2213 610c65bb34 docs: 新增发单系统与寄售仓库技术分析报告
1. docs/项目分析报告-发单系统与寄售仓库.md:项目全景(实测文件/行数、测试结果、迁移规模)、后台发单链路与工单状态机、寄售仓库数据模型与匹配规则、按优先级排序的风险总表与改进路线;
2. docs/寄卖子系统技术分析报告.md:寄卖子系统逐项分析,含端到端流程、077-082 数据模型、核心服务清单、库存与分配状态机、API 清单、风险与测试缺口;
3. 两份报告中的资金与安全类结论均已对照源码复核,标注「待确认」的属需要业务口径拍板、而非代码缺陷的项。
2026-10-01 11:07:36 +08:00

25 KiB
Raw Blame History

寄售仓库 / 寄卖子系统 深度技术分析

分析基线:ef491660(含 9298b31b 白名单总开关)。全部结论取自仓库真实代码,行号可复核。

一、业务定位与术语表(本仓库口径)

术语 代码实体 含义与关键约束
寄卖 / 寄存 consignment_* 全套 打手把自己在《和平精英》的苏尔南手枪挂到平台,由平台撮合卖给买家
现货 / 库存 consignment_inventories 审核通过后落库的一件真实货物,status ∈ in_stock/consumed/archived
提报 / 上架 consignment_submissions(打手端 publishWorkerConsignment) 打手端唯一入口,发布即冻结发布押金;SUB… 单号是全流程唯一锚点(consignment-worker-service.ts:411)
审核入库 approveAdminConsignmentSubmission 客服审核通过 → 生成 INV… 库存行(consignment-repo.ts:1170-1217);驳回退押金
匹配 consignment-matching-service.ts 按系统/区服/系列/钻级/璀璨做规格比对,exact/partial/conflict
履约(两条) internal_stock / worker_deposit 内部现货:客服/内部仓代交付(无接单打手);押金履约:指派打手交货并冻结履约押金
待加好友 parkWorkOrderWaitingFriendAddWithClient 占库后工单停在 gift_phase=material_required,等人确认加好友
赠送冷却 gift_phase=friend_countdown 确认加好友后进入冷却,冷却结束才可上传赠送截图
赔付 / 没收 forfeitConsignmentPublishDeposit*、settleWorkOrderDepositWithClient 卖家发布押金 / 打手履约押金的没收与退还
押金履约押金 worker_wallets.frozen_deposit_amount 发布押金与工单履约押金共用同一冻结池,靠 payload_json.kind 区分口径
白名单 worker_users.can_consign + 总开关 详见第八节

二、端到端流程

flowchart TD
  A[打手端提报 苏尔南] -->|检查白名单+冻结发布押金 ¥20| B[consignment_submissions pending_review]
  B -->|客服驳回| B2[rejected + 释放押金]
  B -->|客服审核通过| C[consignment_inventories in_stock/待安排]
  C -->|工单发布前候选提醒 / 手动匹配| D{匹配方式}
  D -->|internal_stock| E1[原子占库 consumed/已安排 + 工单绑 material + 停待加好友]
  D -->|worker_deposit| E2[条件占库 + 指派打手冻结履约押金 + in_progress]
  E1 --> F[确认加好友 → 赠送冷却]
  E2 --> F
  F -->|冷却结束| G[卖家/打手上传赠送截图 → pending_acceptance]
  G --> H[客服验收 accepted]
  H --> I[释放发布押金 + 结算供货货款 cost_price_fen]
  E1 -.撤销履约 reopen/回库/作废.-> C
  E2 -.撤单/取消指派/超时.-> C

工单终态解绑出口唯一:releaseConsignmentInventoryBindingForWorkOrderWithClient(consignment-repo.ts:765),已接入 work-order-cancel-repo.ts:84,202,325,448,537、worker-cancel-request-repo.ts:572、work-order-issue-repo.ts:344。

三、数据模型

表 关键字段 状态枚举 索引/约束
consignment_inventories (077:3-39, 078:3-7, 079:49-58) inventory_no UNIQUE、item_name/system/server_zone/series/diamond_level/brilliance_count、cost_price_fen、credentials_json(账号密码)、screenshots_json、allow_matching、worker_id(卖家)、assigned_worker_id(履约打手)、deposit_amount_fen、bound_order_id/bound_work_order_id/bound_platform_order_id/shop_name status ∈ in_stock/consumed/archived(077:25-26);allocation_status ∈ 待安排/已安排(080:33-35);source_type ∈ manual/worker_portal/recycle(079:38-40) uniq_consignment_inventories_bound_work_order 部分唯一(bound_work_order_id WHERE status='consumed', 079:93-95);idx_..._status_matching、_game_category、_specs、_buyer、_shop、_batch、_worker_id、_assigned_worker_id
consignment_submissions (077:64-91, 078:9-13) submission_no UNIQUE、result_inventory_id、content_json(characterName/Id/sellerNote/channel)、asking_price_fen、deposit_amount_fen、worker_id/name/phone status ∈ pending_review/approved/rejected(077:83-84) idx_..._status_created;link_id 已被 079:11-12 删除
consignment_collect_links — — 079:15 已整体 DROP
worker_users (082:4-6) can_consign(默认 FALSE)、consignment_activated_at — idx_worker_users_can_consign(082:12-13)
worker_wallet_ledgers ledger_type、payload_json.kind=consignment_publish、consignmentSubmissionId deposit_freeze/deposit_release/deposit_deduction uniq_worker_wallet_ledgers_consignment_publish_freeze(079:177-180)

079 为什么删除收集链接:H5 定向收集(/consignment-collect/:token + 公开 link/submit/upload)存在两个致命问题——上传响应层级取错导致截图恒为空(旧 ConsignmentCollectPage.tsx,优化方案 P0-1),以及按手机号自动绑定打手可让第三方被动冻结其押金(优化方案 P2-5)。业务改为「只能在接单平台提报」,于是删表删列,source_type 的 collection_link 归并为 worker_portal(079:1-8、17-40)。同一次迁移还补了两件资金/一致性收口:assigned_worker_id 拆分(079:47-54,防止占库覆盖卖家导致"我的寄卖"丢件)与冻结流水幂等唯一索引 + 历史重复冻结去重退款(079:97-180)。

四、核心服务清单

文件(仓库根相对) 职责 关键导出
apps/backend/src/services/worker-platform/consignment-worker-service.ts 打手端:提报(白名单+规格校验+冻结押金)、我的寄卖聚合、确认加好友、提交赠送截图、建议价 publishWorkerConsignment(:146)、listWorkerConsignments(:351)、confirmWorkerConsignmentFriendAdded(:551)、submitWorkerConsignmentGiftEvidence(:566)、getWorkerConsignmentPriceSuggestion(:581)
.../consignment-matching-service.ts 规格解析/归一、三态比对、候选排序、手动匹配分类、建议价规则 extractPeacekeeperSpecs(:245)、matchPeacekeeperOrderWithInventory(:309)、classifyConsignmentMatch(:518)、listConsignmentMatchOptions(:552)、findMatchingPeacekeeperInventories(:569)、suggestConsignmentPriceFromRules(:633)
.../consignment-fulfillment-service.ts 履约:内部现货履约、指派打手履约、补偿回滚、撤销、候选查询、服务端规格复检 listWorkOrderInventoryMatchOptions(:179)、listInventoryMatchableOrders(:220)、checkWorkOrderInventoryMatches(:305)、fulfillWorkOrderWithConsignment(:345)、assignWorkOrderWithConsignmentWorker(:603)、resolveConsignmentAssignRewardFen(:590)、revokeWorkOrderConsignmentFulfillment(:910)
.../consignment-admin-service.ts 后台:库存 CRUD/开关/作废/回库、提报审核、映射 mapConsignmentInventory(:158)、fetchAdminConsignmentInventories(:261)、createAdminConsignmentInventory(:319,已 409 停用)、updateAdminConsignmentInventory(:326)、archiveAdminConsignmentInventory(:414)、returnAdminConsignmentInventory(:471)、approve/reject(:564/:648)
.../repositories/worker-platform/consignment-repo.ts 库存/提报全部 SQL、占库/释放/解绑唯一出口 claimConsignmentInventoryForWorkerWithClient(:500)、releaseConsignmentInventoryClaimWithClient(:563)、archiveConsignmentInventoryWithClient(:673)、returnConsignmentInventoryToStockWithClient(:706)、releaseConsignmentInventoryBindingForWorkOrderWithClient(:765)、approveConsignmentSubmissionWithInbound(:1126)
.../repositories/worker-platform/consignment-deposit-repo.ts 发布押金冻结/释放/没收、供货货款结算 freezeConsignmentPublishDepositWithClient(:51)、release...WithClient(:118)、getConsignmentDepositLedgerSnapshot(:186)、forfeit...BySubmissionWithClient(:332)、forfeit...ByWorkOrderWithClient(:399)、settleConsignmentSupplierPayoutByWorkOrderWithClient(:441)
.../repositories/worker-platform/consignment-supplier-repo.ts 从工单反查供货卖家(货款与售后共用) getConsignmentSupplierByWorkOrderWithClient(:20)

手动匹配(f07e4087)如何工作

  1. 两个只读入口:订单侧 GET /matches/:workOrderId/options → listWorkOrderInventoryMatchOptions 返回全部在库现货,按 exact(0)/partial(1)/conflict(2) + 成本价 + id 排序(consignment-fulfillment-service.ts:191-206);库存侧 GET /inventories/:id/match-orders → listInventoryMatchableOrders 反查 unassigned/pending_material/open 工单(:252-261)。
  2. 前端 ConsignmentManualMatchModal.tsx 按 matchType 决定确认项:partial 勾"允许非精确",conflict 勾"确认规格不一致"且必填原因(utils/consignment-manual-match.ts:19-53)。
  3. 落地只走 fulfillWorkOrderWithConsignment(ConsignmentManualMatchModal.tsx:178-184),把 allowPartial/allowSpecMismatch/matchNote 传后端。
  4. 后端二次复检:assertConsignmentSpecMatchedOrder(:73-110) 用宽松模式复检,真冲突默认 409(consignment_spec_mismatch),仅 allowSpecMismatch=true 且 matchNote 非空才放行(resolveManualMatchNote:155-167),冲突明细写入工单事件与 material.consignment.specConflicts(:489-495)。

五、状态机

库存项展示阶段 deriveConsignmentItemStage(domain/consignment-item-stage.ts:146-248)把「提报单 + 库存 + 绑定工单」三套状态推导为唯一阶段:

pending_review → in_stock → (consumed 后按工单) matched_waiting_friend → gift_cooldown → gift_ready → gift_submitted → completed;标准验收走 fulfilling/pending_acceptance;异常终态 rejected/archived/cancelled(:20-34、46-78)。

迁移 触发者 代码位置 附加效果
in_stock → consumed(内部现货) 客服 consignment.fulfill consignment-fulfillment-service.ts:424-453 锁单 FOR UPDATE + 写绑定 + 停在待加好友
in_stock → consumed(指派打手) 客服 consignment-repo.ts:511-540 写 assigned_worker_id='已安排',再调通用指派
consumed → in_stock(撤销回库) 客服 consignment-fulfillment-service.ts:986-1000 带 AND bound_work_order_id=$4 仲裁;发布押金 auto=保持冻结
consumed → archived 客服/自动 同上 :971-984、consignment-admin-service.ts:414-440 已确认加好友强制作废不回池(:968-969)
in_stock → archived 客服 consignment-repo.ts:673 释放发布押金
consumed → in_stock(人工回库) 客服 consignment-repo.ts:706-763 需 NO_LIVE_BOUND_WORK_ORDER_SQL;回库重新冻结押金(ensure...:262)
in_stock → in_stock(终态解绑) 系统 consignment-repo.ts:765 撤单/超时/取消指派自动回池,押金不动

allocation_status 只由系统写:占库写 已安排(consignment-repo.ts:533、履约服务 :428),回库/撤销写 待安排(:990、release :569)。

六、匹配规则

  • 品类隔离门禁:detectItemCategory 识别手枪/密钥/机甲/车皮等,双方都能识别且不一致直接 null;现货为 pistol_suernan 时订单必须含系列或"苏尔南/手枪/枪皮"信号(consignment-matching-service.ts:319-340)。
  • 规格三态:compareSpec(:292-304) 返回 match/mismatch/unknown;通配 SYSTEM_WILDCARDS=['通用']、ZONE_WILDCARDS=['通用','互通'](:288-290)。
  • 档位规则:钻级/璀璨不得低配充高配(:359-360);unknown 项禁止判精确(:363-374)。
  • 严格/宽松:默认严格——系统/区服/系列任一非 match 即排除,且无"高配"正信号也排除(:394-418),回退掉了"只写游戏名就命中全库"的旧行为;宽松模式 allowUnspecifiedSpecs 仅供人工选定后复检(:394、consignment-fulfillment-service.ts:88-90)。
  • 排序:score DESC → cost_price_fen ASC → inbound_at ASC → id ASC(:597-611)。
  • 白名单/手动匹配:白名单只管"能否发布",不参与匹配(domain/consignment-whitelist.ts:7-12);手动匹配可越过任何规格门禁,代价是必填原因并留痕。
  • 建议价:按系统(含通用)+系列精确、钻级/璀璨向上兜底取后台已配最高档,多命中取最高(:633-710)。

七、资金 / 押金 / 赔付

事件 资金动作 代码
发布提报 按 consignmentPublishDepositFen(默认 2000 分 = ¥20,worker-finance-config-service.ts:73-78,108)从可用余额冻结,余额不足 409 consignment-worker-service.ts:255,297;consignment-deposit-repo.ts:51-113
审核驳回 全额释放(幂等:outstanding=0 即空操作) consignment-admin-service.ts:670-674
库存作废 释放发布押金 consignment-admin-service.ts:420-426
人工回库 重新冻结(错失则 409 consignment_deposit_already_settled) :500-506、:262-314
验收通过 释放发布押金 + 补发供货货款 cost_price_fen || asking_price_fen 给卖家 work-order-settlement-repo.ts:99-104、:429、:679;consignment-deposit-repo.ts:441-...
确认加好友后违约/取消/超时 没收发布押金(deposit_deduction) work-order-cancel-repo.ts:140,261,385、work-order-issue-repo.ts:68,254
撤销寄售履约 打手履约押金按 auto/refund/forfeit;卖家发布押金按 auto/archive→release/回库→keep_frozen/forfeit consignment-fulfillment-service.ts:1016-1058;domain/consignment-deposit-policy.ts:54-77
防双付 指派对象≠供货卖家时报酬保持原值,货款走供货货款渠道;=卖家则改报酬为结算价 consignment-fulfillment-service.ts:590-601,689-695;consignment-deposit-repo.ts:459

八、权限与白名单(082 + cb8b249e / 9298b31b)

  • 总开关:worker_finance_config.consignmentWhitelistEnabled(非 DB 列),默认 true(worker-finance-config-service.ts:16-19,79-80,108);后台在资金配置面板切换。
  • 单人权限:worker_users.can_consign,客服在打手管理列表 Switch 实时开关(082:4-6;admin-worker-user-service.ts:182-205;路由 routes/admin/worker-platform/workers.ts:146-158,权限 admin/operator)。空 body 不默认关闭(400 can_consign_required)。
  • 判定:canWorkerPublishConsignment = !whitelistEnabled || workerCanConsign(domain/consignment-whitelist.ts:7-12),后端在提报入口强校验并 403 consignment_permission_required(consignment-worker-service.ts:151-161);前端同口径(utils/consignment-whitelist.ts:5-12)+ 引导卡片(WorkerConsignmentPage.tsx:597-620)。
  • 平滑过渡:082:16-24 把历史上出现在 consignment_submissions 的 worker_id 批量置 can_consign=TRUE。语义已从"永久白名单"演进为"总开关可整体放行"(9298b31b 新增),关闭总开关时保留标记以便再次开启。

九、API 端点清单

打手端(routes/worker/consignment.ts,前缀 /api/v1/worker)

方法/路径 门禁 服务
POST /consignment/publish (:19) requireActiveWorker publishWorkerConsignment
GET /consignment/price-suggestion (:33) requireActiveWorker 建议价
GET /consignment/my-items (:51) 仅会话 listWorkerConsignments
POST /consignment/items/:submissionId/confirm-friend (:65) requireActiveWorker 复用客服加好友链路
POST /consignment/items/:submissionId/gift-evidence (:87) requireActiveWorker 复用 submitWorkerOrderAcceptance(asConsignmentSupplier)

后台(routes/admin/worker-platform/consignment.ts,前缀 /api/v1/admin)

方法与路径 权限 行号
GET /consignment/inventories、/stats、/inventories/:id consignment.view :37,:47,:57
POST /consignment/inventories(已停用 409) consignment.manage :67
PUT /consignment/inventories/:id consignment.manage :80
PATCH /consignment/inventories/:id/matching consignment.manage :98
POST /consignment/inventories/:id/archive、/return consignment.manage :112,:122
GET /consignment/submissions、/submissions/:id consignment.view :142,:152
POST /consignment/submissions/:id/approve、/reject consignment.review :162,:180
GET /consignment/matches/:workOrderId、/.../options consignment.view :200,:211
GET /consignment/inventories/:id/match-orders consignment.view :225
POST /consignment/fulfill consignment.fulfill :242
POST /consignment/assign-worker consignment.fulfill :263(前端已无调用方)
POST /consignment/revoke consignment.fulfill :285
POST /worker-platform/workers/:workerId/consignment-permission admin/operator workers.ts:146

十、风险与问题(按严重程度)

资金安全

  1. 审核改派打手会把货款付给非发货人 — consignment-repo.ts:1227-1230 审核可 worker_id = COALESCE($4, worker_id) 改派;consignment-supplier-repo.ts:67 结算优先取 inventory.worker_id。发布押金已按台账归还原发布人(consignment-deposit-repo.ts:186-241),但 cost_price_fen 货款会打给被改派的打手。若改派是刻意玩法可接受,否则是资金错付。待确认(需业务口径)。
  2. assignWorkerWithConsignment 仍是可达接口但已无前端入口 — routes/admin/worker-platform/consignment.ts:263-283 + services/admin/consignment.ts:151 仍在;直连可绕过 ConsignmentManualMatchModal.tsx:178 的"只走内部现货"收敛,回到追踪文档 §四描述的双重扣押金 + 打手被迫重填资料场景。建议下线或加开关。
  3. mapConsignmentInventory 全量下发 credentialsJson(游戏账号密码) — consignment-admin-service.ts:179-182,仅需 consignment.view,列表接口即返回(优化方案 P2-3 仍成立)。

数据一致性 / 并发

  1. 指派链路跨事务"部分成功"窗口仍在 — claimConsignmentInventoryForWorker(:663) 独立事务提交后,assignAdminWorkOrderToWorker(:680)、updateWorkOrder(:742)、createWorkOrderEvent(:748) 各自独立;失败靠 compensateFailedConsignmentAssignment(:802-849) 收敛,且三步补偿全是 catch {} 吞错(:821-848)。补偿自身失败时会出现「工单 in_progress + 押金已冻 + 现货已回池」→ 同一件货被第二张单占用。文档 寄卖系统优化方案.md:159-165 仍标注"指派事务化未做"。
  2. 改派导致卖家端"我的寄卖"错位 — consignment-worker-service.ts:352-368 分别按 submissions.worker_id 与 inventories.worker_id 过滤(repo :137-140、:902-905)。审核改派后,原卖家有提报无库存 → 永远停在 in_stock「正在入库」(consignment-item-stage.ts:158-161 兜底);被改派者看到从未提报的货。
  3. 提报列表 LEFT JOIN 存在行放大 — consignment-repo.ts:937-943 用 rinv.id = cs.result_inventory_id OR rinv.source_submission_id = cs.id,两个条件同时命中时同一提报单会出两行。
  4. 我的寄卖窗口被硬截断 — consignment-worker-service.ts:353-354 固定 pageSize: 200,超过 200 条的历史寄卖静默不可见;:362-367 同一提报单只保留第一件库存。
  5. internal_stock 履约的押金释放依赖验收事务,撤销/回库路径已覆盖,但"待加好友"无限期挂起时押金与货物同时冻结,无超时策略(文档 寄卖库存优化现状与进度追踪.md:93,98 仍标"待业务拍板")。

性能

  1. 候选匹配全表入内存 — consignment-repo.ts:854-887 listActiveConsignmentInventoriesForMatching 无分页、无 LIMIT,checkWorkOrderInventoryMatches/listWorkOrderInventoryMatchOptions 每次调用全量拉取后在 JS 内比对(consignment-fulfillment-service.ts:190,318)。
  2. 统计接口每次 5 条全表聚合 — consignment-repo.ts:218-303(优化方案 P2-2 未处理)。

可维护性

  1. 仓库关键字搜索仍不覆盖承诺字段 — consignment-repo.ts:178-182 未含 buyer_name/shop_name/seller_phone/worker_name,而前端占位符承诺(追踪文档 :54 仍标"待优化")。
  2. 编辑无法解除打手关联 — ConsignmentInventoryPanel.tsx:255 传 workerId || undefined,后端按 !== undefined 判定(consignment-admin-service.ts:381-386),传空即"不修改"。
  3. remark 拼接式审计无长度上限(:980、:996、release consignment-repo.ts:582)。
  4. listWorkerConsignments 用 Promise.all 两查询 + N 次工单批量查,卖家量大时内存放大(非阻塞问题)。

十一、文档与代码一致性

文档承诺 实际 结论
优化方案 P0-1/2/3/4/5 五项 全部落地(079 迁移、assertConsignmentRevocableWorkOrderStatus:864、绑定唯一索引、release...BindingForWorkOrderWithClient:765) ✅ 一致
P1-2「指派事务化」 仅做了补偿回滚,未合并事务(见风险 4) ⚠️ 半做
P1-8 前端权限门控 已落地(consignment-permissions.test.ts 存在) ✅
批次 E 未做项:报表(节省赏金/出库单数/批次回款)、搜索字段扩展、打手端菜单按账号状态隐藏 仍全部未做 ❌ 未做
§六/§七 统计看板 getConsignmentInventoryStats 只有数量/成本 + 4 个 GROUP BY ❌ 未做
追踪文档 :260 写"if (!worker.can_consign) throw" 实际是 canWorkerPublishConsignment({whitelistEnabled, workerCanConsign})(consignment-worker-service.ts:151-155) ⚠️ 文档滞后于 9298b31b
追踪文档 §四「手动匹配弹窗收敛为内部现货履约」 前端已收敛(ConsignmentManualMatchModal.tsx:178),但后端 assign-worker 接口未下线 ⚠️ 半做
批次策略"回库保留运营匹配开关" releaseConsignmentInventoryClaim(:563) 与 returnConsignmentInventoryToStockWithClient(:706) 均保留 allow_matching ✅
押金策略(待审超期自动驳回、打手自助撤回) 均未实现,consignment_submissions.status 无 cancelled ❌ 未做

历史演进(git log 摘要):1aa6dba9 初版(仓库+H5 收集+内部履约)→ 4a6215e8 接入打手押金履约 → fd1eb581 冷静期 + 押金闭环 → e828e677 权限/竞态/撤销守卫 → 632dddc4 下线 H5 + 状态机封闭 → 5d11bf20 补偿 + 规格复检 → 批次 F–U(押金不退、作废口径、统一状态机、货款结算、手动匹配)→ a5727554「彻底去除非平台内部现货履约」把匹配收敛为指派打手 → 追踪文档 §四发现该路线导致卖家被二次扣押金、被迫重填资料,随后提交把弹窗改回 fulfillWorkOrderWithConsignment;577e0eec 反向清理:从 WorkOrdersPanel.tsx/WorkerOrderDetailDrawer.tsx 移除已废弃的指派打手路径(a5727554 的方向被回退,指派线降级为遗留接口)→ cb8b249e/9298b31b 白名单与总开关。

十二、测试覆盖评估

测试文件 断言重点
domain/consignment-item-stage.test.ts(10 例) 各阶段推导、内部现货 vs 打手线的"确认加好友"按钮差异、冷却到点转待赠送、终态
domain/consignment-deposit-policy.test.ts(6 例) auto/显式覆盖的押金处置矩阵、非法值归一
domain/consignment-whitelist.test.ts(2 例) 总开关开/关
consignment-matching-service.test.ts(24 例) 规格解析、精确/高配充低配、通配互通、空规格不命中、品类隔离、建议价档位兜底
consignment-manual-match.test.ts(7 例) 冲突清单、宽松复检、必填原因、两链路留痕、读接口只读
consignment-worker-service.test.ts(9 例) 单号聚合、状态机接线、卖家归属、白名单 403、璀璨≤钻级
consignment-payout.test.ts(10 例) 单位(分)、双付防护、拼单钩子、079 去重迁移
consignment-fulfillment-price-trace.test.ts(4 例) 发单金额改写与撤销恢复原值
consignment-revoke-policy.test.ts(3 例) 撤销必结算打手押金、作废关匹配开关
consignment-evidence.test.ts(8 例) 凭证取图去重、手工入库停用、链接归一化
consignment-repo.test.ts(3 例) SUB 号搜索、join 列前缀、allocation_status 落在 080 枚举内
admin-worker-consignment-permission.test.ts(3 例) 权限回显、路由注册、空 body 不默认关闭
前端 8 个 consignment 测试 冷却倒计时、凭证、手动匹配挂载(断言走内部现货)、权限、规格选项、白名单、供货打手展示

缺口:(a) 无 approveAdminConsignmentSubmission 的端到端测试(改派+押金归属+货款归属这条链路无回归保护);(b) 无审核/驳回押金释放的金额断言;(c) 无 revokeWorkOrderConsignmentFulfillment 的并发/状态仲裁用例(assertConsignmentRevocableWorkOrderStatus 有纯函数测试但事务内 0 行 409 未覆盖);(d) 无 releaseConsignmentInventoryBindingForWorkOrderWithClient 的幂等/多工单绑定冲突用例;(e) 白名单仅有总开关与单人布尔两种纯函数用例,无"提报时总开关关闭但 can_consign=false 放行"的集成断言;(f) 无前端 ConsignmentManualMatchModal 对 allowSpecMismatch 必填原因的交互测试。