新增打手撤单审核
This commit is contained in:
@@ -96,6 +96,32 @@ export async function createWorkerAcceptanceAdminNotification(input: {
|
||||
})
|
||||
}
|
||||
|
||||
export async function createWorkerCancelRequestAdminNotification(input: {
|
||||
requestId: number
|
||||
workOrderId: number
|
||||
workOrderNo: string
|
||||
productName: string
|
||||
workerName: string
|
||||
reason: string
|
||||
}) {
|
||||
return createConfiguredWorkerPlatformNotification('cancel_requested', {
|
||||
notificationType: 'worker_cancel_requested',
|
||||
priority: 'high',
|
||||
entityType: 'worker_cancel_request',
|
||||
entityId: input.requestId,
|
||||
dedupeKey: `worker_cancel_request:${input.requestId}`,
|
||||
title: '有打手撤单申请待审核',
|
||||
body: [
|
||||
`订单:${input.workOrderNo || input.workOrderId}`,
|
||||
`商品:${input.productName || '-'}`,
|
||||
`打手:${input.workerName || '-'}`,
|
||||
`原因:${input.reason || '-'}`,
|
||||
].join(';'),
|
||||
actionPath: `${ADMIN_WORKER_PLATFORM_PATH}?tab=cancel-requests&cancelRequestId=${input.requestId}`,
|
||||
now: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
|
||||
export async function remindWorkerAcceptanceAdminNotification(workOrderId: number) {
|
||||
const now = new Date().toISOString()
|
||||
const eventConfig = getWorkerPlatformNotificationConfig().events.acceptance_reminded
|
||||
|
||||
@@ -50,6 +50,8 @@ import {
|
||||
resolveProblemWorkOrder,
|
||||
reviewWorkerFinanceRequest,
|
||||
reopenCancelledWorkOrder,
|
||||
listAdminWorkerCancelRequests as listWorkerCancelRequestsForAdmin,
|
||||
reviewWorkerCancelRequest,
|
||||
updateWorkOrder,
|
||||
updateWorkOrderBasic,
|
||||
updateWorkerUser,
|
||||
@@ -108,6 +110,7 @@ import {
|
||||
mapWorkProductRule,
|
||||
mapWorkerLevel,
|
||||
mapWorkerUser,
|
||||
mapWorkerCancelRequest,
|
||||
normalizeAdminFinanceReviewStatus,
|
||||
normalizeAmountFen,
|
||||
normalizeBoolean,
|
||||
@@ -1355,6 +1358,74 @@ export async function listAdminWorkOrders(query: JsonObject = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function listAdminWorkerCancelRequests(query: JsonObject = {}) {
|
||||
const status = String(query.status || 'pending').trim()
|
||||
const normalizedStatus = ['pending', 'approved', 'rejected'].includes(status) ? status : 'pending'
|
||||
return {
|
||||
items: (await listWorkerCancelRequestsForAdmin(normalizedStatus)).map(mapWorkerCancelRequest),
|
||||
}
|
||||
}
|
||||
|
||||
export async function reviewAdminWorkerCancelRequest(
|
||||
requestId: number | string,
|
||||
payload: JsonObject = {},
|
||||
actorName = '',
|
||||
) {
|
||||
const approved = payload.approved === true || String(payload.action || '').trim() === 'approve'
|
||||
const reviewNote = String(payload.reviewNote || payload.note || '').trim()
|
||||
if (!reviewNote) {
|
||||
throw createHttpError('请填写审核备注', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_cancel_request_review_note_required',
|
||||
})
|
||||
}
|
||||
const result = await reviewWorkerCancelRequest({
|
||||
requestId: Number(requestId),
|
||||
approved,
|
||||
reviewNote,
|
||||
actorName,
|
||||
now: nowIso(),
|
||||
})
|
||||
if (result.failureReason === 'request_not_found') {
|
||||
throw createHttpError('撤单申请不存在', {
|
||||
statusCode: 404,
|
||||
errorCode: 'worker_cancel_request_not_found',
|
||||
})
|
||||
}
|
||||
if (result.failureReason === 'request_not_pending') {
|
||||
throw createHttpError('该撤单申请已审核,请刷新后重试', {
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_cancel_request_not_pending',
|
||||
})
|
||||
}
|
||||
if (!result.request || !result.order) {
|
||||
throw createHttpError('订单状态已变化,无法审核该撤单申请', {
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_cancel_request_target_changed',
|
||||
})
|
||||
}
|
||||
await resolveAdminNotificationEntity(
|
||||
'worker_cancel_request',
|
||||
Number(result.request.id),
|
||||
approved ? 'approved' : 'rejected',
|
||||
)
|
||||
const workerId = Number(result.request.worker_id)
|
||||
publishWorkOrderRealtimeChange({
|
||||
workOrderId: Number(result.order.id),
|
||||
workerIds: workerId > 0 ? [workerId] : [],
|
||||
hallChanged: approved,
|
||||
})
|
||||
if (approved && workerId > 0) publishWorkerWalletRealtimeChange(workerId)
|
||||
return {
|
||||
request: mapWorkerCancelRequest(result.request),
|
||||
order: mapWorkOrderAdmin(result.order),
|
||||
approved,
|
||||
releasedDepositAmount: result.releasedDepositAmount,
|
||||
levelChanged: result.levelChanged,
|
||||
accountDisabled: result.accountDisabled,
|
||||
}
|
||||
}
|
||||
|
||||
export async function deductAdminWorkOrderPendingDeposit(
|
||||
workOrderId: number | string,
|
||||
payload: JsonObject = {},
|
||||
|
||||
@@ -257,6 +257,41 @@ export function mapWorkerUser(worker: WorkerUserRow) {
|
||||
}
|
||||
}
|
||||
|
||||
export function mapWorkerCancelRequest(request: {
|
||||
id: number
|
||||
work_order_id: number
|
||||
work_order_share_id: number | null
|
||||
worker_id: number
|
||||
reason: string
|
||||
status: string
|
||||
review_note: string
|
||||
reviewed_by: string
|
||||
created_at: string
|
||||
reviewed_at: string | null
|
||||
work_order_no?: string
|
||||
product_name?: string
|
||||
worker_username?: string
|
||||
worker_display_name?: string
|
||||
share_quantity?: number | null
|
||||
}) {
|
||||
return {
|
||||
requestId: Number(request.id),
|
||||
workOrderId: Number(request.work_order_id),
|
||||
shareId: request.work_order_share_id ? Number(request.work_order_share_id) : null,
|
||||
workerId: Number(request.worker_id),
|
||||
workOrderNo: String(request.work_order_no || ''),
|
||||
productName: String(request.product_name || ''),
|
||||
workerName: String(request.worker_display_name || request.worker_username || ''),
|
||||
shareQuantity: request.share_quantity ? Number(request.share_quantity) : null,
|
||||
reason: String(request.reason || ''),
|
||||
status: String(request.status || ''),
|
||||
reviewNote: String(request.review_note || ''),
|
||||
reviewedBy: String(request.reviewed_by || ''),
|
||||
createdAt: request.created_at,
|
||||
reviewedAt: request.reviewed_at,
|
||||
}
|
||||
}
|
||||
|
||||
export function mapWalletLedger(ledger: WorkerWalletLedgerRow | null | undefined) {
|
||||
if (!ledger) return null
|
||||
return {
|
||||
|
||||
+2
@@ -8,6 +8,7 @@ export const WORKER_PLATFORM_NOTIFICATION_EVENT_KEYS = [
|
||||
'acceptance_submitted',
|
||||
'acceptance_reminded',
|
||||
'material_required',
|
||||
'cancel_requested',
|
||||
] as const
|
||||
|
||||
export type WorkerPlatformNotificationEventKey =
|
||||
@@ -51,6 +52,7 @@ export function createDefaultWorkerPlatformNotificationConfig(): WorkerPlatformN
|
||||
soundEnabled: false,
|
||||
externalPushEnabled: false,
|
||||
},
|
||||
cancel_requested: createEnabledEventConfig(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
acceptWorkOrderAndSettle,
|
||||
acceptWorkOrderShareAndSettle,
|
||||
countWorkerAcceptedOrders,
|
||||
createWorkerCancelRequest,
|
||||
countWorkerActiveOrders,
|
||||
countWorkerTimeoutEvents,
|
||||
countWorkerWithdrawRequestsOnDay,
|
||||
@@ -42,6 +43,7 @@ import {
|
||||
listPendingMaterialWorkOrdersByPlatformOrderId,
|
||||
listWorkerFinanceRequests,
|
||||
listWorkerCompletionLeaderboard,
|
||||
listWorkerCancelRequests,
|
||||
listWorkerSharesByWorker,
|
||||
listWorkerWalletLedgers,
|
||||
listWorkerWorkOrderNotes,
|
||||
@@ -53,6 +55,7 @@ import {
|
||||
listWorkOrderShares,
|
||||
listWorkOrderSharesByOrderIds,
|
||||
listWorkerLevels,
|
||||
getWorkerCancelRequestRisk,
|
||||
listDueDepositUnfreezes,
|
||||
listOverdueWorkOrders,
|
||||
releaseDepositUnfreeze,
|
||||
@@ -82,6 +85,7 @@ import { getWorkerFinanceConfig } from './worker-finance-config-service.js'
|
||||
import { refreshUploadedFileUrls } from '../file-storage/file-storage-service.js'
|
||||
import {
|
||||
createWorkerAcceptanceAdminNotification,
|
||||
createWorkerCancelRequestAdminNotification,
|
||||
createWorkerRechargeAdminNotification,
|
||||
createWorkerWithdrawAdminNotification,
|
||||
getWorkerAcceptanceReminder,
|
||||
@@ -111,6 +115,7 @@ import {
|
||||
mapWorkOrderPublic,
|
||||
mapWorkOrderEvents,
|
||||
mapWorkOrderShare,
|
||||
mapWorkerCancelRequest,
|
||||
mapWorkerUser,
|
||||
normalizeAmountFen,
|
||||
assertWithdrawChannelAllowedForAmount,
|
||||
@@ -1738,6 +1743,82 @@ export async function getWorkerMyOrderEvents(workOrderId: number | string, sessi
|
||||
return { items: await mapWorkOrderEvents(events) }
|
||||
}
|
||||
|
||||
export async function getWorkerCancelRequestSummary(session: WorkerSession) {
|
||||
requireActiveWorkerSession(session)
|
||||
const [risk, requests] = await Promise.all([
|
||||
getWorkerCancelRequestRisk(session.workerId, nowIso()),
|
||||
listWorkerCancelRequests(session.workerId),
|
||||
])
|
||||
return { risk, items: requests.map(mapWorkerCancelRequest) }
|
||||
}
|
||||
|
||||
export async function submitWorkerCancelRequest(
|
||||
workOrderId: number | string,
|
||||
payload: JsonObject = {},
|
||||
session: WorkerSession,
|
||||
) {
|
||||
requireActiveWorkerSession(session)
|
||||
const reason = String(payload.reason || '').trim()
|
||||
if (!reason) {
|
||||
throw createHttpError('请填写申请取消原因', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_cancel_request_reason_required',
|
||||
})
|
||||
}
|
||||
if (reason.length > 300) {
|
||||
throw createHttpError('申请取消原因不能超过 300 个字符', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_cancel_request_reason_too_long',
|
||||
})
|
||||
}
|
||||
const now = nowIso()
|
||||
const result = await createWorkerCancelRequest({
|
||||
workOrderId: Number(workOrderId),
|
||||
workerId: Number(session.workerId),
|
||||
reason,
|
||||
now,
|
||||
})
|
||||
if (result.failureReason === 'daily_limit_reached') {
|
||||
throw createHttpError('今日最多申请取消 5 次,请明日再试', {
|
||||
statusCode: 429,
|
||||
errorCode: 'worker_cancel_request_daily_limit',
|
||||
})
|
||||
}
|
||||
if (result.failureReason === 'request_pending') {
|
||||
throw createHttpError('该订单已有待审核的取消申请', {
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_cancel_request_pending',
|
||||
})
|
||||
}
|
||||
if (result.failureReason === 'share_not_cancellable') {
|
||||
throw createHttpError('当前拼单份额不能申请取消', {
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_cancel_request_share_status_invalid',
|
||||
})
|
||||
}
|
||||
if (!result.request) {
|
||||
throw createHttpError('当前订单不能申请取消', {
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_cancel_request_status_invalid',
|
||||
})
|
||||
}
|
||||
const worker = await getRequiredWorker(session.workerId)
|
||||
await createWorkerCancelRequestAdminNotification({
|
||||
requestId: Number(result.request.id),
|
||||
workOrderId: Number(result.request.work_order_id),
|
||||
workOrderNo: result.request.work_order_no || '',
|
||||
productName: result.request.product_name || '',
|
||||
workerName: worker.display_name || worker.username,
|
||||
reason,
|
||||
})
|
||||
const risk = await getWorkerCancelRequestRisk(session.workerId, now)
|
||||
publishWorkOrderRealtimeChange({
|
||||
workOrderId: Number(result.request.work_order_id),
|
||||
workerIds: [Number(session.workerId)],
|
||||
})
|
||||
return { request: mapWorkerCancelRequest(result.request), risk }
|
||||
}
|
||||
|
||||
export async function saveWorkerOrderNote(
|
||||
workOrderId: number | string,
|
||||
payload: JsonObject = {},
|
||||
|
||||
Reference in New Issue
Block a user