feat(order-service): 支持自定义订单状态节点文案

This commit is contained in:
yml2213
2026-11-04 13:04:52 +08:00
parent 4a7cc2c6fe
commit 3969703ac8
12 changed files with 226 additions and 12 deletions
@@ -0,0 +1,8 @@
-- 137_order_service_status_steps.sql
-- 客户订单状态进度的四个节点文案,可按分类独立配置。
ALTER TABLE order_service_profiles
ADD COLUMN IF NOT EXISTS status_steps_json JSONB NOT NULL DEFAULT '{}'::jsonb;
COMMENT ON COLUMN order_service_profiles.status_steps_json IS
'客户状态进度文案:submitted/scheduled/processing/completed 的 title 与 description';
@@ -13,6 +13,7 @@ export type OrderServiceProfileRow = {
screenshots_json: Record<string, unknown>
auto_publish_json: Record<string, unknown>
material_confirmation_json: Record<string, unknown>
status_steps_json: Record<string, unknown>
submit_text: string
success_text: string
post_submit_text: string
@@ -101,6 +102,7 @@ export async function upsertOrderServiceProfile(input: {
screenshots: Record<string, unknown>
autoPublish: Record<string, unknown>
materialConfirmation: Record<string, unknown>
statusSteps: Record<string, unknown>
submitText: string
successText: string
postSubmitText: string
@@ -111,12 +113,12 @@ export async function upsertOrderServiceProfile(input: {
INSERT INTO order_service_profiles (
category_id, name, title, description,
guide_image_json, fields_json, screenshots_json, auto_publish_json,
material_confirmation_json,
material_confirmation_json, status_steps_json,
submit_text, success_text, post_submit_text, status, created_by_admin_user_id
) VALUES (
$1, $2, $3, $4,
$5::jsonb, $6::jsonb, $7::jsonb, $8::jsonb, $9::jsonb,
$10, $11, $12, 'draft', $13
$5::jsonb, $6::jsonb, $7::jsonb, $8::jsonb, $9::jsonb, $10::jsonb,
$11, $12, $13, 'draft', $14
)
ON CONFLICT (category_id) DO UPDATE SET
name = EXCLUDED.name,
@@ -127,6 +129,7 @@ export async function upsertOrderServiceProfile(input: {
screenshots_json = EXCLUDED.screenshots_json,
auto_publish_json = EXCLUDED.auto_publish_json,
material_confirmation_json = EXCLUDED.material_confirmation_json,
status_steps_json = EXCLUDED.status_steps_json,
submit_text = EXCLUDED.submit_text,
success_text = EXCLUDED.success_text,
post_submit_text = EXCLUDED.post_submit_text,
@@ -148,6 +151,7 @@ export async function upsertOrderServiceProfile(input: {
JSON.stringify(input.screenshots),
JSON.stringify(input.autoPublish),
JSON.stringify(input.materialConfirmation),
JSON.stringify(input.statusSteps),
input.submitText,
input.successText,
input.postSubmitText,
@@ -94,6 +94,31 @@ test('订单服务提交后说明:旧配置使用默认文案,新配置支
)
})
test('客户状态进度:四个节点支持独立标题和说明,旧配置使用默认文案', () => {
assert.deepEqual(config().statusSteps, {
submitted: { title: '提交资料', description: '' },
scheduled: { title: '客服安排', description: '' },
processing: { title: '订单处理', description: '' },
completed: { title: '交付完成', description: '' },
})
assert.deepEqual(
config({
statusSteps: {
submitted: { title: '资料已交', description: '请保持联系方式畅通' },
scheduled: { title: '客服受理', description: '客服正在核对订单' },
processing: { title: '正在安排', description: '请耐心等待接单' },
completed: { title: '处理完成', description: '请检查邮箱' },
},
}).statusSteps,
{
submitted: { title: '资料已交', description: '请保持联系方式畅通' },
scheduled: { title: '客服受理', description: '客服正在核对订单' },
processing: { title: '正在安排', description: '请耐心等待接单' },
completed: { title: '处理完成', description: '请检查邮箱' },
},
)
})
test('订单服务提交确认:旧配置默认展示不可修改与资料错误免责声明', () => {
const normalized = config()
assert.deepEqual(normalized.materialConfirmation, {
@@ -57,6 +57,13 @@ export type OrderServiceMaterialConfirmation = {
cancelText: string
}
export type OrderServiceStatusSteps = {
submitted: { title: string; description: string }
scheduled: { title: string; description: string }
processing: { title: string; description: string }
completed: { title: string; description: string }
}
export type OrderServiceProfileConfig = {
name: string
title: string
@@ -66,6 +73,7 @@ export type OrderServiceProfileConfig = {
screenshots: OrderServiceScreenshots
autoPublish: OrderServiceAutoPublish
materialConfirmation: OrderServiceMaterialConfirmation
statusSteps: OrderServiceStatusSteps
submitText: string
successText: string
postSubmitText: string
@@ -99,6 +107,13 @@ export const DEFAULT_ORDER_SERVICE_MATERIAL_CONFIRMATION: OrderServiceMaterialCo
export const DEFAULT_ORDER_SERVICE_POST_SUBMIT_TEXT = '资料提交后如需修改,请联系客服。'
export const DEFAULT_ORDER_SERVICE_STATUS_STEPS: OrderServiceStatusSteps = {
submitted: { title: '提交资料', description: '' },
scheduled: { title: '客服安排', description: '' },
processing: { title: '订单处理', description: '' },
completed: { title: '交付完成', description: '' },
}
export async function listAdminOrderServiceProfiles() {
const [categories, profiles] = await Promise.all([
listAllWorkCategories(),
@@ -141,6 +156,7 @@ export async function saveAdminOrderServiceProfile(payload: JsonObject = {}) {
const materialConfirmation = normalizeMaterialConfirmation(
payload.materialConfirmation ?? payload.material_confirmation_json,
)
const statusSteps = normalizeStatusSteps(payload.statusSteps ?? payload.status_steps_json)
const guideImage = normalizeGuideImage(payload.guideImage ?? payload.guide_image_json)
const adminUserId = Number(payload.adminUserId ?? payload.admin_user_id ?? 0) || null
@@ -154,6 +170,7 @@ export async function saveAdminOrderServiceProfile(payload: JsonObject = {}) {
screenshots,
autoPublish,
materialConfirmation,
statusSteps,
submitText: text(payload.submitText, 30) || '提交资料',
successText: text(payload.successText, 80) || '资料已提交,请等待处理',
postSubmitText: text(payload.postSubmitText, 500) || DEFAULT_ORDER_SERVICE_POST_SUBMIT_TEXT,
@@ -233,6 +250,7 @@ function mapAdminProfile(row: OrderServiceProfileRow) {
screenshots: row.screenshots_json,
autoPublish: row.auto_publish_json,
materialConfirmation: normalizeMaterialConfirmation(row.material_confirmation_json),
statusSteps: normalizeStatusSteps(row.status_steps_json),
submitText: row.submit_text,
successText: row.success_text,
postSubmitText: buildProfileConfig(row).postSubmitText,
@@ -253,6 +271,7 @@ export function buildProfileConfig(row: {
screenshots_json: unknown
auto_publish_json: unknown
material_confirmation_json: unknown
status_steps_json: unknown
submit_text: string
success_text: string
post_submit_text: string
@@ -266,6 +285,7 @@ export function buildProfileConfig(row: {
screenshots: normalizeScreenshots(row.screenshots_json),
autoPublish: normalizeAutoPublish(row.auto_publish_json),
materialConfirmation: normalizeMaterialConfirmation(row.material_confirmation_json),
statusSteps: normalizeStatusSteps(row.status_steps_json),
submitText: row.submit_text || '提交资料',
successText: row.success_text || '资料已提交,请等待处理',
postSubmitText: text(row.post_submit_text, 500) || DEFAULT_ORDER_SERVICE_POST_SUBMIT_TEXT,
@@ -319,6 +339,7 @@ export function normalizeProfileConfig(value: JsonObject): OrderServiceProfileCo
screenshots: normalizeScreenshots(value.screenshots),
autoPublish: normalizeAutoPublish(value.autoPublish),
materialConfirmation: normalizeMaterialConfirmation(value.materialConfirmation),
statusSteps: normalizeStatusSteps(value.statusSteps),
submitText: text(value.submitText, 30) || '提交资料',
successText: text(value.successText, 80) || '资料已提交,请等待处理',
postSubmitText: text(value.postSubmitText, 500) || DEFAULT_ORDER_SERVICE_POST_SUBMIT_TEXT,
@@ -417,6 +438,27 @@ function normalizeMaterialConfirmation(value: unknown): OrderServiceMaterialConf
}
}
function normalizeStatusSteps(value: unknown): OrderServiceStatusSteps {
const source = value && typeof value === 'object' ? (value as JsonObject) : {}
return {
submitted: normalizeStatusStep(source.submitted, DEFAULT_ORDER_SERVICE_STATUS_STEPS.submitted),
scheduled: normalizeStatusStep(source.scheduled, DEFAULT_ORDER_SERVICE_STATUS_STEPS.scheduled),
processing: normalizeStatusStep(
source.processing,
DEFAULT_ORDER_SERVICE_STATUS_STEPS.processing,
),
completed: normalizeStatusStep(source.completed, DEFAULT_ORDER_SERVICE_STATUS_STEPS.completed),
}
}
function normalizeStatusStep(value: unknown, fallback: { title: string; description: string }) {
const source = value && typeof value === 'object' ? (value as JsonObject) : {}
return {
title: text(source.title, 20) || fallback.title,
description: text(source.description, 80),
}
}
function normalizeGuideImage(value: unknown): Record<string, unknown> {
const normalized = normalizeUploadedFiles([value])[0]
return normalized ? { ...normalized } : {}
@@ -36,6 +36,8 @@ import {
import {
DEFAULT_ORDER_SERVICE_MATERIAL_CONFIRMATION,
DEFAULT_ORDER_SERVICE_POST_SUBMIT_TEXT,
DEFAULT_ORDER_SERVICE_STATUS_STEPS,
getPublishedOrderServiceProfileSnapshotByCategory,
getOrderServiceProfileSnapshotByVersion,
normalizeProfileConfig,
type OrderServiceField,
@@ -85,6 +87,7 @@ export type OrderServicePublicItem = {
canSubmit: boolean
blockedReason: string
materialConfirmation?: OrderServiceProfileConfig['materialConfirmation']
statusSteps: OrderServiceProfileConfig['statusSteps']
fields?: OrderServiceField[]
values?: Record<string, string>
screenshots?: Array<Record<string, unknown>>
@@ -412,6 +415,9 @@ async function resolveTarget(platformOrderId: string, workOrderIdInput: unknown)
async function mapPublicOrderItem(order: WorkOrderRow): Promise<OrderServicePublicItem> {
const state = resolvePublicMaterialOrderState(order)
const profile = await resolveProfileConfig(order)
const currentPresentationProfile = order.category_id
? await getPublishedOrderServiceProfileSnapshotByCategory(Number(order.category_id))
: null
const material = safeParseJson(order.material_json)
const collect = material.collect && typeof material.collect === 'object' ? material.collect : {}
const values = normalizeSubmittedFields(collect.fields || {})
@@ -434,7 +440,10 @@ async function mapPublicOrderItem(order: WorkOrderRow): Promise<OrderServicePubl
description: profile?.description || '',
submitText: profile?.submitText || '提交资料',
successText: profile?.successText || '资料已提交,请等待处理',
postSubmitText: profile?.postSubmitText || DEFAULT_ORDER_SERVICE_POST_SUBMIT_TEXT,
postSubmitText:
currentPresentationProfile?.config.postSubmitText ||
profile?.postSubmitText ||
DEFAULT_ORDER_SERVICE_POST_SUBMIT_TEXT,
state: order.status,
stateLabel: resolveOrderServiceStatusLabel(order.status, state.statusLabel),
message: resolveOrderServiceStatusMessage({
@@ -451,6 +460,10 @@ async function mapPublicOrderItem(order: WorkOrderRow): Promise<OrderServicePubl
...(profile?.materialConfirmation
? { materialConfirmation: profile.materialConfirmation }
: {}),
statusSteps:
currentPresentationProfile?.config.statusSteps ||
profile?.statusSteps ||
DEFAULT_ORDER_SERVICE_STATUS_STEPS,
}
if (!state.canSubmit) return base
@@ -512,6 +525,7 @@ async function resolveProfileConfig(
},
autoPublish: { enabled: false, requireVoucherConsume: true, retryMax: 3 },
materialConfirmation: DEFAULT_ORDER_SERVICE_MATERIAL_CONFIRMATION,
statusSteps: DEFAULT_ORDER_SERVICE_STATUS_STEPS,
submitText: '提交资料',
successText: '资料已提交,请等待处理',
postSubmitText: DEFAULT_ORDER_SERVICE_POST_SUBMIT_TEXT,