From 1bea34367b402225d1b325b23133b5d9136fe96d Mon Sep 17 00:00:00 2001 From: yml2213 Date: Wed, 19 Aug 2026 18:42:34 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9B=B4=E6=96=B0=E8=B5=84?= =?UTF-8?q?=E6=96=99=E6=9C=AA=E4=BF=AE=E6=94=B9=E6=8E=A5=E5=8D=95=E4=BB=B7?= =?UTF-8?q?=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services/worker-platform/admin-service.ts | 48 ++++++++++++------- .../worker-platform-service.test.ts | 45 +++++++++++++++++ .../pages/admin/panels/WorkOrdersPanel.tsx | 22 ++++++--- 3 files changed, 91 insertions(+), 24 deletions(-) diff --git a/apps/backend/src/services/worker-platform/admin-service.ts b/apps/backend/src/services/worker-platform/admin-service.ts index 0dafa700..5b8abb89 100644 --- a/apps/backend/src/services/worker-platform/admin-service.ts +++ b/apps/backend/src/services/worker-platform/admin-service.ts @@ -1473,16 +1473,6 @@ export async function submitAdminWorkOrderMaterial( errorCode: 'work_order_material_status_invalid', }) } - if ( - workOrder.sharing_enabled === true && - Number(workOrder.active_share_quantity || 0) > 0 && - payload.rewardAmount !== undefined - ) { - throw createHttpError('拼单订单的份额奖励已锁定,不能在更新资料时修改价格', { - statusCode: 409, - errorCode: 'work_order_material_reward_sharing_invalid', - }) - } const fields = resolveRequirementFields(workOrder) const submittedFields = normalizeSubmittedFields(payload.fields || payload.material || payload) @@ -1491,12 +1481,13 @@ export async function submitAdminWorkOrderMaterial( ) const now = nowIso() const screenshots = normalizeUploadedFiles(payload.screenshots || payload.files) - const rewardAmount = - workOrder.sharing_enabled === true && - Number(workOrder.active_share_quantity || 0) > 0 && - payload.rewardAmount !== undefined - ? normalizeAmountFen(payload.rewardAmount, workOrder.reward_amount) - : workOrder.reward_amount + 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: { @@ -1507,7 +1498,6 @@ export async function submitAdminWorkOrderMaterial( source: 'admin', }, } - const hasActiveShares = Number(workOrder.active_share_quantity || 0) > 0 const nextStatus = hasActiveShares || workOrder.status === WORK_ORDER_STATUS.IN_PROGRESS ? workOrder.status @@ -1530,7 +1520,12 @@ export async function submitAdminWorkOrderMaterial( : 'material_completed', fromStatus: workOrder.status, toStatus: nextStatus, - payloadJson: JSON.stringify({ fields: submittedFields, complete, rewardAmount }), + payloadJson: JSON.stringify({ + fields: submittedFields, + complete, + previousRewardAmount: Number(workOrder.reward_amount || 0), + rewardAmount, + }), now, }) if (nextStatus !== WORK_ORDER_STATUS.PENDING_MATERIAL) { @@ -1543,6 +1538,23 @@ export async function submitAdminWorkOrderMaterial( 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 async function createAdminMockWorkOrder(payload: JsonObject = {}) { const defaults = await ensureWorkerPlatformDefaults() const now = nowIso() diff --git a/apps/backend/src/services/worker-platform/worker-platform-service.test.ts b/apps/backend/src/services/worker-platform/worker-platform-service.test.ts index 63144a4c..03f054be 100644 --- a/apps/backend/src/services/worker-platform/worker-platform-service.test.ts +++ b/apps/backend/src/services/worker-platform/worker-platform-service.test.ts @@ -24,6 +24,7 @@ import { mapWorkOrderAdmin, maskWorkerLeaderboardDisplayName, resolveCollectSubmitTargetWorkOrder, + resolveAdminMaterialRewardAmount, resolveMatchingProductRuleDecision, resolveWorkerPermissions, resolveSkuNameQuantity, @@ -741,6 +742,50 @@ test('拼单份额撤单只允许未验收份额并让母工单回到大厅状 assert.equal(resolveWorkOrderShareCancellationStatus('open', 'cancelled'), null) }) +test('后台更新资料时普通接单可以修改接单价格', () => { + assert.equal( + resolveAdminMaterialRewardAmount({ + currentRewardAmount: 1280, + requestedRewardAmount: 15.5, + sharingEnabled: false, + activeShareQuantity: 0, + }), + 1550, + ) +}) + +test('后台更新资料未提交价格时保留原接单价格', () => { + assert.equal( + resolveAdminMaterialRewardAmount({ + currentRewardAmount: 1280, + requestedRewardAmount: undefined, + sharingEnabled: false, + activeShareQuantity: 0, + }), + 1280, + ) +}) + +test('后台更新资料禁止修改已有参与者的拼单价格', () => { + assert.throws( + () => + resolveAdminMaterialRewardAmount({ + currentRewardAmount: 3840, + requestedRewardAmount: 15.5, + sharingEnabled: true, + activeShareQuantity: 3, + }), + (error: unknown) => + Boolean( + error && + typeof error === 'object' && + (error as { statusCode?: number }).statusCode === 409 && + (error as { errorCode?: string }).errorCode === + 'work_order_material_reward_sharing_invalid', + ), + ) +}) + test('resolveCollectSubmitTargetWorkOrder returns the only pending work order when no id is provided', () => { const workOrder = buildWorkOrderRow() const selected = resolveCollectSubmitTargetWorkOrder('ORDER-001', [workOrder], '') diff --git a/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx b/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx index 5565fb1c..57c3ab83 100644 --- a/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx +++ b/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx @@ -513,16 +513,26 @@ export default function WorkOrdersPanel() { async function submitMaterial(values: MaterialFormValues) { if (!materialOrder) return + const requestedRewardAmount = + materialOrder.status === 'in_progress' && !materialOrder.sharing?.enabled + ? Number(values.rewardAmount || 0) + : undefined const succeeded = await runAction( - () => - submitAdminWorkOrderMaterial( + async () => { + const response = await submitAdminWorkOrderMaterial( materialOrder.workOrderId, values.fields || {}, materialScreenshots, - materialOrder.status === 'in_progress' && !materialOrder.sharing?.enabled - ? Number(values.rewardAmount || 0) - : undefined, - ), + requestedRewardAmount, + ) + if ( + requestedRewardAmount !== undefined && + Number(response.data.order.rewardAmount || 0) !== Math.round(requestedRewardAmount * 100) + ) { + throw new Error('接单价格保存未生效,请刷新后重试') + } + return response + }, materialOrder.status === 'in_progress' ? '订单资料已更新' : '订单资料已保存', ) if (!succeeded) return