拆分后台模拟工单服务
This commit is contained in:
@@ -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'
|
} from './publish-voucher-service.js'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_DEPOSIT_THRESHOLD_AMOUNT,
|
|
||||||
mapWorkOrderAdmin,
|
mapWorkOrderAdmin,
|
||||||
mapWorkOrderShare,
|
mapWorkOrderShare,
|
||||||
normalizeAmountFen,
|
normalizeAmountFen,
|
||||||
@@ -71,15 +70,9 @@ import {
|
|||||||
resolveWorkerPermissions,
|
resolveWorkerPermissions,
|
||||||
resolveWorkOrderOperationalStatus,
|
resolveWorkOrderOperationalStatus,
|
||||||
} from './mappers.js'
|
} from './mappers.js'
|
||||||
import { ensureWorkerPlatformDefaults } from './worker-platform-defaults.js'
|
|
||||||
import { normalizeWorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
|
import { normalizeWorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
|
||||||
import { getRequiredWorkOrder, getRequiredWorker } from './worker-session-context-service.js'
|
import { getRequiredWorkOrder, getRequiredWorker } from './worker-session-context-service.js'
|
||||||
import { resolveWorkOrderRealtimeWorkerIds } from './admin-work-order-realtime-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 {
|
export {
|
||||||
listAdminWorkerUsers,
|
listAdminWorkerUsers,
|
||||||
@@ -292,169 +285,10 @@ export {
|
|||||||
submitAdminWorkOrderMaterial,
|
submitAdminWorkOrderMaterial,
|
||||||
} from './admin-work-order-material-service.js'
|
} from './admin-work-order-material-service.js'
|
||||||
|
|
||||||
export async function createAdminMockWorkOrder(payload: JsonObject = {}) {
|
export {
|
||||||
const defaults = await ensureWorkerPlatformDefaults()
|
createAdminMockWorkOrder,
|
||||||
const now = nowIso()
|
resolveAdminMockWorkOrderInput,
|
||||||
const input = resolveAdminMockWorkOrderInput(
|
} from './admin-mock-work-order-service.js'
|
||||||
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 async function updateAdminWorkOrder(
|
export async function updateAdminWorkOrder(
|
||||||
workOrderId: number | string,
|
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-sharing-service.js'
|
||||||
export * from './admin-work-order-realtime-service.js'
|
export * from './admin-work-order-realtime-service.js'
|
||||||
export * from './admin-work-order-material-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-product-match-config-service.js'
|
||||||
export * from './worker-announcement-config-service.js'
|
export * from './worker-announcement-config-service.js'
|
||||||
export * from './worker-hall-config-service.js'
|
export * from './worker-hall-config-service.js'
|
||||||
|
|||||||
Reference in New Issue
Block a user