feat(order-service): 客户页展示验收图片与订单进度

This commit is contained in:
yml2213
2026-11-04 12:50:56 +08:00
parent a276256d84
commit 3f7a3ab5d1
5 changed files with 320 additions and 11 deletions
@@ -3,10 +3,45 @@ import test from 'node:test'
import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
import {
resolveOrderServiceAcceptanceImageUrls,
resolveOrderServiceStatusLabel,
resolveOrderServiceStatusMessage,
} from './order-service-public-service.js'
test('客户页验收图片:仅交付相关状态展示图片,并按文件对象去重', () => {
const acceptanceJson = {
files: [
{
objectKey: 'worker-acceptance/a.webp',
url: '/api/v1/files/object?key=worker-acceptance%2Fa.webp',
thumbnailUrl: '/api/v1/files/object?key=worker-acceptance%2Fa.thumb.jpg',
mediumUrl: '/api/v1/files/object?key=worker-acceptance%2Fa.medium.jpg',
contentType: 'image/webp',
},
{
objectKey: 'worker-acceptance/a-video.mp4',
url: 'https://example.com/a-video.mp4',
contentType: 'video/mp4',
},
],
imageUrls: ['/api/v1/files/object?key=worker-acceptance%2Fa.webp'],
}
assert.deepEqual(
resolveOrderServiceAcceptanceImageUrls({
status: WORK_ORDER_STATUS.ACCEPTED,
acceptanceJson,
}),
['/api/v1/files/object?key=worker-acceptance%2Fa.medium.jpg'],
)
assert.deepEqual(
resolveOrderServiceAcceptanceImageUrls({
status: WORK_ORDER_STATUS.IN_PROGRESS,
acceptanceJson,
}),
[],
)
})
test('订单服务状态文案:待抢单提示高峰期等待', () => {
const message = resolveOrderServiceStatusMessage({
status: WORK_ORDER_STATUS.OPEN,
@@ -21,7 +21,9 @@ import { normalizeSubmittedFields, normalizeUploadedFiles } from './worker-input
import {
ORDER_SERVICE_SCREENSHOT_CONTENT_TYPES,
buildFileUrl,
extractObjectKeyFromFileUrl,
refreshUploadedFileUrls,
refreshStoredFileUrl,
type UploadedFileDto,
} from '../file-storage/file-storage-service.js'
import { enqueueCustomerOrderNotification } from '../notification/customer-order-notification-service.js'
@@ -43,6 +45,7 @@ import {
enqueueOrderServiceAutoPublish,
processDueOrderServiceAutoPublishJobs,
} from './order-service-auto-publish-service.js'
import { mapAcceptanceForResponse } from './work-order-material-mappers.js'
const PUBLIC_STATUS_MESSAGES: Record<string, string> = {
[WORK_ORDER_STATUS.PENDING_MATERIAL]: '请补充订单资料。',
@@ -87,8 +90,18 @@ export type OrderServicePublicItem = {
screenshots?: Array<Record<string, unknown>>
screenshotsRule?: OrderServiceProfileConfig['screenshots']
guideImage?: Record<string, unknown>
acceptanceImageUrls?: string[]
submittedAt?: string
acceptanceSubmittedAt?: string
acceptedAt?: string
}
const PUBLIC_ACCEPTANCE_IMAGE_STATUSES = new Set<string>([
WORK_ORDER_STATUS.PENDING_ACCEPTANCE,
WORK_ORDER_STATUS.ACCEPTED,
WORK_ORDER_STATUS.PROBLEM,
])
export function resolveOrderServiceStatusLabel(status: string, fallback = '处理中') {
return PUBLIC_STATUS_LABELS[String(status || '').trim()] || fallback
}
@@ -106,6 +119,47 @@ export function resolveOrderServiceStatusMessage(input: {
)
}
export function resolveOrderServiceAcceptanceImageUrls(input: {
status: string
acceptanceJson: unknown
}) {
if (!PUBLIC_ACCEPTANCE_IMAGE_STATUSES.has(String(input.status || '').trim())) return []
const acceptance = mapAcceptanceForResponse(input.acceptanceJson)
const files = Array.isArray(acceptance.files) ? acceptance.files : []
const imageUrls = Array.isArray(acceptance.imageUrls) ? acceptance.imageUrls : []
const seenObjects = new Set<string>()
const urls: string[] = []
for (const file of files) {
const source =
file && typeof file === 'object' && !Array.isArray(file)
? (file as Record<string, unknown>)
: {}
const objectKey = String(source.objectKey || '').trim()
const url = String(source.url || '').trim()
const contentType = String(source.contentType || '').trim()
const imageLike =
contentType.startsWith('image/') ||
/\.(?:jpe?g|png|webp|gif)$/i.test(objectKey || extractObjectKeyFromFileUrl(url))
if (!imageLike) continue
const identity = objectKey || extractObjectKeyFromFileUrl(url) || url
if (!identity || seenObjects.has(identity)) continue
seenObjects.add(identity)
const displayUrl = String(source.mediumUrl || source.thumbnailUrl || url).trim()
if (displayUrl) urls.push(displayUrl)
}
for (const rawUrl of imageUrls) {
const url = refreshStoredFileUrl(String(rawUrl || '').trim())
if (!url) continue
const identity = extractObjectKeyFromFileUrl(url) || url
if (seenObjects.has(identity)) continue
seenObjects.add(identity)
urls.push(url)
}
return urls
}
export async function getOrderServiceByPlatformOrderId(platformOrderIdInput: unknown) {
const platformOrderId = normalizeOrderNo(platformOrderIdInput)
const orders = await listWorkOrdersByPlatformOrderId(platformOrderId)
@@ -361,6 +415,13 @@ async function mapPublicOrderItem(order: WorkOrderRow): Promise<OrderServicePubl
const material = safeParseJson(order.material_json)
const collect = material.collect && typeof material.collect === 'object' ? material.collect : {}
const values = normalizeSubmittedFields(collect.fields || {})
const rawAcceptance = safeParseJson(order.acceptance_json)
const submittedAt = String(collect.submittedAt || '').trim()
const acceptanceSubmittedAt = String(rawAcceptance.submittedAt || '').trim()
const acceptanceImageUrls = resolveOrderServiceAcceptanceImageUrls({
status: order.status,
acceptanceJson: order.acceptance_json,
})
const screenshots = normalizeUploadedFiles(collect.screenshots || [])
.map(refreshUploadedFileUrls)
.filter(Boolean)
@@ -383,6 +444,10 @@ async function mapPublicOrderItem(order: WorkOrderRow): Promise<OrderServicePubl
}),
canSubmit: state.canSubmit,
blockedReason: state.blockedReason,
...(submittedAt ? { submittedAt } : {}),
...(acceptanceSubmittedAt ? { acceptanceSubmittedAt } : {}),
...(order.accepted_at ? { acceptedAt: String(order.accepted_at) } : {}),
...(acceptanceImageUrls.length > 0 ? { acceptanceImageUrls } : {}),
...(profile?.materialConfirmation
? { materialConfirmation: profile.materialConfirmation }
: {}),