Files
order_site/docs/寄卖库存优化现状与进度追踪.md
T
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

273 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 寄卖库存功能 · 现状分析、进度与证据追踪报告
> 更新时间:2026-09-21
> 项目基线:`040b3db8` (main 分支,工作区干净)
> 当前验证状态:后端 501 测试全绿 (498 pass / 0 fail / 3 skip);前端 54 测试全绿 (54 pass / 0 fail);两侧构建与类型检查通过。
---
## 一、 证据分类记录 (Evidence Classification)
### 1. 代码证据 (Code Implementation)
- **打手提报与建议价闭环**:
- `apps/frontend/src/pages/worker/WorkerConsignmentPage.tsx`: 打手端唯一提报入口,支持系统(苹果/安卓)与区服(互通)规范输入;300ms防抖自动带出「接单模板」建议价;支持高档位溢出兜底与手改价保护;支持 URL/localStorage 记忆停留选项卡。
- `apps/backend/src/services/worker-platform/consignment-worker-service.ts`: `suggestConsignmentPriceFromRules` 严格对齐璀璨数(0/1/2/3璀璨)与钻石等级;发布接口强制 `requireActiveWorker`。
- **仓库与审核管理**:
- `apps/frontend/src/pages/admin/panels/ConsignmentInventoryPanel.tsx`: 寄售仓库管理,支持状态筛选、允许匹配开关、凭证链查看、工单双向匹配与退单回库。
- `apps/frontend/src/pages/admin/panels/ConsignmentSubmissionsPanel.tsx`: 寄卖提报审核,支持通过入库与驳回退还押金。
- **匹配与履约执行引擎**:
- `apps/backend/src/services/worker-platform/consignment-matching-service.ts`: `compareSpec` 三态判定(match / mismatch / unknown);支持区服「互通」对称通配;未知项降级为 partial 并列出缺失字段。
- `apps/backend/src/services/worker-platform/consignment-fulfillment-service.ts`: 内部现货履约(原子占库 + 工单绑定 + 停在待加好友)与指派打手押金履约;服务端强制规格复检与冲突留痕。
- **双向手动匹配与工单详情(最近落地)**:
- `apps/frontend/src/pages/admin/ConsignmentManualMatchModal.tsx`: 双向手动匹配弹窗,支持 WO 工单号与 SUB 提报单号模糊检索,支持规格冲突时勾选强制匹配并必填原因。
- `apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx`: 工单详情抽屉完整回显卖家资料(角色名、数字ID、区服、系统、联系方式、单号等)与凭据截图,支持一键聚合复制与单项复制。
### 2. 数据与迁移证据 (Database & Migrations)
- `077_consignment_inventory.sql`: 寄售仓库表 `consignment_inventories` 与提报表 `consignment_submissions` 基线,建立 RBAC 四大权限。
- `078_consignment_worker_deposit.sql`: 打手端押金履约字段与台账支持。
- `079_drop_consignment_collect_links.sql`: 完全移除历史遗留 H5 定向收集渠道(下线 `consignment_collect_links` 表与 `link_id` 列);拆分 `assigned_worker_id`(履约打手)与 `worker_id`(供货卖家);建立绑定工单部分唯一索引与冻结流水去重幂等索引。
- `080_consignment_allocation_status_and_copy.sql`: 约束 `allocation_status IN ('待安排', '已安排')`。
### 3. 资金与状态机闭环证据 (Financial & State Machine Audit)
- **押金流转**:
- 卖家发布提报:冻结发布押金(`deposit_freeze`)。
- 审核驳回:全额退还(`deposit_unfreeze`)。
- 确认加好友后违约/撤单:没收卖家押金(`deposit_deduction`),只扣冻结额,不退可用余额。
- 履约验收:释放卖家发布押金,同时自动补发「供货货款」(`settleConsignmentSupplierPayoutByWorkOrderWithClient`)。
- **撤销与解绑**:
- 撤销白名单严格限制在 `in_progress | pending_acceptance | problem`,验收后绝对禁止撤销;
- 解绑现货时自动识别是否已加好友(已加好友作废 `archived` 下架不回池,未加好友 `in_stock` 回池)。
---
## 二、 进度管理:已知、未知、目标与确信度登记
| 类型 | 事项描述 | 确信度 | 支撑证据 / 依据 | 状态 / 备注 |
| :--- | :--- | :--- | :--- | :--- |
| **已知** | 提报渠道单一化收敛完成 | 100% | 迁移 079 已删 H5 相关表与路由,手工入库已设 409 守卫,唯一入口为打手端提报 | ✅ 已闭环 |
| **已知** | 统一单号锚定与三态状态机 | 100% | 提报单号(SUB...)作为统一主键暴露给前端与客服,隐藏 INV 编号;`domain/consignment-item-stage.ts` 纯函数推导状态 | ✅ 已闭环 |
| **已知** | 资金流转(货款结算 + 押金没收/释放)闭环 | 100% | 批次 Q 补发货款给供货卖家,修 100 倍单位错;批次 F/G 加好友后违约没收押金不可逆 | ✅ 已闭环 |
| **已知** | 双向手动指定现货与强制匹配留痕 | 100% | commit `f07e4087` 支持在工单调起匹配现货、在仓库调起匹配工单,支持工单号与提报单号搜索,冲突强制匹配带原因留痕 | ✅ 已闭环 |
| **已知** | 资料展示与一键复制功能完成 | 100% | commit `f08a221e`, `2c120bd3` 工单详情抽屉完整展示卖家信息并提供聚合及单项复制按钮 | ✅ 已闭环 |
| **已知** | 建议价精准对齐璀璨数与溢出兜底 | 100% | 批次 X/Z:按系统+系列+钻级+璀璨数全匹配接单模板;超出档位按最高配报;手改不覆盖 | ✅ 已闭环 |
| **已知** | 卖家上架主页截图在工单详情抽屉与打手端正常展示 | 100% | 修复 `normalizeUploadedFiles` 字符串解析与 `effectiveBoosterMaterial` 截图兜底,504后端/54前端测试全绿,真实工单 #100140 成功解析出截图 | ✅ 已闭环 |
| **已知** | 仓库搜索框占位符承诺字段与后端实现存在偏差 | 100% | `ConsignmentInventoryPanel.tsx:789` 承诺按“收购人/店铺/卖家”,但 `consignment-repo.ts:178` 未覆盖 `buyer_name`, `shop_name`, `seller_phone` | ⚠️ 待优化(低成本) |
---
## 五、 专项优化:接单工单匹配后详情抽屉展示卖家上架主页截图(2026-09-21 已闭环)
### 1. 现象与线上真实证据
- **问题现象**:接单工单匹配寄售现货后,在管理后台工单详情抽屉的「寄售赠送进度」模块中,「卖家上架/主页截图」区域显示为“未提交”,无法查看卖家寄卖时上传的游戏主页与物资截图。
- **数据库真实记录查证**(工单 `WOc5502148fd73`,ID: `100140`):
- 关联寄售库存 ID: `23` (`INV26091917439822`);
- `material_json.consignment.screenshots` 中确切存有截图:`["/api/v1/files/object?key=consignment_screenshot%2F2026%2F09%2F19%2Fb8b974206a8a0cf6c89a6296.webp"]`(纯字符串 URL 数组);
- 但后端接口返回的 `gift.boosterMaterial.screenshots` 却为空数组 `[]`。
### 2. 代码级根因定位
1. **文件归一化函数 `normalizeUploadedFiles` 丢失字符串 URL 支持(核心根因)**:
- `apps/backend/src/services/worker-platform/worker-input-normalizers.ts:36-40`:
原逻辑仅接受 `{ objectKey, url }` 对象格式;当传入纯字符串 URL 数组(寄售提报老格式与历史库数据)时,`typeof item === 'object'` 判定为 false,提取出的 `objectKey` 和 `url` 均为空串,最终被 `filter(Boolean)` 全数过滤为 `[]`!
2. **工单展示映射器 `resolveWorkOrderEffectiveBoosterMaterial` 缺少截图 fallback**:
- `apps/backend/src/services/worker-platform/work-order-display-mappers.ts:104`:
当工单的 `booster_material_json` 中已存在 `fields`(如之前同步过卖家昵称/ID)但 `screenshots` 为空时,直接提前返回了空截图列表,没有回退到寄售数据的 `consignment.screenshots`。
3. **前端工单详情渲染缺乏兜底**:
- `apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx:2412` 与 `WorkerOrderDetailDrawer.tsx:244`:
原逻辑只读取 `detailOrder.gift.boosterMaterial?.screenshots`,未将 `consignmentInfo?.screenshots` 字符串数组作为第二层降级展示。
### 3. 解决对策与落地成果
1. **文件/URL 解析能力全方位增强**:
- `apps/backend/src/services/file-storage/file-storage-service.ts`:导出 `extractObjectKeyFromFileUrl`,支持从 `/api/v1/files/object?key=...` 中稳定提取 `objectKey`。
- `apps/backend/src/services/worker-platform/worker-input-normalizers.ts`:`normalizeUploadedFiles` 增加分支支持 `typeof item === 'string'`,自动提取 `objectKey` 并标准化为带有 `url`、`thumbnailUrl`、`mediumUrl` 的完整 `UploadedFilePayload`。
2. **展示层截图自动回退**:
- `apps/backend/src/services/worker-platform/work-order-display-mappers.ts`:在 `resolveWorkOrderEffectiveBoosterMaterial` 中,当打手/工单未传截图且关联寄售时,自动取寄售卖家的主页截图兜底填充,并保留 `isConsignmentSellerMaterial` 标识。
3. **前后端双保险与打手端对齐**:
- `apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx`:详情抽屉在 `detailOrder.gift.boosterMaterial.screenshots` 之后追加 `consignmentInfo.screenshots` 字符串列表降级渲染。
- `apps/frontend/src/pages/worker/WorkerOrderDetailDrawer.tsx`:打手端详情抽屉同步增加对寄售截图的降级渲染。
4. **自动化测试与实际验证**:
- 新增后端单元测试 `apps/backend/src/services/worker-platform/consignment-booster-material.test.ts`;
- 真实工单数据(`WOc5502148fd73`)映射执行验证,成功输出标准卖家主页截图对象;
- 后端 504 单测全绿、前端 54 单测全绿,构建与样式检查全部通过。
| **未知** | 待审核状态(pending_review)下打手是否需要自主撤回能力 | 80% | 业务目前仅提供客服审核通过/驳回,打手填错价格或想自用无法自主撤回,需业务确认 | ⏳ 待业务拍板 |
| **未知** | 待加好友停滞是否有超时策略 | 75% | 现货履约停在待加好友,若客服/卖家长时间不操作,工单永久挂起,是否需超时自动预警/工单回退 | ⏳ 待业务拍板 |
| **未知** | 批次 Q 之前已验收的历史寄卖单是否需要补发货款 | 70% | 历史单(如 #100171)在批次 Q 之前结算未获货款,需与运营对账确认是否需要补账脚本 | ⏳ 待对账确认 |
| **目标** | 完善寄卖仓库检索与展示体验 | 95% | 将后端搜索扩展至 `buyer_name`、`shop_name`、`seller_phone`、打手昵称等,兑现前端占位符承诺 | 🎯 近期可推进 |
| **目标** | 打手端支持“撤回提报”并即时解冻押金 | 90% | 在打手端“我的寄卖-待审核”卡片增加撤回功能,避免误提报占用押金和客服审核工时 | 🎯 近期可推进 |
| **目标** | 增加寄卖模块统计与节省赏金看板 | 80% | 补齐设计方案中的“累计节省赏金”、“出库单数”、“货款结算对账”统计图表 | 🎯 中期规划 |
| **目标** | 待加好友超时预警与催促机制 | 85% | 工单列表中对停留在待加好友超过 24/48 小时的寄售工单显示催促角标 | 🎯 近期可推进 |
---
## 三、 思考、灵感与演进分析 (Insights & Inspirations)
### 1. 思考:从“被动等待匹配”到“主动撮合建议”
- **现状**:目前系统主要依靠进单发布时自动检查现货候选,或者客服在管理后台手动调起匹配弹窗。
- **灵感**:当打手发布的高价值现货(例如安卓微信暗潮5钻)审核入库时,后台可自动检索当前排队池(抢单大厅、未分配工单)中是否有完全匹配的订单,并在审核通过成功提示或管理后台右上角通知栏弹窗提醒:“该现货已自动匹配待发工单 #WO26...,是否立即一键履约?”。这将将货找单的时延缩短至秒级。
### 2. 思考:打手端自闭环与客服减负
- **现状**:打手一旦提交寄卖,如果填错账号或写错价格,只能联系客服在后台驳回。
- **灵感**:在 `pending_review`(待审核)阶段,打手拥有随时撤回的权利(打手自主取消 -> 事务内释放押金 -> 状态转 `cancelled`)。这既能防止客服审核了无效的错单,也能大幅降低双方沟通成本。
### 3. 思考:待加好友阶段的履约节奏管控
- **现状**:为了防止错发和押金纠纷,内部现货履约被设计为停在“待加好友”,待加好友后才启动 72 小时赠送冷却。
- **灵感**:游戏内加好友存在单向通过的时滞。可以在工单面板上为“待加好友”超过 12 小时的工单打上“⚠️ 待加好友超时”标签,让客服能集中优先跟进买家/号主,保障履约时效。
---
## 四、 专项排查:打手 A 重复扣押金与二次填写资料的根因分析
### 1. 现象复现路径
- 打手 A 在接单平台提报寄卖一个物品,系统冻结发布押金(`deposit_freeze`,如 ¥20 或 ¥100);
- 后续外部进单匹配到此物品时,实际走入了【指派打手履约(`worker_deposit`)】而非【内部现货直接履约(`internal_stock`)】;
- 导致打手 A 账户被额外扣除一笔【接单履约押金】(双倍资金占用);
- 工单在打手端显示为“待提交资料”,打手 A 被迫重新手动上传游戏昵称、ID 及截图。
### 2. 代码级根因定位
1. **手动匹配弹窗硬编码为指派打手(关键漏项)**:
- `apps/frontend/src/components/admin/ConsignmentManualMatchModal.tsx:184`:
不论从寄售仓库还是工单操作列调起手动匹配弹窗,提交按钮直接硬编码调用了 `assignWorkOrderWithConsignmentWorker`(指派打手履约接口);
弹窗内**完全没有提供【内部现货直接履约】(`fulfillWorkOrderWithConsignment`)的选项**。
2. **发布拦截弹窗排版引导偏差**:
- `apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx:3434-3475`:
候选卡片中,【走接单平台指派打手(冻结押金交付)】被置于顶部且为高亮紫色主按钮(Primary),而真正推荐的【内部现货直接履约(免外派佣金)】为次要按钮(Default),客服极易默认点击主按钮。
3. **指派打手履约复用普通工单逻辑导致双冻结与资料丢失**:
- `consignment-fulfillment-service.ts:680` 调用了通用的 `assignAdminWorkOrderToWorker`;
- 通用指派将打手 A 视作外部抢单打手,再次执行 `freezeWorkerOrderDepositWithClient` 冻结履约押金;
- 通用指派将工单置为 `acceptance_mode = 'friend_gift', gift_phase = 'material_required'`,但未自动将现货中的 `seller_game_id`、`credentials_json`、`screenshots_json` 自动写入工单 `booster_material_json` 并推进阶段,导致打手端仍停留在“待提交资料”。
### 3. 解决对策与落地成果(2026-09-21 已闭环)
1. **手动匹配弹窗收敛为单一【内部现货直接履约】**:
- 在 `ConsignmentManualMatchModal.tsx` 中彻底废除 `assignWorkOrderWithConsignmentWorker`,切换为调用 `fulfillWorkOrderWithConsignment`;
- 彻底解除 `selected.workerId` 强依赖与无法指派错误提示,详情卡片调整为展示供货来源(“供货打手”或“内部现货”);
- 文案统一调整为“确认内部现货履约”,明确告知“免外派佣金、不冻结重复押金、自动跳过打手资料”。
2. **工单发布拦截弹窗候选操作收敛为【内部现货直接履约】**:
- 在 `WorkOrdersPanel.tsx` 中将卡片操作按钮替换为【内部现货直接履约】高亮主按钮(绿色),副标题标注“免外派佣金 · 不扣打手押金 · 进入待加好友”;
- 杜绝客服误点走旧指派流程。
3. **测试套件与类型风格全面达标**:
- `consignment-manual-match-mount.test.ts` 更新为断言手动匹配走内部现货直接履约;
- 前端 54 测试全绿、后端 501 测试全绿;两侧 `typecheck`、`format:check`、`build` 均 100% 通过。
---
## 六、 专项优化:接单工单表格「打手」列回显寄售供货打手与现货资料(2026-09-21 已闭环)
### 1. 现象与用户反馈
- **问题现象**:接单工单在走内部现货直接履约后,工单列表表格的【打手】列以及工单详情基本信息中的【打手】字段显示为冰冷的一横杠 `-`,客服/运营在列表视图下无法直观查看是谁供的货或对应哪个游戏角色。
### 2. 代码级根因定位
- `apps/frontend/src/pages/admin/panels/work-order-panel-utils.tsx:31`:
原 `renderWorkOrderWorkers(order)` 仅针对 `order.worker`(传统指派/抢单打手)与 `order.sharingWorkers`(拼单打手)进行了渲染,而内部现货履约单没有 `order.worker`,直接走到底部返回了 `'-'`。
### 3. 解决对策与落地成果
1. **提炼寄售供货展示纯函数**:
- 在 `apps/frontend/src/utils/consignment-display.ts` 中建立 `resolveConsignmentWorkerDisplay(order: WorkOrder)`;
- 自动解析 `material.consignment` 与 `gift.boosterMaterial.fields`:
- 当存在 `workerName` 时,主标题展示供货打手名称,打上 `<Tag color="cyan">供货打手</Tag>`,副标题自动拼接游戏角色昵称与游戏 ID;
- 当无 `workerName`(纯仓库现货)时,主标题展示卖家角色昵称/游戏 ID,打上 `<Tag color="cyan">内部现货</Tag>`。
2. **列表与详情无缝集成**:
- `renderWorkOrderWorkers` 统一接入 `resolveConsignmentWorkerDisplay`,使工单列表表格中的【打手】列和工单详情抽屉基本信息中的【打手】栏均能清晰、美观地回显供货人。
3. **自动化测试与样式达标**:
- 新增前端单元测试 `apps/frontend/tests/consignment-worker-display.test.ts`,验证普通工单、有供货打手寄售单、无打手纯现货单的解析分支;
- 前端 57 个测试全绿,`format:check`、`lint:check`、`typecheck` 全部通过。
---
## 七、 专项优化:打手端「我要寄卖」顶部臃肿卡片轻量化重构(2026-09-21 已闭环)
### 1. 现象与用户反馈
- **问题现象**:打手在手机端打开“我要寄卖”页面,顶部纵向堆叠了 4 个巨型卡片(`待审核`、`履约中`、`已完成`、`发布寄卖 苏尔南`),占用高达 400px+ 竖向空间,几乎占满整块手机屏幕。打手进页面完全看不到下方的发布表单,必须下滑整屏才能开始填表,操作链路极长且视觉严重失调。
### 2. 代码级根因定位
1. **通用样式在移动端粗暴单列堆叠**:
- `apps/frontend/src/pages/worker/WorkerConsignmentPage.tsx:510` 原先套用了通用 `.worker-orders-kpi-grid`;
- 其在 `@media (max-width: 640px)` 下被定义为 `grid-template-columns: 1fr`,4 个卡片被强制纵向拉成长条大块;
2. **信息噪音多、占位无意义**:
- 每个卡片塞入了诸如“客服审核中,通过后自动入库待匹配”、“待加好友 / 冷却 / 待赠送 / 待验收”、“含已完成、已驳回、已下架作废”等多余的长文本;
- 第 4 个卡片“发布寄卖 苏尔南”纯属凑数占位,点击只是切到下方的发布 Tab,自身毫无数据与功能意义。
### 3. 解决对策与落地成果
1. **彻底移除无意义的第 4 个卡片**:
- 移除“发布寄卖 苏尔南”假 KPI 卡片,将核心关注点精准收敛为【待审核】、【履约中】、【已完成】3 大关键数据。
2. **重构为专属轻量级横向数据栏**:
- 提取并新增 `.worker-consignment-kpi-grid` 与 `.worker-consignment-kpi-card`:
- 移动端采用 `repeat(3, 1fr)` 紧凑横向三等分网格,高度由原来的 320px+ 骤降至约 52px(**竖向空间释放超 80%**);
- 剔除无意义的长句副文案,采用居中微标签布局:上行小字展示状态(11px),下行加粗展示数值(16px);
- 待审核(紫/橙光)、履约中(科技蓝光)、已完成(清新绿光),选中态带优雅的主题高亮指示;
- 桌面端保留图文两列的优雅排布,兼顾大屏美观。
3. **首屏即见核心发布表单**:
- 优化后,打手打开手机首屏顶端即可在不滑动的情况下直接看到“履约押金提示”、“操作系统(苹果/安卓)”与“游戏区服”选择,提报效率大幅提升!
4. **自动化测试与全量构建**:
- 前端 59 个自动化测试全绿(**59 pass / 0 fail**);
- `format:check`、`lint:check`、`typecheck`、`build` 全部通过。
---
## 八、 专项优化:打手抢单大厅首页新增「联系客服」入口与通用客服弹窗组件(2026-09-21 已闭环)
### 1. 现状与用户诉求(User Feedback & Target)
- **用户反馈**:“首页 增加联系客服 现在联系客服藏的太深了”,并附带手机端首页截图,用红圈标注了移动端顶部状态栏(`余额 / 上限` 胶囊右侧、操作按钮区左侧的空白区域)。
- **目标**:
1. 在抢单大厅首页移动端顶部状态栏(红圈标注的核心首屏黄金位置)增加醒目高质感的【联系客服】胶囊按钮;
2. 在桌面端抢单大厅头部操作栏同步补齐【联系客服】入口,打通双端闭环;
3. 将原本深藏在个人中心底层的“联系客服/管理员”弹窗抽象为全站通用的 `WorkerContactModal` 组件,统一维护二维码、账号、余额等信息,提供一键复制与标准沟通提示。
### 2. 证据分类与代码级根因(Evidence & Root Cause)
- **已知信息 [确信度: 100%]**:
1. 官方公告栏循环播放:“☆接单请先个人中心联系客服☆方便客服处理”,证明新打手或接单打手频繁需要找客服沟通充值、提现或报备;
2. 原联系管理员弹窗代码内联在 `apps/frontend/src/pages/worker/WorkerProfileAccountModals.tsx:187-240`,打手必须切换至“个人中心”页面并向下滑动到底部才能找到入口,链路过长、深度太深;
3. `WorkerHallPage.tsx` 中的 `profileQuery.data?.data` 原生就包含了 `worker` 实体与 `financeConfig`(含管理员微信二维码配置 `adminContact.wechatQrCodeImage`),数据已就绪,仅缺前端呈现入口。
- **未知/风险预判 [确信度: 100%]**:
- 移动端 375px 或超窄屏(如 320px)下顶部栏元素横向空间是否会挤压拥挤;
- 应对对策:采用弹性缩放与媒体查询控制,超小屏下保持单行不折叠。
### 3. 解决对策与落地成果(Solution & Milestones)
1. **抽离通用联系客服弹窗组件**:
- 新建 `apps/frontend/src/components/worker/WorkerContactModal.tsx`,集中管理微信二维码大图、当前打手账号、昵称、手机号、账号状态、可用余额,以及一键复制和温馨沟通提示;
- 在 `WorkerProfileAccountModals.tsx` 与 `WorkerHallPage.tsx` 中全面复用该组件,消灭重复代码。
2. **移动端黄金位置新增【联系客服】胶囊按钮**:
- 在 `WorkerHallPage.tsx` 移动端顶部状态条(用户红圈位置)插入专属胶囊:
```html
<button class="worker-hall-mobile-contact-pill" onClick={() => setContactOpen(true)}>
<CustomerServiceOutlined />
<span>联系客服</span>
</button>
```
- 采用浅暖橙背景(`#fff7ed`)+ 优雅橙边框(`#fed7aa`)+ 微动效,与打手端整体暖橙调性浑然一体,点击即弹出客服微信与账号卡片。
3. **桌面端同步补齐入口**:
- 在桌面端头部操作按钮栏(刷新与排行榜旁)新增 `<Button icon={<CustomerServiceOutlined />} onClick={() => setContactOpen(true)}>联系客服</Button>`。
4. **验证门禁全绿通过 [确信度: 100%]**:
- 前端 59 个自动化测试全绿(**59 pass / 0 fail**);
- 前端 `format:check`、`lint:check`、`typecheck`、`build` 均零错误零警告通过;
- 后端 504 个自动化测试全绿(**501 pass / 3 skipped / 0 fail**)。
---
## 九、 专项优化:打手寄卖白名单开关控制与存量平滑过渡(2026-09-21 已闭环)
### 1. 现状与用户诉求(User Feedback & Target)
- **用户反馈**:“寄卖系统 我想增加一个类似白名单 激活模式, 如果有新的注册的人员, 默认无法开启寄卖; 必须联系客服... 方案 A 就够了, 能实现控制 一个开关也行, 然后 存量老打手平滑过渡 发布过的默认开启”。
- **目标**:
1. 实行寄卖白名单准入机制:新注册打手默认不开启寄卖,防止虚假提报与黑产刷单;
2. 存量老打手平滑过渡:历史上在 `consignment_submissions` 中发布过寄卖的打手,在迁移中自动批量赋权开启;
3. 后台提供开关:客服/运营在打手管理表格与详情抽屉中可一键开通/关闭打手寄卖权限;
4. 打手端友好提示与拦截:未开通寄卖权限的打手在「我要寄卖」页面展示专属引导卡片,一键唤起客服弹窗;后端接口进行服务端强拦截(403 `consignment_permission_required`)。
### 2. 证据分类与实现细节(Evidence & Implementation)
- **已知信息 [确信度: 100%]**:
1. `worker_users` 表增加了 `can_consign`(布尔值,默认 `FALSE`)与 `consignment_activated_at`(开通时间戳);
2. 迁移文件 `082_worker_consignment_whitelist.sql` 通过 `IN (SELECT DISTINCT worker_id FROM consignment_submissions WHERE worker_id IS NOT NULL)` 实现存量打手秒级平滑过渡;
3. 打手端提报接口 `publishWorkerConsignment` 增加了 `if (!worker.can_consign) throw ...` 强校验;
4. 前端管理后台 `WorkersPanel.tsx` 增加了「寄卖权限」列,直接嵌入 Switch 开关实时切换;打手详情抽屉 `WorkerDetailDrawer.tsx` 补充了权限和开通时间回显。
### 3. 验证与门禁报告(Quality Gate Status)
- **后端单测与类型检查 [确信度: 100%]**:
- 新增 `admin-worker-consignment-permission.test.ts` 及 `consignment-worker-service.test.ts` 权限拦截测试;
- 后端 507 项单测全绿(**504 pass / 3 skipped / 0 fail**);
- 后端 `typecheck`、`check:sql` 全部通过;
- **前端门禁与生产构建 [确信度: 100%]**:
- 前端 59 项单元测试全绿(**59 pass / 0 fail**);
- 前端 `format:check`、`lint:check`、`typecheck`、`build` 零错误全绿通过。