拆分后台工单资料服务

This commit is contained in:
yml2213
2026-08-21 17:17:06 +08:00
parent 12e789affc
commit 0031df580a
4 changed files with 131 additions and 107 deletions
@@ -61,8 +61,6 @@ import {
normalizeProblemResolutionAction,
normalizeRequirementFields,
normalizeRequirementFieldsFromPayload,
normalizeSubmittedFields,
normalizeUploadedFiles,
resolveFreezeDepositAmount,
resolveMatchingProductRuleDecision,
resolveKuaishouWorkProductMatchContext,
@@ -76,6 +74,7 @@ import {
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,
@@ -288,104 +287,10 @@ export {
updateAdminWorkOrderSharing,
} from './admin-work-order-sharing-service.js'
export async function submitAdminWorkOrderMaterial(
workOrderId: number | string,
payload: JsonObject = {},
actorName = '',
) {
const workOrder = await getRequiredWorkOrder(workOrderId)
const materialCapabilities = mapWorkOrderAdmin(workOrder).capabilities
if (!materialCapabilities.canUpdateMaterial) {
throw createHttpError('当前状态不能更新订单资料', {
statusCode: 409,
errorCode: 'work_order_material_status_invalid',
})
}
const fields = resolveRequirementFields(workOrder)
const submittedFields = normalizeSubmittedFields(payload.fields || payload.material || payload)
const complete = fields.every(
(field) => !field.required || String(submittedFields[field.key] || '').trim(),
)
const now = nowIso()
const screenshots = normalizeUploadedFiles(payload.screenshots || payload.files)
const hasActiveShares = Number(workOrder.active_share_quantity || 0) > 0
const rewardAmount = resolveAdminMaterialRewardAmount({
currentRewardAmount: Number(workOrder.reward_amount || 0),
requestedRewardAmount: payload.rewardAmount,
sharingEnabled: workOrder.sharing_enabled === true,
activeShareQuantity: Number(workOrder.active_share_quantity || 0),
})
const material = {
...safeParseJson(workOrder.material_json),
collect: {
fields: submittedFields,
screenshots,
submittedAt: now,
complete,
source: 'admin',
},
}
const nextStatus =
hasActiveShares ||
workOrder.status === WORK_ORDER_STATUS.OPEN ||
workOrder.status === WORK_ORDER_STATUS.IN_PROGRESS
? workOrder.status
: complete
? WORK_ORDER_STATUS.UNASSIGNED
: WORK_ORDER_STATUS.PENDING_MATERIAL
const updated = await updateWorkOrder(workOrder.id, {
status: nextStatus,
material_json: material,
reward_amount: rewardAmount,
updated_at: now,
})
await createWorkOrderEvent({
workOrderId: workOrder.id,
actorType: 'admin',
actorId: actorName,
eventType:
hasActiveShares ||
workOrder.status === WORK_ORDER_STATUS.OPEN ||
workOrder.status === WORK_ORDER_STATUS.IN_PROGRESS
? 'material_updated'
: 'material_completed',
fromStatus: workOrder.status,
toStatus: nextStatus,
payloadJson: JSON.stringify({
fields: submittedFields,
complete,
previousRewardAmount: Number(workOrder.reward_amount || 0),
rewardAmount,
}),
now,
})
if (nextStatus !== WORK_ORDER_STATUS.PENDING_MATERIAL) {
await resolveAdminNotificationEntity('work_order', Number(workOrder.id), 'material_completed')
}
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: await resolveWorkOrderRealtimeWorkerIds(workOrder),
})
return { order: mapWorkOrderAdmin(updated || workOrder), complete }
}
/** 更新资料时仅普通接单可改价;已有参与者的拼单必须保留份额价格快照。 */
export function resolveAdminMaterialRewardAmount(input: {
currentRewardAmount: number
requestedRewardAmount: unknown
sharingEnabled: boolean
activeShareQuantity: number
}): number {
if (input.requestedRewardAmount === undefined) return input.currentRewardAmount
if (input.sharingEnabled && input.activeShareQuantity > 0) {
throw createHttpError('拼单订单的份额奖励已锁定,不能在更新资料时修改价格', {
statusCode: 409,
errorCode: 'work_order_material_reward_sharing_invalid',
})
}
return normalizeAmountFen(input.requestedRewardAmount, input.currentRewardAmount)
}
export {
resolveAdminMaterialRewardAmount,
submitAdminWorkOrderMaterial,
} from './admin-work-order-material-service.js'
export async function createAdminMockWorkOrder(payload: JsonObject = {}) {
const defaults = await ensureWorkerPlatformDefaults()
@@ -1208,13 +1113,6 @@ export async function cancelAdminWorkOrder(
return { order: mapWorkOrderAdmin(updated), action }
}
async function resolveWorkOrderRealtimeWorkerIds(workOrder: WorkOrderRow) {
const shares = await listWorkOrderShares(workOrder.id)
return [Number(workOrder.assigned_worker_id), ...shares.map((share) => Number(share.worker_id))]
.filter((workerId) => Number.isInteger(workerId) && workerId > 0)
.filter((workerId, index, values) => values.indexOf(workerId) === index)
}
export async function getAdminWorkerPlatformSummary() {
const hallConfig = getWorkerHallConfig()
const [pendingWorkers, pendingMaterial, openOrders, inProgressOrders] = await Promise.all([
@@ -0,0 +1,116 @@
import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
import { createWorkOrderEvent, updateWorkOrder } from '../../repositories/worker-platform/index.js'
import type { JsonObject } from '../../types/json.js'
import { createHttpError } from '../../utils/http.js'
import { nowIso } from '../../utils/time.js'
import { safeParseJson } from '../admin/admin-query-utils.js'
import { resolveAdminNotificationEntity } from '../admin/admin-notification-service.js'
import { publishWorkOrderRealtimeChange } from '../realtime/realtime-event-service.js'
import {
mapWorkOrderAdmin,
normalizeAmountFen,
normalizeSubmittedFields,
normalizeUploadedFiles,
resolveRequirementFields,
} from './mappers.js'
import { resolveWorkOrderRealtimeWorkerIds } from './admin-work-order-realtime-service.js'
import { getRequiredWorkOrder } from './worker-session-context-service.js'
export async function submitAdminWorkOrderMaterial(
workOrderId: number | string,
payload: JsonObject = {},
actorName = '',
) {
const workOrder = await getRequiredWorkOrder(workOrderId)
const materialCapabilities = mapWorkOrderAdmin(workOrder).capabilities
if (!materialCapabilities.canUpdateMaterial) {
throw createHttpError('当前状态不能更新订单资料', {
statusCode: 409,
errorCode: 'work_order_material_status_invalid',
})
}
const fields = resolveRequirementFields(workOrder)
const submittedFields = normalizeSubmittedFields(payload.fields || payload.material || payload)
const complete = fields.every(
(field) => !field.required || String(submittedFields[field.key] || '').trim(),
)
const now = nowIso()
const screenshots = normalizeUploadedFiles(payload.screenshots || payload.files)
const hasActiveShares = Number(workOrder.active_share_quantity || 0) > 0
const rewardAmount = resolveAdminMaterialRewardAmount({
currentRewardAmount: Number(workOrder.reward_amount || 0),
requestedRewardAmount: payload.rewardAmount,
sharingEnabled: workOrder.sharing_enabled === true,
activeShareQuantity: Number(workOrder.active_share_quantity || 0),
})
const material = {
...safeParseJson(workOrder.material_json),
collect: {
fields: submittedFields,
screenshots,
submittedAt: now,
complete,
source: 'admin',
},
}
const nextStatus =
hasActiveShares ||
workOrder.status === WORK_ORDER_STATUS.OPEN ||
workOrder.status === WORK_ORDER_STATUS.IN_PROGRESS
? workOrder.status
: complete
? WORK_ORDER_STATUS.UNASSIGNED
: WORK_ORDER_STATUS.PENDING_MATERIAL
const updated = await updateWorkOrder(workOrder.id, {
status: nextStatus,
material_json: material,
reward_amount: rewardAmount,
updated_at: now,
})
await createWorkOrderEvent({
workOrderId: workOrder.id,
actorType: 'admin',
actorId: actorName,
eventType:
hasActiveShares ||
workOrder.status === WORK_ORDER_STATUS.OPEN ||
workOrder.status === WORK_ORDER_STATUS.IN_PROGRESS
? 'material_updated'
: 'material_completed',
fromStatus: workOrder.status,
toStatus: nextStatus,
payloadJson: JSON.stringify({
fields: submittedFields,
complete,
previousRewardAmount: Number(workOrder.reward_amount || 0),
rewardAmount,
}),
now,
})
if (nextStatus !== WORK_ORDER_STATUS.PENDING_MATERIAL) {
await resolveAdminNotificationEntity('work_order', Number(workOrder.id), 'material_completed')
}
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: await resolveWorkOrderRealtimeWorkerIds(workOrder),
})
return { order: mapWorkOrderAdmin(updated || workOrder), complete }
}
/** 更新资料时仅普通接单可改价;已有参与者的拼单必须保留份额价格快照。 */
export function resolveAdminMaterialRewardAmount(input: {
currentRewardAmount: number
requestedRewardAmount: unknown
sharingEnabled: boolean
activeShareQuantity: number
}): number {
if (input.requestedRewardAmount === undefined) return input.currentRewardAmount
if (input.sharingEnabled && input.activeShareQuantity > 0) {
throw createHttpError('拼单订单的份额奖励已锁定,不能在更新资料时修改价格', {
statusCode: 409,
errorCode: 'work_order_material_reward_sharing_invalid',
})
}
return normalizeAmountFen(input.requestedRewardAmount, input.currentRewardAmount)
}
@@ -0,0 +1,8 @@
import { listWorkOrderShares, type WorkOrderRow } from '../../repositories/worker-platform/index.js'
export async function resolveWorkOrderRealtimeWorkerIds(workOrder: WorkOrderRow) {
const shares = await listWorkOrderShares(workOrder.id)
return [Number(workOrder.assigned_worker_id), ...shares.map((share) => Number(share.worker_id))]
.filter((workerId) => Number.isInteger(workerId) && workerId > 0)
.filter((workerId, index, values) => values.indexOf(workerId) === index)
}
@@ -29,6 +29,8 @@ export * from './admin-work-order-query-service.js'
export * from './admin-worker-feedback-service.js'
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 './worker-product-match-config-service.js'
export * from './worker-announcement-config-service.js'
export * from './worker-hall-config-service.js'