From c17b8a78d5239d5eb508dbef004ad35d040d3c53 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 18 Sep 2026 18:35:15 +0800 Subject: [PATCH] =?UTF-8?q?fix(worker-consignment):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=8D=96=E5=AE=B6=E6=8F=90=E4=BA=A4=E8=B5=A0=E9=80=81=E6=88=AA?= =?UTF-8?q?=E5=9B=BE=E5=90=8E=E5=9C=A8=E6=8E=A5=E5=8D=95=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E5=9B=9E=E6=98=BE=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 后端 listWorkerConsignments 接口从关联工单验收资料中提取 giftEvidenceImages、giftEvidenceNote、giftEvidenceSubmittedAt 并在打手端寄卖列表中下发; 2. 接单平台「我的寄卖」卡片新增已提交赠送凭证展示模块,支持展示缩略图、张数统计、大图预览及赠送备注; 3. 优化提报单与库存、工单的 SQL 关联兼容双向绑定,确保后台与前台均能稳定查到赠送凭证。 --- .../worker-platform/consignment-repo.ts | 9 +++- .../consignment-worker-service.ts | 52 +++++++++++++++++++ .../pages/worker/WorkerConsignmentPage.tsx | 39 +++++++++++++- apps/frontend/src/services/worker.ts | 3 ++ 4 files changed, 99 insertions(+), 4 deletions(-) diff --git a/apps/backend/src/repositories/worker-platform/consignment-repo.ts b/apps/backend/src/repositories/worker-platform/consignment-repo.ts index a840a6ed..481af492 100644 --- a/apps/backend/src/repositories/worker-platform/consignment-repo.ts +++ b/apps/backend/src/repositories/worker-platform/consignment-repo.ts @@ -903,7 +903,7 @@ export async function listConsignmentSubmissions(filters: { wo.status AS gift_work_order_status FROM consignment_submissions cs LEFT JOIN work_categories wc ON wc.id = cs.category_id - LEFT JOIN consignment_inventories rinv ON rinv.id = cs.result_inventory_id + LEFT JOIN consignment_inventories rinv ON (rinv.id = cs.result_inventory_id OR rinv.source_submission_id = cs.id) LEFT JOIN work_orders wo ON wo.id = rinv.bound_work_order_id ${whereClause} ORDER BY cs.id DESC @@ -946,9 +946,14 @@ export async function getConsignmentSubmissionById( const sql = ` SELECT cs.*, - wc.name AS category_name + wc.name AS category_name, + wo.acceptance_json AS gift_evidence_json, + wo.work_order_no AS gift_work_order_no, + wo.status AS gift_work_order_status FROM consignment_submissions cs LEFT JOIN work_categories wc ON wc.id = cs.category_id + LEFT JOIN consignment_inventories rinv ON (rinv.id = cs.result_inventory_id OR rinv.source_submission_id = cs.id) + LEFT JOIN work_orders wo ON wo.id = rinv.bound_work_order_id WHERE cs.id = $1 LIMIT 1 ` diff --git a/apps/backend/src/services/worker-platform/consignment-worker-service.ts b/apps/backend/src/services/worker-platform/consignment-worker-service.ts index c66a8c67..11486a3c 100644 --- a/apps/backend/src/services/worker-platform/consignment-worker-service.ts +++ b/apps/backend/src/services/worker-platform/consignment-worker-service.ts @@ -23,8 +23,52 @@ import { getConsignmentPublishDepositFen as getConfiguredConsignmentPublishDepos import { confirmAdminWorkOrderFriendAdded } from './admin-work-order-gift-service.js' import { submitWorkerOrderAcceptance } from './worker-acceptance-service.js' import type { WorkerSession } from './worker-session-context-service.js' +import { safeParseJson } from '../admin/admin-query-utils.js' import { extractConsignmentBuyerInfoFromWorkOrder } from './work-order-material-mappers.js' +function toRecord(value: unknown): Record | null { + if (value && typeof value === 'object' && !Array.isArray(value)) { + return value as Record + } + const parsed = safeParseJson(value) + return parsed && typeof parsed === 'object' && !Array.isArray(parsed) + ? (parsed as Record) + : null +} + +function extractConsignmentGiftEvidenceFromWorkOrder(boundWo?: WorkOrderRow | null) { + if (!boundWo) { + return { + giftEvidenceImages: [] as string[], + giftEvidenceNote: '', + giftEvidenceSubmittedAt: null as string | null, + } + } + const acceptance = toRecord(boundWo.acceptance_json) + const files = Array.isArray(acceptance?.files) ? (acceptance.files as unknown[]) : [] + const fileUrls = files.map((file) => { + const record = toRecord(file) + return refreshStoredFileUrl( + String(record?.url || record?.mediumUrl || record?.thumbnailUrl || '').trim(), + ) + }) + const rawImageUrls = Array.isArray(acceptance?.imageUrls) ? acceptance.imageUrls : [] + const giftEvidenceImages = [ + ...new Set([ + ...rawImageUrls + .filter((url): url is string => typeof url === 'string' && url.trim().length > 0) + .map((url) => refreshStoredFileUrl(url)), + ...fileUrls.filter(Boolean), + ]), + ] + return { + giftEvidenceImages, + giftEvidenceNote: String(acceptance?.note || '').trim(), + giftEvidenceSubmittedAt: + typeof acceptance?.submittedAt === 'string' ? acceptance.submittedAt : null, + } +} + export function getConsignmentPublishDepositFen() { return getConfiguredConsignmentPublishDepositFen() } @@ -281,6 +325,7 @@ export async function listWorkerConsignments(workerId: number) { ? workOrderById.get(Number(inv.bound_work_order_id)) : undefined const stage = resolveStage(String(sub.status || ''), inv) + const evidence = extractConsignmentGiftEvidenceFromWorkOrder(boundWo) return { /** 唯一锚点:提报单号贯穿发布→审核→在库→匹配→冷却→赠送→结算 */ consignmentNo: sub.submission_no, @@ -300,6 +345,9 @@ export async function listWorkerConsignments(workerId: number) { screenshotsJson: (Array.isArray(sub.screenshots_json) ? sub.screenshots_json : []) .filter((url): url is string => typeof url === 'string' && url.trim().length > 0) .map((url) => refreshStoredFileUrl(url)), + giftEvidenceImages: evidence.giftEvidenceImages, + giftEvidenceNote: evidence.giftEvidenceNote, + giftEvidenceSubmittedAt: evidence.giftEvidenceSubmittedAt, createdAt: sub.created_at, ...stage, /** 冷却截止时间,前端拿它显示倒计时 */ @@ -318,6 +366,7 @@ export async function listWorkerConsignments(workerId: number) { ? workOrderById.get(Number(inv.bound_work_order_id)) : undefined const stage = resolveStage('approved', inv) + const evidence = extractConsignmentGiftEvidenceFromWorkOrder(boundWo) return { consignmentNo: '客服手工入库', ...(stage.canConfirmFriend || stage.canSubmitEvidence @@ -336,6 +385,9 @@ export async function listWorkerConsignments(workerId: number) { depositAmountFen: inv.deposit_amount_fen, reviewNote: '', screenshotsJson: [], + giftEvidenceImages: evidence.giftEvidenceImages, + giftEvidenceNote: evidence.giftEvidenceNote, + giftEvidenceSubmittedAt: evidence.giftEvidenceSubmittedAt, createdAt: inv.created_at, ...stage, giftAvailableAt: inv.bound_work_order_id diff --git a/apps/frontend/src/pages/worker/WorkerConsignmentPage.tsx b/apps/frontend/src/pages/worker/WorkerConsignmentPage.tsx index 860e3157..c5b159c8 100644 --- a/apps/frontend/src/pages/worker/WorkerConsignmentPage.tsx +++ b/apps/frontend/src/pages/worker/WorkerConsignmentPage.tsx @@ -393,7 +393,11 @@ export default function WorkerConsignmentPage() { setEvidenceSubmitting(true) try { const note = evidenceNote.trim() - const payload = note ? { imageUrls: urls, note } : { imageUrls: urls } + const payload = { + files: evidenceFiles, + imageUrls: urls, + ...(note ? { note } : {}), + } await submitWorkerConsignmentGiftEvidence(submissionId, payload) message.success('赠送截图已提交,等待客服验收') closeEvidenceModal() @@ -1044,6 +1048,9 @@ function ConsignmentRecordCard({ const shots = Array.isArray(record.screenshotsJson) ? record.screenshotsJson.filter((s): s is string => typeof s === 'string') : [] + const giftShots = Array.isArray(record.giftEvidenceImages) + ? record.giftEvidenceImages.filter((s): s is string => typeof s === 'string') + : [] // 只有后端把阶段标成 gift_cooldown 时才显示倒计时,前端不自己推阶段 const cooldown = record.stage === 'gift_cooldown' && record.giftAvailableAt @@ -1096,7 +1103,35 @@ function ConsignmentRecordCard({ ) : null} ) : null} - {shots.length > 0 ? ( + {giftShots.length > 0 ? ( +
+ + + 已提交赠送凭证(共 {giftShots.length} 张截图,等待客服验收): + +
+ + + {giftShots.map((src, i) => ( + + ))} + + +
+ {record.giftEvidenceNote ? ( + + 赠送备注:{record.giftEvidenceNote} + + ) : null} +
+ ) : null} + {shots.length > 0 && giftShots.length === 0 ? (
diff --git a/apps/frontend/src/services/worker.ts b/apps/frontend/src/services/worker.ts index 405c14ae..16b1f563 100644 --- a/apps/frontend/src/services/worker.ts +++ b/apps/frontend/src/services/worker.ts @@ -432,6 +432,9 @@ export type WorkerConsignmentRecord = { depositAmountFen: number reviewNote: string screenshotsJson: string[] + giftEvidenceImages?: string[] + giftEvidenceNote?: string + giftEvidenceSubmittedAt?: string | null createdAt: string stage: WorkerConsignmentStage /** 中文阶段名,如「已匹配-待加好友」 */