diff --git a/apps/backend/src/routes/admin/worker-platform/orders.ts b/apps/backend/src/routes/admin/worker-platform/orders.ts index 1fc67840..81da73c4 100644 --- a/apps/backend/src/routes/admin/worker-platform/orders.ts +++ b/apps/backend/src/routes/admin/worker-platform/orders.ts @@ -31,6 +31,7 @@ import { updateAdminWorkOrderPaymentAmount, updateAdminWorkOrderSharing, } from '../../../services/worker-platform/index.js' +import { buildWorkOrderPaymentAmountAuditData } from '../../../services/worker-platform/work-order-payment-amount.js' import { createJsonHandler, requireAdminPermission, @@ -251,20 +252,14 @@ router.put( errorMessage: '修改用户付款金额失败', scope: '[admin/worker-platform/orders/:workOrderId/payment-amount]', audit: (req, data) => { - const result = (data || {}) as { - previousAmountFen?: number | null - amountFen?: number - reason?: string - } + // 金额未变化时服务层不会写工单事件,这里也不留下一条“已更新”的审计记录。 + const auditData = buildWorkOrderPaymentAmountAuditData(data) + if (!auditData) return null return { action: 'work_order_payment_amount_updated', targetType: 'work_order', targetId: String(req.params.workOrderId || ''), - data: { - previousAmountFen: result.previousAmountFen ?? null, - amountFen: result.amountFen ?? null, - reason: result.reason || '', - }, + data: auditData, } }, }, diff --git a/apps/backend/src/services/worker-platform/work-order-payment-amount.test.ts b/apps/backend/src/services/worker-platform/work-order-payment-amount.test.ts index 00495eea..db5e5c4d 100644 --- a/apps/backend/src/services/worker-platform/work-order-payment-amount.test.ts +++ b/apps/backend/src/services/worker-platform/work-order-payment-amount.test.ts @@ -2,6 +2,7 @@ import assert from 'node:assert/strict' import test from 'node:test' import { + buildWorkOrderPaymentAmountAuditData, mergeUserPaymentAmountIntoMaterial, normalizeUserPaymentAmountFen, resolveDisplayedUserPaymentAmountFen, @@ -87,3 +88,38 @@ test('mergeUserPaymentAmountIntoMaterial:兼容 JSON 字符串与异常结构' source: { orderAmountFen: 1234 }, }) }) + +test('buildWorkOrderPaymentAmountAuditData:未变化不写审计,变化时保留原值', () => { + assert.equal( + buildWorkOrderPaymentAmountAuditData({ + unchanged: true, + previousAmountFen: 1000, + amountFen: 1000, + reason: '', + }), + null, + '未变化提交不能留下「已更新」审计', + ) + + assert.deepEqual( + buildWorkOrderPaymentAmountAuditData({ + unchanged: false, + previousAmountFen: 1000, + amountFen: 1234, + reason: ' 修正 ', + }), + { previousAmountFen: 1000, amountFen: 1234, reason: '修正' }, + ) + + assert.deepEqual( + buildWorkOrderPaymentAmountAuditData({ + unchanged: false, + previousAmountFen: null, + amountFen: 1234, + }), + { previousAmountFen: null, amountFen: 1234, reason: '' }, + '原标题金额缺失时保留 null,不能误写成 0', + ) + + assert.equal(buildWorkOrderPaymentAmountAuditData(null), null) +}) diff --git a/apps/backend/src/services/worker-platform/work-order-payment-amount.ts b/apps/backend/src/services/worker-platform/work-order-payment-amount.ts index f8d7dbc6..2be94b20 100644 --- a/apps/backend/src/services/worker-platform/work-order-payment-amount.ts +++ b/apps/backend/src/services/worker-platform/work-order-payment-amount.ts @@ -78,3 +78,39 @@ export function mergeUserPaymentAmountIntoMaterial( } return { ...material, source: nextSource } } + +export type WorkOrderPaymentAmountAuditData = { + previousAmountFen: number | null + amountFen: number | null + reason: string +} + +function toAmountFenOrNull(value: unknown): number | null { + if (value === null || value === undefined || value === '') return null + const parsed = Number(value) + return Number.isFinite(parsed) ? parsed : null +} + +/** + * 后台审计载荷:金额未变化时不写「已更新」审计。 + * + * 前端已拦截未变化提交,但接口仍可能被直接调用或重放;此时服务层不会写 + * payment_amount_updated 工单事件,审计也不能留下一条原金额等于新金额的修改记录。 + */ +export function buildWorkOrderPaymentAmountAuditData( + data: unknown, +): WorkOrderPaymentAmountAuditData | null { + if (!data || typeof data !== 'object' || Array.isArray(data)) return null + const result = data as { + previousAmountFen?: unknown + amountFen?: unknown + reason?: unknown + unchanged?: unknown + } + if (result.unchanged === true) return null + return { + previousAmountFen: toAmountFenOrNull(result.previousAmountFen), + amountFen: toAmountFenOrNull(result.amountFen), + reason: String(result.reason || '').trim(), + } +}