feat(order-service): 重构分类资料收集与订单状态链路
This commit is contained in:
@@ -1108,3 +1108,129 @@ export function revokeAdminWorkerRiskBan(banId: string, reason?: string) {
|
||||
{ reason },
|
||||
)
|
||||
}
|
||||
|
||||
export type OrderServiceField = {
|
||||
key: string
|
||||
label: string
|
||||
type: 'text' | 'textarea' | 'select' | 'radio' | 'number' | 'date'
|
||||
required: boolean
|
||||
placeholder?: string
|
||||
helpText?: string
|
||||
options?: string[]
|
||||
sortOrder?: number
|
||||
}
|
||||
|
||||
export type AdminOrderServiceProfile = {
|
||||
id: number
|
||||
categoryId: number
|
||||
name: string
|
||||
title: string
|
||||
description: string
|
||||
guideImage: UploadedFile | Record<string, unknown>
|
||||
fields: OrderServiceField[]
|
||||
screenshots: {
|
||||
enabled: boolean
|
||||
required: boolean
|
||||
minCount: number
|
||||
maxCount: number
|
||||
label: string
|
||||
helpText?: string
|
||||
}
|
||||
autoPublish: {
|
||||
enabled: boolean
|
||||
requireVoucherConsume: boolean
|
||||
retryMax: number
|
||||
}
|
||||
submitText: string
|
||||
successText: string
|
||||
status: 'draft' | 'published' | 'archived' | string
|
||||
currentVersionNo: number | null
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type AdminOrderServiceCategory = {
|
||||
categoryId: number
|
||||
categoryKey: string
|
||||
name: string
|
||||
parentId?: number | null
|
||||
status: string
|
||||
}
|
||||
|
||||
export type AdminOrderServiceProfileList = {
|
||||
categories: AdminOrderServiceCategory[]
|
||||
profiles: AdminOrderServiceProfile[]
|
||||
}
|
||||
|
||||
export function fetchAdminOrderServiceProfiles() {
|
||||
return apiGet<AdminOrderServiceProfileList>(
|
||||
'/api/v1/admin/worker-platform/order-service/profiles',
|
||||
)
|
||||
}
|
||||
|
||||
export function saveAdminOrderServiceProfile(payload: {
|
||||
categoryId: number
|
||||
name: string
|
||||
title?: string
|
||||
description?: string
|
||||
guideImage?: UploadedFile | Record<string, unknown>
|
||||
fields: OrderServiceField[]
|
||||
screenshots: {
|
||||
enabled: boolean
|
||||
required: boolean
|
||||
minCount: number
|
||||
maxCount: number
|
||||
label: string
|
||||
helpText?: string
|
||||
}
|
||||
autoPublish: {
|
||||
enabled: boolean
|
||||
requireVoucherConsume: boolean
|
||||
retryMax: number
|
||||
}
|
||||
submitText?: string
|
||||
successText?: string
|
||||
}) {
|
||||
return apiPost<{ profile: AdminOrderServiceProfile }>(
|
||||
'/api/v1/admin/worker-platform/order-service/profiles',
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function publishAdminOrderServiceProfile(profileId: number) {
|
||||
return apiPost<{ versionNo: number }>(
|
||||
`/api/v1/admin/worker-platform/order-service/profiles/${profileId}/publish`,
|
||||
)
|
||||
}
|
||||
|
||||
export function archiveAdminOrderServiceProfile(profileId: number) {
|
||||
return apiPost<{ profile: AdminOrderServiceProfile }>(
|
||||
`/api/v1/admin/worker-platform/order-service/profiles/${profileId}/archive`,
|
||||
)
|
||||
}
|
||||
|
||||
export type AdminOrderServiceAutoPublishJob = {
|
||||
id: number
|
||||
workOrderId: number
|
||||
status: 'pending' | 'processing' | 'succeeded' | 'failed' | 'skipped' | string
|
||||
attemptCount: number
|
||||
maxAttempts: number
|
||||
nextRunAt: string | null
|
||||
lastError: string
|
||||
productName: string
|
||||
platformOrderId: string
|
||||
workOrderStatus: string
|
||||
updatedAt: string | null
|
||||
}
|
||||
|
||||
export function fetchAdminOrderServiceAutoPublishJobs(status = 'failed') {
|
||||
return apiGet<{ items: AdminOrderServiceAutoPublishJob[] }>(
|
||||
'/api/v1/admin/worker-platform/order-service/auto-publish-jobs',
|
||||
{ status },
|
||||
)
|
||||
}
|
||||
|
||||
export function retryAdminOrderServiceAutoPublishJob(jobId: number) {
|
||||
return apiPost<{ jobId: number }>(
|
||||
`/api/v1/admin/worker-platform/order-service/auto-publish-jobs/${jobId}/retry`,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,44 +5,18 @@ import { optimizeImageForUpload } from '@/utils/image-upload'
|
||||
type UploadScope = 'admin' | 'worker' | 'collect'
|
||||
|
||||
export async function uploadFile(file: File, scene: string, scope: UploadScope) {
|
||||
if (scope === 'collect') {
|
||||
throw new Error('订单服务页面必须使用订单号上传入口')
|
||||
}
|
||||
const uploadTarget = await optimizeImageForUpload(file, scene)
|
||||
const form = new FormData()
|
||||
form.append('file', uploadTarget)
|
||||
form.append('scene', scene)
|
||||
const endpoint =
|
||||
scope === 'admin'
|
||||
? '/api/v1/admin/files/upload'
|
||||
: scope === 'collect'
|
||||
? '/api/v1/collect/upload'
|
||||
: '/api/v1/worker/files/upload'
|
||||
const endpoint = scope === 'admin' ? '/api/v1/admin/files/upload' : '/api/v1/worker/files/upload'
|
||||
const response = await apiPostForm<{ file: UploadedFile }>(endpoint, form)
|
||||
return response.data.file
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次性补资料链接的截图上传。
|
||||
*
|
||||
* 与公开 collect/upload 的区别:路径里带平台订单号,多工单时再带 workOrderId;
|
||||
* 后端会校验对应链接是否仍可用,链接已使用/过期时不会落文件。
|
||||
*/
|
||||
export async function uploadCollectMaterialLinkFile(
|
||||
file: File,
|
||||
scene: string,
|
||||
code: string,
|
||||
workOrderId?: number,
|
||||
) {
|
||||
const uploadTarget = await optimizeImageForUpload(file, scene)
|
||||
const form = new FormData()
|
||||
form.append('file', uploadTarget)
|
||||
form.append('scene', scene)
|
||||
const query = workOrderId ? `?workOrderId=${encodeURIComponent(String(workOrderId))}` : ''
|
||||
const response = await apiPostForm<{ file: UploadedFile }>(
|
||||
`/api/v1/collect/link/${encodeURIComponent(code)}/upload${query}`,
|
||||
form,
|
||||
)
|
||||
return response.data.file
|
||||
}
|
||||
|
||||
/**
|
||||
* 验收视频上传:视频已在浏览器内压好,这里不再做图片压缩。
|
||||
* 带上 durationSeconds 供服务端在容器解析不到时长时兜底(见 resolveVideoDuration)。
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { apiDelete, apiGet, apiPost, apiPut } from '@/lib/http'
|
||||
import { apiDelete, apiGet, apiPost, apiPostForm, apiPut } from '@/lib/http'
|
||||
import { optimizeImageForUpload } from '@/utils/image-upload'
|
||||
import type {
|
||||
CollectLookupResponse,
|
||||
CollectMaterialLinkInfo,
|
||||
CollectMaterialLinkSubmitResult,
|
||||
UploadedFile,
|
||||
WorkOrder,
|
||||
WorkOrderEvent,
|
||||
@@ -372,40 +370,6 @@ export function supplementWorkerAcceptedOrderEvidence(
|
||||
)
|
||||
}
|
||||
|
||||
export function lookupCollectOrder(orderNo: string) {
|
||||
return apiPost<CollectLookupResponse>('/api/v1/collect/lookup', { orderNo })
|
||||
}
|
||||
|
||||
export function submitCollectOrder(payload: {
|
||||
orderNo: string
|
||||
workOrderId?: number
|
||||
fields: Record<string, string>
|
||||
screenshots?: UploadedFile[]
|
||||
}) {
|
||||
return apiPost<{ complete: boolean; order: Record<string, unknown> }>(
|
||||
'/api/v1/collect/submit',
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchCollectMaterialLink(code: string) {
|
||||
return apiGet<CollectMaterialLinkInfo>(`/api/v1/collect/link/${encodeURIComponent(code)}`)
|
||||
}
|
||||
|
||||
export function submitCollectMaterialLink(
|
||||
code: string,
|
||||
payload: {
|
||||
workOrderId?: number
|
||||
fields: Record<string, string>
|
||||
screenshots?: UploadedFile[]
|
||||
},
|
||||
) {
|
||||
return apiPost<CollectMaterialLinkSubmitResult>(
|
||||
`/api/v1/collect/link/${encodeURIComponent(code)}/submit`,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------- 打手端:我要寄卖与手枪库存 ----------------
|
||||
|
||||
export type WorkerConsignmentPublishPayload = {
|
||||
@@ -591,3 +555,91 @@ export function fetchWorkerConsignmentPriceSuggestion(params: {
|
||||
matchedRules: { ruleKey: string; productName: string; rewardAmountFen: number }[]
|
||||
}>('/api/v1/worker/consignment/price-suggestion', params)
|
||||
}
|
||||
|
||||
export type OrderServiceField = {
|
||||
key: string
|
||||
label: string
|
||||
type: 'text' | 'textarea' | 'select' | 'radio' | 'number' | 'date'
|
||||
required: boolean
|
||||
placeholder?: string
|
||||
helpText?: string
|
||||
options?: string[]
|
||||
sortOrder?: number
|
||||
}
|
||||
|
||||
export type OrderServiceScreenshots = {
|
||||
enabled: boolean
|
||||
required: boolean
|
||||
minCount: number
|
||||
maxCount: number
|
||||
label: string
|
||||
helpText?: string
|
||||
}
|
||||
|
||||
export type OrderServiceItem = {
|
||||
workOrderId: number
|
||||
productName: string
|
||||
categoryName: string
|
||||
title: string
|
||||
description: string
|
||||
submitText: string
|
||||
successText: string
|
||||
state: string
|
||||
stateLabel: string
|
||||
message: string
|
||||
canSubmit: boolean
|
||||
blockedReason: '' | 'already_submitted' | 'status' | 'assigned' | 'shares'
|
||||
fields?: OrderServiceField[]
|
||||
values?: Record<string, string>
|
||||
screenshots?: UploadedFile[]
|
||||
screenshotsRule?: OrderServiceScreenshots
|
||||
guideImage?: UploadedFile | Record<string, unknown>
|
||||
}
|
||||
|
||||
export type OrderServiceInfo = {
|
||||
platformOrderId: string
|
||||
items: OrderServiceItem[]
|
||||
pendingCount: number
|
||||
}
|
||||
|
||||
export type OrderServiceSubmitResult = {
|
||||
complete: boolean
|
||||
workOrderId: number
|
||||
successText: string
|
||||
order: { platformOrderId: string; productName: string; status: string }
|
||||
}
|
||||
|
||||
export function fetchOrderService(orderNo: string) {
|
||||
return apiGet<OrderServiceInfo>(`/api/v1/o/${encodeURIComponent(orderNo)}`)
|
||||
}
|
||||
|
||||
export function submitOrderServiceMaterial(
|
||||
orderNo: string,
|
||||
payload: {
|
||||
workOrderId?: number
|
||||
fields: Record<string, string>
|
||||
screenshots?: UploadedFile[]
|
||||
},
|
||||
) {
|
||||
return apiPost<OrderServiceSubmitResult>(
|
||||
`/api/v1/o/${encodeURIComponent(orderNo)}/material`,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export async function uploadOrderServiceFile(
|
||||
file: File,
|
||||
scene: string,
|
||||
orderNo: string,
|
||||
workOrderId?: number,
|
||||
) {
|
||||
const optimized = await optimizeImageForUpload(file, scene)
|
||||
const form = new FormData()
|
||||
form.append('file', optimized)
|
||||
form.append('scene', scene)
|
||||
const query = workOrderId ? `?workOrderId=${encodeURIComponent(String(workOrderId))}` : ''
|
||||
return apiPostForm<{ file: UploadedFile }>(
|
||||
`/api/v1/o/${encodeURIComponent(orderNo)}/files${query}`,
|
||||
form,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user