feat(work-order): 后台支持修改用户付款金额并留痕

This commit is contained in:
2026-11-01 14:14:23 +08:00
parent b9d8630026
commit c1dd2ee632
15 changed files with 568 additions and 19 deletions
@@ -305,6 +305,51 @@ export async function updateWorkOrderBasic(
return getWorkOrderById(workOrderId)
}
/**
* 后台修正「用户付款金额」:同时更新 083 派生列与 material_json 来源快照。
*
* WHERE 里再守一次状态与接单人,避免读完到写入之间被打手接走;
* 待抢单(open)还要求没有有效拼单份额,与 capabilities.canEdit 口径一致。
*/
export async function updateWorkOrderPaymentAmount(input: {
workOrderId: number | string
amountFen: number
materialJson: unknown
allowedStatuses: string[]
now: string
}): Promise<WorkOrderRow | null> {
const result = await query<{ id: number }>(
`
UPDATE work_orders
SET source_order_amount_fen = $1,
material_json = $2::jsonb,
updated_at = $3
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
`,
[
input.amountFen,
toJsonString(input.materialJson),
input.now,
Number(input.workOrderId),
input.allowedStatuses,
],
)
if (!result.rows[0]) return null
return getWorkOrderById(result.rows[0].id)
}
export async function updateWorkOrderAdminNote(
workOrderId: number | string,
note: string,
@@ -28,6 +28,7 @@ import {
unpublishAdminWorkOrder,
updateAdminWorkOrder,
updateAdminWorkOrderNote,
updateAdminWorkOrderPaymentAmount,
updateAdminWorkOrderSharing,
} from '../../../services/worker-platform/index.js'
import {
@@ -237,6 +238,39 @@ router.put(
),
)
router.put(
'/worker-platform/orders/:workOrderId/payment-amount',
requireAdminPermission('worker_order.edit'),
createJsonHandler(
(req) =>
updateAdminWorkOrderPaymentAmount(String(req.params.workOrderId || ''), req.body || {}, {
actorName: req.adminSession?.displayName || req.adminSession?.username || '',
}),
{
successMessage: '用户付款金额已更新',
errorMessage: '修改用户付款金额失败',
scope: '[admin/worker-platform/orders/:workOrderId/payment-amount]',
audit: (req, data) => {
const result = (data || {}) as {
previousAmountFen?: number | null
amountFen?: number
reason?: string
}
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 || '',
},
}
},
},
),
)
router.put(
'/worker-platform/orders/:workOrderId/note',
requireAdminPermission('worker_order.edit'),
@@ -10,8 +10,9 @@ import {
reopenCancelledWorkOrder,
resolveProblemWorkOrder,
updateWorkOrder,
updateWorkOrderBasic,
updateWorkOrderAdminNote,
updateWorkOrderBasic,
updateWorkOrderPaymentAmount,
} from '../../repositories/worker-platform/index.js'
import type { JsonObject } from '../../types/json.js'
import { createHttpError } from '../../utils/http.js'
@@ -34,6 +35,12 @@ import {
} from './mappers.js'
import { resolveWorkOrderRealtimeWorkerIds } from './admin-work-order-realtime-service.js'
import { getWorkerHallConfig } from './worker-hall-config-service.js'
import {
USER_PAYMENT_AMOUNT_REASON_MAX_LENGTH,
mergeUserPaymentAmountIntoMaterial,
normalizeUserPaymentAmountFen,
resolveDisplayedUserPaymentAmountFen,
} from './work-order-payment-amount.js'
import { invalidateWorkOrderShopOptionsCache } from './work-order-shop-options-cache.js'
import { getRequiredWorkOrder } from './worker-session-context-service.js'
import { normalizeWorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
@@ -120,6 +127,100 @@ export async function updateAdminWorkOrder(
return { order: mapWorkOrderAdmin(updated || workOrder) }
}
/**
* 后台修正「用户付款金额」。
*
* 只允许和普通编辑相同的状态(待完善/未分配/待抢单且未指派),
* 金额按元提交、按分落库;修改会写入 work_order_events + 后台审计。
* 同步 material_json.source 快照,避免列表展示与 083 派生列分叉。
*/
export async function updateAdminWorkOrderPaymentAmount(
workOrderId: number | string,
payload: JsonObject = {},
options: { actorName?: string } = {},
) {
const workOrder = await getRequiredWorkOrder(workOrderId)
if (!mapWorkOrderAdmin(workOrder).capabilities.canEdit) {
throw createHttpError('只有待完善、未分配或待抢单的工单可以编辑', {
statusCode: 409,
errorCode: 'work_order_edit_status_invalid',
})
}
if (workOrder.assigned_worker_id) {
throw createHttpError('工单已被打手接走,不能编辑', {
statusCode: 409,
errorCode: 'work_order_edit_assigned_invalid',
})
}
const amountFen = normalizeUserPaymentAmountFen(payload.paymentAmount)
if (amountFen === null) {
throw createHttpError('用户付款金额需在 0.01 元到 1000000 元之间', {
statusCode: 400,
errorCode: 'work_order_payment_amount_invalid',
})
}
const reason = String(payload.reason ?? '').trim()
if (reason.length > USER_PAYMENT_AMOUNT_REASON_MAX_LENGTH) {
throw createHttpError(`修改原因不能超过 ${USER_PAYMENT_AMOUNT_REASON_MAX_LENGTH} 个字符`, {
statusCode: 400,
errorCode: 'work_order_payment_amount_reason_too_long',
})
}
const previousAmountFen = resolveDisplayedUserPaymentAmountFen(workOrder)
if (previousAmountFen === amountFen) {
return {
order: mapWorkOrderAdmin(workOrder),
previousAmountFen,
amountFen,
reason,
unchanged: true,
}
}
const now = nowIso()
const updated = await updateWorkOrderPaymentAmount({
workOrderId: workOrder.id,
amountFen,
materialJson: mergeUserPaymentAmountIntoMaterial(workOrder.material_json, amountFen),
allowedStatuses: [
WORK_ORDER_STATUS.PENDING_MATERIAL,
WORK_ORDER_STATUS.UNASSIGNED,
WORK_ORDER_STATUS.OPEN,
],
now,
})
if (!updated) {
throw createHttpError('订单状态已变化,无法修改用户付款金额,请刷新后重试', {
statusCode: 409,
errorCode: 'work_order_payment_amount_conflict',
})
}
await createWorkOrderEvent({
workOrderId: workOrder.id,
actorType: 'admin',
actorId: options.actorName || '',
eventType: 'payment_amount_updated',
fromStatus: workOrder.status,
toStatus: workOrder.status,
payloadJson: JSON.stringify({ previousAmountFen, amountFen, reason }),
now,
})
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
hallChanged: workOrder.status === WORK_ORDER_STATUS.OPEN,
})
return {
order: mapWorkOrderAdmin(updated),
previousAmountFen,
amountFen,
reason,
unchanged: false,
}
}
export async function updateAdminWorkOrderNote(
workOrderId: number | string,
note: unknown,
@@ -0,0 +1,89 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {
mergeUserPaymentAmountIntoMaterial,
normalizeUserPaymentAmountFen,
resolveDisplayedUserPaymentAmountFen,
} from './work-order-payment-amount.js'
test('normalizeUserPaymentAmountFen:元转分并保留两位小数', () => {
assert.equal(normalizeUserPaymentAmountFen(0.01), 1)
assert.equal(normalizeUserPaymentAmountFen(12.34), 1234)
assert.equal(normalizeUserPaymentAmountFen('88.8'), 8880)
assert.equal(normalizeUserPaymentAmountFen(1_000_000), 100_000_000)
})
test('normalizeUserPaymentAmountFen:0、负数、超上限与非数字都判非法', () => {
for (const value of [0, -0.01, 1_000_000.01, '', 'abc', null, undefined, NaN, Infinity]) {
assert.equal(normalizeUserPaymentAmountFen(value), null, `${String(value)} 应判非法`)
}
})
test('resolveDisplayedUserPaymentAmountFen:优先送码实付,其次来源金额,最后派生列', () => {
assert.equal(
resolveDisplayedUserPaymentAmountFen({
material_json: {
source: { orderAmountFen: 1000, kuaishouSendCode: { paymentFen: 900 } },
},
source_order_amount_fen: 800,
}),
900,
)
assert.equal(
resolveDisplayedUserPaymentAmountFen({
material_json: { source: { orderAmountFen: 1000 } },
source_order_amount_fen: 800,
}),
1000,
)
assert.equal(
resolveDisplayedUserPaymentAmountFen({
material_json: { source: {} },
source_order_amount_fen: 800,
}),
800,
)
assert.equal(resolveDisplayedUserPaymentAmountFen({ material_json: '{}' }), null)
})
test('mergeUserPaymentAmountIntoMaterial:同步两处金额并保留其它资料字段', () => {
const merged = mergeUserPaymentAmountIntoMaterial(
{
source: {
orderId: 1,
orderAmountFen: 1000,
kuaishouSendCode: { paymentFen: 1000, code: 'ABC' },
},
collect: { fields: { gameId: '123' } },
},
1234,
)
assert.deepEqual(merged, {
source: {
orderId: 1,
orderAmountFen: 1234,
kuaishouSendCode: { paymentFen: 1234, code: 'ABC' },
},
collect: { fields: { gameId: '123' } },
})
})
test('mergeUserPaymentAmountIntoMaterial:没有送码字段时不凭空创建', () => {
const merged = mergeUserPaymentAmountIntoMaterial({ source: { orderAmountFen: 1000 } }, 1234)
assert.deepEqual(merged, { source: { orderAmountFen: 1234 } })
})
test('mergeUserPaymentAmountIntoMaterial:兼容 JSON 字符串与异常结构', () => {
assert.deepEqual(
mergeUserPaymentAmountIntoMaterial(
JSON.stringify({ source: { orderAmountFen: '1000' } }),
1234,
),
{ source: { orderAmountFen: 1234 } },
)
assert.deepEqual(mergeUserPaymentAmountIntoMaterial('not-json', 1234), {
source: { orderAmountFen: 1234 },
})
})
@@ -0,0 +1,80 @@
import type { JsonObject } from '../../types/json.js'
import { safeParseJson } from '../admin/admin-query-utils.js'
/** 用户付款金额允许范围:0.01 元 ~ 100 万元(与后台其它金额编辑口径一致,统一存分)。 */
export const USER_PAYMENT_AMOUNT_MIN_FEN = 1
export const USER_PAYMENT_AMOUNT_MAX_FEN = 100_000_000
export const USER_PAYMENT_AMOUNT_REASON_MAX_LENGTH = 200
type PaymentAmountWorkOrder = {
material_json?: unknown
source_order_amount_fen?: unknown
}
function asObject(value: unknown): JsonObject {
return value && typeof value === 'object' && !Array.isArray(value) ? (value as JsonObject) : {}
}
/**
* 后台表单以「元」提交,落库统一为分。
* 非法或超出范围返回 null,由调用方返回 400。
*/
export function normalizeUserPaymentAmountFen(value: unknown): number | null {
const text = String(value ?? '').trim()
if (!text) return null
const yuan = Number(text)
if (!Number.isFinite(yuan)) return null
const fen = Math.round(yuan * 100)
if (fen < USER_PAYMENT_AMOUNT_MIN_FEN || fen > USER_PAYMENT_AMOUNT_MAX_FEN) return null
return fen
}
/**
* 读取工单当前展示的「用户付款金额」(分)。
*
* 与前端列表口径一致:优先 material.source.kuaishouSendCode.paymentFen,
* 其次 material.source.orderAmountFen,最后回退 083 派生列。
* 都没有时返回 null(列表显示 `-`)。
*/
export function resolveDisplayedUserPaymentAmountFen(
workOrder: PaymentAmountWorkOrder,
): number | null {
const material = asObject(safeParseJson(workOrder.material_json))
const source = asObject(material.source)
const sendCode = asObject(source.kuaishouSendCode)
for (const raw of [sendCode.paymentFen, source.orderAmountFen]) {
const text = String(raw ?? '').trim()
if (!text) continue
const parsed = Number(text)
if (Number.isFinite(parsed)) return parsed
}
if (
workOrder.source_order_amount_fen !== null &&
workOrder.source_order_amount_fen !== undefined
) {
const columnAmount = Number(workOrder.source_order_amount_fen)
if (Number.isFinite(columnAmount)) return columnAmount
}
return null
}
/**
* 把后台修正后的付款金额写回 material_json,保持列表、详情与后续资料更新同源。
*
* 只改 source.orderAmountFen;如果原来就有送码实付字段,也同步 paymentFen。
* 其它来源字段原样保留。
*/
export function mergeUserPaymentAmountIntoMaterial(
materialJson: unknown,
amountFen: number,
): JsonObject {
const material = asObject(safeParseJson(materialJson))
const source = asObject(material.source)
const sendCode = asObject(source.kuaishouSendCode)
const nextSource: JsonObject = { ...source, orderAmountFen: amountFen }
if (Object.keys(sendCode).length > 0) {
nextSource.kuaishouSendCode = { ...sendCode, paymentFen: amountFen }
}
return { ...material, source: nextSource }
}
@@ -308,7 +308,10 @@ export async function getWorkerMyOrderEvents(workOrderId: number | string, sessi
})
}
}
const events = await listWorkOrderEventsByOrderId(workOrderId)
// 用户付款金额属于后台财务口径,打手端时间线不展示该事件。
const events = (await listWorkOrderEventsByOrderId(workOrderId)).filter(
(event) => event.event_type !== 'payment_amount_updated',
)
return { items: await mapWorkOrderEvents(events) }
}
@@ -304,6 +304,23 @@ export function formatWorkOrderEvent(event: WorkOrderEvent): EventNodeStyle {
}
// —— 其他 ——
case 'payment_amount_updated': {
const previousAmountFen = readPayloadAmount(event, 'previousAmountFen')
const amountFen = readPayloadAmount(event, 'amountFen')
const reason = readPayload(event, 'reason')
return {
title: '修改了用户付款金额',
description: [
previousAmountFen > 0
? `${formatFenAmount(previousAmountFen)} → ${formatFenAmount(amountFen)}`
: `改为 ${formatFenAmount(amountFen)}`,
reason ? `原因:${reason}` : '',
]
.filter(Boolean)
.join(';'),
color: 'gold',
}
}
case 'edited': {
const productName = readPayload(event, 'productName')
const rewardAmount = readPayloadAmount(event, 'rewardAmount')
@@ -1,11 +1,12 @@
import { App, Input, InputNumber, Space, Tag, Typography } from 'antd'
import { EditOutlined } from '@ant-design/icons'
import { App, Input, InputNumber, Modal, Space, Tag, Typography } from 'antd'
import { useRef, useState } from 'react'
import { updateAdminWorkOrder } from '@/services/admin'
import { updateAdminWorkOrder, updateAdminWorkOrderPaymentAmount } from '@/services/admin'
import type { WorkOrder } from '@/types/worker-platform'
import { formatMoney } from './shared'
import { getRewardAbovePayment } from './work-order-panel-utils'
import { getRewardAbovePayment, getUserPaymentFen } from './work-order-panel-utils'
/** 商品名:单击看详情,双击原地修改(仅未接走工单) */
export function ProductNameEditableCell({
@@ -91,6 +92,128 @@ export function ProductNameEditableCell({
)
}
/** 用户付款金额:双击弹窗修改,保留修改原因并写入工单流转记录 */
export function UserPaymentAmountEditableCell({
order,
onSaved,
}: {
order: WorkOrder
onSaved: () => Promise<void> | void
}) {
const { message } = App.useApp()
const editable = order.capabilities?.canEdit === true
const currentFen = getUserPaymentFen(order)
const [open, setOpen] = useState(false)
const [draft, setDraft] = useState<number | null>(null)
const [reason, setReason] = useState('')
const [saving, setSaving] = useState(false)
const savingRef = useRef(false)
function openEditor() {
if (!editable || savingRef.current) return
setDraft(currentFen === null ? null : Number((currentFen / 100).toFixed(2)))
setReason('')
setOpen(true)
}
async function save() {
if (savingRef.current) return
if (draft === null || !Number.isFinite(Number(draft))) {
message.error('请输入用户付款金额')
return
}
const nextYuan = Number(draft)
if (nextYuan < 0.01 || nextYuan > 1000000) {
message.error('用户付款金额需在 0.01 元到 1000000 元之间')
return
}
if (currentFen !== null && Math.abs(nextYuan * 100 - currentFen) < 0.5) {
message.info('用户付款金额未变化')
setOpen(false)
return
}
savingRef.current = true
setSaving(true)
try {
await updateAdminWorkOrderPaymentAmount(order.workOrderId, {
paymentAmount: nextYuan,
reason: reason.trim(),
})
message.success('用户付款金额已更新')
setOpen(false)
await onSaved()
} catch (error) {
message.error(error instanceof Error ? error.message : '保存失败')
} finally {
savingRef.current = false
setSaving(false)
}
}
return (
<>
<div
title={editable ? '双击修改用户付款金额' : undefined}
style={{ cursor: editable ? 'pointer' : 'default' }}
onDoubleClick={openEditor}
>
<Space size={4} wrap>
<Typography.Text style={editable ? { color: 'rgba(0,0,0,0.65)' } : undefined}>
{currentFen === null ? '-' : formatMoney(currentFen)}
</Typography.Text>
{editable ? <EditOutlined style={{ color: 'rgba(0,0,0,0.35)', fontSize: 12 }} /> : null}
</Space>
</div>
<Modal
title="修改用户付款金额"
open={open}
width={420}
okText="确认修改"
cancelText="取消"
confirmLoading={saving}
destroyOnHidden
onCancel={() => {
if (savingRef.current) return
setOpen(false)
}}
onOk={() => void save()}
>
<div style={{ display: 'grid', gap: 12 }}>
<Typography.Text type="secondary">
当前金额:{currentFen === null ? '-' : formatMoney(currentFen)}
</Typography.Text>
<InputNumber
autoFocus
min={0.01}
max={1000000}
precision={2}
step={1}
addonBefore="¥"
style={{ width: '100%' }}
placeholder="请输入用户实际付款金额"
value={draft}
onChange={setDraft}
onPressEnter={() => void save()}
/>
<Input.TextArea
value={reason}
maxLength={200}
showCount
autoSize={{ minRows: 2, maxRows: 4 }}
placeholder="修改原因(选填,最多 200 字)"
onChange={(event) => setReason(event.target.value)}
/>
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
修改会记录到工单流转记录与后台审计日志;财务统计按下一次定时同步更新。
</Typography.Text>
</div>
</Modal>
</>
)
}
export function AmountEditableCell({
order,
field,
@@ -130,11 +130,14 @@ import {
resolveSharingShareStatusColor,
resolveStatusColor,
} from './shared'
import { AmountEditableCell, ProductNameEditableCell } from './WorkOrderTableCells'
import {
AmountEditableCell,
ProductNameEditableCell,
UserPaymentAmountEditableCell,
} from './WorkOrderTableCells'
import { MockWorkOrderForm, WorkOrderStatisticsBar } from './WorkOrderPanelParts'
import {
formatRequirementFieldsText,
formatUserPaymentAmount,
getDisplayOrderNo,
getRewardAbovePayment,
parseWorkerMaterialText,
@@ -1724,7 +1727,7 @@ export default function WorkOrdersPanel() {
{
title: '用户付款金额',
width: 130,
render: (_, row) => formatUserPaymentAmount(row),
render: (_, row) => <UserPaymentAmountEditableCell order={row} onSaved={refreshAll} />,
},
{
title: '发单金额',
@@ -3,9 +3,6 @@ import { Space, Tag, Typography } from 'antd'
import type { CollectField, WorkOrder } from '@/types/worker-platform'
import { resolveConsignmentWorkerDisplay } from '@/utils/consignment-display'
import { formatMoney } from './shared'
import { getUserPaymentFen } from './work-order-payment'
export { getRewardAbovePayment, getUserPaymentFen } from './work-order-payment'
const SKIP_PUBLISH_CONFIRM_KEY = 'admin-work-order-skip-publish-confirm'
@@ -26,11 +23,6 @@ export function setSkipPublishConfirm() {
}
}
export function formatUserPaymentAmount(order: WorkOrder): string {
const paymentFen = getUserPaymentFen(order)
return paymentFen === null ? '-' : formatMoney(paymentFen)
}
/** 渲染工单关联打手:普通打手 / 拼单参与打手 / 寄卖供货打手与现货资料 */
export function renderWorkOrderWorkers(order: WorkOrder) {
if (order.worker) return order.worker.displayName || order.worker.username || '-'
@@ -1,6 +1,8 @@
type PaymentSourceOrder = {
material?: unknown
rewardAmount?: number
/** 后台列表接口返回的 083 派生列(分);来源 JSON 缺失时兜底 */
sourceOrderAmount?: number
}
function asRecord(value: unknown): Record<string, unknown> {
@@ -9,13 +11,26 @@ function asRecord(value: unknown): Record<string, unknown> {
: {}
}
function parsePaymentFen(value: unknown): number | null {
if (value === undefined || value === null) return null
const text = String(value).trim()
if (!text) return null
const parsed = Number(text)
return Number.isFinite(parsed) ? parsed : null
}
/** 用户付款金额(分);未知返回 null(不参与超价提示) */
export function getUserPaymentFen(order: PaymentSourceOrder): number | null {
const material = asRecord(order.material)
const source = asRecord(material.source)
const sendCode = asRecord(source.kuaishouSendCode)
const paymentFen = Number(sendCode.paymentFen ?? source.orderAmountFen)
return Number.isFinite(paymentFen) ? paymentFen : null
const fromMaterial =
parsePaymentFen(sendCode.paymentFen) ?? parsePaymentFen(source.orderAmountFen)
if (fromMaterial !== null) return fromMaterial
// 历史单可能只有派生列;0 同时代表「NULL 回退值」,按未知处理。
const fromColumn = parsePaymentFen(order.sourceOrderAmount)
return fromColumn !== null && fromColumn > 0 ? fromColumn : null
}
/** 发单金额高于用户付款时返回两边金额,否则 null(付款未知时不提示) */
@@ -806,6 +806,24 @@ export function updateAdminWorkOrder(
)
}
export function updateAdminWorkOrderPaymentAmount(
workOrderId: number,
payload: {
/** 用户付款金额(元) */
paymentAmount: number
/** 修改原因(选填,≤200 字) */
reason?: string
},
) {
return apiPut<{
order: WorkOrder
previousAmountFen: number | null
amountFen: number
reason: string
unchanged: boolean
}>(`/api/v1/admin/worker-platform/orders/${workOrderId}/payment-amount`, payload)
}
export function updateAdminWorkOrderNote(workOrderId: number, note: string) {
return apiPut<{ order: WorkOrder }>(`/api/v1/admin/worker-platform/orders/${workOrderId}/note`, {
note,
+1
View File
@@ -37,6 +37,7 @@ export function formatAuditAction(action: string) {
work_order_published: '工单发布',
work_order_unpublished: '工单下架',
work_order_updated: '工单信息更新',
work_order_payment_amount_updated: '用户付款金额修改',
work_order_deleted: '工单删除',
work_order_accepted: '工单验收',
work_orders_batch_accepted: '批量验收',
+1
View File
@@ -87,6 +87,7 @@ export const adminAuditActionOptions = [
{ label: '工单发布', value: 'work_order_published' },
{ label: '工单下架', value: 'work_order_unpublished' },
{ label: '工单信息更新', value: 'work_order_updated' },
{ label: '用户付款金额修改', value: 'work_order_payment_amount_updated' },
{ label: '工单删除', value: 'work_order_deleted' },
{ label: '工单验收', value: 'work_order_accepted' },
{ label: '批量验收', value: 'work_orders_batch_accepted' },
@@ -1,7 +1,10 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { getRewardAbovePayment } from '../src/pages/admin/panels/work-order-payment.ts'
import {
getRewardAbovePayment,
getUserPaymentFen,
} from '../src/pages/admin/panels/work-order-payment.ts'
function orderWithPayment(paymentFen: number, rewardFen: number) {
return {
@@ -35,3 +38,27 @@ test('可传入即将保存的发单金额做预检', () => {
assert.equal(getRewardAbovePayment(order), null)
assert.ok(getRewardAbovePayment(order, 1000))
})
test('getUserPaymentFen:送码实付优先,其次来源金额,最后派生列', () => {
assert.equal(
getUserPaymentFen({
material: { source: { orderAmountFen: 1000, kuaishouSendCode: { paymentFen: 900 } } },
sourceOrderAmount: 800,
}),
900,
)
assert.equal(
getUserPaymentFen({
material: { source: { orderAmountFen: 1000 } },
sourceOrderAmount: 800,
}),
1000,
)
assert.equal(getUserPaymentFen({ material: {}, sourceOrderAmount: 800 }), 800)
})
test('getUserPaymentFen:空值或非法值不误判成 0 元', () => {
assert.equal(getUserPaymentFen({ material: { source: { orderAmountFen: '' } } }), null)
assert.equal(getUserPaymentFen({ material: { source: { orderAmountFen: 'abc' } } }), null)
assert.equal(getUserPaymentFen({ material: {}, sourceOrderAmount: 0 }), null)
})