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);按寄卖单号搜索命中正确。临时数据已清理。
This commit is contained in:
yml2213
2026-09-17 10:57:57 +08:00
parent 99752dce9e
commit 44de11a3a7
19 changed files with 1574 additions and 204 deletions
+99 -13
View File
@@ -390,21 +390,68 @@ export type WorkerConsignmentItem = {
createdAt: string
}
export type WorkerInventoryItem = {
id: number | string
inventoryNo: string
/** 寄卖单统一状态机阶段(后端推导,前端只透传展示) */
export type WorkerConsignmentStage =
| 'pending_review'
| 'in_stock'
| 'matched_waiting_friend'
| 'gift_cooldown'
| 'gift_ready'
| 'gift_submitted'
| 'fulfilling'
| 'pending_acceptance'
| 'completed'
| 'rejected'
| 'archived'
| 'cancelled'
export type WorkerConsignmentGroup = 'reviewing' | 'active' | 'finished'
/**
* 「我的寄卖」列表行:**一件货一行**。
*
* 后端已把提报单(SUB…)/ 库存(INV…)/ 绑定工单三套状态聚合成一条记录,
* 以 `consignmentNo` 为唯一锚点;阶段文案、配色与可执行动作也由后端下发,
* 前端不再自己推导状态,避免和客服侧口径不一致。
*/
export type WorkerConsignmentRecord = {
/** 唯一锚点:提报单号(客服手工入库时是库存号),界面只显示它 */
consignmentNo: string
submissionId: number | null
inventoryId: number | null
workOrderId: number | null
itemName: string
system: string
serverZone: string
series: string
diamondLevel: number
brillianceCount: number
costPriceFen: number
status: 'in_stock' | 'consumed' | 'archived'
allocationStatus: string
boundPlatformOrderId: string
depositAmountFen?: number
askingPriceFen: number
depositAmountFen: number
reviewNote: string
screenshotsJson: string[]
createdAt: string
stage: WorkerConsignmentStage
/** 中文阶段名,如「已匹配-待加好友」 */
label: string
group: WorkerConsignmentGroup
/** antd Tag 颜色名 */
color: string
/** 给卖家看的下一步提示,直接展示 */
hint: string
canConfirmFriend: boolean
canSubmitEvidence: boolean
evidenceButtonLabel: '提交赠送截图' | '提交验收材料'
/** 赠送冷却截止时间,前端拿它做倒计时 */
giftAvailableAt: string | null
friendAddedAt: string | null
}
export type WorkerConsignmentListResponse = {
publishDepositFen: number
items: WorkerConsignmentRecord[]
/** 各阶段件数,用于筛选 tab 角标 */
stageCounts: Record<string, number>
}
export function publishWorkerConsignment(payload: WorkerConsignmentPublishPayload) {
@@ -412,9 +459,48 @@ export function publishWorkerConsignment(payload: WorkerConsignmentPublishPayloa
}
export function fetchWorkerConsignments() {
return apiGet<{
publishDepositFen: number
submissions: WorkerConsignmentItem[]
inventories: WorkerInventoryItem[]
}>('/api/v1/worker/consignment/my-items')
return apiGet<WorkerConsignmentListResponse>('/api/v1/worker/consignment/my-items')
}
/**
* 卖家确认已在游戏内加好买家好友 → 进入赠送冷却期。
* 注意:确认后若未完成赠送,履约押金不退。
*/
export function confirmWorkerConsignmentFriendAdded(submissionId: number | string) {
return apiPost<{ order: WorkOrder }>(
`/api/v1/worker/consignment/items/${submissionId}/confirm-friend`,
{},
)
}
/** 卖家提交赠送完成截图(后端复用打手验收链路,工单转「待验收」交客服审核)。 */
export function submitWorkerConsignmentGiftEvidence(
submissionId: number | string,
payload: { imageUrls: string[]; note?: string },
) {
return apiPost<{ order: WorkOrder }>(
`/api/v1/worker/consignment/items/${submissionId}/gift-evidence`,
payload,
)
}
/**
* 客服手工入库的现货没有提报单号,按库存号走同一套动作
* (后端单独开路由,不用"一个 id 两义",避免提报单号与库存号撞号时误操作)。
*/
export function confirmWorkerConsignmentInventoryFriendAdded(inventoryId: number | string) {
return apiPost<{ order: WorkOrder }>(
`/api/v1/worker/consignment/inventories/${inventoryId}/confirm-friend`,
{},
)
}
export function submitWorkerConsignmentInventoryGiftEvidence(
inventoryId: number | string,
payload: { imageUrls: string[]; note?: string },
) {
return apiPost<{ order: WorkOrder }>(
`/api/v1/worker/consignment/inventories/${inventoryId}/gift-evidence`,
payload,
)
}