diff --git a/docs/寄卖子系统技术分析报告.md b/docs/寄卖子系统技术分析报告.md
new file mode 100644
index 00000000..37dc0374
--- /dev/null
+++ b/docs/寄卖子系统技术分析报告.md
@@ -0,0 +1,213 @@
+# 寄售仓库 / 寄卖子系统 深度技术分析
+
+> 分析基线:`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` + 总开关 | 详见第八节 |
+
+## 二、端到端流程
+
+```mermaid
+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 仍成立)。
+
+### 数据一致性 / 并发
+
+4. **指派链路跨事务"部分成功"窗口仍在** — `claimConsignmentInventoryForWorker`(:663) 独立事务提交后,`assignAdminWorkOrderToWorker`(:680)、`updateWorkOrder`(:742)、`createWorkOrderEvent`(:748) 各自独立;失败靠 `compensateFailedConsignmentAssignment`(:802-849) 收敛,且三步补偿全是 `catch {}` 吞错(:821-848)。补偿自身失败时会出现「工单 in_progress + 押金已冻 + 现货已回池」→ 同一件货被第二张单占用。文档 `寄卖系统优化方案.md:159-165` 仍标注"指派事务化未做"。
+5. **改派导致卖家端"我的寄卖"错位** — `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` 兜底);被改派者看到从未提报的货。
+6. **提报列表 LEFT JOIN 存在行放大** — `consignment-repo.ts:937-943` 用 `rinv.id = cs.result_inventory_id OR rinv.source_submission_id = cs.id`,两个条件同时命中时同一提报单会出两行。
+7. **我的寄卖窗口被硬截断** — `consignment-worker-service.ts:353-354` 固定 `pageSize: 200`,超过 200 条的历史寄卖静默不可见;`:362-367` 同一提报单只保留第一件库存。
+8. **`internal_stock` 履约的押金释放依赖验收事务**,撤销/回库路径已覆盖,但"待加好友"无限期挂起时押金与货物同时冻结,无超时策略(文档 `寄卖库存优化现状与进度追踪.md:93,98` 仍标"待业务拍板")。
+
+### 性能
+
+9. **候选匹配全表入内存** — `consignment-repo.ts:854-887` `listActiveConsignmentInventoriesForMatching` 无分页、无 LIMIT,`checkWorkOrderInventoryMatches`/`listWorkOrderInventoryMatchOptions` 每次调用全量拉取后在 JS 内比对(`consignment-fulfillment-service.ts:190,318`)。
+10. **统计接口每次 5 条全表聚合** — `consignment-repo.ts:218-303`(优化方案 P2-2 未处理)。
+
+### 可维护性
+
+11. **仓库关键字搜索仍不覆盖承诺字段** — `consignment-repo.ts:178-182` 未含 `buyer_name/shop_name/seller_phone/worker_name`,而前端占位符承诺(追踪文档 `:54` 仍标"待优化")。
+12. **编辑无法解除打手关联** — `ConsignmentInventoryPanel.tsx:255` 传 `workerId || undefined`,后端按 `!== undefined` 判定(`consignment-admin-service.ts:381-386`),传空即"不修改"。
+13. **`remark` 拼接式审计无长度上限**(`:980`、`:996`、release `consignment-repo.ts:582`)。
+14. **`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` 必填原因的交互测试。
diff --git a/docs/项目分析报告-发单系统与寄售仓库.md b/docs/项目分析报告-发单系统与寄售仓库.md
new file mode 100644
index 00000000..e5accbfa
--- /dev/null
+++ b/docs/项目分析报告-发单系统与寄售仓库.md
@@ -0,0 +1,314 @@
+# order_site 项目分析报告
+
+> 重点:**后台发单系统**(进单 → 工单 → 抢单大厅 → 履约 → 验收 → 结算)与 **寄售仓库 / 寄卖** 子系统。
+> 分析基线:`ef491660`(含 `9298b31b` 寄卖白名单总开关),工作区干净,949 commits。
+> 说明:本报告中的**资金/安全类关键结论均已二次复核源码**;标注「待确认」的是需要业务口径拍板、而非代码缺陷的项。
+> 配套明细:寄卖子系统的逐行分析见 [寄卖子系统技术分析报告.md](寄卖子系统技术分析报告.md)。
+
+---
+
+## 一、结论速览
+
+| # | 结论 | 影响 | 证据 |
+| --- | --- | --- | --- |
+| 1 | **「发单金额高于用户付款强制确认」只是前端弹窗,后端无任何校验** | 直调 API 可发出亏损单 | `apps/frontend/src/pages/admin/panels/work-order-payment.ts:22`;`apps/backend/src/services/worker-platform/admin-work-order-management-service.ts:60` |
+| 2 | **审核入库接口可由请求体改 `workerId`,货款按改后的归属人结算** | 资金错付风险 | `apps/backend/src/services/worker-platform/consignment-admin-service.ts:583`;`apps/backend/src/repositories/worker-platform/consignment-supplier-repo.ts:67` |
+| 3 | **抢单与后台指派的加锁顺序相反**(钱包→工单 vs 工单→钱包) | 并发下 40P01 死锁 | 同文件两个函数:`work-order-grab-repo.ts:24 → :58`(grab)vs `:154 → :169`(assign) |
+| 4 | **寄卖「内部现货履约」已收敛,但后端 `assign-worker` 接口仍可达** | 可绕过收敛,复现"双重扣押金 + 打手重填资料" | `apps/backend/src/routes/admin/worker-platform/consignment.ts:263` |
+| 5 | **claim token 默认永久有效;`/collect/*` 与 `/files/object` 无鉴权** | 买家 UID / 私有文件对外暴露面 | `apps/backend/src/config/defaults.ts:64`;`apps/backend/src/app.ts:106,108` |
+| 6 | **模板配置的押金不会带进自动工单**:建单硬编码 0 | 自动发单实际零押金 | `apps/backend/src/services/worker-platform/sync-worker-orders-service.ts:196` |
+| 7 | **迁移在启动时自动执行且无 down 脚本** | 无快速回滚路径,多副本有竞态 | `apps/backend/src/startup/bootstrap.ts:32` |
+| 8 | 后端测试纪律好(零 SQL 拼接 + `check:sql` 护栏 + 508 测试全绿),**前端与 DB 仓库层是测试真空** | 回归网不均衡 | 实测:后端 16.6% / 前端 1.9% |
+
+---
+
+## 二、项目全景(全部实测)
+
+| 指标 | 后端 | 前端 |
+| --- | --- | --- |
+| 源码文件 / 行数(不含测试) | 327 / 67,089 | 158 / 45,977(另 CSS 7,344) |
+| 测试文件 / 行数 | 85 / 11,171 | 17 / 872 |
+| 测试/源码比 | 16.6% | **1.9%** |
+| 测试实测结果(本次复跑) | **508 pass / 3 skipped / 0 fail**(511 项) | **66 pass / 0 fail** |
+| 迁移 | 82 个 SQL / 2,403 行,全库 51 张表 | — |
+
+- 技术栈:Node + Express 5 + TS 6.0 + PostgreSQL + `node-pg-migrate`;React 19 + Vite 8 + AntD 6 + TanStack Query 5(`updatedAt: Infinity`)。
+- 运行形态:**单实例**(内存限流、内存调度器状态、启动即迁移),Compose + Caddy 部署,生产存储走 OSS。
+- 代码集中在少数巨型文件:前端 `WorkOrdersPanel.tsx` **3,657 行**、后端 `consignment-repo.ts` 1,253 / `consignment-fulfillment-service.ts` 1,174。
+- 最大模块:`services/worker-platform/` 15,325 行(占后端 23%),寄卖与工单两部分几乎平分。
+
+```mermaid
+flowchart LR
+ P["91卡券 / 快手电子凭证 / PDD / affiliate-dash"]
+ P --> IN["services/order
进单·商品匹配·建履约任务"]
+ IN --> FUL["services/fulfillment
planner → executor"]
+ FUL --> CLAIM["claim token → 领取页 claimUrl"]
+ IN --> DISPATCH["发单消费者
syncWorkerOrdersForSourceOrder"]
+ DISPATCH --> WO["work_orders
pending_material / unassigned"]
+ WO --> HALL["管理员发布 → open → 抢单大厅"]
+ HALL --> W["打手抢单/拼单
冻结押金 in_progress"]
+ W --> ACC["待验收 → 后台或自动验收 → 结算"]
+ WO -.->|寄卖现货| CONS["寄售仓库
consignment_inventories"]
+ CONS --> ACC
+ SCH["scheduler
超时扫描·押金解冻·PDD同步·数据保留"] --> WO
+```
+
+---
+
+## 三、后台发单系统
+
+### 3.1 端到端链路
+
+```mermaid
+flowchart TD
+ A["91 /orders/create
routes/open-91.ts:27"] --> B["upsertOrderFromSource
order-service.ts:98"]
+ B --> C["规则匹配
work-product-match-mappers.ts:93"]
+ C -->|未命中/歧义| X1["写匹配日志并跳过
sync-worker-orders-service.ts:119"]
+ C -->|命中| D["createWorkOrder
sync-worker-orders-service.ts:185"]
+ D --> E{"必填资料齐?"}
+ E -->|齐| F["unassigned"]
+ E -->|缺| G["pending_material + 待办通知:266"]
+ F --> H["管理员补资料/改价 → unassigned
admin-work-order-material-service.ts:44"]
+ G --> H
+ H --> I["publishAdminWorkOrder
校验 reward>0 :219"]
+ I --> J["open + 核销电子凭证
publish-voucher-service.ts:32"]
+ J --> K["抢单大厅 worker-hall-service.ts:29"]
+ K --> L{"拼单?"}
+ L -->|是| M["joinWorkOrderShare 按份冻押金"]
+ L -->|否| N["grabWorkOrder open→in_progress
work-order-grab-repo.ts:11"]
+ M --> O["提交验收 pending_acceptance"]
+ N --> O
+ O --> P["acceptWorkOrderAndSettle
work-order-settlement-repo.ts:23"]
+ P --> Q["释放/延迟解冻押金 + 结算报酬 + 抵扣售后欠款 + 升级"]
+ N -.->|超时| R["超时扫描
work-order-timeout-service.ts:13"]
+```
+
+**关键澄清**:工单**不会自动上架**到大厅。自动建单只到 `pending_material` / `unassigned`,必须由后台 `publishAdminWorkOrder`(`admin-work-order-management-service.ts:220`)把状态推到 `open` 才会出现在抢单大厅;`reward_amount <= 0` 会被拒绝(:228)。另一条路是后台直接指定打手(`assign`)。
+
+### 3.2 工单状态机
+
+```
+pending_material ──补资料──> unassigned ──发布──> open ──抢单/指派──> in_progress
+ │
+ ┌───────────────┴───────────────┐
+ 提交验收 超时/问题单
+ ▼ ▼
+ pending_acceptance problem / open(重开)
+ │ │
+ 验收 accepted cancelled(退/扣押金)
+```
+
+- 状态常量:`domain/work-order-status.ts:1-9`(8 态 + `problem`)。
+- 履约模式:`standard` / `friend_gift`(赠送冷却默认 **72h**,上限 30 天,`domain/work-order-acceptance-mode.ts:12-13`)。
+- 拼单母单可长期停 `open`,份额状态 `joined → submitted → accepted/cancelled`;`friend_gift` 模式强制关闭拼单(`sync-worker-orders-service.ts:180`)。
+
+### 3.3 核心模块
+
+| 模块(相对仓库根) | 职责 | 关键函数 |
+| --- | --- | --- |
+| `apps/backend/src/services/order/order-service.ts` | 来源订单 upsert(advisory lock 去重)、双消费者分流 | `upsertOrderFromSource:98`,发单调用点 `:211,:241` |
+| `.../services/worker-platform/sync-worker-orders-service.ts` | **发单核心**:匹配 → 建工单 → 事件 → 待办 | `syncWorkerOrdersForSourceOrder:37` |
+| `.../sync-work-orders-from-send-code.ts` | 快手发码回流补全建单 / 全量重扫 | `:90,:176` |
+| `.../work-order-pricing.ts` | 按件/固定/拼单定价 | `:20,:46` |
+| `.../admin-work-order-material-service.ts` | 补资料、改价(含拼单价锁定) | `:23`,改价守卫 `:102` |
+| `.../admin-work-order-management-service.ts` | 编辑、发布、下架、置顶 | `updateAdminWorkOrder:37`、`publishAdminWorkOrder:220` |
+| `.../admin-work-order-assignment-service.ts` | 后台指派 | `:24` |
+| `.../admin-work-order-acceptance-service.ts` | 验收 / 份额验收 / 批量验收 | `:33,:89,:254` |
+| `.../worker-hall-service.ts` | 大厅列表(2s 进程内缓存、分类/延迟/容量) | `:29` |
+| `.../worker-hall-action-service.ts` | 抢单 / 拼单入口 | `:33,:129` |
+| `.../worker-permission-utils.ts` | 等级权限、免押额度、冻结额 | `:9,:58` |
+| `.../publish-voucher-service.ts` | 发布/指派前核销电子凭证 | `:32` |
+| `.../repositories/worker-platform/work-order-{grab,share,settlement,issue,cancel}-repo.ts` | 事务与资金落库 | `grabWorkOrder:11`、`acceptWorkOrderAndSettle:23` |
+
+### 3.4 资金与押金规则(已核实的不变量)
+
+1. **冻结额** = `max(0, required_deposit_amount − 免押额度)`(`worker-permission-utils.ts:58`);免押 = 等级额度 + 个人追加。
+2. **抢单原子性**:状态条件更新 `WHERE status='open' AND assigned_worker_id IS NULL`,抢单时在事务内复核大厅容量与可见延迟(`work-order-grab-repo.ts:41-73`)。
+3. **结算**:`acceptWorkOrderAndSettle` 用 `FOR UPDATE` 锁工单(`:34`)→ 拼单未全部提交则拒绝(`:56`)→ 置 `accepted` → 释放寄卖发布押金(`:99`)→ 逐份额结算:先抵扣售后退款欠款(`:121`),再按 `depositUnfreezeDays`(默认 3 天)与 `rewardUnfreezeEnabled`(默认 true)决定即时到账还是进 `pending_unfreeze_amount`(`:128-140`)。
+4. **超时三策略**:`reopen` / `cancel_release` / `cancel_deduct`(`work-order-timeout-policy.ts`);赠送单在 `material_submitted` 阶段跳过超时。
+5. **赠送单押金不可退**:确认加好友后 `friend_added_at` 非空 → 取消/脱离一律没收(`domain/work-order-acceptance-mode.ts:75`)。
+6. **可编辑边界**:仅 `pending_material/unassigned/open(无活跃份额)` 可编辑/改价,已接单不可改(`admin-work-order-management-service.ts:44-53`)。
+
+### 3.5 API(后台发单,前缀 `/api/v1/admin`)
+
+`GET/POST /worker-platform/orders`、`PUT|DELETE /orders/:id`、`/material`、`/publish`、`/unpublish`、`/assign`、`/unassign`、`/cancel`、`/reopen`、`/confirm-friend`、`/complete-gift-cooldown`、`/reset-gift-material`、`/sharing`、`/note`、`/pin`、`/events`、`/accept-batch`、`/reprocess-matches`(`routes/admin/worker-platform/orders.ts`)。
+打手端:`routes/worker/hall.ts`(大厅、抢单、拼单)、`routes/worker/orders.ts`(我的订单、提交验收)。
+
+---
+
+## 四、寄售仓库(寄卖)
+
+### 4.1 业务定位与术语
+
+打手把自己《和平精英》的**苏尔南手枪**挂到平台(寄卖),平台撮合给买家,履约完成后结算**货款**给卖家。全流程以 **`SUB…` 提报单号**为唯一锚点(对外隐藏 `INV…` 库存号)。
+
+| 术语 | 实体 | 含义 |
+| --- | --- | --- |
+| 提报 / 上架 | `consignment_submissions` | 打手端唯一入口,发布即冻结发布押金(默认 ¥20) |
+| 现货 / 库存 | `consignment_inventories` | 审核入库的一件真实货物 |
+| 内部现货履约 | `internal_stock` | 客服/内部仓代交付,无接单打手,免外派佣金 |
+| 押金履约 | `worker_deposit` | 指派打手交付并冻结履约押金(历史路径,已降级为遗留接口) |
+| 待加好友 / 冷却 | `gift_phase` | 占库后停 `material_required`,确认加好友后进入 72h 冷却 |
+| 供货卖家 | `getConsignmentSupplierByWorkOrderWithClient` | 从工单反查该收货款的人(内部现货履约时工单无接单打手) |
+
+### 4.2 数据模型(迁移 077–082)
+
+| 表 | 关键字段 | 状态枚举 | 约束/索引 |
+| --- | --- | --- | --- |
+| `consignment_inventories`(077:3 / 078 / 079:49) | `inventory_no` UNIQUE、规格五项、`cost_price_fen`、`allow_matching`、`worker_id`(卖家)、`assigned_worker_id`(履约打手)、`bound_order_id`/`bound_work_order_id`、`credentials_json` | `status ∈ in_stock/consumed/archived`;`allocation_status ∈ 待安排/已安排`(080 CHECK);`source_type ∈ manual/worker_portal/recycle` | `uniq_consignment_inventories_bound_work_order`(`bound_work_order_id WHERE status='consumed'`,079:93)+ 8 个普通索引 |
+| `consignment_submissions` | `submission_no` UNIQUE、`result_inventory_id`、`content_json`、`asking_price_fen`、`worker_id`(提报人) | `pending_review/approved/rejected` | `idx_status_created`;`link_id` 已删(079:11) |
+| `consignment_collect_links` | — | — | **079:15 已 DROP** |
+| `worker_users`(082) | `can_consign`(默认 FALSE)、`consignment_activated_at` | — | `idx_worker_users_can_consign` |
+| `worker_wallet_ledgers` | `payload_json.kind='consignment_publish'` + `consignmentSubmissionId` | `deposit_freeze/release/deduction` | `uniq_..._consignment_publish_freeze`(079:177,发布冻结幂等) |
+
+**079 迁移为何重要**:一次性做了三件危险事——删 H5 收集渠道(`collection_link` 归并为 `worker_portal`)、拆分卖家 `worker_id` 与 `assigned_worker_id`(防止占库覆盖卖家导致「我的寄卖」丢件)、清理历史脏数据(重复绑定工单解绑回库 + 重复冻结押金退还并建幂等索引)。**生产上库前必须先跑 [寄卖系统迁移079上库前检查.sql](寄卖系统迁移079上库前检查.sql)**,其中 2.1 段明确:若某提报单的押金此前已被释放/没收过,079 的自动退款会**重复退款**,必须先人工处理(该脚本:43-93)。
+
+### 4.3 状态机(展示口径)
+
+`domain/consignment-item-stage.ts:146-248` 把「提报单 + 库存 + 绑定工单」三套状态**推导成一个**卖家可见阶段:
+
+```
+待审核 → 已通过(在库待匹配) → 已匹配-待加好友 → 加好友赠送冷却中 → 待赠送
+ → 已赠送待审核 → 已完成
+异常终态:已驳回 / 已下架作废 / 已取消
+```
+标准验收(非好友赠送)走 `履约中 → 待验收 → 已完成`。
+
+库存与分配迁移:
+
+| 迁移 | 触发者 | 代码位置 |
+| --- | --- | --- |
+| `in_stock → consumed`(内部现货) | 客服 | `consignment-fulfillment-service.ts:424-453`(锁工单 + 条件占库 + 写绑定 + 停待加好友) |
+| `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=...` 仲裁) |
+| `consumed → archived` | 客服/自动 | 同上 `:971-984`;**已确认加好友时强制作废不回池**(`:968`) |
+| 终态解绑回池 | 系统 | `consignment-repo.ts:765`(唯一出口,被撤单/超时/取消指派等 7 处调用) |
+
+### 4.4 匹配规则(防错发的核心)
+
+`consignment-matching-service.ts:309` `matchPeacekeeperOrderWithInventory`:
+
+1. **品类强隔离门禁**(`:319-340`):基于生产分类体系识别订单/现货品类,双方可识别且不一致直接拒绝;现货是苏尔南手枪时,订单必须带系列或「苏尔南/手枪/枪皮」信号。
+2. **三态比对** `compareSpec`(`:292-304`):`match / mismatch / unknown`;通配 `系统=通用`、`区服=通用|互通`。
+3. **低配不充高配**(`:359`):订单明确钻级/璀璨高于库存即拒绝。
+4. **unknown 不得判精确**(`:363-374`):订单没写清的字段降级为 `partial` 并列出缺失项。
+5. **严格 vs 宽松**(`:394-418`):默认严格(系统/区服/系列任一非 match 即排除);`allowUnspecifiedSpecs` 只给「人工已选定现货后服务端复检」用。
+6. **排序**:score → `cost_price_fen` → `inbound_at` → id(`:597-611`)。
+
+**手动匹配(commit `f07e4087`)**:工单侧列全部在库现货(含 conflict),仓库侧反查可匹配工单;冲突必须 `allowSpecMismatch=true` + 必填 `matchNote`,冲突明细写入工单事件与 `material.consignment.specConflicts`(`:489`)。
+
+### 4.5 白名单与权限
+
+- **总开关**:`worker_finance_config.consignmentWhitelistEnabled`(配置项,非 DB 列,默认 true)。
+- **单人开关**:`worker_users.can_consign`(082,默认 FALSE)。
+- **判定**:`canWorkerPublishConsignment = !whitelistEnabled || canConsign`(`domain/consignment-whitelist.ts:7`);后端提报入口 403 `consignment_permission_required`(`consignment-worker-service.ts:151`)。
+- **存量平滑过渡**:082 把历史上在 `consignment_submissions` 提报过的打手批量置 TRUE(082:16-24)。
+- 后台四个权限:`consignment.view / manage / review / fulfill`(077:126-158,080 修正文案)。
+
+### 4.6 资金规则
+
+| 事件 | 动作 | 代码 |
+| --- | --- | --- |
+| 提报发布 | 冻结发布押金(默认 2000 分) | `consignment-deposit-repo.ts:51` |
+| 审核驳回 | 全额释放(幂等) | `consignment-admin-service.ts:670` |
+| 下架作废 | 释放 | `:420` |
+| 人工回库 | **重新冻结**;已结算则 409 | `:500`、`:262-314` |
+| 验收完成 | 释放发布押金 + 补发**货款** `cost_price_fen \|\| asking_price_fen` | `work-order-settlement-repo.ts:99,429` |
+| 加好友后违约/取消/超时 | 没收发布押金(`deposit_deduction`,只扣冻结额) | `work-order-cancel-repo.ts:140,261,385` |
+| 撤销履约 | 打手押金 auto/refund/forfeit;卖家押金 auto → 作废 release / 回库 keep_frozen;显式覆盖必须填原因 | `consignment-fulfillment-service.ts:1016`、`domain/consignment-deposit-policy.ts:54-77` |
+| 防双付 | 指派对象≠卖家时报酬保持原值;=卖家时改写为结算价 | `consignment-fulfillment-service.ts:590,689` |
+
+### 4.7 API
+
+- 打手端(`/api/v1/worker`):`POST /consignment/publish`、`GET /consignment/price-suggestion`、`GET /consignment/my-items`、`POST /consignment/items/:submissionId/confirm-friend`、`.../gift-evidence`。
+- 后台(`/api/v1/admin`):`GET /worker-platform/consignment/inventories|stats|inventories/:id`(view)、`PUT inventories/:id`、`PATCH inventories/:id/matching`、`archive`、`return`(manage)、`GET submissions`(view)、`POST submissions/:id/approve|reject`(review)、`POST fulfill|assign-worker|revoke`(fulfill)、`POST /worker-platform/workers/:workerId/consignment-permission`。
+
+---
+
+## 五、风险总表(按优先级,已去重)
+
+### P0 · 资金安全
+
+| # | 问题 | 证据 | 建议 |
+| --- | --- | --- | --- |
+| 1 | **发单金额 > 用户付款仅前端确认**,后端改价/发布/指派三处均不做金额上限或与付款额比对 | 前端 `work-order-payment.ts:22`;后端 `admin-work-order-management-service.ts:60`、`admin-work-order-material-service.ts:102`(仅拼单价锁定)、发布仅校验 `>0`(`:228`) | 后端补齐同一校验并写事件留痕;发布/指派/改价三入口共用 |
+| 2 | **审核入库可由请求体 `workerId` 改归属**,货款按 `inventory.worker_id` 结算 | `consignment-admin-service.ts:587`(`payload.workerId` 优先);`consignment-supplier-repo.ts:67`(库存优先于提报);`consignment-repo.ts:1227`(`COALESCE($4, worker_id)`) | 若改派非刻意玩法:审核接口禁止改 `worker_id`,或改派必须留痕 + 双人复核 |
+| 3 | **同路径仍存在「按手机号自动绑定打手」**:审核时若未传 workerId,会用卖家手机号反查并绑定 | `consignment-admin-service.ts:597` | 与 079 下线 H5 的初衷一致化:审核不再自动绑定,需显式选择 |
+| 4 | 审核接口全量返回 `credentialsJson`(游戏账号密码),仅需 `consignment.view` | `consignment-admin-service.ts:179` | 列表脱敏、详情按权限拆分或二次校验 |
+| 5 | 模板 `required_deposit_amount` 可配但**建单硬编码 0** | `sync-worker-orders-service.ts:196`;规则侧 `work-product-rule-repo.ts:375,421` | 明确设计意图:要么带上,要么删字段避免误配 |
+| 6 | `deposit_threshold_amount` 只被读写与展示,**无任何冻结/结算逻辑读取** | 全仓 grep 仅 `work-order-management-repo.ts` / `admin-work-order-management-service.ts:68` / `work-order-display-mappers.ts:385` | 确认语义后删除或接线 |
+
+### P0 · 并发与数据一致性
+
+| # | 问题 | 证据 | 建议 |
+| --- | --- | --- | --- |
+| 7 | **加锁顺序相反可死锁**:`grabWorkOrder` 先锁钱包(`:24` FOR UPDATE)再更新工单(`:58`);`assignWorkOrderToWorker` 先锁工单(`:154` FOR UPDATE)再锁钱包(`:169`)。同一单并发指派+抢单构成环 | `repositories/worker-platform/work-order-grab-repo.ts:24→:58` 与 `:154→:169`;钱包锁见 `repositories/worker-platform/shared.ts:28` | 统一锁序,或抢单先 `SELECT ... FOR UPDATE` 工单 |
+| 8 | 寄卖指派链路**跨事务部分成功**,补偿三步全部 `catch {}` 吞错 | `consignment-fulfillment-service.ts:663/680/742/748`,补偿 `:802-849` | 合并为单事务;补偿失败必须告警而非静默 |
+| 9 | 改派会让原卖家「我的寄卖」永远停在「正在入库」,被改派者看到未提报的货 | `consignment-worker-service.ts:352-368`;`consignment-item-stage.ts:158` | 与 #2 一并收敛 |
+| 10 | 待加好友阶段可无限期挂起,货与押金同时冻结,无超时策略 | `寄卖库存优化现状与进度追踪.md:93,98` 仍标「待业务拍板」 | 加超时预警 + 自动回退/催促 |
+
+### P1 · 安全
+
+| # | 问题 | 证据 | 建议 |
+| --- | --- | --- | --- |
+| 11 | **claim token 默认永久有效**(`tokenTtlHours: 0`),链接即唯一凭据 | `config/defaults.ts:64`;`services/claim/claim-service.ts:40`;迁移 `015` | 生产强制 TTL > 0,并考虑首次访问后才可见 |
+| 12 | **`/collect/*` 完全免鉴权**,凭订单号可读工单公开投影;限流 key 含攻击者可控的 `orderNo` → 每个新单号即新桶 | `app.ts:106`;`routes/collect.ts:13-27`;`middleware/rate-limit.ts:88` | 一次性 lookup token + 纯 IP 维度限流 |
+| 13 | **`/files/object` 无鉴权**,仅靠 key 不可枚举 | `app.ts:108`;`routes/files.ts:12`;`file-storage-service.ts:233` | 私有 scene 恢复短期签名(需同步改写入侧) |
+| 14 | 无安全响应头(无 helmet:CSP/HSTS/X-Frame-Options 缺失) | `app.ts:35-45`;依赖列表无 helmet | 应用内或 Caddy 统一注入 |
+| 15 | 后台 token 与权限列表存 localStorage | `apps/frontend/src/utils/admin-auth.ts:1,44` | XSS 即接管后台,建议 httpOnly Cookie + CSRF |
+| 16 | PDD 扩展路由回显任意 Origin | `routes/pdd-extension.ts:26-30` | 白名单 `chrome-extension://` |
+| 17 | 备份脚本会把 `data/`(含 PDD RSA 私钥)打包上传 OSS | `deploy/backup-db.sh:3-6`;`services/platforms/pdd/pdd-crypto-service.ts:112` | 私钥移出备份范围或备份加密 |
+
+**做得好**:91 / 快手 / affiliate-dash 三个回调**全部强制验签 + 时间戳容差**(`open-91/order-create-service.ts:32`);dev-mock **双重门禁**(admin 会话 + `isDevMockEnabled`,非生产或显式开关,`services/dev-mock/dev-mock-service.ts:54`);admin 口令 scrypt + 盐;会话 HMAC + DB 会话表 + `session_version` 三查;请求体 2MB 上限;日志脱敏并有单测。
+
+### P2 · 性能与可维护性
+
+| # | 问题 | 证据 |
+| --- | --- | --- |
+| 18 | 建单对每个 order_item 串行 3 次查询(含循环内重复查同平台单号) | `sync-worker-orders-service.ts:73,87,150` |
+| 19 | 匹配候选**全表入内存**、无分页,每次 `checkWorkOrderInventoryMatches` 都触发 | `consignment-repo.ts:854-887`;`consignment-fulfillment-service.ts:190,318` |
+| 20 | 寄卖统计每次 5 条全表聚合 | `consignment-repo.ts:218-303` |
+| 21 | 仓库搜索未覆盖前端占位符承诺的 `buyer_name/shop_name/seller_phone/worker_name` | `consignment-repo.ts:178` vs `ConsignmentInventoryPanel.tsx:789` |
+| 22 | 「我的寄卖」固定 `pageSize: 200`,超出静默不可见 | `consignment-worker-service.ts:353` |
+| 23 | 6 个 God file:前端 `WorkOrdersPanel.tsx` 3,657、后端 `consignment-repo.ts` 1,253、`consignment-fulfillment-service.ts` 1,174、`work-order-query-repo.ts` 1,057 | 实测 |
+
+### 工程与运维
+
+| # | 问题 | 证据 |
+| --- | --- | --- |
+| 24 | **无 CI**(无 `.github/workflows`),质量门禁只靠本机 pre-push,可 `SKIP_CHECKS=1` 绕过 | `.githooks/pre-push`、`scripts/install-git-hooks.sh` |
+| 25 | **迁移启动时自动执行且无 down**;多副本会并发迁移 | `startup/bootstrap.ts:32`(失败每 5s 重试) |
+| 26 | 无指标 / APM / 告警;调度器运行态只在进程内存 | `services/scheduler/scheduler-service.ts:11` |
+| 27 | 单实例耦合:内存限流 + 内存调度器 | `middleware/rate-limit.ts:26` |
+| 28 | Caddyfile 硬编码其他项目 vhost | `deploy/caddy/Caddyfile:22-30` |
+| 29 | bus factor ≈ 1(949 commits,2 作者,其中 704 来自一人) | `git shortlog -sn` |
+
+### 测试缺口(真实)
+
+1. 无抢单/拼单的**并发与押金事务**测试(死锁、重复抢单、余额不足回滚无回归保护)。
+2. 无超时扫描 `settleOverdueWorkOrders` 的测试(三策略 × 赠送单 forfeit 组合)。
+3. 无 `syncWorkerOrdersForSourceOrder` 端到端分支测试(`already_exists` / `platform_order_exists` / `auto_create_disabled` / 状态落点)。
+4. 无「发单金额 vs 用户付款」后端门禁测试(因为后端逻辑不存在)。
+5. 无结算**金额守恒不变量**测试(报酬 + 押金 + 欠款抵扣 == 钱包变化)。
+6. 无 `approveAdminConsignmentSubmission` 端到端测试(改派 + 押金归属 + 货款归属链无回归保护)。
+7. `repositories/` 16,484 行仅约 10 个测试文件——**DB 层几乎无回归网**,而这正是 SQL 最复杂处。
+8. 前端 `pages/` 45,977 行仅 872 行纯函数测试,**无组件渲染测试**。
+
+---
+
+## 六、改进路线建议(按性价比排序)
+
+1. **建 CI**(跑两侧 `npm run check` + PG service 集成测试,把 pre-push 降级为可选)。当前最大杠杆:102 个测试与 `check:sql` 护栏现在只在本机生效。
+2. **资金门禁后端化**:发单金额 vs 付款额(P0-1)、审核改派与手机号自动绑定(P0-2/3)、押金模板漏带(P0-5)三件事都在既有文件内可闭环,且都能补上测试。
+3. **统一加锁顺序**(P0-7)并把寄卖指派链路事务化(P0-8);这两处是唯一可能造成"钱和货同时错"的并发窗口。
+4. **收紧对外凭据**:claim token 生产强制 TTL、`/collect/*` 一次性 token + IP 限流、私有文件短期签名(P1-11/12/13)。
+5. **拆分 6 个巨型文件**,先动 `WorkOrdersPanel.tsx` 与 `consignment-repo.ts`,同步补测试,把前端测试比从 1.9% 拉到 15% 量级。
+6. **迁移与发布解耦**:部署脚本先 `npm run db:migrate` 再滚动重启,为多副本与蓝绿留出空间;高风险迁移补 `down` 段。
+7. **补最小可观测栈**:`/metrics`(进程 + 连接池 + 调度运行态)+ 调度失败推送 + helmet。
+
+---
+
+## 七、一句话总结
+
+后端是一个**分层清晰、DB 纪律与配置治理明显优于同类项目、交付与备份脚本相当完备**的成熟业务系统;发单主链路(进单→匹配→建单→发布→抢单→验收→结算)状态机与押金规则闭合度高,寄卖子系统的规格匹配门禁与押金/货款双账设计也相当讲究。
+当前最该优先处理的三件事是:**① 资金门禁从「前端提醒」下沉到后端强制;② 抢单/指派的加锁顺序与寄卖指派事务化;③ 对外三处凭据薄弱/无鉴权接口**。中长期短板集中在**前端与 DB 仓库层的测试真空、6 个巨型文件、单实例耦合、以及无 CI/无指标的运维盲区**。