发布工单时自动核销电子凭证
This commit is contained in:
@@ -91,6 +91,7 @@ import {
|
||||
getWorkerFinanceConfig,
|
||||
saveWorkerFinanceConfig,
|
||||
} from './worker-finance-config-service.js'
|
||||
import { consumeIndustryVouchersBeforeWorkOrderPublish } from './publish-voucher-service.js'
|
||||
|
||||
import { DEFAULT_CATEGORY_KEY, DEFAULT_DEPOSIT_THRESHOLD_AMOUNT, DEFAULT_LEVEL_KEY, DEFAULT_LEVEL_NAME, mapFinanceRequest, mapWallet, mapWorkCategory, mapWorkOrderAdmin, mapWorkOrderShare, mapWorkProductRule, mapWorkerLevel, mapWorkerUser, normalizeAdminFinanceReviewStatus, normalizeAmountFen, normalizeBoolean, normalizeEnabledStatus, normalizeFinanceRequestStatus, normalizeFinanceRequestType, normalizeInteger, normalizeMatchType, normalizeOptionalId, normalizePositiveInteger, normalizeProblemResolutionAction, normalizeRequirementFields, normalizeRequirementFieldsFromPayload, normalizeReviewStatus, normalizeSessionVersion, normalizeSlugKey, normalizeStatuses, normalizeSubmittedFields, normalizeUploadedFiles, normalizeWorkerType, resolveFreezeDepositAmount, resolveMatchingProductRule, resolveRequirementFields, resolveSkuNameQuantity, resolveWorkerPermissions } from './mappers.js'
|
||||
import { ensureWorkerPlatformDefaults, getRequiredWorkOrder, getRequiredWorker, normalizeWorkOrderTimeoutPolicy } from './worker-service.js'
|
||||
@@ -1039,12 +1040,45 @@ export async function publishAdminWorkOrder(workOrderId: number | string) {
|
||||
errorCode: 'work_order_publish_reward_invalid',
|
||||
})
|
||||
}
|
||||
|
||||
const voucherConsume = await consumeIndustryVouchersBeforeWorkOrderPublish(workOrder)
|
||||
if (!voucherConsume.ok) {
|
||||
const failedAt = nowIso()
|
||||
await createWorkOrderEvent({
|
||||
workOrderId: workOrder.id,
|
||||
actorType: 'admin',
|
||||
actorId: '',
|
||||
eventType: 'publish_voucher_consume_failed',
|
||||
fromStatus: workOrder.status,
|
||||
toStatus: workOrder.status,
|
||||
payloadJson: JSON.stringify(voucherConsume),
|
||||
now: failedAt,
|
||||
})
|
||||
throw createHttpError(voucherConsume.errorMessage || '电子凭证核销失败,订单未发布', {
|
||||
statusCode: 409,
|
||||
errorCode: 'work_order_publish_voucher_consume_failed',
|
||||
context: voucherConsume,
|
||||
})
|
||||
}
|
||||
|
||||
const now = nowIso()
|
||||
const updated = await updateWorkOrder(workOrder.id, {
|
||||
status: WORK_ORDER_STATUS.OPEN,
|
||||
published_at: now,
|
||||
updated_at: now,
|
||||
})
|
||||
if (!updated) {
|
||||
throw createHttpError(
|
||||
voucherConsume.voucherCount > 0
|
||||
? '电子凭证已核销,但订单发布失败,请重试发布'
|
||||
: '订单发布失败,请重试',
|
||||
{
|
||||
statusCode: 409,
|
||||
errorCode: 'work_order_publish_update_failed',
|
||||
context: voucherConsume,
|
||||
},
|
||||
)
|
||||
}
|
||||
await createWorkOrderEvent({
|
||||
workOrderId: workOrder.id,
|
||||
actorType: 'admin',
|
||||
@@ -1052,9 +1086,10 @@ export async function publishAdminWorkOrder(workOrderId: number | string) {
|
||||
eventType: 'published',
|
||||
fromStatus: workOrder.status,
|
||||
toStatus: WORK_ORDER_STATUS.OPEN,
|
||||
payloadJson: JSON.stringify({ voucherConsume }),
|
||||
now,
|
||||
})
|
||||
return { order: mapWorkOrderAdmin(updated || workOrder) }
|
||||
return { order: mapWorkOrderAdmin(updated), voucherConsume }
|
||||
}
|
||||
|
||||
export async function unpublishAdminWorkOrder(workOrderId: number | string) {
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import type { WorkOrderRow } from '../../repositories/worker-platform/index.js'
|
||||
import type { KuaishouIndustryVoucherRow, TaskRow } from '../../types/repository/rows.js'
|
||||
import {
|
||||
consumeIndustryVouchersBeforeWorkOrderPublish,
|
||||
resolveWorkOrderVoucherTargets,
|
||||
} from './publish-voucher-service.js'
|
||||
|
||||
function buildWorkOrder(overrides: Partial<WorkOrderRow> = {}): WorkOrderRow {
|
||||
return {
|
||||
id: 1,
|
||||
work_order_no: 'WO001',
|
||||
order_id: 10,
|
||||
order_item_id: 20,
|
||||
task_id: null,
|
||||
platform_order_id: 'OID001',
|
||||
product_name: '测试商品',
|
||||
category_id: null,
|
||||
status: 'unassigned',
|
||||
reward_amount: 100,
|
||||
required_deposit_amount: 0,
|
||||
deposit_threshold_amount: 20000,
|
||||
assigned_worker_id: null,
|
||||
last_assigned_worker_id: null,
|
||||
material_json: '{}',
|
||||
requirement_json: '{}',
|
||||
acceptance_json: '{}',
|
||||
draft_acceptance_json: '{}',
|
||||
problem_note: '',
|
||||
sharing_enabled: false,
|
||||
sharing_total_quantity: 1,
|
||||
sharing_unit_reward: 0,
|
||||
published_at: null,
|
||||
pinned_at: null,
|
||||
assigned_at: null,
|
||||
submitted_at: null,
|
||||
accepted_at: null,
|
||||
deadline_at: null,
|
||||
timeout_minutes: 0,
|
||||
timeout_policy: 'reopen',
|
||||
created_at: '2026-08-16T00:00:00.000Z',
|
||||
updated_at: '2026-08-16T00:00:00.000Z',
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
function buildTask(id: number, orderItemId: number): TaskRow {
|
||||
return {
|
||||
id,
|
||||
order_id: 10,
|
||||
order_item_id: orderItemId,
|
||||
unit_index: 1,
|
||||
platform_order_id: 'OID001',
|
||||
profile_id: 1,
|
||||
task_no: `DT${id}`,
|
||||
executor_key: 'manual_dispatch',
|
||||
task_status: 'pending',
|
||||
delivery_status: 'pending',
|
||||
result_code: '',
|
||||
result_message: '',
|
||||
automation_mode: 'manual',
|
||||
requires_claim: false,
|
||||
user_action_status: 'not_required',
|
||||
attempt_count: 0,
|
||||
runtime_session_id: '',
|
||||
login_type: '',
|
||||
nickname: '',
|
||||
role_name: '',
|
||||
role_id: '',
|
||||
area: '',
|
||||
partition_name: '',
|
||||
claim_token: '',
|
||||
primary_claim_token: '',
|
||||
primary_claim_token_id: null,
|
||||
primary_claim_token_status: '',
|
||||
artifacts_json: '{}',
|
||||
context_json: '{}',
|
||||
screenshot_path: '',
|
||||
last_error: '',
|
||||
retry_count: 0,
|
||||
created_at: '2026-08-16T00:00:00.000Z',
|
||||
updated_at: '2026-08-16T00:00:00.000Z',
|
||||
claimed_at: null,
|
||||
role_confirmed_at: null,
|
||||
redeemed_at: null,
|
||||
}
|
||||
}
|
||||
|
||||
function buildVoucher(
|
||||
id: number,
|
||||
taskId: number | null,
|
||||
status = 'UNUSED',
|
||||
): KuaishouIndustryVoucherRow {
|
||||
return {
|
||||
id,
|
||||
voucher_code: `KSV${id}`,
|
||||
oid: 'OID001',
|
||||
order_id: 10,
|
||||
task_id: taskId,
|
||||
unit_index: id,
|
||||
seller_id: 'seller-1',
|
||||
token: 'token',
|
||||
eticket_type: 'GAME_OPEN_TICKET_CONSUME',
|
||||
status,
|
||||
valid_start_time: 0,
|
||||
valid_end_time: 0,
|
||||
consume_serial_num: '',
|
||||
consume_details_json: [],
|
||||
consumed_at: status === 'CONSUMED' ? '2026-08-16T00:00:00.000Z' : null,
|
||||
destroyed_at: null,
|
||||
send_callback_status: 'success',
|
||||
send_callback_attempt_count: 1,
|
||||
send_callback_last_error: '',
|
||||
send_callback_response_json: {},
|
||||
send_callback_sent_at: '2026-08-16T00:00:00.000Z',
|
||||
raw_payload_json: {},
|
||||
created_at: '2026-08-16T00:00:00.000Z',
|
||||
updated_at: '2026-08-16T00:00:00.000Z',
|
||||
}
|
||||
}
|
||||
|
||||
test('发布核销优先选择当前订单商品关联任务的券码', () => {
|
||||
const tasks = [buildTask(100, 20), buildTask(200, 30)]
|
||||
const vouchers = [buildVoucher(1, 100), buildVoucher(2, 200)]
|
||||
|
||||
const targets = resolveWorkOrderVoucherTargets(buildWorkOrder(), tasks, vouchers)
|
||||
|
||||
assert.deepEqual(
|
||||
targets.map((target) => target.voucher.voucher_code),
|
||||
['KSV1'],
|
||||
)
|
||||
assert.equal(targets[0]?.task?.id, 100)
|
||||
})
|
||||
|
||||
test('历史券码未绑定任务时回退核销同一平台订单的券码', () => {
|
||||
const targets = resolveWorkOrderVoucherTargets(
|
||||
buildWorkOrder(),
|
||||
[buildTask(100, 20)],
|
||||
[buildVoucher(1, null)],
|
||||
)
|
||||
|
||||
assert.deepEqual(
|
||||
targets.map((target) => target.voucher.voucher_code),
|
||||
['KSV1'],
|
||||
)
|
||||
assert.equal(targets[0]?.task, null)
|
||||
})
|
||||
|
||||
test('不会核销已明确绑定到其他商品任务的券码', () => {
|
||||
const targets = resolveWorkOrderVoucherTargets(
|
||||
buildWorkOrder(),
|
||||
[buildTask(100, 20), buildTask(200, 30)],
|
||||
[buildVoucher(2, 200)],
|
||||
)
|
||||
|
||||
assert.deepEqual(targets, [])
|
||||
})
|
||||
|
||||
test('核销失败时阻止发布并返回失败信息', async () => {
|
||||
const voucher = buildVoucher(1, null)
|
||||
const result = await consumeIndustryVouchersBeforeWorkOrderPublish(buildWorkOrder(), {
|
||||
listVouchersByOid: async () => [voucher],
|
||||
listTasksByOrderId: async () => [],
|
||||
consumeVoucher: async () => ({
|
||||
ok: false,
|
||||
voucher,
|
||||
callbackSuccess: false,
|
||||
errorMessage: '快手核销回调失败',
|
||||
}),
|
||||
attachVoucherToTask: async (task) => task,
|
||||
})
|
||||
|
||||
assert.equal(result.ok, false)
|
||||
assert.equal(result.failedCount, 1)
|
||||
assert.equal(result.errorMessage, '快手核销回调失败')
|
||||
})
|
||||
|
||||
test('发布前核销券码并同步关联任务状态', async () => {
|
||||
const task = buildTask(100, 20)
|
||||
const voucher = buildVoucher(1, 100)
|
||||
const consumedVoucher = {
|
||||
...voucher,
|
||||
status: 'CONSUMED',
|
||||
consumed_at: '2026-08-16T01:00:00.000Z',
|
||||
}
|
||||
const consumedCodes: string[] = []
|
||||
const attachedTaskIds: number[] = []
|
||||
|
||||
const result = await consumeIndustryVouchersBeforeWorkOrderPublish(buildWorkOrder(), {
|
||||
listVouchersByOid: async () => [voucher],
|
||||
listTasksByOrderId: async () => [task],
|
||||
consumeVoucher: async (currentVoucher) => {
|
||||
consumedCodes.push(currentVoucher.voucher_code)
|
||||
return { ok: true, voucher: consumedVoucher, callbackSuccess: true }
|
||||
},
|
||||
attachVoucherToTask: async (currentTask) => {
|
||||
attachedTaskIds.push(currentTask.id)
|
||||
return currentTask
|
||||
},
|
||||
})
|
||||
|
||||
assert.equal(result.ok, true)
|
||||
assert.equal(result.voucherCount, 1)
|
||||
assert.equal(result.consumedCount, 1)
|
||||
assert.equal(result.alreadyConsumedCount, 0)
|
||||
assert.deepEqual(consumedCodes, ['KSV1'])
|
||||
assert.deepEqual(attachedTaskIds, [100])
|
||||
})
|
||||
|
||||
test('已核销券码按幂等成功处理', async () => {
|
||||
const voucher = buildVoucher(1, null, 'CONSUMED')
|
||||
const result = await consumeIndustryVouchersBeforeWorkOrderPublish(buildWorkOrder(), {
|
||||
listVouchersByOid: async () => [voucher],
|
||||
listTasksByOrderId: async () => [],
|
||||
consumeVoucher: async () => ({ ok: true, voucher, callbackSuccess: true }),
|
||||
attachVoucherToTask: async (task) => task,
|
||||
})
|
||||
|
||||
assert.equal(result.ok, true)
|
||||
assert.equal(result.voucherCount, 1)
|
||||
assert.equal(result.consumedCount, 0)
|
||||
assert.equal(result.alreadyConsumedCount, 1)
|
||||
})
|
||||
@@ -0,0 +1,179 @@
|
||||
import { listKuaishouIndustryVouchersByOid } from '../../repositories/kuaishou-industry-voucher-repo.js'
|
||||
import { listTasksByOrderId } from '../../repositories/task-repo.js'
|
||||
import type { KuaishouIndustryVoucherRow, TaskRow } from '../../types/repository/rows.js'
|
||||
import type { WorkOrderRow } from '../../repositories/worker-platform/index.js'
|
||||
import { attachKuaishouIndustryVoucherToTask } from '../platforms/kuaishou-industry/voucher-binding-service.js'
|
||||
import { consumeKuaishouIndustryVoucher } from '../platforms/kuaishou-industry/voucher-service.js'
|
||||
|
||||
export type WorkOrderVoucherConsumeResult = {
|
||||
ok: boolean
|
||||
voucherCount: number
|
||||
consumedCount: number
|
||||
alreadyConsumedCount: number
|
||||
failedCount: number
|
||||
voucherCodes: string[]
|
||||
errorMessage: string
|
||||
}
|
||||
|
||||
type PublishVoucherServiceDeps = {
|
||||
listVouchersByOid: typeof listKuaishouIndustryVouchersByOid
|
||||
listTasksByOrderId: typeof listTasksByOrderId
|
||||
consumeVoucher: typeof consumeKuaishouIndustryVoucher
|
||||
attachVoucherToTask: typeof attachKuaishouIndustryVoucherToTask
|
||||
}
|
||||
|
||||
const defaultDeps: PublishVoucherServiceDeps = {
|
||||
listVouchersByOid: listKuaishouIndustryVouchersByOid,
|
||||
listTasksByOrderId,
|
||||
consumeVoucher: consumeKuaishouIndustryVoucher,
|
||||
attachVoucherToTask: attachKuaishouIndustryVoucherToTask,
|
||||
}
|
||||
|
||||
export async function consumeIndustryVouchersBeforeWorkOrderPublish(
|
||||
workOrder: WorkOrderRow,
|
||||
deps: PublishVoucherServiceDeps = defaultDeps,
|
||||
): Promise<WorkOrderVoucherConsumeResult> {
|
||||
const oid = String(workOrder.platform_order_id || '').trim()
|
||||
if (!oid) {
|
||||
return buildConsumeResult([])
|
||||
}
|
||||
|
||||
const [vouchers, tasks] = await Promise.all([
|
||||
deps.listVouchersByOid(oid),
|
||||
workOrder.order_id ? deps.listTasksByOrderId(workOrder.order_id) : Promise.resolve([]),
|
||||
])
|
||||
const targets = resolveWorkOrderVoucherTargets(workOrder, tasks, vouchers)
|
||||
let consumedCount = 0
|
||||
let alreadyConsumedCount = 0
|
||||
|
||||
for (const target of targets) {
|
||||
const voucherCode = String(target.voucher.voucher_code || '').trim()
|
||||
const alreadyConsumed = normalizeVoucherStatus(target.voucher.status) === 'CONSUMED'
|
||||
let consumeResult: Awaited<ReturnType<typeof consumeKuaishouIndustryVoucher>>
|
||||
|
||||
try {
|
||||
consumeResult = await deps.consumeVoucher(target.voucher, {
|
||||
source: 'work_order_publish',
|
||||
consumeTime: Date.now(),
|
||||
...(target.task ? { task: target.task } : {}),
|
||||
})
|
||||
} catch (error) {
|
||||
return buildConsumeResult(targets, {
|
||||
consumedCount,
|
||||
alreadyConsumedCount,
|
||||
errorMessage: error instanceof Error ? error.message : '电子凭证核销失败',
|
||||
})
|
||||
}
|
||||
|
||||
if (!consumeResult.ok || !consumeResult.voucher) {
|
||||
return buildConsumeResult(targets, {
|
||||
consumedCount,
|
||||
alreadyConsumedCount,
|
||||
errorMessage: consumeResult.errorMessage || `电子凭证 ${voucherCode} 核销失败`,
|
||||
})
|
||||
}
|
||||
|
||||
if (alreadyConsumed) {
|
||||
alreadyConsumedCount += 1
|
||||
} else {
|
||||
consumedCount += 1
|
||||
}
|
||||
|
||||
try {
|
||||
if (target.task) {
|
||||
await deps.attachVoucherToTask(target.task, consumeResult.voucher, {
|
||||
source: 'work_order_publish',
|
||||
now: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
return buildConsumeResult(targets, {
|
||||
consumedCount,
|
||||
alreadyConsumedCount,
|
||||
errorMessage: `电子凭证 ${voucherCode} 已核销,但任务状态同步失败:${
|
||||
error instanceof Error ? error.message : '未知错误'
|
||||
}`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return buildConsumeResult(targets, {
|
||||
consumedCount,
|
||||
alreadyConsumedCount,
|
||||
})
|
||||
}
|
||||
|
||||
export function resolveWorkOrderVoucherTargets(
|
||||
workOrder: WorkOrderRow,
|
||||
tasks: TaskRow[],
|
||||
vouchers: KuaishouIndustryVoucherRow[],
|
||||
) {
|
||||
const taskById = new Map(tasks.map((task) => [Number(task.id), task]))
|
||||
const linkedTaskIds = new Set<number>()
|
||||
|
||||
if (workOrder.task_id) {
|
||||
linkedTaskIds.add(Number(workOrder.task_id))
|
||||
}
|
||||
if (workOrder.order_item_id) {
|
||||
for (const task of tasks) {
|
||||
if (Number(task.order_item_id) === Number(workOrder.order_item_id)) {
|
||||
linkedTaskIds.add(Number(task.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const linkedVouchers = vouchers.filter(
|
||||
(voucher) => voucher.task_id && linkedTaskIds.has(Number(voucher.task_id)),
|
||||
)
|
||||
// 历史数据可能尚未完成任务绑定;此时回退到同一平台订单,避免仍需人工核销。
|
||||
const hasTaskBoundVoucher = vouchers.some((voucher) => Number(voucher.task_id || 0) > 0)
|
||||
const selectedVouchers =
|
||||
linkedVouchers.length > 0
|
||||
? linkedVouchers
|
||||
: linkedTaskIds.size > 0 && hasTaskBoundVoucher
|
||||
? []
|
||||
: vouchers
|
||||
|
||||
return deduplicateVouchers(selectedVouchers).map((voucher) => ({
|
||||
voucher,
|
||||
task: voucher.task_id ? taskById.get(Number(voucher.task_id)) || null : null,
|
||||
}))
|
||||
}
|
||||
|
||||
function buildConsumeResult(
|
||||
targets: Array<{ voucher: KuaishouIndustryVoucherRow }>,
|
||||
input: {
|
||||
consumedCount?: number
|
||||
alreadyConsumedCount?: number
|
||||
errorMessage?: string
|
||||
} = {},
|
||||
): WorkOrderVoucherConsumeResult {
|
||||
const errorMessage = String(input.errorMessage || '').trim()
|
||||
const processedCount = Number(input.consumedCount || 0) + Number(input.alreadyConsumedCount || 0)
|
||||
|
||||
return {
|
||||
ok: !errorMessage,
|
||||
voucherCount: targets.length,
|
||||
consumedCount: Number(input.consumedCount || 0),
|
||||
alreadyConsumedCount: Number(input.alreadyConsumedCount || 0),
|
||||
failedCount: errorMessage ? Math.max(1, targets.length - processedCount) : 0,
|
||||
voucherCodes: targets.map((target) => String(target.voucher.voucher_code || '').trim()),
|
||||
errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
function deduplicateVouchers(vouchers: KuaishouIndustryVoucherRow[]) {
|
||||
const seen = new Set<string>()
|
||||
return vouchers.filter((voucher) => {
|
||||
const key = String(voucher.voucher_code || voucher.id || '').trim()
|
||||
if (!key || seen.has(key)) return false
|
||||
seen.add(key)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
function normalizeVoucherStatus(value: unknown) {
|
||||
return String(value || '')
|
||||
.trim()
|
||||
.toUpperCase()
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Descriptions,
|
||||
Drawer,
|
||||
Form,
|
||||
@@ -128,6 +129,8 @@ const FILTERABLE_WORK_ORDER_STATUSES = [
|
||||
'cancelled',
|
||||
] as const
|
||||
|
||||
const SKIP_PUBLISH_CONFIRM_KEY = 'admin-work-order-skip-publish-confirm'
|
||||
|
||||
type MaterialFormValues = {
|
||||
fields?: Record<string, string>
|
||||
rewardAmount?: number
|
||||
@@ -347,13 +350,15 @@ export default function WorkOrdersPanel() {
|
||||
])
|
||||
}
|
||||
|
||||
async function runAction(
|
||||
action: () => Promise<unknown>,
|
||||
successMessage: string,
|
||||
async function runAction<T>(
|
||||
action: () => Promise<T>,
|
||||
successMessage: string | ((result: T) => string),
|
||||
) {
|
||||
try {
|
||||
await action()
|
||||
message.success(successMessage)
|
||||
const result = await action()
|
||||
message.success(
|
||||
typeof successMessage === 'function' ? successMessage(result) : successMessage,
|
||||
)
|
||||
await refreshAll()
|
||||
return true
|
||||
} catch (error) {
|
||||
@@ -362,6 +367,48 @@ export default function WorkOrdersPanel() {
|
||||
}
|
||||
}
|
||||
|
||||
function publishWorkOrder(row: WorkOrder) {
|
||||
return runAction(
|
||||
() => publishAdminWorkOrder(row.workOrderId),
|
||||
(response) =>
|
||||
response.data.voucherConsume.voucherCount > 0
|
||||
? '电子凭证已自动核销,订单已发布到大厅'
|
||||
: '订单已发布到大厅',
|
||||
)
|
||||
}
|
||||
|
||||
function confirmPublishWorkOrder(row: WorkOrder) {
|
||||
if (shouldSkipPublishConfirm()) {
|
||||
void publishWorkOrder(row)
|
||||
return
|
||||
}
|
||||
|
||||
let skipFutureConfirm = false
|
||||
modal.confirm({
|
||||
title: '确认发布并核销该订单?',
|
||||
content: (
|
||||
<div>
|
||||
<Typography.Paragraph>
|
||||
如订单关联电子凭证,系统会先自动核销,成功后再发布到大厅。核销后即使下架也不会自动恢复凭证。
|
||||
</Typography.Paragraph>
|
||||
<Checkbox
|
||||
onChange={(event) => {
|
||||
skipFutureConfirm = event.target.checked
|
||||
}}
|
||||
>
|
||||
不再提示
|
||||
</Checkbox>
|
||||
</div>
|
||||
),
|
||||
okText: '发布并核销',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
if (skipFutureConfirm) setSkipPublishConfirm()
|
||||
return publishWorkOrder(row)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function confirmBatchAcceptance() {
|
||||
const workOrderIds = selectedAcceptanceOrderIds
|
||||
if (workOrderIds.length === 0) {
|
||||
@@ -792,14 +839,9 @@ export default function WorkOrdersPanel() {
|
||||
? '接单金额为 0,不能发布到大厅'
|
||||
: undefined
|
||||
}
|
||||
onClick={() =>
|
||||
runAction(
|
||||
() => publishAdminWorkOrder(row.workOrderId),
|
||||
'已发布到大厅',
|
||||
)
|
||||
}
|
||||
onClick={() => confirmPublishWorkOrder(row)}
|
||||
>
|
||||
发布
|
||||
发布并核销
|
||||
</Button>
|
||||
) : null}
|
||||
{row.status === 'open' ? (
|
||||
@@ -1687,6 +1729,22 @@ export default function WorkOrdersPanel() {
|
||||
)
|
||||
}
|
||||
|
||||
function shouldSkipPublishConfirm() {
|
||||
try {
|
||||
return localStorage.getItem(SKIP_PUBLISH_CONFIRM_KEY) === '1'
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function setSkipPublishConfirm() {
|
||||
try {
|
||||
localStorage.setItem(SKIP_PUBLISH_CONFIRM_KEY, '1')
|
||||
} catch {
|
||||
// 浏览器禁用本地存储时继续发布,但不记住本次选择。
|
||||
}
|
||||
}
|
||||
|
||||
function formatUserPaymentAmount(order: WorkOrder): string {
|
||||
const paymentFen = getUserPaymentFen(order)
|
||||
return paymentFen === null ? '-' : formatMoney(paymentFen)
|
||||
|
||||
@@ -247,7 +247,18 @@ export function submitAdminWorkOrderMaterial(
|
||||
}
|
||||
|
||||
export function publishAdminWorkOrder(workOrderId: number) {
|
||||
return apiPost<{ order: WorkOrder }>(
|
||||
return apiPost<{
|
||||
order: WorkOrder
|
||||
voucherConsume: {
|
||||
ok: boolean
|
||||
voucherCount: number
|
||||
consumedCount: number
|
||||
alreadyConsumedCount: number
|
||||
failedCount: number
|
||||
voucherCodes: string[]
|
||||
errorMessage: string
|
||||
}
|
||||
}>(
|
||||
`/api/v1/admin/worker-platform/orders/${workOrderId}/publish`,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user