fix(worker-platform): 修复售后欠款抵扣导致验收失败,并支持余额偿还

有待还欠款时结算写入 settled_at 发生 timestamptz/text 类型错误会整单 500;顺带支持充值/解冻自动偿还与后台手动偿还。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yml2213
2026-09-13 20:44:35 +08:00
co-authored by Cursor
parent aba508f4f6
commit a49db84771
10 changed files with 409 additions and 11 deletions
@@ -5,6 +5,7 @@ import {
listAfterSalesCaseEvents,
listAfterSalesCases,
listWorkerAfterSalesCases,
repayAfterSalesCaseDebt,
resolveAfterSalesCase,
submitWorkerAfterSalesResponse,
} from '../../repositories/worker-platform/after-sales-repo.js'
@@ -230,6 +231,80 @@ export async function resolveAdminAfterSalesCase(
}
}
export async function repayAdminAfterSalesCaseDebt(
caseId: number | string,
payload: JsonObject = {},
actorName = '',
) {
const afterSalesCase = await getAfterSalesCaseById(caseId)
if (!afterSalesCase) {
throw createHttpError('售后问题单不存在', {
statusCode: 404,
errorCode: 'after_sales_case_not_found',
})
}
const note = String(payload.note || payload.resolutionNote || '后台手动偿还售后欠款').trim()
if (!note) {
throw createHttpError('请填写偿还备注', {
statusCode: 400,
errorCode: 'after_sales_case_repay_note_required',
})
}
if (note.length > 2_000) {
throw createHttpError('偿还备注不能超过 2000 个字符', {
statusCode: 400,
errorCode: 'after_sales_case_repay_note_too_long',
})
}
const amountRaw = payload.amount ?? payload.repayAmount
const amount =
amountRaw === undefined || amountRaw === null || String(amountRaw).trim() === ''
? null
: normalizeAmountFen(amountRaw, 0)
if (amount !== null && amount <= 0) {
throw createHttpError('偿还金额必须大于 0', {
statusCode: 400,
errorCode: 'after_sales_case_repay_amount_invalid',
})
}
const result = await repayAfterSalesCaseDebt({
caseId: Number(caseId),
amount,
note,
repaidBy: actorName,
now: nowIso(),
})
if (result.failureReason === 'not_found' || !result.case) {
throw createHttpError('售后问题单不存在', {
statusCode: 404,
errorCode: 'after_sales_case_not_found',
})
}
if (result.failureReason === 'no_open_debt') {
throw createHttpError('该售后问题单没有待还欠款', {
statusCode: 409,
errorCode: 'after_sales_case_no_open_debt',
})
}
if (result.failureReason === 'insufficient_balance') {
throw createHttpError('打手可用余额不足,无法偿还欠款', {
statusCode: 409,
errorCode: 'after_sales_case_repay_insufficient_balance',
})
}
const workerId = Number(result.case.worker_id)
publishWorkOrderRealtimeChange({
workOrderId: Number(result.case.work_order_id),
workerIds: workerId > 0 ? [workerId] : [],
})
if (workerId > 0) publishWorkerWalletRealtimeChange(workerId)
return {
case: mapWorkerAfterSalesCase(result.case),
repaidAmount: result.repaidAmount,
availableAfter: result.availableAfter,
}
}
export async function getWorkerAfterSalesCaseSummary(session: WorkerSession) {
requireActiveWorkerSession(session)
return { items: (await listWorkerAfterSalesCases(session.workerId)).map(mapWorkerAfterSalesCase) }