fix(worker): 用户付款金额仅待完善可修改并补后端校验

This commit is contained in:
2026-11-01 16:22:45 +08:00
parent d135a34e35
commit 5879568ad0
8 changed files with 91 additions and 27 deletions
@@ -308,8 +308,8 @@ export async function updateWorkOrderBasic(
/**
* 后台修正「用户付款金额」:同时更新 083 派生列与 material_json 来源快照。
*
* WHERE 里再守一次状态与接单人,避免读完到写入之间被打手接走;
* 待抢单(open)还要求没有有效拼单份额,与 capabilities.canEdit 口径一致。
* 只允许待完善(pending_material)阶段;WHERE 里再守一次状态与接单人,
* 避免读完到写入之间状态已推进或被打手接走。
*/
export async function updateWorkOrderPaymentAmount(input: {
workOrderId: number | string
@@ -327,15 +327,6 @@ export async function updateWorkOrderPaymentAmount(input: {
WHERE id = $4
AND assigned_worker_id IS NULL
AND status = ANY($5::text[])
AND (
status <> 'open'
OR NOT EXISTS (
SELECT 1
FROM work_order_shares wos
WHERE wos.work_order_id = work_orders.id
AND wos.status <> 'cancelled'
)
)
RETURNING id
`,
[
@@ -37,6 +37,7 @@ import { resolveWorkOrderRealtimeWorkerIds } from './admin-work-order-realtime-s
import { getWorkerHallConfig } from './worker-hall-config-service.js'
import {
USER_PAYMENT_AMOUNT_REASON_MAX_LENGTH,
canModifyUserPaymentAmountStatus,
mergeUserPaymentAmountIntoMaterial,
normalizeUserPaymentAmountFen,
resolveDisplayedUserPaymentAmountFen,
@@ -130,9 +131,9 @@ export async function updateAdminWorkOrder(
/**
* 后台修正「用户付款金额」。
*
* 只允许和普通编辑相同的状态(待完善/未分配/待抢单且未指派),
* 金额按元提交、按分落库;修改会写入 work_order_events + 后台审计。
* 同步 material_json.source 快照,避免列表展示与 083 派生列分叉。
* 只允许「待完善(pending_material)」阶段修改:金额进入未分配/待抢单后已参与
* 发单与大厅展示,再改会破坏对账口径。金额按元提交、按分落库;修改会写入
* work_order_events + 后台审计,并同步 material_json.source 快照。
*/
export async function updateAdminWorkOrderPaymentAmount(
workOrderId: number | string,
@@ -140,16 +141,16 @@ export async function updateAdminWorkOrderPaymentAmount(
options: { actorName?: string } = {},
) {
const workOrder = await getRequiredWorkOrder(workOrderId)
if (!mapWorkOrderAdmin(workOrder).capabilities.canEdit) {
throw createHttpError('只有待完善、未分配或待抢单的工单可以编辑', {
if (!canModifyUserPaymentAmountStatus(workOrder.status)) {
throw createHttpError('只有待完善的工单可以修改用户付款金额', {
statusCode: 409,
errorCode: 'work_order_edit_status_invalid',
errorCode: 'work_order_payment_amount_status_invalid',
})
}
if (workOrder.assigned_worker_id) {
throw createHttpError('工单已被打手接走,不能编辑', {
throw createHttpError('工单已被打手接走,不能修改用户付款金额', {
statusCode: 409,
errorCode: 'work_order_edit_assigned_invalid',
errorCode: 'work_order_payment_amount_assigned_invalid',
})
}
@@ -184,11 +185,7 @@ export async function updateAdminWorkOrderPaymentAmount(
workOrderId: workOrder.id,
amountFen,
materialJson: mergeUserPaymentAmountIntoMaterial(workOrder.material_json, amountFen),
allowedStatuses: [
WORK_ORDER_STATUS.PENDING_MATERIAL,
WORK_ORDER_STATUS.UNASSIGNED,
WORK_ORDER_STATUS.OPEN,
],
allowedStatuses: [WORK_ORDER_STATUS.PENDING_MATERIAL],
now,
})
if (!updated) {
@@ -3,6 +3,7 @@ import test from 'node:test'
import {
buildWorkOrderPaymentAmountAuditData,
canModifyUserPaymentAmountStatus,
mergeUserPaymentAmountIntoMaterial,
normalizeUserPaymentAmountFen,
resolveDisplayedUserPaymentAmountFen,
@@ -21,6 +22,28 @@ test('normalizeUserPaymentAmountFen:0、负数、超上限与非数字都判
}
})
test('canModifyUserPaymentAmountStatus:只有待完善(pending_material)可改', () => {
assert.equal(canModifyUserPaymentAmountStatus('pending_material'), true)
for (const status of [
'unassigned',
'open',
'in_progress',
'pending_acceptance',
'problem',
'accepted',
'cancelled',
'',
null,
undefined,
]) {
assert.equal(
canModifyUserPaymentAmountStatus(status),
false,
`${String(status)} 不应允许修改用户付款金额`,
)
}
})
test('resolveDisplayedUserPaymentAmountFen:优先送码实付,其次来源金额,最后派生列', () => {
assert.equal(
resolveDisplayedUserPaymentAmountFen({
@@ -1,3 +1,4 @@
import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
import type { JsonObject } from '../../types/json.js'
import { safeParseJson } from '../admin/admin-query-utils.js'
@@ -29,6 +30,16 @@ export function normalizeUserPaymentAmountFen(value: unknown): number | null {
return fen
}
/**
* 用户付款金额只允许在「待完善」阶段修改。
*
* 进入未分配/待抢单后金额已经参与发单与大厅展示,后台再改会破坏对账口径;
* 已接单/已验收阶段更不允许改动。
*/
export function canModifyUserPaymentAmountStatus(status: unknown): boolean {
return String(status || '').trim() === WORK_ORDER_STATUS.PENDING_MATERIAL
}
/**
* 读取工单当前展示的「用户付款金额」(分)。
*