refactor(consignment): 删掉按库存号操作寄卖的卖家入口

手动入库停用后,"没有提报单的现货"已不可能再产生,批次 I 为兜这类货加的两个
按库存号入口(/worker/consignment/inventories/:inventoryId/...)成了纯负担。

- 删除两个按库存号的路由/服务函数、前端两个请求函数与页面里的锚点分派逻辑,
  卖家动作只按提报单号(寄卖单号)定位
- 卖家列表里"没有提报单"的兜底行不再暴露内部仓库号(标题改「客服手工入库」),
  且一律不给操作能力位,不给用户一个点下去必然失败的按钮
- 加静态守卫测试钉住以上两点,防止以后又长出按库存号的入口

真实库验证:卖家列表三件货都带寄卖单号与 submissionId,没有提报单的现货 0 件。
This commit is contained in:
yml2213
2026-09-17 11:45:50 +08:00
parent 0c10f0e135
commit e731348bb0
6 changed files with 61 additions and 132 deletions
@@ -2,11 +2,9 @@ import { Router } from 'express'
import {
confirmWorkerConsignmentFriendAdded,
confirmWorkerConsignmentInventoryFriendAdded,
listWorkerConsignments,
publishWorkerConsignment,
submitWorkerConsignmentGiftEvidence,
submitWorkerConsignmentInventoryGiftEvidence,
} from '../../services/worker-platform/consignment-worker-service.js'
import { sendRouteError } from '../../utils/http.js'
import { getRequiredWorkerSession, requireActiveWorker } from './session.js'
@@ -86,45 +84,4 @@ router.post(
},
)
/**
* 客服手工入库的现货没有提报单,按库存号走同一套卖家动作。
* 单独开路由而不是"一个 id 两义",避免提报单 id 与库存 id 撞号时误操作别人的货。
*/
router.post(
'/consignment/inventories/:inventoryId/confirm-friend',
requireActiveWorker,
async (req, res) => {
try {
const session = getRequiredWorkerSession(req)
const result = await confirmWorkerConsignmentInventoryFriendAdded(
session.workerId,
String(req.params.inventoryId || ''),
session.username || '',
)
res.json({ ok: true, data: result })
} catch (error) {
sendRouteError(res, error, '确认加好友失败', '[worker/consignment/inventory-confirm-friend]')
}
},
)
router.post(
'/consignment/inventories/:inventoryId/gift-evidence',
requireActiveWorker,
async (req, res) => {
try {
const session = getRequiredWorkerSession(req)
const result = await submitWorkerConsignmentInventoryGiftEvidence(
session.workerId,
String(req.params.inventoryId || ''),
req.body || {},
session,
)
res.json({ ok: true, data: result })
} catch (error) {
sendRouteError(res, error, '提交赠送截图失败', '[worker/consignment/inventory-gift-evidence]')
}
},
)
export default router
@@ -10,7 +10,8 @@ test('我的寄卖以提报单号为唯一锚点聚合,不再返回 submission
const serviceSource = await readServiceSource()
// 以前接口返回两个数组、前端分两个列表渲染 → 同一件货两行两个号
assert.ok(serviceSource.includes('consignmentNo: sub.submission_no'))
assert.ok(serviceSource.includes('consignmentNo: inv.inventory_no'))
// 没有提报单的兜底行也不再暴露内部仓库号(手动入库已停用,理论上不该再有这种行)
assert.ok(!serviceSource.includes('consignmentNo: inv.inventory_no'))
assert.ok(!serviceSource.includes('submissions: submissionsRes.items.map'))
assert.ok(!serviceSource.includes('inventories: inventoriesRes.items.map'))
})
@@ -35,3 +36,18 @@ test('赠送截图复用打手验收提交链路(冷却校验、必须带图
assert.ok(serviceSource.includes('submitWorkerOrderAcceptance('))
assert.ok(serviceSource.includes("errorCode: 'consignment_item_not_found'"))
})
test('卖家动作只按提报单号定位:手动入库停用后,按库存号的入口必须不存在', async () => {
const serviceSource = await readServiceSource()
// 手动入库已停用 → 每件货必然由提报单产出 → 不该再保留 inventoryId 入口(含"一个 id 两义"的旧设计)
assert.ok(!serviceSource.includes('confirmWorkerConsignmentInventoryFriendAdded'))
assert.ok(!serviceSource.includes('submitWorkerConsignmentInventoryGiftEvidence'))
assert.ok(!serviceSource.includes('inventoryId: number | string'))
assert.ok(serviceSource.includes('resolveSellerConsignmentWorkOrder(workerId, submissionId)'))
})
test('没有提报单的兜底行不给卖家操作能力,也不暴露内部仓库号', async () => {
const serviceSource = await readServiceSource()
assert.ok(serviceSource.includes("consignmentNo: '客服手工入库'"))
assert.ok(serviceSource.includes('canConfirmFriend: false, canSubmitEvidence: false'))
})
@@ -2,13 +2,11 @@ import { withTransaction } from '../../db/client.js'
import { freezeConsignmentPublishDepositWithClient } from '../../repositories/worker-platform/consignment-deposit-repo.js'
import {
createConsignmentSubmissionWithClient,
getConsignmentInventoryById,
getConsignmentInventoryBySubmissionId,
getConsignmentSubmissionById,
listConsignmentInventories,
listConsignmentSubmissions,
} from '../../repositories/worker-platform/consignment-repo.js'
import type { ConsignmentInventoryRow } from '../../repositories/worker-platform/consignment-repo.js'
import { getWorkerUserById } from '../../repositories/worker-platform/index.js'
import { listWorkOrdersByIds } from '../../repositories/worker-platform/work-order-query-repo.js'
import { deriveConsignmentItemStage } from '../../domain/consignment-item-stage.js'
@@ -282,9 +280,13 @@ export async function listWorkerConsignments(workerId: number) {
}
}),
...orphanInventories.map((inv) => {
// 手动入库已停用:没有提报单的货不该再出现,这里只做兜底展示(不显示内部仓库号、不给操作能力)
const stage = resolveStage('approved', inv)
return {
consignmentNo: inv.inventory_no,
consignmentNo: '客服手工入库',
...(stage.canConfirmFriend || stage.canSubmitEvidence
? { canConfirmFriend: false, canSubmitEvidence: false }
: {}),
submissionId: null,
inventoryId: inv.id,
workOrderId: inv.bound_work_order_id ?? null,
@@ -328,30 +330,18 @@ export async function listWorkerConsignments(workerId: number) {
*/
async function resolveSellerConsignmentWorkOrder(
workerId: number,
anchor: { submissionId: number | string } | { inventoryId: number | string },
submissionId: number | string,
): Promise<{ workOrderId: number; inventoryId: number }> {
let inventory: ConsignmentInventoryRow | null = null
if ('submissionId' in anchor) {
const submission = await getConsignmentSubmissionById(anchor.submissionId)
if (!submission || Number(submission.worker_id) !== Number(workerId)) {
throw createHttpError('寄卖单不存在或不属于当前账号', {
statusCode: 404,
errorCode: 'consignment_item_not_found',
})
}
inventory = await getConsignmentInventoryBySubmissionId(submission.id)
} else {
// 客服手工入库的现货没有提报单,只能按库存号定位(提报单线不走这里,避免 id 撞号误操作)
inventory = await getConsignmentInventoryById(anchor.inventoryId)
if (!inventory || Number(inventory.worker_id || 0) !== Number(workerId)) {
throw createHttpError('寄卖单不存在或不属于当前账号', {
statusCode: 404,
errorCode: 'consignment_item_not_found',
})
}
const submission = await getConsignmentSubmissionById(submissionId)
if (!submission || Number(submission.worker_id) !== Number(workerId)) {
throw createHttpError('寄卖单不存在或不属于当前账号', {
statusCode: 404,
errorCode: 'consignment_item_not_found',
})
}
// 手动入库已停用,每件货必然由提报单产出,所以只按提报单号定位(不再有 inventoryId 入口)
const inventory = await getConsignmentInventoryBySubmissionId(submission.id)
if (!inventory) {
throw createHttpError('该寄卖单还没有生成库存,暂无可执行的操作', {
statusCode: 409,
@@ -395,17 +385,7 @@ export async function confirmWorkerConsignmentFriendAdded(
submissionId: number | string,
workerName = '',
) {
const { workOrderId } = await resolveSellerConsignmentWorkOrder(workerId, { submissionId })
return confirmAdminWorkOrderFriendAdded(workOrderId, workerName || `worker:${workerId}`)
}
/** 客服手工入库现货(没有提报单)的同一动作:按库存号定位 */
export async function confirmWorkerConsignmentInventoryFriendAdded(
workerId: number,
inventoryId: number | string,
workerName = '',
) {
const { workOrderId } = await resolveSellerConsignmentWorkOrder(workerId, { inventoryId })
const { workOrderId } = await resolveSellerConsignmentWorkOrder(workerId, submissionId)
return confirmAdminWorkOrderFriendAdded(workOrderId, workerName || `worker:${workerId}`)
}
@@ -421,17 +401,6 @@ export async function submitWorkerConsignmentGiftEvidence(
payload: JsonObject,
session: WorkerSession,
) {
const { workOrderId } = await resolveSellerConsignmentWorkOrder(workerId, { submissionId })
return submitWorkerOrderAcceptance(workOrderId, payload, session)
}
/** 客服手工入库现货(没有提报单)的同一动作:按库存号定位 */
export async function submitWorkerConsignmentInventoryGiftEvidence(
workerId: number,
inventoryId: number | string,
payload: JsonObject,
session: WorkerSession,
) {
const { workOrderId } = await resolveSellerConsignmentWorkOrder(workerId, { inventoryId })
const { workOrderId } = await resolveSellerConsignmentWorkOrder(workerId, submissionId)
return submitWorkerOrderAcceptance(workOrderId, payload, session)
}