fix(worker-consignment): 修复卖家提交赠送截图后在接单平台无法回显的问题
1. 后端 listWorkerConsignments 接口从关联工单验收资料中提取 giftEvidenceImages、giftEvidenceNote、giftEvidenceSubmittedAt 并在打手端寄卖列表中下发; 2. 接单平台「我的寄卖」卡片新增已提交赠送凭证展示模块,支持展示缩略图、张数统计、大图预览及赠送备注; 3. 优化提报单与库存、工单的 SQL 关联兼容双向绑定,确保后台与前台均能稳定查到赠送凭证。
This commit is contained in:
@@ -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
|
||||
`
|
||||
|
||||
@@ -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<string, unknown> | null {
|
||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
return value as Record<string, unknown>
|
||||
}
|
||||
const parsed = safeParseJson(value)
|
||||
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
||||
? (parsed as Record<string, unknown>)
|
||||
: 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
|
||||
|
||||
@@ -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}
|
||||
</Space>
|
||||
) : null}
|
||||
{shots.length > 0 ? (
|
||||
{giftShots.length > 0 ? (
|
||||
<div className="worker-consignment-gift-shots-wrap" style={{ marginTop: 8, padding: '8px 12px', background: '#f0fdf4', borderRadius: 6, border: '1px solid #bbf7d0' }}>
|
||||
<Typography.Text strong style={{ fontSize: 13, color: '#15803d', display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<CheckCircleOutlined />
|
||||
已提交赠送凭证(共 {giftShots.length} 张截图,等待客服验收):
|
||||
</Typography.Text>
|
||||
<div style={{ marginTop: 6 }}>
|
||||
<Image.PreviewGroup>
|
||||
<Space size={8} wrap>
|
||||
{giftShots.map((src, i) => (
|
||||
<Image
|
||||
key={`gift-shot-${i}`}
|
||||
src={src}
|
||||
width={52}
|
||||
height={52}
|
||||
style={{ objectFit: 'cover', borderRadius: 6, border: '1px solid #86efac' }}
|
||||
/>
|
||||
))}
|
||||
</Space>
|
||||
</Image.PreviewGroup>
|
||||
</div>
|
||||
{record.giftEvidenceNote ? (
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12, display: 'block', marginTop: 4 }}>
|
||||
赠送备注:{record.giftEvidenceNote}
|
||||
</Typography.Text>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{shots.length > 0 && giftShots.length === 0 ? (
|
||||
<div className="worker-consignment-record-shots">
|
||||
<Image.PreviewGroup>
|
||||
<Space size={8}>
|
||||
|
||||
@@ -432,6 +432,9 @@ export type WorkerConsignmentRecord = {
|
||||
depositAmountFen: number
|
||||
reviewNote: string
|
||||
screenshotsJson: string[]
|
||||
giftEvidenceImages?: string[]
|
||||
giftEvidenceNote?: string
|
||||
giftEvidenceSubmittedAt?: string | null
|
||||
createdAt: string
|
||||
stage: WorkerConsignmentStage
|
||||
/** 中文阶段名,如「已匹配-待加好友」 */
|
||||
|
||||
Reference in New Issue
Block a user