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

27 KiB
Raw Blame History

order_site 项目分析报告

重点:后台发单系统(进单 → 工单 → 抢单大厅 → 履约 → 验收 → 结算)与 寄售仓库 / 寄卖 子系统。 分析基线:ef491660(含 9298b31b 寄卖白名单总开关),工作区干净,949 commits。 说明:本报告中的资金/安全类关键结论均已二次复核源码;标注「待确认」的是需要业务口径拍板、而非代码缺陷的项。 配套明细:寄卖子系统的逐行分析见 寄卖子系统技术分析报告.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%),寄卖与工单两部分几乎平分。
flowchart LR
  P["91卡券 / 快手电子凭证 / PDD / affiliate-dash"]
  P --> IN["services/order<br/>进单·商品匹配·建履约任务"]
  IN --> FUL["services/fulfillment<br/>planner → executor"]
  FUL --> CLAIM["claim token → 领取页 claimUrl"]
  IN --> DISPATCH["发单消费者<br/>syncWorkerOrdersForSourceOrder"]
  DISPATCH --> WO["work_orders<br/>pending_material / unassigned"]
  WO --> HALL["管理员发布 → open → 抢单大厅"]
  HALL --> W["打手抢单/拼单<br/>冻结押金 in_progress"]
  W --> ACC["待验收 → 后台或自动验收 → 结算"]
  WO -.->|寄卖现货| CONS["寄售仓库<br/>consignment_inventories"]
  CONS --> ACC
  SCH["scheduler<br/>超时扫描·押金解冻·PDD同步·数据保留"] --> WO

三、后台发单系统

3.1 端到端链路

flowchart TD
  A["91 /orders/create<br/>routes/open-91.ts:27"] --> B["upsertOrderFromSource<br/>order-service.ts:98"]
  B --> C["规则匹配<br/>work-product-match-mappers.ts:93"]
  C -->|未命中/歧义| X1["写匹配日志并跳过<br/>sync-worker-orders-service.ts:119"]
  C -->|命中| D["createWorkOrder<br/>sync-worker-orders-service.ts:185"]
  D --> E{"必填资料齐?"}
  E -->|齐| F["unassigned"]
  E -->|缺| G["pending_material + 待办通知:266"]
  F --> H["管理员补资料/改价 → unassigned<br/>admin-work-order-material-service.ts:44"]
  G --> H
  H --> I["publishAdminWorkOrder<br/>校验 reward>0 :219"]
  I --> J["open + 核销电子凭证<br/>publish-voucher-service.ts:32"]
  J --> K["抢单大厅 worker-hall-service.ts:29"]
  K --> L{"拼单?"}
  L -->|是| M["joinWorkOrderShare 按份冻押金"]
  L -->|否| N["grabWorkOrder open→in_progress<br/>work-order-grab-repo.ts:11"]
  M --> O["提交验收 pending_acceptance"]
  N --> O
  O --> P["acceptWorkOrderAndSettle<br/>work-order-settlement-repo.ts:23"]
  P --> Q["释放/延迟解冻押金 + 结算报酬 + 抵扣售后欠款 + 升级"]
  N -.->|超时| R["超时扫描<br/>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,其中 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://<id>
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/无指标的运维盲区。