Commit Graph
279 Commits
Author SHA1 Message Date
yml2213 6971002579 feat(admin): 履约完成或终态时自动隐藏暂停与关闭兑换按钮
- 任务详情页(AdminTaskDetailPage):若任务已履约完成(completed/delivered/success)或订单已完成/已退款,自动隐藏【暂停兑换】和【关闭兑换】按钮,避免误操作
- 订单详情页(AdminOrderDetailPage):若订单下任务已全部履约完成或订单终态,同样自动隐藏【暂停兑换】和【关闭兑换】
- 针对已被暂停/关闭的历史订单,始终保留【恢复启用】入口以便误操作时补救
- 验证方式:前端类型检查(0 error)、前端单测 132 项全通及前端 Vite 重新构建成功
2026-10-10 18:42:12 +08:00
yml2213 9b29f7513a fix(frontend): 修复 AdminTaskDetailPage 中 useMutation 的 Hook 调用顺序
- 将 useMutation 提升至组件顶层,避免因位于 query.isLoading 等早期 return 分支之后导致 React Hooks 顺序变动报错
- 验证方式:前端类型检查(0 error)、单测 132 项全通及前端 Vite 重新构建成功
2026-10-10 18:40:07 +08:00
yml2213 122144a2b7 feat(task): 任务详情与列表页增加H5订单号领取链接及兑换有效性控制
- 后端 AdminTaskDetail 视图补充关联订单的 claimStatus、claimStatusReason 等状态字段
- 任务详情页升级“领取链接”卡片:支持固定 H5 链接展示、状态标签(有效/暂停/关闭),并支持一键复制及【暂停/关闭/恢复启用】弹窗操作
- 任务详情页保留历史 Token 链接作为辅助对比展示
- 任务列表页在平台订单号旁增加一键复制 H5 领取链接的快捷按钮
- 验证方式:前后端类型检查、全量单元测试(后端 674、前端 132)及前后端全量构建均通过
2026-10-10 18:38:49 +08:00
yml2213 412d219cca feat(claim): 支持固定H5订单号领取与订单兑换有效性后台控制
- 数据库新增 099_order_claim_status.sql 迁移,扩展 claim_status、claim_status_reason 等字段
- 后端新增 /claims/h5/:orderNo、UID提交、分销提交接口,保留原有 token 接口双轨兼容
- 增加兑换有效性强拦截:暂停/关闭/退款/取消订单精准阻断并返回友好提示
- 后台新增 POST /admin/orders/:orderId/claim-status 接口与审计日志记录
- 前端管理后台(订单列表、详情页)支持 H5 兑换状态展示、一键复制链接及暂停/关闭/启用弹窗
- 前端领取页面适配 /h5/:orderNo 路由及暂停/关闭等状态的异常展示
- 验证方式:运行前后端类型检查、全量单元测试(后端 674、前端 132)及全量构建编译均通过
2026-10-10 18:05:46 +08:00
yml2213 2d727a6797 fix(worker-platform): 修复对已冻结打手调级时误报冻结原因必填的问题
- 做了什么:
  1. 后端 reviewAdminWorkerUser 中优化 freezeReason 校验条件:区分是否为新变为 rejected 状态,若账号原本已处于冻结态且本次仅调整等级未变动状态,直接沿用已有冻结原因,不再强制要求传 freezeReason;
  2. 前端 submitLevel 在提交调级时补充传递打手既有的 freezeReason;
  3. 后端新增对应测试用例确保后续回归。
- 为什么:
  当打手处于冻结状态(rejected)时,管理员对其执行调级操作因未携带 freezeReason 触发了冻结原因校验错误,导致调级失败。
- 验证方式:
  运行 npm run check(671 项测试全绿,类型与构建检查通过)。
2026-10-10 13:51:58 +08:00
yml2213 85fc2eaa7c feat(worker-platform): 支持后台人工调级锁定等级与解除自动升级限制
- 做了什么:
  1. 新增数据库迁移 098_worker_level_lock.sql,在 worker_users 表增加 is_level_locked、level_locked_at、level_locked_reason、level_locked_by 字段;
  2. 后端自动升级函数 maybeUpgradeWorkerLevelWithClient 和判定函数 shouldApplyAutomaticWorkerLevel 增加 is_level_locked 守卫,被锁定的打手跳过自动升级;
  3. 后台打手审核调级服务 reviewAdminWorkerUser 支持等级锁定参数与变更记录,人工调级时自动写入 worker_level_logs 流水,并提供 updateAdminWorkerLevelLock 专用接口;
  4. 前端打手列表等级列展示「已锁定」状态及 Tooltip 原因,调级弹窗支持「锁定自动升级」开关与原因输入,操作列提供「解除限制」快捷操作;
  5. 打手详情抽屉补充展示等级锁定说明。
- 为什么:
  解决打手被人工调整等级(如 VIP5 调至 VIP4)后,因历史累计完成单数满足门槛导致后续结算再次自动升级回高等级的问题,并支持后续随时解除限制恢复自动升级。
- 验证方式:
  1. 后端新增 shouldApplyAutomaticWorkerLevel 与 mapWorkerUser 单元测试,运行 npm run check(670 测试全过,类型与 SQL guard 验证通过);
  2. 前端新增 worker-level-lock.test.ts 单元测试,运行 npm run check(130 测试全过,typecheck 与 build 验证通过)。
2026-10-10 13:44:57 +08:00
yml2213 1339c6887e refactor(claim): 删除自建皮肤领取页,路径式 /claim/<token> 复用现成领取页 + 物品图片
**删掉上一轮自建的领取页**
- `pages/claim-skin/*`、`services/claim-skin.ts`、`types/claim-skin.ts`、`tests/claim-skin.test.ts`
  及其样式(client.css 里 `.claim-skin-*` 整段 254 行)
- 后台我加的展示项:「皮肤自动发货」筛选项、执行器标签、「买家提交信息」卡片

**路径式链接复用现成领取页**
- `utils/claim-path.ts`(中性命名 `isClaimPath` / `resolveClaimToken`)+ 单测
- `App.tsx`:pathname 命中 `/claim/<token>` → 独立 BrowserRouter 渲染**现成的 `ClaimPage`**
  (页面零复制零改动:它只用 `useParams`,不读 hash);`#/claim/:token` 仍走主站 HashRouter,
  两者互不影响,且有用例断言 hash 入口不命中路径判定

**物品图片**
- 后端领取详情的 `product.imageUrl` 接上(内部 SKU 在 `spec_json.code` / `sku_code` /
  `sku_name` 哪个字段都能回退找到图,映射不到返回空串)
- 领取页新增 `ClaimProductImage`:居中大图(宽度 min(220px,62%)、圆角 16px),
  空值或加载失败都降级虚线占位块,不出现破图

`npm run check`:前端 128 / 128 全绿(去掉旧用例 17 个、新增路径与图片用例)
2026-10-09 16:13:46 +08:00
yml2213 b70741daec feat(admin): 后台可配领取链接/发消息模板 + 可见买家账号 + 修正皮肤领取链接
**面板新增三项(抖音码速达接入配置)**
- 领取链接基址 (claimLinkBase):带实时预览「皮肤订单最终链接」,并拦截两类误配 ——
  带 `#`(`#/claim` 是打手领取页,会把买家带进绑定 UID 流程)、缺 http(s) 协议
- 发消息模板 (messageTemplate):提示可用占位符;模板里没有 `{link}` 直接报错提示
- 付款后发领取消息开关 (claimMessageEnabled)
- 顺带把默认出网超时的展示值对齐后端(10s → 20s)

**后台可见买家提交的账号(运营发货依据)**
- `getAdminTaskDetail` 新增 `claimSubmission`,按 `canViewSensitiveTaskData` 门禁下发
  (与 claimToken 明文同一门禁);任务详情新增「买家提交信息」卡片(可一键复制账号)

**修正:皮肤任务的领取链接**
- 详情接口原来用全局 `buildClaimUrl`(默认 hash 形式 `…/#/claim/…`),那是**打手**领取页;
  皮肤任务改用 `claimLinkBase + '/' + token`,运营复制给买家的链接才是买家页
- 顺带把「履约渠道」筛选项加上「皮肤自动发货」、任务详情执行器标签加上它、
  「来源渠道」加上「抖音码速达」(此前筛不出来抖音订单)
2026-10-09 15:06:49 +08:00
yml2213 7834ee7bef feat(douyin-mashida): 商品展示统一改为内部 SKU(引流标题降级)
平台 sku_order_list[].code 是商家维护的**内部 SKU**(如「幸运币2个」),
product_name 只是店铺用来**引流**的长标题(如「【测试专用】和平精英皮肤小店入口…」)。
商家按内部 SKU 认货(后续履约匹配也用它),原来的展示把引流标题当商品名,
列表/详情/兑换页三处都会让人认错货。

改为以内部 SKU 为主、引流标题降级为副信息:
- 后台订单列表「商品 / 任务」:itemTitle = 内部 SKU,skuNick = 引流标题
- 后台订单详情「商品明细」:商品 = 内部 SKU;SKU/规格 不再重复展示同一串
- 兑换中心 /redeem:商品 = 内部 SKU(粗体)+ 引流标题(灰色小字,新增 productTitle 字段)

实现只在 `source === 'douyin_mashida'` 时生效(resolveOrderItemTitle /
resolveOrderItemSkuNick 加 douyin 分支),其它平台展示逻辑不变;
用真实订单形态补了单测。
2026-10-09 13:31:45 +08:00
yml2213 080480f03b feat(admin): 后台新增「抖音码速达」配置面板
- 新增 Tab:/admin/platform-shops?tab=douyinMashida
- 面板字段:enabled / baseUrl / uid / secret / certLink / afterVerifyLink /
  afterVerifyMessage / messageType / timeoutMs / handledTopics(tags)
- 密钥脱敏:出参 secret 永远为空,输入框未编辑时对外 value 恒为 '',
  掩码不会污染后端「空串=保留原值」语义
- 顶部展示 ready 状态,未就绪时用 Alert 列出缺哪几项;
  始终展示平台侧要配的推送地址(可复制)
- baseUrl 命中本地地址时提示「当前指向本地网关桩,不会请求真实码速达」
- 把 AdminPlatformShopsPage 内联的 SecretFieldControl 抽成共享组件
  (函数体与原实现逐字节一致,8 处既有调用点参数不变)
2026-10-09 12:01:29 +08:00
yml2213 893388b37f feat(consignment): 寄售仓库在库列表增加通过审核入库时间并在手动匹配中补全时间展示
- 寄售仓库 (ConsignmentInventoryPanel):表格中增加「通过审核入库时间」列并格式化展示,详情弹窗规范化展示入库时间、出库时间与提报时间
- 手动匹配弹窗 (ConsignmentManualMatchModal):候选订单列表展示工单创建时间,顶部横幅与已选概要展示现货入库时间及订单创建时间;从订单匹配现货时展示现货入库时间
- 内部匹配接口与工单拦截:后端 listInventoryMatchableOrders 返回工单创建时间及平台单号;发布拦截弹窗中增加展示现货入库时间
- 验证方式:运行前后端单元测试全部通过,前端 build 校验通过
2026-10-08 15:41:23 +08:00
yml2213 5466783017 feat(worker-platform): 接单模板列表支持一键导出到本地 CSV
后台接单平台「接单模板列表」右上角新增「导出」按钮:

- 按当前筛选(搜索关键词 + 一级/二级分类)导出全部模板,服务端分页的
  情况按每页 100 条循环拉全量(页数上限兜底),不会只导出当前页;
- 导出 UTF-8 带 BOM 的 CSV,Excel/WPS 双击直接打开,文件名带时间戳
  (接单模板_YYYYMMDD_HHmm.csv);逗号/引号/换行按 CSV 规范转义;
- 22 列:模板名称/标识/分类/系统/平台/商品SKU/接单金额/按件价/拼单配置/
  验收模式/自动建单/状态/超时策略/资料字段/已绑定商品与绑定明细/时间等;
- 空结果、超出上限、浏览器不支持下载分别给出明确提示。

导出逻辑集中在 apps/frontend/src/utils/product-rules-export.ts(纯函数,
无 @/ 别名与运行时相对导入,便于 node --experimental-strip-types 单测),
新增 tests/product-rules-export.test.ts 覆盖分类回退、金额与拼单转换、
绑定明细、CSV 转义、文件名、空列表与非浏览器环境。
2026-10-08 13:50:19 +08:00
yml2213andCursor dbdafd3dc4 fix(consignment): 撤销寄售履约按选择回库,不再因加好友强制作废扣押金
买家退款时应能把现货退回仓库继续匹配;卖家押金只在客服显式选择扣除时才没收。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-05 14:28:38 +08:00
yml2213 0173efb277 feat(pdd): 店铺连续同步失败 3 次后熔断,停止自动重试
背景:生产上「木塔网游专营店」Cookie 已过期,每约 24 秒重试一次、每轮刷一条 ERROR 日志,
持续数天。Cookie 失效不会自愈,继续重试只刷日志、白白占用同步周期。

行为:
  - 失败 1~2 次:累计计数,继续重试;
  - 第 3 次:熔断,打一条显式 ERROR(含"更新 Cookie 会自动恢复"提示);
  - 熔断后:自动同步直接跳过,不再刷错误日志;
  - 恢复:更新 Cookie(后台编辑或浏览器插件)自动解除;手动同步成功一次亦可。
  熔断状态持久化在店铺配置里,容器重启后依然生效。

改动:
  - pdd-source-config-service:阈值常量 + consecutiveFailures/syncSuspendedAt/
    syncSuspendedReason 持久化字段 + 纯函数 resolvePddStoreFailureState /
    applyPddStoreBreakerState / isPddStoreSyncSuspended / shouldResetPddSyncBreaker。
  - pdd-sync-service:syncAllPddStores 跳过熔断店铺并上报 suspendedCount;
    syncStoreOrders 失败记录、成功清零(健康店铺不产生额外配置写)。
  - pdd-order-sync-job:上报熔断数,且存在熔断店铺时强制留在 INFO 级
    (否则"全部店铺已停同步"会因新单为 0 被降级成 debug 而不可见)。
  - 前端加「已熔断 · 停止重试」标签与类型字段。

顺带修掉一个隐患:saveAdminPddStore 收的是 Partial 载荷,normalizePddStoreItem
会为缺失字段产出 undefined,导致一次「只改店铺名」的保存就把熔断洗掉。
由 applyPddStoreBreakerState 回填既有状态解决。

验证:9 项熔断单测(含局部保存不洗状态、Cookie 变更解除熔断)。
2026-10-03 14:27:38 +08:00
yml2213 90de6b9419 perf(worker): 单号检索收敛为可走索引的等值/前缀,并支持关联单号反查
问题:后台「按单号检索」在平台单号/工单号分支里 OR 了一支
`work_order_no ILIKE '%'||$n||'%'`。前置通配符非 sargable,任何 B-Tree 都用不上,
整条查询退化成 work_orders 的 Seq Scan。

生产实测(10.3 万行 / 572MB,pg_stat_statements 46 小时窗口):
  - 列表查询 + 统计查询用同一份 filters 并行执行,一次检索打两遍全表;
    两者合计占全库 66% 的磁盘读(1.1TB/46h),单次读 26,442 个块(约 211MB)、398ms、返回 1 行。
  - 收敛后同库同参数:等值 0.19ms,前缀 30ms(约 2000 倍)。
  - 真实统计查询端到端 760ms → 61.6ms。

改动:
1. 平台单号/工单号分支收敛为「等值 + 前缀」,与 order-repo / task-repo 既有写法一致;
   工单号含随机小写段(10.2 万行里 7.4 万行非全大写),保留 LOWER() 比较。
2. 迁移 090 补 text_pattern_ops 前缀索引:当前库 collation 为 en_US.utf8,
   前缀 LIKE 在默认 B-Tree 上会退化成全索引扫(实测 438ms)。
3. 新增关联单号反查分支:SUB/INV/AS/DT 不在 work_orders 上,
   经关联表反查工单 id,四条链路均命中唯一索引。
4. 前端 isExplicitOrderNoSearch 同步补齐(否则搜到的单会被状态页签挡掉、
   实时补丁还会把行删掉)。

验证:生产 EXPLAIN 逐条比对 + 14 项仓储单测 + 6 项分类器单测。
2026-10-03 14:27:32 +08:00
yml2213 56e718a65d fix(admin): 显式单号检索下,其它筛选不得把正在看的工单从实时补丁里删掉
## 修的是什么

实时补丁的判定链里,「状态 / 接单人员 / 日期范围」这些可证伪条件排在关键词护栏之前,
任一不成立就先返回 mismatch(允许删行),于是第 156 行那条「有关键词就不删行」的护栏
在带筛选时根本执行不到。

d6523635 只在 UI 侧补了一半:搜显式单号时清空 状态/赠送阶段/验收模式,
但**没清接单人员、分类、日期范围**,而 workerId 与 createdRange 同样会先返回 mismatch。
所以「单号 + 接单人员」或「单号 + 日期范围」仍会把行删掉。

实测(真实函数):
  单号 + 状态筛选      -> mismatch(删行)
  单号 + 接单人员筛选  -> mismatch(删行)
  单号 + 日期范围      -> mismatch(删行)

## 修法

只对「显式单号检索」提前短路为 unknown(只原地更新,不插不移):
  if (isExplicitOrderNoSearch(filters.keyword)) return 'unknown'

刻意**不**推翻既有设计:模糊关键词(产品名/昵称等)仍保留「状态可证伪即移除」的语义,
因此原来锁定该语义的用例继续通过。

顺带把 isExplicitOrderNoSearch 合并到 utils:此前面板与 AdminLayout 各有一份规则,
重复两份正是上次 tab 名单漏改那类问题的温床。

## 边界(重要)

**本次修复不覆盖「无任何其它筛选、只按单号搜索」的场景。**
实测该场景下判定本就是 unknown、行不会被删(状态原地更新、分页与统计一致),
因此线上反馈的「状态变化后搜不到、必须整页刷新」若发生在无筛选条件下,另有他因,尚未定位。
2026-10-02 17:51:39 +08:00
yml2213 e0d7398d37 fix(admin): 风控 tab 点了没反应 —— tab key 白名单漏登记,并合并为唯一来源
## 现象与根因

风控 tab 渲染得出来、但怎么点都切不过去,且不报错、不白屏。

原因是 tab key 名单在同一文件里写了两遍:
  - isWorkerPlatformTab()(changeTab 的守卫)
  - loadActiveTabPreference()(localStorage 旧选择校验)
新增风控 tab 时只把 'risk-control' 加了 TAB_QUERY_KEYS 与 Tabs items,漏了上面两处,
于是 changeTab('risk-control') 在第一行 `if (!isWorkerPlatformTab(nextTab)) return` 就返回了。

这类漏改没有任何报错信号,只能靠点一下才发现——上一轮的验证只查了构建产物里
是否含面板代码,没有验证 tab 真的能切换,所以没拦住。

## 修法

把名单抽到 utils/admin-worker-platform-tabs.ts,只保留唯一一份
WORKER_PLATFORM_TAB_KEYS;查询缓存映射与「只登记缓存但不是 tab」的例外(hall-config,
它是「杂项配置」tab 内部的区块)也一并显式化,避免下次再靠人记。

## 回归测试

新增 tests/admin-worker-platform-tabs.test.ts(5 个用例):
  - 风控 tab 必须被 isWorkerPlatformTab 接受
  - 每个登记了切页缓存的 tab 都必须在合法 tab 名单里 ← 正是本次漏改的形态
  - 名单无重复项 / 非法 key 被拒 / 名单内自洽
2026-10-02 17:13:59 +08:00
yml2213 78c676d2b9 feat(worker-risk): 风控后台页面(可调阈值/实时状态/事件留痕/人工封禁解封),并修复解封后仍被 403 的 bug
## 修复的 bug(生产已发生)

风控三级封号把 tracker.banned 置为 true 后,banned 检查发生在鉴权之前,
而它只存在于进程内存。后台把账号改回 active 时数据库变了、内存没变,
于是账号在库里已经正常、请求却永远 403,只能等容器重启才恢复。

修法两道:
- **数据库是唯一事实来源**:banned 改为带 30 秒 TTL 的缓存,到点回查
  worker_users.status;已解封(或账号已删)则清除标记并放行。
  对被判 banned 的 key 最多每 30 秒查一次,即便对方仍以 65 QPS 刷也只是
  每 30 秒一次查询。查询失败时**保守维持封禁**,不因数据库抖动放行刷子。
- **后台改状态时主动释放**:review 只允许设 active/rejected/disabled/pending_review
  (没有 frozen),所以走到那里就意味着不再由风控冻结,直接释放内存状态。

顺带修掉一个连带缺陷:解封时只清了 violationCount,没清滑动窗口计数,
账号刚恢复就会被上一轮残留的 windowCount 立刻限流、并在 60 秒内二次封禁。
现在整轮窗口状态一并清零。

## 新增后台页面

接单平台 → 风控(/admin/worker-platform?tab=risk-control),三块:
- 阈值配置:6 个阈值可视化编辑,落 app_config_entries,保存即热生效,无需重启
- 当前实时风控:进程内存状态(已封禁/惩罚中/仅记录),支持人工解封、封禁、仅释放内存状态
- 风控事件留痕:worker_risk_events 分页查询,可按类型/IP 筛选

## 关键取舍

只记**显著事件**(二级惩罚、三级封号、人工处置),不记每一次被拒请求。
被刷时实测 45~65 QPS,按请求写库正是最初要解决的问题。实时计数仍走进程内存。
事件写入为 fire-and-forget 并吞异常,留痕失败绝不影响大厅请求。

## 验证

- 新增 6 个用例覆盖解封立即生效、对账自愈、TTL 不重复查库、对账失败保守维持、阈值可配置
- 迁移 089 已在临时库实测:表结构/4 索引/CHECK 约束/外键均正确,非法 event_type 被拒
- 后端 557 测试 554 通过 0 失败;前后端 typecheck/lint/format/build 全绿
2026-10-02 17:05:25 +08:00
yml2213andCursor 1239393fc2 perf: 筛选统计去掉 JSON 兜底,并补上 orders 覆盖索引
去掉 work_orders 堆访问后,JOIN 会把 orders 整表扫进 Hash Right Join。
领取页在快照新鲜时不再打上游;快手出网统一 10 秒超时。倒计时改为自适应间隔。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 15:05:38 +08:00
yml2213 40ee36d72a fix(consignment): 修复寄卖取消解绑、SUB 单号写空、拼单门禁与后台工单列表实时乱跳
1. 问题单处置「取消」后打手信息不消失:resolveProblemWorkOrder 的取消分支从未解绑寄卖现货,现货永久停在 consumed 占库不还,工单 material 上的寄售标记残留又让列表继续显示「供货打手」。取消时补上解绑;调用必须排在状态 UPDATE 之前——那条 UPDATE 会清空 friend_added_at,清完再解绑会把已确认加好友的货误判成可回池(二次出货);
2. 解绑函数在没有现货可解绑时直接 return,跳过了摘标记,于是「库存行早已不存在」的老帖永远摘不掉寄售标记(列表一直显示供货打手)。现在无论是否解绑到现货都摘标记,痕迹保留在 material.consignmentHistory 的 detachReason 里;
3. 内部现货履约写入的 sourceSubmissionNo 恒为空:取的是占库 `UPDATE ... RETURNING *` 的行,只带回本表列,没有 join 出来的 submission_no,导致工单列表把所有寄卖单都显示成「老单无提报单号」,SUB 单号这个唯一锚点丢失。改取带 join 的预读库存(内部现货资料、指派工单资料、指派事件留痕三处)。存量老单按业务口径不回填;
4. 后台接单工单列表实时乱跳:SSE 快照补丁按数组下标读 queryKey,面板 key 加了字段后下标全错(page 被读成 targetWorkOrderId → 任何页都被当成第 1 页),且完全没判「内部寄售匹配」等筛选,于是任意一条不在当前页的老单只要发生变更就被插到列表顶部。改为结构化 queryKey + 三态判定(match 才允许插顶、mismatch 才允许移除、其余只原地更新),插入还要求 page===1 且工单 id 大于当前首行;状态比对改用 rawStatus(快照的 status 是运营态,拼单母单会被显示成 in_progress),分类筛选因快照不带 parentCategoryId 归入不可证伪;
5. 新增「寄卖只做整单」硬门禁:已有人参与的拼单工单不参与现货匹配与库存提醒,履约入口一律返回 work_order_sharing_not_consignable,前端「手动匹配寄卖」入口按同一口径收敛;开了拼单但无人参与的整单不受影响,避免过度拦截。

验证:后端 520 项单测(517 pass / 3 skip)、前端 78 项全绿;并对真实开发库跑通问题单取消三类场景(老帖无库存行 / 回库 / 已加好友作废)、退回打手与退回大厅的反向用例(不得解绑)、履约写入真实 SUB 单号、拼单门禁四入口 + 未过度拦截反向用例。
2026-10-01 12:17:00 +08:00
yml2213andCursor ef491660e7 fix(admin-worker): 发单金额高于用户付款时强制确认,避免只靠红色字提醒
发布、指派和改价都会弹出亏损确认,且不能用「不再提示」跳过;列表发单金额旁增加「高于付款」标签。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-21 15:48:23 +08:00
yml2213andCursor 9298b31b9b feat(worker-platform): 增加寄卖白名单总开关,关闭后全员可发布
资金配置与打手列表可一键切换白名单模式;发布校验、打手端入口按总开关放行。同时收紧未开通判定、关权限确认和开通弹窗文案。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-21 14:23:00 +08:00
yml2213 937e51db6b fix(admin-worker): 修复打手管理切换寄卖权限后表格状态未自动刷新的问题
1. 修复 WorkersPanel 中 queryKey 匹配偏差,改用统一的 admin-worker-platform-workers 查询键与 refreshWorkers();
2. 引入 setQueriesData 乐观更新机制,点击 Switch 切换后毫秒级原地更新行状态,无需手动刷新页面。
2026-09-21 13:37:54 +08:00
yml2213 cb8b249e57 feat(worker-platform): 实现打手寄卖白名单权限控制与存量发布打手平滑过渡
1. 新增数据库迁移 082_worker_consignment_whitelist.sql,worker_users 表增加 can_consign 与 consignment_activated_at 字段,存量在 consignment_submissions 提报过的打手默认自动开启;
2. 打手端提报接口 publishWorkerConsignment 增加白名单权限服务端强校验拦截(403 consignment_permission_required);
3. 管理后台打手管理表格增加「寄卖权限」实时 Switch 开关列与打手详情抽屉权限展示,支持客服即时开通或关闭打手寄卖权限;
4. 打手端我要寄卖页面在未开通权限时展示专属白名单引导卡片,一键呼起联系客服开通寄卖弹窗;
5. 补充寄卖权限拦截与路由单测,前端59项单测与后端507项单测全部通过。
2026-09-21 13:30:33 +08:00
yml2213andCursor 577e0eec02 fix(consignment): 内部现货履约文案对齐待加好友,并移除已废弃的指派打手路径
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-21 12:04:30 +08:00
yml2213 29589bb9af feat(worker-platform): 接单工单表格「打手」列支持展示寄售供货打手与现货资料
1. 新增 resolveConsignmentWorkerDisplay 工具函数,自动解析工单寄售现货与资料字段(供货打手、游戏角色昵称、游戏ID及联系方式);
2. 优化 renderWorkOrderWorkers 渲染逻辑:当工单为内部寄售现货履约时,打手列带「供货打手」或「内部现货」专属角标,主标题展示打手名/角色名,副标题拼接游戏ID与联系方式,解决原先显示为破折号 - 的问题;
3. 新增 consignment-worker-display 单元测试,更新寄卖库存优化现状与进度追踪文档,前端 57 个测试全绿并通过类型检查。
2026-09-21 11:40:25 +08:00
yml2213 c80bd0e337 fix(worker-platform): 修复寄售匹配工单详情中卖家上架截图丢失问题,并收敛手动匹配为内部现货直接履约
1. 修复文件归一化器 normalizeUploadedFiles 遇到纯字符串 URL 数组被误过滤为空的问题,自动提取 objectKey 并构造规范 UploadedFile 结构;
2. 优化工单展示映射器 resolveWorkOrderEffectiveBoosterMaterial:当工单资料中截图为空时,自动回退并加载寄售卖家上架/主页截图;
3. 前端管理后台详情抽屉与打手端详情抽屉支持读取 detailOrder.gift.boosterMaterial.screenshots 与 consignmentInfo.screenshots 降级兜底渲染;
4. 手动匹配弹窗与发布拦截弹窗全面收敛为单一标准的【内部现货直接履约】,废除旧版指派打手带来的重复扣押金与二次填资料问题;
5. 新增 consignment-booster-material 单元测试,更新寄卖库存优化现状与进度追踪文档,前后端全套测试通过。
2026-09-21 11:35:38 +08:00
yml2213andCursor 040b3db84c fix(finance): 移除资金审核取消按钮,避免提现冻结资金未退回
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-20 21:59:29 +08:00
yml2213 f07e4087ad feat(consignment): 优化寄售仓库与接单工单双向手动匹配,支持工单号与提报单号检索与强制匹配
1. 寄售仓库手动匹配订单扩充支持 open 状态工单,增强 WO 工单号与纯数字的模糊/前缀搜索,修复工单搜不到的问题并在卡片高亮展示内部工单号与状态;
2. 接单工单操作列全面支持【手动匹配寄售】,允许从接单工单主动调起寄售匹配弹窗;
3. 寄售现货筛选支持通过 SUB 提报单号、INV 仓库号、供货打手等快速搜索;
4. 完善规格冲突下的强制匹配确认与必填原因提交机制,确保履约全流程留痕合规。
2026-09-19 17:59:05 +08:00
yml2213 f08a221e15 feat(worker-platform): 接单平台工单详情抽屉支持卖家与买家资料一键复制与单项复制
1. 寄售赠送进度卡片右上角新增【一键复制卖家资料】按钮,一键聚合复制卖家角色昵称、游戏ID、区服、系统、联系方式(微信/QQ/手机)、提报/仓库单号及备注;
2. 资料信息卡片右上角新增【一键复制买家资料】按钮,一键复制买家提交的全部表单字段;
3. 卖家资料与买家资料的各项字段值(角色名、数字ID、区服、系统、联系方式、单号等)右侧均补充内联一键复制小图标,支持快捷单项提取。
2026-09-19 14:39:43 +08:00
yml2213 2c120bd3e5 fix(worker-platform): 接单平台工单详情抽屉完整展示寄售手动匹配与指派订单的卖家资料
1. 修复管理后台「接单平台 -> 接单工单」抽屉中,手动匹配或指派寄售库存履约的工单因 booster_material_json 为空导致卖家资料全部显示 '-' 的问题;
2. 后端在履约及指派时将卖家角色昵称、数字ID、联系方式、手机号、备注及上架凭证截图完整写入工单 material.consignment;
3. 后端工单查询及展示映射层增加自动回退与批量库存补充解析机制,确保历史与新增的寄售工单均能准确解析出卖家角色名、ID、区服、系统与截图;
4. 前端工单抽屉卡片优化寄售订单的资料展示与阶段标签,清晰展示卖家角色名、游戏ID、联系方式(微信/QQ/手机)、提报/仓库单号及上架截图。
2026-09-19 14:33:56 +08:00
yml2213 8631100d07 style: 统一代码格式化与代码规范 2026-09-19 10:41:15 +08:00
yml2213 a5727554a4 feat(worker-platform): 彻底去除非平台内部现货履约,寄卖匹配全面收敛为接单平台指派打手
1. 手动指定现货弹窗移除 deliveryKind 冗余单选框,统一收敛为一键指派供货打手履约;
2. 匹配确认后自动冻结打手履约押金、启动交付倒计时与履约时效监控,消除脱管盲区;
3. 订单发布前置拦截弹窗移除内部代交付入口,统一为走接单平台指派;
4. 未关联供货打手的现货进行明确阻断与告警提示,避免无监管指派;
5. 同步更新前端静态单测并保证全量测试与构建通过。
2026-09-19 10:28:23 +08:00
yml2213 d6523635ea fix(worker-platform): 优化工单号精准检索与跨状态流转展示,修复状态更新后工单消失问题
1. 后端查询重构:新增 isWorkOrderNoLookupKeyword 工单号前缀与忽略大小写精准索引匹配,优化后端 work-order-query-repo 的工单号检索路径;
2. 前端单号智能穿透:管理员输入 WO... 等精准单号查询时,自动放宽细粒度排他状态/阶段过滤,避免加好友/验收等状态流转后工单因旧状态筛选脱靶消失;
3. 搜索栏交互升级:输入框清空即时重置关键词与页码,支持回车/按钮/清除全方位无缝触发;
4. 详情抽屉与多面板联动:增加 ordersQuery 数据更新对 detailOrder 的自动同步,并将 refreshAll 广播范围覆盖至寄售仓库与统计指标。
2026-09-18 19:36:17 +08:00
yml2213 68cd309bdd refactor(consignment): 清理寄卖库存详情与编辑中的上号凭据旧字段
1. 物品详情弹窗中移除登录方式、账号、密码、上号说明等旧上号字段,保留履约信息与备注;
2. 编辑物品弹窗中移除上号凭据信息卡片,聚焦现货规格、成本、供货打手与备注。
2026-09-18 18:45:59 +08:00
yml2213 ace4791005 feat(consignment): 寄卖物品已验收后隐藏退单回库按钮并拦截退库
1. 寄卖库存管理面板中,已验收通过的现货记录不再展示「退单回库」按钮;
2. 后端 returnAdminConsignmentInventory 接口增加校验,若关联工单已验收结算则拒绝退库,后续问题引导走售后流程。
2026-09-18 18:43:22 +08:00
yml2213 c5d1e2c375 feat(consignment): 寄售匹配成功后向卖家端与客服后台透出买家加好友资料 2026-09-17 20:46:45 +08:00
yml2213 eff45bb9a8 fix(consignment): 优化撤销寄售履约弹窗(无接单打手时隐藏打手履约押金,正名供货打手发布押金) 2026-09-17 20:10:00 +08:00
yml2213 f8101c1730 feat(consignment): 规格口径定案——互通通配 + 后台全量/发布端按物品展示
业务定案:
- 操作后台全量:系统 苹果/安卓/通用,区服 微信区/QQ区/互通;提报审核与
  库存编辑的系统/区服从自由文本改为受控下拉(单一来源 utils/consignment-spec.ts)
- 打手端发布按物品类型展示:苏尔南系统二选一(必须区分端),区服唯一
  「互通」选项且默认选中;后续其他品类在共享常量按品类加条目
- 匹配引擎通配符对称化:区服通配=互通(通用/全区/不限归一化为别名),
  系统通配=通用(双端/全端同理);订单侧写通配同样放行,只增不减匹配面;
  差异描述不把通配对当冲突
- 提报白名单校验(API 层):系统只收 苹果/安卓,区服只收 微信/QQ/互通,
  旧页面缓存或直调接口绕不过
- 物品名拼接:区服为互通时不拼入(对买家无信息量),前后端同口径

后端 481(+互通双向匹配/通配不算差异/真冲突仍排除/白名单校验)、
前端 54(+选项口径/表单引用/默认值)测试全绿;typecheck/lint/format/check:sql 通过
docs/寄卖系统优化方案.md 记录批次 W
2026-09-17 13:01:59 +08:00
yml2213 b666a9c951 fix(consignment): 拼单份额验收收官补寄卖结算钩子 + 手动匹配弹窗三处复核遗留
- acceptWorkOrderShareAndSettle 最后一个份额把母单转 accepted 的分支,
  此前不释放发布押金也不结算供货货款(两钩子只在统一验收里):
  现在同口径补上;供货货款传 assignedWorkerId=0(拼单母单无单一接单人,
  份额打手已各自按 share_reward 结算,不会重复付款)
- 手动匹配弹窗:race_condition/unavailable/spec_mismatch/partial_requires_confirm
  四个错误码清空已选并自动重拉列表,数据过期不再死循环撞错
- 手动匹配弹窗库存侧:keyword+pageSize100 透传后端(原来 keyword 是死参数、
  只拉 50 条),300ms 防抖后端搜索;无供货打手时禁用指派选项并在提交前
  再拦一道(原来必 400 死路);选中订单回显 workerId/workerName
- 凭证核销反馈抽共享 utils/voucher-consume,WorkOrdersPanel 与弹窗共用,
  口径不再漂移(弹窗顺带获得"已提前核销 N 张"提示)
- docs/寄卖系统优化方案.md 记录批次 V

后端 477(+4)/ 前端 50(+1)测试全绿,typecheck / lint / format / check:sql 通过
2026-09-17 12:45:51 +08:00
yml2213 63837e473b fix(consignment): 复核修复——占库撞 080 CHECK 必 500、指派非卖家双付货款、手动匹配权限门控
- 占库默认 allocation_status 从自由文本改为「已安排」,不再撞 080 的
  CHECK ('待安排','已安排');加静态守卫测试(剥注释断言枚举内默认值)
- resolveConsignmentAssignRewardFen 加 assigneeIsSupplier:指派供货卖家本人
  → 报酬=结算价(货款走报酬渠道);指派别的打手 → 报酬保持原值(货款走
  供货货款渠道),两条渠道互斥,封住平台双付;补行为级测试 + 调用点钉住
- 手动匹配两处入口(库存面板行内按钮、拦截弹窗 footer)统一按
  consignment.fulfill 门控,编辑/作废仍为 manage,与后端权限一一对应
- docs/寄卖系统优化方案.md 记录批次 U

后端 473(+3)/ 前端 49(+1)测试全绿,typecheck / lint / format / check:sql / build 通过
2026-09-17 12:36:12 +08:00
yml2213 0476339ec4 fix(consignment): 手动匹配订单按钮点了不弹窗(弹窗挂错了位置)
用户反馈:在库待匹配列表点「手动匹配订单」没有反应。

根因:上一批把手动匹配弹窗挂到了「编辑寄售物品」弹窗**内部**,外层弹窗未打开时
该组件根本没被挂载,点击只改状态、界面无变化。

- 弹窗移到面板顶层(与其它弹窗同级),点击即弹
- 新增静态守卫测试:断言手动匹配弹窗前面不能有未闭合的 Modal(即不能嵌在别的弹窗里)、
  按钮必须真的 setState、open 由该状态驱动;已验证该守卫能拦下错误写法
- 顺带补入口缺口:订单侧原先只在"检测到系统候选"的提醒弹窗里给入口,订单没有任何候选时
  反而进不去手动匹配 —— 工单行内新增常驻「手动匹配现货」(仅待指派/待补资料,
  与后端前置校验同口径)
- 已暂停匹配的现货点按钮会直接 409,改为按钮禁用并提示先打开「允许匹配」开关
- destroyOnClose → antd 6 的 destroyOnHidden(前者已废弃)

验证:前端 typecheck/lint/format/48 例测试/build 全过。
2026-09-17 12:27:58 +08:00
yml2213 a7a55d6c16 feat(consignment): 客服手动指定现货匹配的界面入口
后端批次已提供判定/差异/强制留痕与双向读取接口,这里补界面,让客服真的能用上。

- 新增共享弹窗 ConsignmentManualMatchModal:订单侧(匹配弹窗「手动指定现货匹配」)与
  库存侧(寄售仓库行内「手动匹配订单」)共用同一套交互与落地逻辑,避免两套 UI 漂移
- 交互:搜索 → 选中(精确/非精确/规格冲突三档标签 + 字段级差异说明)→ 选交付方式
  (内部现货履约 / 指派供货打手)→ 非精确需勾选确认、冲突需勾选「确认规格不一致」并必填原因
  → 落地;并在落地前提示"现货立刻被占用、点错需撤销寄售履约"
- 判定规则收敛成纯函数 consignment-manual-match.ts(需不需要确认/填原因、能否提交、
  关键词过滤、标签文案)并加 4 例单测
- 服务与类型补齐:fetchWorkOrderConsignmentMatchOptions / fetchInventoryMatchableOrders,
  fulfill/assign 入参增加 allowSpecMismatch 与 matchNote
- 凭证核销失败的提示沿用既有规则(核销失败不能报成功)

验证:前端 typecheck/lint/format/44 例测试/build 全过;两个新接口未登录访问均 401
(路由已挂载、权限位生效)。
2026-09-17 12:23:30 +08:00
yml2213 0c10f0e135 feat(consignment): 停用客服手动入库,寄卖货源只走平台提报
背景:手动入库是唯一会造出「没有提报单的现货」的入口,这类货没有寄卖单号,
统一状态机/凭证链/搜索/卖家端单号锚定都串不起来,只能在界面回退显示内部仓库号 INV,
正是"一件货两个号"的残留。业务定调:寄卖货源只走「打手平台提报 → 客服审核入库」。

- 前端寄售仓库的「手动入库」入口整块删除(按钮 + 入库弹窗 + 提交逻辑 + 服务函数)
- 后端同时挡住,防旧页面缓存或直接调接口绕过:
  POST /worker-platform/consignment/inventories 返回 409 consignment_manual_inbound_disabled;
  死代码一并清掉(服务层原实现、仓库层 createConsignmentInventory)
- 提报审核入库路径不受影响,照旧生成内部仓库号 INV 并写 source_submission_id,
  即每件货都必然挂着寄卖单号,INV 只作为内部编号存在、界面不展示

真实库验证:接口被拒(409),且「没有提报单的现货行数」前后均为 0。
2026-09-17 11:41:46 +08:00
yml2213 47e09d72ce fix(consignment): 内部寄售匹配弹窗防点错
问题:弹窗里「走接单平台指派打手(冻结押金交付)」与「内部现货履约」两个按钮上下紧挨、
样式相近,而且只有"部分匹配"才弹确认——精确匹配时两个按钮点下去都立刻执行,
点错就得走「撤销寄售履约」才能退回来。

- 两个动作一律二次确认(不再只有部分匹配才确认),确认框写明:寄卖单号(可复制)、
  动作(给谁接单/是否冻结押金)、结果(立刻占用现货、其它订单不能再匹配、
  内部履约还会跳过「待打手提交资料」直接进冷却)、以及退回要走撤销寄售履约;
  部分匹配额外红字提示"不是精确匹配、无法自动回退"并把确认按钮降为 danger
- 按钮区改成独立的「选择履约方式」分区小卡片:两个按钮各占一行 block 铺满
  (点击目标更大、不再小按钮挨着)、间距 14px、各加图标(打手 TeamOutlined /
  库存 PushpinOutlined),每个按钮下方一句后果说明
2026-09-17 11:33:23 +08:00
yml2213 54f8a27e97 fix(consignment): 匹配弹窗改用寄卖单号,客服界面不再展示内部仓库号
问题:内部寄售库存匹配弹窗显示的是「寄卖单号 INV26091711253394」——候选现货走的
是另一个查询 listActiveConsignmentInventoriesForMatching,它没 join 提报单,
sourceSubmissionNo 取不到,前端就回退显示内部仓库号;客服拿这个号去寄卖仓库又搜不到。

- 匹配候选查询补 LEFT JOIN consignment_submissions 带出 source_submission_no;
  该查询原本是 SELECT * 且列名无前缀,而 consignment_submissions 同样有
  status/game_name/category_id,加 join 会让条件变成 ambiguous column 直接 500,
  故列名统一加 ci. 前缀并显式列字段(加静态守卫测试钉住)
- 客服界面只展示寄卖单号:匹配弹窗候选卡片、工单寄售标识、工单详情、寄卖仓库列表与详情
  全部去掉内部仓库号;手工入库现货写「客服手工入库(无提报单号)」,
  历史老单写「老单无提报单号」,不再抛内部编号
- 内部仓库号保留:数据/接口/按它搜索都还在,撤销解绑等链路未动

真实库验证:仓库层候选查询与 checkWorkOrderInventoryMatches 都带出了寄卖单号。临时数据已清理。
2026-09-17 11:31:01 +08:00
yml2213 ca04e09deb feat(consignment): 客服端凭证链可视化(提报凭证 + 赠送凭证)
问题:一件寄卖有两类凭证,客服界面只看得见第一类,还是 48x48 一张缩略图,
看不出是主页还是物品;第二类(证明"这件货真送出去了"的赠送完成截图)只在工单验收界面,
客服要证明赠送是否完成得自己跨模块翻,没有任何地方把两类凭证并排呈现。

- 两类凭证分别在哪里:
  提报凭证 = 提报表 screenshots_json(卖家提报时的游戏主页截图,含物品/仓库详情)
  赠送凭证 = 绑定工单的验收资料 work_orders.acceptance_json(冷却结束后上传、客服凭此验收)
- 后端把凭证链从客服两个列表/详情接口一起下发:提报表/库存 → 结果库存 → 绑定工单,
  新增 submissionScreenshots / giftEvidenceImages / giftEvidenceNote / giftEvidenceSubmittedAt /
  giftWorkOrderNo / giftWorkOrderStatus / giftEvidenceReviewStatus(none|pending|accepted|submitted);
  赠送凭证图从 imageUrls 与 files[].url|mediumUrl|thumbnailUrl 两处取并去重
- 提报凭证口径统一:有来源提报单用提报单主页截图,客服手工入库现货回退库存自身截图,
  前端只看 submissionScreenshots 一个字段,不会拿错
- 前端新增共享组件 ConsignmentEvidenceGallery(缩略图 + 大图预览 + 共 N 张,
  预览用 Image.PreviewGroup items,被折叠的图在大图里仍能翻看);审核面板把「凭据截图」
  拆成「提报凭证(游戏主页截图)」与「赠送凭证」(带待验收/已验收 Tag + 关联工单 Tooltip),
  仓库面板新增「凭证」列并分组展示,两个详情弹窗同口径(含备注/提交时间/工单号/一句话说明)

真实库验证:赠送凭证解析在构造的工单验收资料上取到 3 张图(imageUrls 1 + files 2)并识别
pending_acceptance → 待客服验收;提报凭证在真实数据上取到 1 张。临时数据已清理。
2026-09-17 11:22:59 +08:00
yml2213 44de11a3a7 feat(consignment): 一个寄卖单号锚定全流程 + 统一状态机 + 卖家侧确认加好友/提交赠送截图
问题:一件货身上挂着三套互不相通的状态(提报单审核态、仓库库存态、工单赠送态),
my-items 同时返回两个数组、卖家端分两个列表渲染,同一件货显示两行两个号;
且卖家端只映射了前两套状态,"已出库"之后流程就断了,卖家不知道该干什么。

- 提报单号 SUB… 作为唯一「寄卖单号」从发布锚定到结算(零迁移);
  listWorkerConsignments 改成一件货一条记录,库存/工单信息挂上去,
  客服手工入库的现货(无提报单)才回退用库存号当锚点
- 新增 domain/consignment-item-stage.ts:把三套底层状态推导成卖家看得懂的一个阶段
  (待审核/已通过待匹配/已匹配-待加好友/冷却中/待赠送/已赠送待审核/已完成/驳回/作废/取消)
  + 一句"下一步做什么" + 能力位(谁能确认加好友、谁该传截图),列表筛选与 KPI 也改由它驱动
- 卖家侧新增动作(以前只有 publish + my-items,卖家只能干看):
  POST /worker/consignment/items/:submissionId/{confirm-friend,gift-evidence}
  POST /worker/consignment/inventories/:inventoryId/{confirm-friend,gift-evidence}(手工入库现货)
  复用既有工单链路 confirmWorkOrderFriendAdded / submitWorkerOrderAcceptance,
  只加归属校验(不是本人履约的订单 403),顺带白拿"冷却没结束不许提交凭证""必须带图"
- 客服端用同一个号:库存列表/详情补 sourceSubmissionNo(LEFT JOIN 提报单),
  履约/指派写进工单的寄售信息也带上;关键字搜索支持按寄卖单号搜(计数 SQL 同步补 join)

真实库端到端验证:确认加好友 → gift_phase=friend_countdown / friend_added_at 落库 /
gift_available_at 正好 +72h;冷却期内提交截图被拒(work_order_gift_cooldown_active);
越权操作被拒(consignment_item_not_found);按寄卖单号搜索命中正确。临时数据已清理。
2026-09-17 10:57:57 +08:00
yml2213 99752dce9e fix(consignment): 修掉发布时乱弹内部库存提醒,并把弹窗赏金换算成元
乱弹窗根因:matchPeacekeeperOrderWithInventory 精确匹配判定失败后无条件 fall through 到
部分匹配;而系统/区服/系列/钻级缺失时 compareSpec 返回 unknown(不是 mismatch)会被放行,
于是任何只写了游戏名的订单都命中仓库里全部在库货,表现为"随便发布点什么都会弹一下"。

严格模式(自动提醒)现在要求:① 系统/区服/系列都能确认一致;② 存在正信号(库存配置更高,
或订单压根没提钻级)。"订单没写规格"不再算命中。

同时给 matchPeacekeeperOrderWithInventory / findMatchingPeacekeeperInventories 增加
allowUnspecifiedSpecs 选项:人工履约的服务端复检(运营已人工选定现货)走宽松模式,
标题没写全规格不硬拦、真冲突仍拦,避免收紧自动提醒时把人工履约一起挡掉。

赏金显示:弹窗用 rewardAmount(单位分)直接 toFixed,把 ¥40 显示成 ¥4000;
同文件其他 30 余处均为 /100,仅此处漏了。弹窗内收购成本/履约押金本来就已 /100,核对无误。

验证:用截图里的现货(苹果QQ区怒嚎3钻2璀璨)对照 6 种订单——
"怒嚎"、"苏尔南" 从命中变为不命中;"苹果QQ区怒嚎1钻" 仍按高配充低配提醒;
"5钻"(低配充高配)、"安卓微信区怒嚎1钻"(系统/区服不符)不命中。
后端 435 例(新增 3 例、改写 3 例)全绿。
2026-09-17 10:30:17 +08:00
yml2213 d158cc91bc feat(consignment): 确认加好友后现货作废不回池,撤销履约押金可选并结算打手押金
现货口径(严):已确认加好友 = 买家好友已加上、货随时可能送出去,回池会被下一个工单
再次占用(等于二次出货)。收口在 releaseConsignmentInventoryBindingForWorkOrderWithClient:
解绑前读工单 acceptance_mode/friend_added_at,命中则 archived + allow_matching=FALSE + 留痕,
否则仍回池。所有解绑路径自动继承,发单系统接口未动。

撤销寄售履约(寄卖专属入口)支持两笔押金各自可选,默认按规则自动:
- 打手履约押金:auto / refund / forfeit
- 卖家发布押金:auto / keep_frozen / forfeit
策略抽成 domain/consignment-deposit-policy.ts 纯函数;覆盖时原因照旧必填,选项与金额写进
工单事件便于对账。

顺带修掉两个 bug:
1. 撤销寄售履约从不结算打手履约押金——只清 assigned_worker_id、只处理卖家押金,
   worker_deposit 单撤销后打手押金永久冻结。现按选项结算(退还/没收)。
2. detachConsignmentMaterialFromWorkOrderWithClient 里 $1 既作 $1::text(detachedAt)
   又赋值给 updated_at,Postgres 整体推断成 text → 42804。凡解绑现货摘寄售标记的路径
   (取消指派/撤单/退单/售后/撤销履约/人工回库)在真实库上必然 500,假 client 单测抓不到。
   已显式 ::timestamptz,并补类级静态护栏扫同类写法。

验证:真实库(全回滚)——已确认加好友解绑 → archived/allow_matching=false;
未确认 → 仍 in_stock;打手押金冻结 3000 → 退还 3000(重复结算 0)→ 再冻 2500 → 没收 2500
(可用余额不变、冻结归零、流水 deposit_deduction)。后端 432 例(新增 5 例)全绿。
2026-09-17 10:21:38 +08:00