Files
order_site/apps/frontend/tests/consignment-cooldown.test.ts
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

69 lines
2.4 KiB
TypeScript

import assert from 'node:assert/strict'
import test from 'node:test'
import { formatGiftCooldownRemaining } from '../src/utils/consignment-cooldown.ts'
const MINUTE_MS = 60_000
const HOUR_MS = 3_600_000
const DAY_MS = 24 * HOUR_MS
const BASE_MS = Date.parse('2026-03-01T00:00:00.000Z')
function iso(offsetMs: number) {
return new Date(BASE_MS + offsetMs).toISOString()
}
test('未来时间:按「X 天 Y 小时」展示剩余冷却', () => {
const result = formatGiftCooldownRemaining(iso(2 * DAY_MS + 3 * HOUR_MS), BASE_MS)
assert.deepEqual(result, { text: '冷却中,剩余 2 天 3 小时', finished: false })
})
test('跨天的整小时剩余省略「0 小时」', () => {
assert.deepEqual(formatGiftCooldownRemaining(iso(3 * DAY_MS), BASE_MS), {
text: '冷却中,剩余 3 天',
finished: false,
})
})
test('不足一天:按「X 小时 Y 分」展示,整小时省略「0 分」', () => {
assert.deepEqual(formatGiftCooldownRemaining(iso(2 * HOUR_MS + 30 * MINUTE_MS), BASE_MS), {
text: '冷却中,剩余 2 小时 30 分',
finished: false,
})
assert.deepEqual(formatGiftCooldownRemaining(iso(2 * HOUR_MS), BASE_MS), {
text: '冷却中,剩余 2 小时',
finished: false,
})
})
test('小于 1 小时:按分钟向上取整,不会出现「剩余 0 分」', () => {
assert.deepEqual(formatGiftCooldownRemaining(iso(25 * MINUTE_MS), BASE_MS), {
text: '冷却中,剩余 25 分',
finished: false,
})
assert.deepEqual(formatGiftCooldownRemaining(iso(30_000), BASE_MS), {
text: '冷却中,剩余 1 分',
finished: false,
})
})
test('已过期(或刚好到点)判定为冷却结束', () => {
assert.deepEqual(formatGiftCooldownRemaining(iso(-1), BASE_MS), {
text: '冷却已结束,可赠送',
finished: true,
})
assert.deepEqual(formatGiftCooldownRemaining(iso(0), BASE_MS), {
text: '冷却已结束,可赠送',
finished: true,
})
})
test('非法与空输入返回空文案且不误判为已结束', () => {
const empty = { text: '', finished: false }
assert.deepEqual(formatGiftCooldownRemaining(null, BASE_MS), empty)
assert.deepEqual(formatGiftCooldownRemaining(undefined, BASE_MS), empty)
assert.deepEqual(formatGiftCooldownRemaining('', BASE_MS), empty)
assert.deepEqual(formatGiftCooldownRemaining(' ', BASE_MS), empty)
assert.deepEqual(formatGiftCooldownRemaining('2026-03-01 不是时间', BASE_MS), empty)
})