拆分后台模拟工单服务

This commit is contained in:
yml2213
2026-08-21 17:19:04 +08:00
parent 0031df580a
commit eeedb4a4da
3 changed files with 189 additions and 170 deletions
@@ -0,0 +1,184 @@
import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
import { createWorkOrder, createWorkOrderEvent } from '../../repositories/worker-platform/index.js'
import type { JsonObject } from '../../types/json.js'
import { createHttpError } from '../../utils/http.js'
import { randomId } from '../../utils/random.js'
import { nowIso } from '../../utils/time.js'
import {
getKuaishouIndustrySourceConfig,
listKuaishouIndustryShopConfigs,
type KuaishouIndustryShopConfig,
} from '../platforms/kuaishou-industry/source-config-service.js'
import {
DEFAULT_DEPOSIT_THRESHOLD_AMOUNT,
mapWorkOrderAdmin,
normalizeAmountFen,
normalizeInteger,
normalizeRequirementFields,
} from './mappers.js'
import { ensureWorkerPlatformDefaults } from './worker-platform-defaults.js'
import { normalizeWorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
export async function createAdminMockWorkOrder(payload: JsonObject = {}) {
const defaults = await ensureWorkerPlatformDefaults()
const now = nowIso()
const input = resolveAdminMockWorkOrderInput(
payload,
listKuaishouIndustryShopConfigs(getKuaishouIndustrySourceConfig()),
)
const rewardAmount = input.rewardAmount
const depositThresholdAmount = normalizeAmountFen(
payload.depositThresholdAmount ?? payload.depositThresholdAmountYuan,
DEFAULT_DEPOSIT_THRESHOLD_AMOUNT,
)
const requiredDepositAmount =
payload.requiredDepositAmount === undefined
? rewardAmount > depositThresholdAmount
? rewardAmount
: 0
: normalizeAmountFen(payload.requiredDepositAmount, 0)
const paymentFen = input.paymentAmount
const materialComplete = payload.materialComplete === true || payload.materialComplete === 'true'
const fields = normalizeRequirementFields(payload.fields)
const { platformOrderId, productName, shop } = input
const material = {
...(materialComplete
? {
collect: {
complete: true,
submittedAt: now,
fields: fields.reduce<Record<string, string>>((result, field) => {
result[field.key] = String(field.mockValue || `${field.label}测试值`)
return result
}, {}),
},
}
: {}),
source: {
orderAmountFen: paymentFen,
syncSource: 'mock',
mock: true,
provider: 'mock',
platform: 'mock',
sellerId: String(shop.sellerId || shop.shopId || '').trim(),
shopId: String(shop.shopId || shop.sellerId || '').trim(),
shopName: String(
shop.customShopName || shop.shopName || shop.shopId || shop.sellerId || '',
).trim(),
},
}
const workOrder = await createWorkOrder({
workOrderNo: randomId('WO'),
platformOrderId,
productName,
categoryId: defaults.category?.id || null,
status: materialComplete ? WORK_ORDER_STATUS.UNASSIGNED : WORK_ORDER_STATUS.PENDING_MATERIAL,
rewardAmount,
requiredDepositAmount,
depositThresholdAmount,
timeoutMinutes: Math.max(0, normalizeInteger(payload.timeoutMinutes, 0)),
timeoutPolicy: normalizeWorkOrderTimeoutPolicy(payload.timeoutPolicy),
materialJson: JSON.stringify(material),
requirementJson: JSON.stringify({ fields }),
now,
})
if (!workOrder) {
throw createHttpError('接单工单创建失败', {
statusCode: 500,
errorCode: 'work_order_create_failed',
})
}
await createWorkOrderEvent({
workOrderId: workOrder.id,
actorType: 'admin',
actorId: 'dev_mock',
eventType: 'mock_created',
fromStatus: '',
toStatus: workOrder.status,
payloadJson: JSON.stringify({
platformOrderId,
productName,
rewardAmount,
sellerId: String(shop.sellerId || shop.shopId || '').trim(),
}),
now,
})
return { order: mapWorkOrderAdmin(workOrder) }
}
/** 本地 Mock 与真实订单保持相同的关键字段约束,避免生成无法追溯的测试工单。 */
export function resolveAdminMockWorkOrderInput(
payload: JsonObject,
shops: KuaishouIndustryShopConfig[],
) {
const platformOrderId = String(payload.platformOrderId || '').trim()
if (!platformOrderId) {
throw createHttpError('请填写订单号', {
statusCode: 400,
errorCode: 'mock_work_order_no_required',
})
}
const productName = String(payload.productName || '').trim()
if (!productName) {
throw createHttpError('请填写商品名称', {
statusCode: 400,
errorCode: 'mock_work_order_product_name_required',
})
}
const shopId = String(payload.sellerId || payload.shopId || '').trim()
if (!shopId) {
throw createHttpError('请选择店铺', {
statusCode: 400,
errorCode: 'mock_work_order_shop_required',
})
}
const shop =
shops.find(
(item) =>
String(item.sellerId || item.shopId || '').trim() === shopId ||
String(item.shopId || '').trim() === shopId,
) || null
if (!shop) {
throw createHttpError('所选店铺不存在', {
statusCode: 400,
errorCode: 'mock_work_order_shop_invalid',
})
}
if (shop.enabled === false) {
throw createHttpError('所选店铺已停用', {
statusCode: 409,
errorCode: 'mock_work_order_shop_disabled',
})
}
return {
platformOrderId,
productName,
shop,
rewardAmount: requireMockWorkOrderAmount(payload.rewardAmount, '接单金额', 'reward_amount'),
paymentAmount: requireMockWorkOrderAmount(
payload.paymentAmount,
'用户付款金额',
'payment_amount',
),
}
}
function requireMockWorkOrderAmount(value: unknown, label: string, field: string): number {
if (value === undefined || value === null || String(value).trim() === '') {
throw createHttpError(`请填写${label}`, {
statusCode: 400,
errorCode: `mock_work_order_${field}_required`,
})
}
const amount = normalizeAmountFen(value, 0)
if (amount <= 0) {
throw createHttpError(`${label}必须大于 0`, {
statusCode: 400,
errorCode: `mock_work_order_${field}_invalid`,
})
}
return amount
}
@@ -51,7 +51,6 @@ import {
} from './publish-voucher-service.js'
import {
DEFAULT_DEPOSIT_THRESHOLD_AMOUNT,
mapWorkOrderAdmin,
mapWorkOrderShare,
normalizeAmountFen,
@@ -71,15 +70,9 @@ import {
resolveWorkerPermissions,
resolveWorkOrderOperationalStatus,
} from './mappers.js'
import { ensureWorkerPlatformDefaults } from './worker-platform-defaults.js'
import { normalizeWorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
import { getRequiredWorkOrder, getRequiredWorker } from './worker-session-context-service.js'
import { resolveWorkOrderRealtimeWorkerIds } from './admin-work-order-realtime-service.js'
import {
getKuaishouIndustrySourceConfig,
listKuaishouIndustryShopConfigs,
type KuaishouIndustryShopConfig,
} from '../platforms/kuaishou-industry/source-config-service.js'
export {
listAdminWorkerUsers,
@@ -292,169 +285,10 @@ export {
submitAdminWorkOrderMaterial,
} from './admin-work-order-material-service.js'
export async function createAdminMockWorkOrder(payload: JsonObject = {}) {
const defaults = await ensureWorkerPlatformDefaults()
const now = nowIso()
const input = resolveAdminMockWorkOrderInput(
payload,
listKuaishouIndustryShopConfigs(getKuaishouIndustrySourceConfig()),
)
const rewardAmount = input.rewardAmount
const depositThresholdAmount = normalizeAmountFen(
payload.depositThresholdAmount ?? payload.depositThresholdAmountYuan,
DEFAULT_DEPOSIT_THRESHOLD_AMOUNT,
)
const requiredDepositAmount =
payload.requiredDepositAmount === undefined
? rewardAmount > depositThresholdAmount
? rewardAmount
: 0
: normalizeAmountFen(payload.requiredDepositAmount, 0)
const paymentFen = input.paymentAmount
const materialComplete = payload.materialComplete === true || payload.materialComplete === 'true'
const fields = normalizeRequirementFields(payload.fields)
const { platformOrderId, productName, shop } = input
const material = {
...(materialComplete
? {
collect: {
complete: true,
submittedAt: now,
fields: fields.reduce<Record<string, string>>((result, field) => {
result[field.key] = String(field.mockValue || `${field.label}测试值`)
return result
}, {}),
},
}
: {}),
source: {
orderAmountFen: paymentFen,
syncSource: 'mock',
mock: true,
provider: 'mock',
platform: 'mock',
sellerId: String(shop.sellerId || shop.shopId || '').trim(),
shopId: String(shop.shopId || shop.sellerId || '').trim(),
shopName: String(
shop.customShopName || shop.shopName || shop.shopId || shop.sellerId || '',
).trim(),
},
}
const workOrder = await createWorkOrder({
workOrderNo: randomId('WO'),
platformOrderId,
productName,
categoryId: defaults.category?.id || null,
status: materialComplete ? WORK_ORDER_STATUS.UNASSIGNED : WORK_ORDER_STATUS.PENDING_MATERIAL,
rewardAmount,
requiredDepositAmount,
depositThresholdAmount,
timeoutMinutes: Math.max(0, normalizeInteger(payload.timeoutMinutes, 0)),
timeoutPolicy: normalizeWorkOrderTimeoutPolicy(payload.timeoutPolicy),
materialJson: JSON.stringify(material),
requirementJson: JSON.stringify({ fields }),
now,
})
if (!workOrder) {
throw createHttpError('接单工单创建失败', {
statusCode: 500,
errorCode: 'work_order_create_failed',
})
}
await createWorkOrderEvent({
workOrderId: workOrder.id,
actorType: 'admin',
actorId: 'dev_mock',
eventType: 'mock_created',
fromStatus: '',
toStatus: workOrder.status,
payloadJson: JSON.stringify({
platformOrderId,
productName,
rewardAmount,
sellerId: String(shop.sellerId || shop.shopId || '').trim(),
}),
now,
})
return { order: mapWorkOrderAdmin(workOrder) }
}
/** 本地 Mock 与真实订单保持相同的关键字段约束,避免生成无法追溯的测试工单。 */
export function resolveAdminMockWorkOrderInput(
payload: JsonObject,
shops: KuaishouIndustryShopConfig[],
) {
const platformOrderId = String(payload.platformOrderId || '').trim()
if (!platformOrderId) {
throw createHttpError('请填写订单号', {
statusCode: 400,
errorCode: 'mock_work_order_no_required',
})
}
const productName = String(payload.productName || '').trim()
if (!productName) {
throw createHttpError('请填写商品名称', {
statusCode: 400,
errorCode: 'mock_work_order_product_name_required',
})
}
const shopId = String(payload.sellerId || payload.shopId || '').trim()
if (!shopId) {
throw createHttpError('请选择店铺', {
statusCode: 400,
errorCode: 'mock_work_order_shop_required',
})
}
const shop =
shops.find(
(item) =>
String(item.sellerId || item.shopId || '').trim() === shopId ||
String(item.shopId || '').trim() === shopId,
) || null
if (!shop) {
throw createHttpError('所选店铺不存在', {
statusCode: 400,
errorCode: 'mock_work_order_shop_invalid',
})
}
if (shop.enabled === false) {
throw createHttpError('所选店铺已停用', {
statusCode: 409,
errorCode: 'mock_work_order_shop_disabled',
})
}
return {
platformOrderId,
productName,
shop,
rewardAmount: requireMockWorkOrderAmount(payload.rewardAmount, '接单金额', 'reward_amount'),
paymentAmount: requireMockWorkOrderAmount(
payload.paymentAmount,
'用户付款金额',
'payment_amount',
),
}
}
function requireMockWorkOrderAmount(value: unknown, label: string, field: string): number {
if (value === undefined || value === null || String(value).trim() === '') {
throw createHttpError(`请填写${label}`, {
statusCode: 400,
errorCode: `mock_work_order_${field}_required`,
})
}
const amount = normalizeAmountFen(value, 0)
if (amount <= 0) {
throw createHttpError(`${label}必须大于 0`, {
statusCode: 400,
errorCode: `mock_work_order_${field}_invalid`,
})
}
return amount
}
export {
createAdminMockWorkOrder,
resolveAdminMockWorkOrderInput,
} from './admin-mock-work-order-service.js'
export async function updateAdminWorkOrder(
workOrderId: number | string,
@@ -31,6 +31,7 @@ export * from './admin-worker-cancel-service.js'
export * from './admin-work-order-sharing-service.js'
export * from './admin-work-order-realtime-service.js'
export * from './admin-work-order-material-service.js'
export * from './admin-mock-work-order-service.js'
export * from './worker-product-match-config-service.js'
export * from './worker-announcement-config-service.js'
export * from './worker-hall-config-service.js'