接单工单支持任务时限,超时自动判定失败并处置
This commit is contained in:
@@ -86,7 +86,7 @@ import {
|
||||
} from './worker-finance-config-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, normalizeSubmittedFields, normalizeUploadedFiles, resolveMatchingProductRule, resolveRequirementFields } from './mappers.js'
|
||||
import { ensureWorkerPlatformDefaults, getRequiredWorkOrder, getRequiredWorker } from './worker-service.js'
|
||||
import { ensureWorkerPlatformDefaults, getRequiredWorkOrder, getRequiredWorker, normalizeWorkOrderTimeoutPolicy } from './worker-service.js'
|
||||
|
||||
export async function listAdminWorkerLevels() {
|
||||
await ensureWorkerPlatformDefaults()
|
||||
@@ -281,6 +281,8 @@ export async function saveAdminWorkProductRule(payload: JsonObject = {}) {
|
||||
sharingEnabled,
|
||||
sharingTotalQuantity,
|
||||
sharingUnitReward,
|
||||
timeoutMinutes: Math.max(0, normalizeInteger(payload.timeoutMinutes, 0)),
|
||||
timeoutPolicy: normalizeWorkOrderTimeoutPolicy(payload.timeoutPolicy),
|
||||
requirementJson: JSON.stringify({ fields }),
|
||||
sortOrder: normalizeInteger(payload.sortOrder, 100),
|
||||
now: nowIso(),
|
||||
@@ -646,6 +648,8 @@ export async function createAdminMockWorkOrder(payload: JsonObject = {}) {
|
||||
rewardAmount,
|
||||
requiredDepositAmount,
|
||||
depositThresholdAmount,
|
||||
timeoutMinutes: Math.max(0, normalizeInteger(payload.timeoutMinutes, 0)),
|
||||
timeoutPolicy: normalizeWorkOrderTimeoutPolicy(payload.timeoutPolicy),
|
||||
materialJson: JSON.stringify(material),
|
||||
requirementJson: JSON.stringify({ fields }),
|
||||
now,
|
||||
@@ -721,6 +725,13 @@ export async function updateAdminWorkOrder(
|
||||
rewardAmount,
|
||||
requiredDepositAmount,
|
||||
depositThresholdAmount,
|
||||
timeoutMinutes:
|
||||
payload.timeoutMinutes === undefined
|
||||
? workOrder.timeout_minutes
|
||||
: Math.max(0, normalizeInteger(payload.timeoutMinutes, workOrder.timeout_minutes)),
|
||||
timeoutPolicy: normalizeWorkOrderTimeoutPolicy(
|
||||
payload.timeoutPolicy ?? workOrder.timeout_policy,
|
||||
),
|
||||
requirementJson: JSON.stringify({ fields }),
|
||||
updatedAt: now,
|
||||
})
|
||||
@@ -735,6 +746,8 @@ export async function updateAdminWorkOrder(
|
||||
productName: updated?.product_name,
|
||||
rewardAmount,
|
||||
requiredDepositAmount,
|
||||
timeoutMinutes: updated?.timeout_minutes,
|
||||
timeoutPolicy: updated?.timeout_policy,
|
||||
fields,
|
||||
}),
|
||||
now,
|
||||
@@ -995,6 +1008,8 @@ export async function syncWorkerOrdersForSourceOrder(
|
||||
sharingEnabled: rule.sharing_enabled === true,
|
||||
sharingTotalQuantity: Number(rule.sharing_total_quantity || 1),
|
||||
sharingUnitReward: Number(rule.sharing_unit_reward || 0),
|
||||
timeoutMinutes: Number(rule.timeout_minutes || 0),
|
||||
timeoutPolicy: String(rule.timeout_policy || 'reopen').trim(),
|
||||
materialJson: JSON.stringify({
|
||||
source: {
|
||||
orderId: Number(order.id),
|
||||
|
||||
@@ -247,6 +247,8 @@ export function mapWorkProductRule(rule: WorkProductRuleRow | null | undefined)
|
||||
totalQuantity: Number(rule.sharing_total_quantity || 1),
|
||||
unitReward: Number(rule.sharing_unit_reward || 0),
|
||||
},
|
||||
timeoutMinutes: Number(rule.timeout_minutes || 0),
|
||||
timeoutPolicy: String(rule.timeout_policy || 'reopen').trim(),
|
||||
requirement: {
|
||||
fields: normalizeRequirementFields(requirement.fields),
|
||||
},
|
||||
@@ -355,6 +357,9 @@ export function mapWorkOrderAdmin(workOrder: WorkOrderRow) {
|
||||
totalQuantity: Number(workOrder.sharing_total_quantity || 1),
|
||||
unitReward: Number(workOrder.sharing_unit_reward || 0),
|
||||
},
|
||||
timeoutMinutes: Number(workOrder.timeout_minutes || 0),
|
||||
timeoutPolicy: String(workOrder.timeout_policy || 'reopen').trim(),
|
||||
deadlineAt: workOrder.deadline_at,
|
||||
depositThresholdAmount: Number(
|
||||
workOrder.deposit_threshold_amount || DEFAULT_DEPOSIT_THRESHOLD_AMOUNT,
|
||||
),
|
||||
|
||||
@@ -47,8 +47,10 @@ import {
|
||||
listWorkerUsers,
|
||||
listWorkOrderShares,
|
||||
listWorkOrderSharesByOrderIds,
|
||||
listOverdueWorkOrders,
|
||||
resolveProblemWorkOrder,
|
||||
reviewWorkerFinanceRequest,
|
||||
settleOverdueWorkOrder,
|
||||
submitWorkOrderShareAcceptance,
|
||||
updateWorkOrder,
|
||||
updateWorkerPassword,
|
||||
@@ -642,6 +644,7 @@ export async function listWorkerHallOrders(query: JsonObject = {}, session: Work
|
||||
const pageSize = normalizePageSize(query.pageSize)
|
||||
const categoryId = normalizeOptionalId(query.categoryId)
|
||||
const worker = await getRequiredWorker(session.workerId)
|
||||
await settleOverdueWorkOrders({ workerId: worker.id, limit: 50 })
|
||||
const permissions = resolveWorkerPermissions(worker)
|
||||
const visibleDelaySeconds = Number(permissions.visibleDelaySeconds || 0)
|
||||
const visibleAfterIso =
|
||||
@@ -706,6 +709,7 @@ export async function grabWorkerHallOrder(workOrderId: number | string, session:
|
||||
workerId: Number(worker.id),
|
||||
depositAmount: freezeAmount,
|
||||
maxActiveOrders: permissions.maxActiveOrders,
|
||||
deadlineAt: resolveWorkOrderDeadlineAt(workOrder, nowIso()),
|
||||
now: nowIso(),
|
||||
})
|
||||
if (!grabbed.order) {
|
||||
@@ -715,11 +719,61 @@ export async function grabWorkerHallOrder(workOrderId: number | string, session:
|
||||
return { order: mapWorkOrderForWorker(grabbed.order, permissions) }
|
||||
}
|
||||
|
||||
function resolveWorkOrderDeadlineAt(
|
||||
workOrder: WorkOrderRow,
|
||||
now: string,
|
||||
): string | null {
|
||||
const timeoutMinutes = Number(workOrder.timeout_minutes || 0)
|
||||
if (timeoutMinutes <= 0) return null
|
||||
return new Date(new Date(now).getTime() + timeoutMinutes * 60 * 1000).toISOString()
|
||||
}
|
||||
|
||||
export const WORK_ORDER_TIMEOUT_POLICIES = ['reopen', 'cancel_release', 'cancel_deduct'] as const
|
||||
|
||||
export type WorkOrderTimeoutPolicy = (typeof WORK_ORDER_TIMEOUT_POLICIES)[number]
|
||||
|
||||
export function isWorkOrderTimeoutPolicy(value: unknown): value is WorkOrderTimeoutPolicy {
|
||||
return WORK_ORDER_TIMEOUT_POLICIES.includes(value as WorkOrderTimeoutPolicy)
|
||||
}
|
||||
|
||||
export function normalizeWorkOrderTimeoutPolicy(value: unknown): WorkOrderTimeoutPolicy {
|
||||
return isWorkOrderTimeoutPolicy(value) ? value : 'reopen'
|
||||
}
|
||||
|
||||
export async function settleOverdueWorkOrders(
|
||||
options: { limit?: number; workerId?: number } = {},
|
||||
) {
|
||||
const overdue = await listOverdueWorkOrders({
|
||||
limit: Math.max(1, Number(options.limit || 50)),
|
||||
workerId: Number(options.workerId || 0),
|
||||
})
|
||||
let processedCount = 0
|
||||
let skippedCount = 0
|
||||
for (const workOrder of overdue) {
|
||||
const result = await settleOverdueWorkOrder({
|
||||
workOrderId: workOrder.id,
|
||||
policy: normalizeWorkOrderTimeoutPolicy(workOrder.timeout_policy),
|
||||
now: nowIso(),
|
||||
})
|
||||
if (result.order) {
|
||||
processedCount += 1
|
||||
} else {
|
||||
skippedCount += 1
|
||||
}
|
||||
}
|
||||
return {
|
||||
checkedCount: overdue.length,
|
||||
processedCount,
|
||||
skippedCount,
|
||||
}
|
||||
}
|
||||
|
||||
export async function listWorkerMyOrders(query: JsonObject = {}, session: WorkerSession) {
|
||||
requireActiveWorkerSession(session)
|
||||
const worker = await getRequiredWorker(session.workerId)
|
||||
await settleOverdueWorkOrders({ workerId: worker.id, limit: 50 })
|
||||
const page = normalizePage(query.page)
|
||||
const pageSize = normalizePageSize(query.pageSize)
|
||||
const worker = await getRequiredWorker(session.workerId)
|
||||
const { items, total } = await listWorkOrders({
|
||||
page,
|
||||
pageSize,
|
||||
@@ -835,6 +889,22 @@ export async function submitWorkerOrderAcceptance(
|
||||
if (sharingShare) {
|
||||
return submitWorkerSharingAcceptance(workOrder, sharingShare, payload, session)
|
||||
}
|
||||
const now = nowIso()
|
||||
if (
|
||||
workOrder.status === WORK_ORDER_STATUS.IN_PROGRESS &&
|
||||
workOrder.deadline_at &&
|
||||
new Date(workOrder.deadline_at).getTime() <= Date.now()
|
||||
) {
|
||||
await settleOverdueWorkOrder({
|
||||
workOrderId: workOrder.id,
|
||||
policy: normalizeWorkOrderTimeoutPolicy(workOrder.timeout_policy),
|
||||
now,
|
||||
})
|
||||
throw createHttpError('任务已超时,系统已按超时策略处置', {
|
||||
statusCode: 409,
|
||||
errorCode: 'work_order_timeout_expired',
|
||||
})
|
||||
}
|
||||
if (Number(workOrder.assigned_worker_id || 0) !== workerId) {
|
||||
throw createHttpError('只能提交自己的订单', {
|
||||
statusCode: 403,
|
||||
@@ -850,7 +920,6 @@ export async function submitWorkerOrderAcceptance(
|
||||
})
|
||||
}
|
||||
|
||||
const now = nowIso()
|
||||
const files = normalizeUploadedFiles(payload.files)
|
||||
const imageUrls = [
|
||||
...files.map((file) => file.url || file.mediumUrl || file.thumbnailUrl).filter(Boolean),
|
||||
|
||||
Reference in New Issue
Block a user