优化后端-接单平台部分逻辑
This commit is contained in:
@@ -74,6 +74,9 @@ export type WorkerFinanceRequestRow = {
|
||||
created_at: string
|
||||
updated_at: string
|
||||
reviewed_at: string | null
|
||||
worker_username?: string
|
||||
worker_display_name?: string
|
||||
worker_phone?: string
|
||||
}
|
||||
|
||||
export type WorkCategoryRow = {
|
||||
@@ -179,6 +182,7 @@ type FinanceRequestListInput = {
|
||||
workerId?: number
|
||||
status?: string
|
||||
requestType?: string
|
||||
keyword?: string
|
||||
}
|
||||
|
||||
type CreateWorkerInput = {
|
||||
@@ -242,6 +246,16 @@ const WORK_PRODUCT_RULE_SELECT = `
|
||||
LEFT JOIN work_categories wc ON wc.id = wpr.category_id
|
||||
`
|
||||
|
||||
const WORKER_FINANCE_REQUEST_SELECT = `
|
||||
SELECT
|
||||
wfr.*,
|
||||
wu.username AS worker_username,
|
||||
wu.display_name AS worker_display_name,
|
||||
wu.phone AS worker_phone
|
||||
FROM worker_finance_requests wfr
|
||||
LEFT JOIN worker_users wu ON wu.id = wfr.worker_id
|
||||
`
|
||||
|
||||
export async function getWorkerLevelByKey(levelKey: string): Promise<WorkerLevelRow | null> {
|
||||
const result = await query<WorkerLevelRow>(
|
||||
'SELECT * FROM worker_levels WHERE level_key = $1 LIMIT 1',
|
||||
@@ -580,32 +594,43 @@ export async function createWorkerFinanceRequest(input: {
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function getWorkerFinanceRequestById(
|
||||
requestId: number | string,
|
||||
): Promise<WorkerFinanceRequestRow | null> {
|
||||
const result = await query<WorkerFinanceRequestRow>(
|
||||
`${WORKER_FINANCE_REQUEST_SELECT} WHERE wfr.id = $1 LIMIT 1`,
|
||||
[Number(requestId)],
|
||||
)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function listWorkerFinanceRequests({
|
||||
page = 1,
|
||||
pageSize = 20,
|
||||
workerId = 0,
|
||||
status = '',
|
||||
requestType = '',
|
||||
keyword = '',
|
||||
}: FinanceRequestListInput = {}): Promise<{ items: WorkerFinanceRequestRow[]; total: number }> {
|
||||
const { whereClause, params } = buildWorkerFinanceRequestWhere({
|
||||
workerId,
|
||||
status,
|
||||
requestType,
|
||||
keyword,
|
||||
})
|
||||
const totalResult = await query<{ total: number }>(
|
||||
`SELECT COUNT(*)::int AS total FROM worker_finance_requests wfr ${whereClause}`,
|
||||
`SELECT COUNT(*)::int AS total FROM worker_finance_requests wfr
|
||||
LEFT JOIN worker_users wu ON wu.id = wfr.worker_id
|
||||
${whereClause}`,
|
||||
params,
|
||||
)
|
||||
const offset = (page - 1) * pageSize
|
||||
params.push(pageSize, offset)
|
||||
const itemsResult = await query<WorkerFinanceRequestRow>(
|
||||
`
|
||||
SELECT *
|
||||
FROM worker_finance_requests wfr
|
||||
${whereClause}
|
||||
ORDER BY wfr.created_at DESC, wfr.id DESC
|
||||
LIMIT $${params.length - 1} OFFSET $${params.length}
|
||||
`,
|
||||
`${WORKER_FINANCE_REQUEST_SELECT}
|
||||
${whereClause}
|
||||
ORDER BY wfr.created_at DESC, wfr.id DESC
|
||||
LIMIT $${params.length - 1} OFFSET $${params.length}`,
|
||||
params,
|
||||
)
|
||||
return {
|
||||
@@ -614,6 +639,146 @@ export async function listWorkerFinanceRequests({
|
||||
}
|
||||
}
|
||||
|
||||
export async function reviewWorkerFinanceRequest(input: {
|
||||
requestId: number
|
||||
status: string
|
||||
reviewedNote: string
|
||||
now: string
|
||||
}): Promise<{
|
||||
request: WorkerFinanceRequestRow | null
|
||||
failureReason: 'request_not_pending' | 'withdraw_insufficient' | null
|
||||
}> {
|
||||
return withTransaction(async (client) => {
|
||||
const currentResult = await client.query<WorkerFinanceRequestRow>(
|
||||
`
|
||||
SELECT *
|
||||
FROM worker_finance_requests
|
||||
WHERE id = $1
|
||||
LIMIT 1
|
||||
FOR UPDATE
|
||||
`,
|
||||
[input.requestId],
|
||||
)
|
||||
const current = currentResult.rows[0] || null
|
||||
if (!current) {
|
||||
return {
|
||||
request: null,
|
||||
failureReason: null,
|
||||
}
|
||||
}
|
||||
|
||||
if (current.status !== 'pending') {
|
||||
return {
|
||||
request: await getWorkerFinanceRequestByIdWithClient(client, input.requestId),
|
||||
failureReason: 'request_not_pending',
|
||||
}
|
||||
}
|
||||
|
||||
if (input.status === 'approved') {
|
||||
await ensureWorkerWalletWithClient(client, Number(current.worker_id), input.now)
|
||||
const wallet = await getWorkerWalletWithClient(client, Number(current.worker_id))
|
||||
const availableAmount = Number(wallet?.available_amount || 0)
|
||||
const frozenAmount = Number(wallet?.frozen_deposit_amount || 0)
|
||||
const amount = Number(current.amount || 0)
|
||||
const note =
|
||||
input.reviewedNote ||
|
||||
(current.request_type === 'withdraw' ? '提现申请审核通过' : '充值申请审核通过')
|
||||
|
||||
if (current.request_type === 'withdraw') {
|
||||
if (availableAmount < amount) {
|
||||
return {
|
||||
request: await getWorkerFinanceRequestByIdWithClient(client, input.requestId),
|
||||
failureReason: 'withdraw_insufficient',
|
||||
}
|
||||
}
|
||||
|
||||
const nextAvailable = availableAmount - amount
|
||||
await client.query(
|
||||
`
|
||||
UPDATE worker_wallets
|
||||
SET available_amount = $1, updated_at = $2
|
||||
WHERE worker_id = $3
|
||||
`,
|
||||
[nextAvailable, input.now, Number(current.worker_id)],
|
||||
)
|
||||
await client.query(
|
||||
`
|
||||
INSERT INTO worker_wallet_ledgers (
|
||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||
audit_status, note, payload_json, created_at
|
||||
) VALUES ($1, 'withdraw_paid', $2, $3, $4, 'approved', $5, $6::jsonb, $7)
|
||||
`,
|
||||
[
|
||||
Number(current.worker_id),
|
||||
-amount,
|
||||
nextAvailable,
|
||||
frozenAmount,
|
||||
note,
|
||||
JSON.stringify({
|
||||
requestId: Number(current.id),
|
||||
requestType: current.request_type,
|
||||
source: 'admin_finance_review',
|
||||
}),
|
||||
input.now,
|
||||
],
|
||||
)
|
||||
} else {
|
||||
const nextAvailable = availableAmount + amount
|
||||
await client.query(
|
||||
`
|
||||
UPDATE worker_wallets
|
||||
SET
|
||||
available_amount = $1,
|
||||
total_credited_amount = total_credited_amount + $2,
|
||||
updated_at = $3
|
||||
WHERE worker_id = $4
|
||||
`,
|
||||
[nextAvailable, amount, input.now, Number(current.worker_id)],
|
||||
)
|
||||
await client.query(
|
||||
`
|
||||
INSERT INTO worker_wallet_ledgers (
|
||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||
audit_status, note, payload_json, created_at
|
||||
) VALUES ($1, 'manual_credit', $2, $3, $4, 'approved', $5, $6::jsonb, $7)
|
||||
`,
|
||||
[
|
||||
Number(current.worker_id),
|
||||
amount,
|
||||
nextAvailable,
|
||||
frozenAmount,
|
||||
note,
|
||||
JSON.stringify({
|
||||
requestId: Number(current.id),
|
||||
requestType: current.request_type,
|
||||
source: 'admin_finance_review',
|
||||
}),
|
||||
input.now,
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
await client.query(
|
||||
`
|
||||
UPDATE worker_finance_requests
|
||||
SET
|
||||
status = $1,
|
||||
reviewed_note = $2,
|
||||
reviewed_at = $3,
|
||||
updated_at = $3
|
||||
WHERE id = $4
|
||||
`,
|
||||
[input.status, input.reviewedNote, input.now, input.requestId],
|
||||
)
|
||||
|
||||
return {
|
||||
request: await getWorkerFinanceRequestByIdWithClient(client, input.requestId),
|
||||
failureReason: null,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function getWorkerFinanceRequestSummary(workerId: number | string) {
|
||||
const result = await query<{
|
||||
pending_withdraw_amount: number
|
||||
@@ -1421,6 +1586,17 @@ async function getWorkerUserByIdWithClient(
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
async function getWorkerFinanceRequestByIdWithClient(
|
||||
client: PoolClient,
|
||||
requestId: number | string,
|
||||
): Promise<WorkerFinanceRequestRow | null> {
|
||||
const result = await client.query<WorkerFinanceRequestRow>(
|
||||
`${WORKER_FINANCE_REQUEST_SELECT} WHERE wfr.id = $1 LIMIT 1`,
|
||||
[Number(requestId)],
|
||||
)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
async function getWorkOrderByIdWithClient(
|
||||
client: PoolClient,
|
||||
workOrderId: number | string,
|
||||
@@ -1500,6 +1676,7 @@ function buildWorkerFinanceRequestWhere({
|
||||
workerId = 0,
|
||||
status = '',
|
||||
requestType = '',
|
||||
keyword = '',
|
||||
}: FinanceRequestListInput) {
|
||||
const filters: string[] = []
|
||||
const params: unknown[] = []
|
||||
@@ -1515,6 +1692,16 @@ function buildWorkerFinanceRequestWhere({
|
||||
params.push(requestType)
|
||||
filters.push(`wfr.request_type = $${params.length}`)
|
||||
}
|
||||
if (keyword) {
|
||||
params.push(`%${keyword}%`)
|
||||
filters.push(
|
||||
`(wu.username ILIKE $${params.length}
|
||||
OR wu.display_name ILIKE $${params.length}
|
||||
OR wu.phone ILIKE $${params.length}
|
||||
OR wfr.account_name ILIKE $${params.length}
|
||||
OR wfr.account_no ILIKE $${params.length})`,
|
||||
)
|
||||
}
|
||||
return {
|
||||
whereClause: filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '',
|
||||
params,
|
||||
|
||||
Reference in New Issue
Block a user