拆分工单异常处置仓储
This commit is contained in:
@@ -11,6 +11,7 @@ export * from './work-order-query-repo.js'
|
|||||||
export * from './work-order-management-repo.js'
|
export * from './work-order-management-repo.js'
|
||||||
export * from './work-order-grab-repo.js'
|
export * from './work-order-grab-repo.js'
|
||||||
export * from './work-order-cancel-repo.js'
|
export * from './work-order-cancel-repo.js'
|
||||||
|
export * from './work-order-issue-repo.js'
|
||||||
export * from './work-order-deposit-query-repo.js'
|
export * from './work-order-deposit-query-repo.js'
|
||||||
export * from './work-order-deposit-repo.js'
|
export * from './work-order-deposit-repo.js'
|
||||||
export * from './work-order-event-repo.js'
|
export * from './work-order-event-repo.js'
|
||||||
|
|||||||
@@ -0,0 +1,269 @@
|
|||||||
|
import { withTransaction } from '../../db/client.js'
|
||||||
|
import {
|
||||||
|
ensureWorkerWalletWithClient,
|
||||||
|
getWorkerWalletWithClient,
|
||||||
|
} from './shared.js'
|
||||||
|
import { createWorkOrderEventWithClient } from './work-order-event-repo.js'
|
||||||
|
import { getWorkOrderByIdWithClient } from './work-order-query-repo.js'
|
||||||
|
import { getOutstandingDepositAmountWithClient } from './work-order-deposit-repo.js'
|
||||||
|
import type { ProblemWorkOrderResolutionAction, WorkOrderRow } from './types.js'
|
||||||
|
|
||||||
|
export async function resolveProblemWorkOrder(input: {
|
||||||
|
workOrderId: number
|
||||||
|
action: ProblemWorkOrderResolutionAction
|
||||||
|
note: string
|
||||||
|
now: string
|
||||||
|
actorName?: string
|
||||||
|
}): Promise<WorkOrderRow | null> {
|
||||||
|
return withTransaction(async (client) => {
|
||||||
|
const currentResult = await client.query<WorkOrderRow>(
|
||||||
|
`
|
||||||
|
SELECT *
|
||||||
|
FROM work_orders
|
||||||
|
WHERE id = $1
|
||||||
|
AND status = 'problem'
|
||||||
|
FOR UPDATE
|
||||||
|
`,
|
||||||
|
[input.workOrderId],
|
||||||
|
)
|
||||||
|
const workOrder = currentResult.rows[0] || null
|
||||||
|
if (!workOrder) return null
|
||||||
|
|
||||||
|
const shouldReleaseDeposit = ['reopen', 'cancel_release'].includes(input.action)
|
||||||
|
const shouldDeductDeposit = input.action === 'cancel_deduct'
|
||||||
|
const workerId = Number(workOrder.assigned_worker_id || 0)
|
||||||
|
let resolvedDepositAmount = 0
|
||||||
|
|
||||||
|
if (workerId > 0 && (shouldReleaseDeposit || shouldDeductDeposit)) {
|
||||||
|
await ensureWorkerWalletWithClient(client, workerId, input.now)
|
||||||
|
const wallet = await getWorkerWalletWithClient(client, workerId)
|
||||||
|
resolvedDepositAmount = Math.min(
|
||||||
|
await getOutstandingDepositAmountWithClient(client, workerId, input.workOrderId),
|
||||||
|
Number(wallet?.frozen_deposit_amount || 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (resolvedDepositAmount > 0) {
|
||||||
|
const nextAvailable =
|
||||||
|
Number(wallet?.available_amount || 0) + (shouldReleaseDeposit ? resolvedDepositAmount : 0)
|
||||||
|
const nextFrozen = Math.max(
|
||||||
|
0,
|
||||||
|
Number(wallet?.frozen_deposit_amount || 0) - resolvedDepositAmount,
|
||||||
|
)
|
||||||
|
const ledgerType = shouldReleaseDeposit ? 'deposit_release' : 'deposit_deduction'
|
||||||
|
const ledgerAmount = shouldReleaseDeposit ? resolvedDepositAmount : -resolvedDepositAmount
|
||||||
|
const ledgerNote = shouldReleaseDeposit ? '问题单处置释放押金' : '问题单处置扣除押金'
|
||||||
|
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
UPDATE worker_wallets
|
||||||
|
SET available_amount = $1, frozen_deposit_amount = $2, updated_at = $3
|
||||||
|
WHERE worker_id = $4
|
||||||
|
`,
|
||||||
|
[nextAvailable, nextFrozen, input.now, workerId],
|
||||||
|
)
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9)
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
workerId,
|
||||||
|
ledgerType,
|
||||||
|
ledgerAmount,
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
input.workOrderId,
|
||||||
|
ledgerNote,
|
||||||
|
JSON.stringify({ action: input.action, note: input.note }),
|
||||||
|
input.now,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetStatus =
|
||||||
|
input.action === 'return_to_worker'
|
||||||
|
? 'in_progress'
|
||||||
|
: input.action === 'reopen'
|
||||||
|
? 'open'
|
||||||
|
: 'cancelled'
|
||||||
|
const shouldClearAssignment = input.action === 'reopen'
|
||||||
|
const shouldClearSubmission = ['return_to_worker', 'reopen'].includes(input.action)
|
||||||
|
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
UPDATE work_orders
|
||||||
|
SET
|
||||||
|
status = $1,
|
||||||
|
assigned_worker_id = CASE WHEN $2 THEN NULL ELSE assigned_worker_id END,
|
||||||
|
assigned_at = CASE WHEN $2 THEN NULL ELSE assigned_at END,
|
||||||
|
acceptance_json = CASE WHEN $3 THEN '{}'::jsonb ELSE acceptance_json END,
|
||||||
|
submitted_at = CASE WHEN $3 THEN NULL ELSE submitted_at END,
|
||||||
|
problem_note = CASE WHEN $2 THEN '' ELSE problem_note END,
|
||||||
|
deadline_at = CASE
|
||||||
|
WHEN $1 = 'in_progress' AND timeout_minutes > 0
|
||||||
|
THEN NOW() + (timeout_minutes * INTERVAL '1 minute')
|
||||||
|
WHEN $1 = 'open' THEN NULL
|
||||||
|
ELSE deadline_at
|
||||||
|
END,
|
||||||
|
updated_at = $4
|
||||||
|
WHERE id = $5
|
||||||
|
`,
|
||||||
|
[targetStatus, shouldClearAssignment, shouldClearSubmission, input.now, input.workOrderId],
|
||||||
|
)
|
||||||
|
|
||||||
|
await createWorkOrderEventWithClient(client, {
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
actorType: 'admin',
|
||||||
|
actorId: input.actorName || '',
|
||||||
|
eventType: `problem_${input.action}`,
|
||||||
|
fromStatus: 'problem',
|
||||||
|
toStatus: targetStatus,
|
||||||
|
payloadJson: JSON.stringify({
|
||||||
|
action: input.action,
|
||||||
|
note: input.note,
|
||||||
|
depositAmount: resolvedDepositAmount,
|
||||||
|
}),
|
||||||
|
now: input.now,
|
||||||
|
})
|
||||||
|
|
||||||
|
return getWorkOrderByIdWithClient(client, input.workOrderId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function settleOverdueWorkOrder(input: {
|
||||||
|
workOrderId: number
|
||||||
|
policy: 'reopen' | 'cancel_release' | 'cancel_deduct'
|
||||||
|
now: string
|
||||||
|
}): Promise<{
|
||||||
|
order: WorkOrderRow | null
|
||||||
|
failureReason: 'work_order_not_overdue' | 'work_order_has_active_shares' | null
|
||||||
|
}> {
|
||||||
|
return withTransaction(async (client) => {
|
||||||
|
const currentResult = await client.query<WorkOrderRow>(
|
||||||
|
`
|
||||||
|
SELECT *
|
||||||
|
FROM work_orders
|
||||||
|
WHERE id = $1
|
||||||
|
AND status = 'in_progress'
|
||||||
|
AND deadline_at IS NOT NULL
|
||||||
|
AND deadline_at < NOW()
|
||||||
|
FOR UPDATE
|
||||||
|
`,
|
||||||
|
[input.workOrderId],
|
||||||
|
)
|
||||||
|
const workOrder = currentResult.rows[0] || null
|
||||||
|
if (!workOrder) {
|
||||||
|
return { order: null, failureReason: 'work_order_not_overdue' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const shareResult = await client.query<{ total: number }>(
|
||||||
|
`
|
||||||
|
SELECT COUNT(*)::int AS total
|
||||||
|
FROM work_order_shares
|
||||||
|
WHERE work_order_id = $1 AND status != 'cancelled'
|
||||||
|
`,
|
||||||
|
[input.workOrderId],
|
||||||
|
)
|
||||||
|
if (Number(shareResult.rows[0]?.total || 0) > 0) {
|
||||||
|
return { order: null, failureReason: 'work_order_has_active_shares' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldReleaseDeposit = ['reopen', 'cancel_release'].includes(input.policy)
|
||||||
|
const shouldDeductDeposit = input.policy === 'cancel_deduct'
|
||||||
|
const workerId = Number(workOrder.assigned_worker_id || 0)
|
||||||
|
let resolvedDepositAmount = 0
|
||||||
|
|
||||||
|
if (workerId > 0 && (shouldReleaseDeposit || shouldDeductDeposit)) {
|
||||||
|
await ensureWorkerWalletWithClient(client, workerId, input.now)
|
||||||
|
const wallet = await getWorkerWalletWithClient(client, workerId)
|
||||||
|
resolvedDepositAmount = Math.min(
|
||||||
|
await getOutstandingDepositAmountWithClient(client, workerId, input.workOrderId),
|
||||||
|
Number(wallet?.frozen_deposit_amount || 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (resolvedDepositAmount > 0) {
|
||||||
|
const nextAvailable =
|
||||||
|
Number(wallet?.available_amount || 0) + (shouldReleaseDeposit ? resolvedDepositAmount : 0)
|
||||||
|
const nextFrozen = Math.max(
|
||||||
|
0,
|
||||||
|
Number(wallet?.frozen_deposit_amount || 0) - resolvedDepositAmount,
|
||||||
|
)
|
||||||
|
const ledgerType = shouldReleaseDeposit ? 'deposit_release' : 'deposit_deduction'
|
||||||
|
const ledgerAmount = shouldReleaseDeposit ? resolvedDepositAmount : -resolvedDepositAmount
|
||||||
|
const ledgerNote = shouldReleaseDeposit ? '超时处置释放押金' : '超时处置扣除押金'
|
||||||
|
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
UPDATE worker_wallets
|
||||||
|
SET available_amount = $1, frozen_deposit_amount = $2, updated_at = $3
|
||||||
|
WHERE worker_id = $4
|
||||||
|
`,
|
||||||
|
[nextAvailable, nextFrozen, input.now, workerId],
|
||||||
|
)
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9)
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
workerId,
|
||||||
|
ledgerType,
|
||||||
|
ledgerAmount,
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
input.workOrderId,
|
||||||
|
ledgerNote,
|
||||||
|
JSON.stringify({ action: `timeout_${input.policy}` }),
|
||||||
|
input.now,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetStatus = input.policy === 'reopen' ? 'open' : 'cancelled'
|
||||||
|
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
UPDATE work_orders
|
||||||
|
SET
|
||||||
|
status = $1,
|
||||||
|
assigned_worker_id = NULL,
|
||||||
|
assigned_at = NULL,
|
||||||
|
deadline_at = NULL,
|
||||||
|
acceptance_json = CASE WHEN $1 = 'open' THEN '{}'::jsonb ELSE acceptance_json END,
|
||||||
|
submitted_at = CASE WHEN $1 = 'open' THEN NULL ELSE submitted_at END,
|
||||||
|
problem_note = CASE WHEN $1 = 'open' THEN '' ELSE problem_note END,
|
||||||
|
published_at = CASE WHEN $1 = 'open' THEN $2 ELSE published_at END,
|
||||||
|
hall_queued_at = CASE WHEN $1 = 'open' THEN $2 ELSE hall_queued_at END,
|
||||||
|
updated_at = $2
|
||||||
|
WHERE id = $3
|
||||||
|
`,
|
||||||
|
[targetStatus, input.now, input.workOrderId],
|
||||||
|
)
|
||||||
|
|
||||||
|
await createWorkOrderEventWithClient(client, {
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
actorType: 'system',
|
||||||
|
actorId: '',
|
||||||
|
eventType: `timeout_${input.policy}`,
|
||||||
|
fromStatus: 'in_progress',
|
||||||
|
toStatus: targetStatus,
|
||||||
|
payloadJson: JSON.stringify({
|
||||||
|
policy: input.policy,
|
||||||
|
depositAmount: resolvedDepositAmount,
|
||||||
|
workerId: workerId > 0 ? workerId : null,
|
||||||
|
}),
|
||||||
|
now: input.now,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
order: await getWorkOrderByIdWithClient(client, input.workOrderId),
|
||||||
|
failureReason: null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -69,6 +69,10 @@ export {
|
|||||||
returnAssignedWorkOrderToHall,
|
returnAssignedWorkOrderToHall,
|
||||||
unassignWorkOrder,
|
unassignWorkOrder,
|
||||||
} from './work-order-cancel-repo.js'
|
} from './work-order-cancel-repo.js'
|
||||||
|
export {
|
||||||
|
resolveProblemWorkOrder,
|
||||||
|
settleOverdueWorkOrder,
|
||||||
|
} from './work-order-issue-repo.js'
|
||||||
|
|
||||||
export async function acceptWorkOrderAndSettle(input: {
|
export async function acceptWorkOrderAndSettle(input: {
|
||||||
workOrderId: number
|
workOrderId: number
|
||||||
@@ -767,263 +771,3 @@ export async function cancelWorkOrderShare(input: {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resolveProblemWorkOrder(input: {
|
|
||||||
workOrderId: number
|
|
||||||
action: ProblemWorkOrderResolutionAction
|
|
||||||
note: string
|
|
||||||
now: string
|
|
||||||
actorName?: string
|
|
||||||
}): Promise<WorkOrderRow | null> {
|
|
||||||
return withTransaction(async (client) => {
|
|
||||||
const currentResult = await client.query<WorkOrderRow>(
|
|
||||||
`
|
|
||||||
SELECT *
|
|
||||||
FROM work_orders
|
|
||||||
WHERE id = $1
|
|
||||||
AND status = 'problem'
|
|
||||||
FOR UPDATE
|
|
||||||
`,
|
|
||||||
[input.workOrderId],
|
|
||||||
)
|
|
||||||
const workOrder = currentResult.rows[0] || null
|
|
||||||
if (!workOrder) return null
|
|
||||||
|
|
||||||
const shouldReleaseDeposit = ['reopen', 'cancel_release'].includes(input.action)
|
|
||||||
const shouldDeductDeposit = input.action === 'cancel_deduct'
|
|
||||||
const workerId = Number(workOrder.assigned_worker_id || 0)
|
|
||||||
let resolvedDepositAmount = 0
|
|
||||||
|
|
||||||
if (workerId > 0 && (shouldReleaseDeposit || shouldDeductDeposit)) {
|
|
||||||
await ensureWorkerWalletWithClient(client, workerId, input.now)
|
|
||||||
const wallet = await getWorkerWalletWithClient(client, workerId)
|
|
||||||
resolvedDepositAmount = Math.min(
|
|
||||||
await getOutstandingDepositAmountWithClient(client, workerId, input.workOrderId),
|
|
||||||
Number(wallet?.frozen_deposit_amount || 0),
|
|
||||||
)
|
|
||||||
|
|
||||||
if (resolvedDepositAmount > 0) {
|
|
||||||
const nextAvailable =
|
|
||||||
Number(wallet?.available_amount || 0) + (shouldReleaseDeposit ? resolvedDepositAmount : 0)
|
|
||||||
const nextFrozen = Math.max(
|
|
||||||
0,
|
|
||||||
Number(wallet?.frozen_deposit_amount || 0) - resolvedDepositAmount,
|
|
||||||
)
|
|
||||||
const ledgerType = shouldReleaseDeposit ? 'deposit_release' : 'deposit_deduction'
|
|
||||||
const ledgerAmount = shouldReleaseDeposit ? resolvedDepositAmount : -resolvedDepositAmount
|
|
||||||
const ledgerNote = shouldReleaseDeposit ? '问题单处置释放押金' : '问题单处置扣除押金'
|
|
||||||
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
UPDATE worker_wallets
|
|
||||||
SET available_amount = $1, frozen_deposit_amount = $2, updated_at = $3
|
|
||||||
WHERE worker_id = $4
|
|
||||||
`,
|
|
||||||
[nextAvailable, nextFrozen, input.now, workerId],
|
|
||||||
)
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9)
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
workerId,
|
|
||||||
ledgerType,
|
|
||||||
ledgerAmount,
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
input.workOrderId,
|
|
||||||
ledgerNote,
|
|
||||||
JSON.stringify({ action: input.action, note: input.note }),
|
|
||||||
input.now,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetStatus =
|
|
||||||
input.action === 'return_to_worker'
|
|
||||||
? 'in_progress'
|
|
||||||
: input.action === 'reopen'
|
|
||||||
? 'open'
|
|
||||||
: 'cancelled'
|
|
||||||
const shouldClearAssignment = input.action === 'reopen'
|
|
||||||
const shouldClearSubmission = ['return_to_worker', 'reopen'].includes(input.action)
|
|
||||||
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
UPDATE work_orders
|
|
||||||
SET
|
|
||||||
status = $1,
|
|
||||||
assigned_worker_id = CASE WHEN $2 THEN NULL ELSE assigned_worker_id END,
|
|
||||||
assigned_at = CASE WHEN $2 THEN NULL ELSE assigned_at END,
|
|
||||||
acceptance_json = CASE WHEN $3 THEN '{}'::jsonb ELSE acceptance_json END,
|
|
||||||
submitted_at = CASE WHEN $3 THEN NULL ELSE submitted_at END,
|
|
||||||
problem_note = CASE WHEN $2 THEN '' ELSE problem_note END,
|
|
||||||
deadline_at = CASE
|
|
||||||
WHEN $1 = 'in_progress' AND timeout_minutes > 0
|
|
||||||
THEN NOW() + (timeout_minutes * INTERVAL '1 minute')
|
|
||||||
WHEN $1 = 'open' THEN NULL
|
|
||||||
ELSE deadline_at
|
|
||||||
END,
|
|
||||||
updated_at = $4
|
|
||||||
WHERE id = $5
|
|
||||||
`,
|
|
||||||
[targetStatus, shouldClearAssignment, shouldClearSubmission, input.now, input.workOrderId],
|
|
||||||
)
|
|
||||||
|
|
||||||
await createWorkOrderEventWithClient(client, {
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
actorType: 'admin',
|
|
||||||
actorId: input.actorName || '',
|
|
||||||
eventType: `problem_${input.action}`,
|
|
||||||
fromStatus: 'problem',
|
|
||||||
toStatus: targetStatus,
|
|
||||||
payloadJson: JSON.stringify({
|
|
||||||
action: input.action,
|
|
||||||
note: input.note,
|
|
||||||
depositAmount: resolvedDepositAmount,
|
|
||||||
}),
|
|
||||||
now: input.now,
|
|
||||||
})
|
|
||||||
|
|
||||||
return getWorkOrderByIdWithClient(client, input.workOrderId)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function settleOverdueWorkOrder(input: {
|
|
||||||
workOrderId: number
|
|
||||||
policy: 'reopen' | 'cancel_release' | 'cancel_deduct'
|
|
||||||
now: string
|
|
||||||
}): Promise<{
|
|
||||||
order: WorkOrderRow | null
|
|
||||||
failureReason: 'work_order_not_overdue' | 'work_order_has_active_shares' | null
|
|
||||||
}> {
|
|
||||||
return withTransaction(async (client) => {
|
|
||||||
const currentResult = await client.query<WorkOrderRow>(
|
|
||||||
`
|
|
||||||
SELECT *
|
|
||||||
FROM work_orders
|
|
||||||
WHERE id = $1
|
|
||||||
AND status = 'in_progress'
|
|
||||||
AND deadline_at IS NOT NULL
|
|
||||||
AND deadline_at < NOW()
|
|
||||||
FOR UPDATE
|
|
||||||
`,
|
|
||||||
[input.workOrderId],
|
|
||||||
)
|
|
||||||
const workOrder = currentResult.rows[0] || null
|
|
||||||
if (!workOrder) {
|
|
||||||
return { order: null, failureReason: 'work_order_not_overdue' }
|
|
||||||
}
|
|
||||||
|
|
||||||
const shareResult = await client.query<{ total: number }>(
|
|
||||||
`
|
|
||||||
SELECT COUNT(*)::int AS total
|
|
||||||
FROM work_order_shares
|
|
||||||
WHERE work_order_id = $1 AND status != 'cancelled'
|
|
||||||
`,
|
|
||||||
[input.workOrderId],
|
|
||||||
)
|
|
||||||
if (Number(shareResult.rows[0]?.total || 0) > 0) {
|
|
||||||
return { order: null, failureReason: 'work_order_has_active_shares' }
|
|
||||||
}
|
|
||||||
|
|
||||||
const shouldReleaseDeposit = ['reopen', 'cancel_release'].includes(input.policy)
|
|
||||||
const shouldDeductDeposit = input.policy === 'cancel_deduct'
|
|
||||||
const workerId = Number(workOrder.assigned_worker_id || 0)
|
|
||||||
let resolvedDepositAmount = 0
|
|
||||||
|
|
||||||
if (workerId > 0 && (shouldReleaseDeposit || shouldDeductDeposit)) {
|
|
||||||
await ensureWorkerWalletWithClient(client, workerId, input.now)
|
|
||||||
const wallet = await getWorkerWalletWithClient(client, workerId)
|
|
||||||
resolvedDepositAmount = Math.min(
|
|
||||||
await getOutstandingDepositAmountWithClient(client, workerId, input.workOrderId),
|
|
||||||
Number(wallet?.frozen_deposit_amount || 0),
|
|
||||||
)
|
|
||||||
|
|
||||||
if (resolvedDepositAmount > 0) {
|
|
||||||
const nextAvailable =
|
|
||||||
Number(wallet?.available_amount || 0) + (shouldReleaseDeposit ? resolvedDepositAmount : 0)
|
|
||||||
const nextFrozen = Math.max(
|
|
||||||
0,
|
|
||||||
Number(wallet?.frozen_deposit_amount || 0) - resolvedDepositAmount,
|
|
||||||
)
|
|
||||||
const ledgerType = shouldReleaseDeposit ? 'deposit_release' : 'deposit_deduction'
|
|
||||||
const ledgerAmount = shouldReleaseDeposit ? resolvedDepositAmount : -resolvedDepositAmount
|
|
||||||
const ledgerNote = shouldReleaseDeposit ? '超时处置释放押金' : '超时处置扣除押金'
|
|
||||||
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
UPDATE worker_wallets
|
|
||||||
SET available_amount = $1, frozen_deposit_amount = $2, updated_at = $3
|
|
||||||
WHERE worker_id = $4
|
|
||||||
`,
|
|
||||||
[nextAvailable, nextFrozen, input.now, workerId],
|
|
||||||
)
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9)
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
workerId,
|
|
||||||
ledgerType,
|
|
||||||
ledgerAmount,
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
input.workOrderId,
|
|
||||||
ledgerNote,
|
|
||||||
JSON.stringify({ action: `timeout_${input.policy}` }),
|
|
||||||
input.now,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetStatus = input.policy === 'reopen' ? 'open' : 'cancelled'
|
|
||||||
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
UPDATE work_orders
|
|
||||||
SET
|
|
||||||
status = $1,
|
|
||||||
assigned_worker_id = NULL,
|
|
||||||
assigned_at = NULL,
|
|
||||||
deadline_at = NULL,
|
|
||||||
acceptance_json = CASE WHEN $1 = 'open' THEN '{}'::jsonb ELSE acceptance_json END,
|
|
||||||
submitted_at = CASE WHEN $1 = 'open' THEN NULL ELSE submitted_at END,
|
|
||||||
problem_note = CASE WHEN $1 = 'open' THEN '' ELSE problem_note END,
|
|
||||||
published_at = CASE WHEN $1 = 'open' THEN $2 ELSE published_at END,
|
|
||||||
hall_queued_at = CASE WHEN $1 = 'open' THEN $2 ELSE hall_queued_at END,
|
|
||||||
updated_at = $2
|
|
||||||
WHERE id = $3
|
|
||||||
`,
|
|
||||||
[targetStatus, input.now, input.workOrderId],
|
|
||||||
)
|
|
||||||
|
|
||||||
await createWorkOrderEventWithClient(client, {
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
actorType: 'system',
|
|
||||||
actorId: '',
|
|
||||||
eventType: `timeout_${input.policy}`,
|
|
||||||
fromStatus: 'in_progress',
|
|
||||||
toStatus: targetStatus,
|
|
||||||
payloadJson: JSON.stringify({
|
|
||||||
policy: input.policy,
|
|
||||||
depositAmount: resolvedDepositAmount,
|
|
||||||
workerId: workerId > 0 ? workerId : null,
|
|
||||||
}),
|
|
||||||
now: input.now,
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
order: await getWorkOrderByIdWithClient(client, input.workOrderId),
|
|
||||||
failureReason: null,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user