资金申请增加渠道筛选合计与审核留痕

This commit is contained in:
yml2213
2026-08-22 09:57:28 +08:00
parent 3618df74c5
commit 985e61209d
10 changed files with 273 additions and 14 deletions
@@ -471,6 +471,8 @@ export type FinanceRequestListInput = {
/** 申请时间区间(YYYY-MM-DD,上海时区,含起止日);后台列表筛选使用。 */
createdFrom?: string
createdTo?: string
/** 收款渠道筛选:alipay / wechat */
accountChannel?: string
}
export type CreateWorkerInput = {
@@ -205,6 +205,7 @@ export async function listWorkerFinanceRequests({
keyword = '',
createdFrom = '',
createdTo = '',
accountChannel = '',
}: FinanceRequestListInput = {}): Promise<{ items: WorkerFinanceRequestRow[]; total: number }> {
const { whereClause, params } = buildWorkerFinanceRequestWhere({
requestId,
@@ -214,6 +215,7 @@ export async function listWorkerFinanceRequests({
keyword,
createdFrom,
createdTo,
accountChannel,
})
const totalResult = await query<{ total: number }>(
`SELECT COUNT(*)::int AS total FROM worker_finance_requests wfr
@@ -236,11 +238,46 @@ export async function listWorkerFinanceRequests({
}
}
/** 按当前列表筛选条件汇总各收款渠道的笔数与金额,供后台合计条展示。 */
export async function summarizeWorkerFinanceRequests(
input: FinanceRequestListInput = {},
): Promise<Array<{ channel: string; count: number; amount: number }>> {
const { whereClause, params } = buildWorkerFinanceRequestWhere({
requestId: input.requestId || 0,
workerId: input.workerId || 0,
status: input.status || '',
requestType: input.requestType || '',
keyword: input.keyword || '',
createdFrom: input.createdFrom || '',
createdTo: input.createdTo || '',
accountChannel: input.accountChannel || '',
})
const result = await query<{ channel: string; total: number; amount: string | number }>(
`
SELECT
COALESCE(NULLIF(wfr.account_channel, ''), 'other') AS channel,
COUNT(*)::int AS total,
COALESCE(SUM(wfr.amount), 0)::bigint AS amount
FROM worker_finance_requests wfr
LEFT JOIN worker_users wu ON wu.id = wfr.worker_id
${whereClause}
GROUP BY 1
`,
params,
)
return result.rows.map((row) => ({
channel: String(row.channel || 'other'),
count: Number(row.total || 0),
amount: Number(row.amount || 0),
}))
}
export async function reviewWorkerFinanceRequest(input: {
requestId: number
status: string
reviewedNote: string
now: string
reviewedBy?: string
}): Promise<{
request: WorkerFinanceRequestRow | null
failureReason: 'request_not_pending' | 'withdraw_insufficient' | null
@@ -357,10 +394,24 @@ export async function reviewWorkerFinanceRequest(input: {
status = $1,
reviewed_note = $2,
reviewed_at = $3,
updated_at = $3
updated_at = $3,
payload_json = payload_json || $5::jsonb
WHERE id = $4
`,
[input.status, input.reviewedNote, input.now, input.requestId],
[
input.status,
input.reviewedNote,
input.now,
input.requestId,
JSON.stringify({
review: {
by: String(input.reviewedBy || '').trim(),
at: input.now,
status: input.status,
note: input.reviewedNote,
},
}),
],
)
return {
@@ -421,9 +472,14 @@ function buildWorkerFinanceRequestWhere({
keyword = '',
createdFrom = '',
createdTo = '',
accountChannel = '',
}: FinanceRequestListInput) {
const filters: string[] = []
const params: unknown[] = []
if (accountChannel) {
params.push(accountChannel)
filters.push(`wfr.account_channel = $${params.length}`)
}
if (createdFrom) {
params.push(createdFrom)
filters.push(
@@ -272,6 +272,7 @@ export {
listWorkerFinanceRequests,
listWorkerWithdrawalAccounts,
reviewWorkerFinanceRequest,
summarizeWorkerFinanceRequests,
upsertWorkerWithdrawalAccount,
} from './worker-finance-repo.js'
@@ -22,7 +22,12 @@ router.post(
'/worker-platform/finance-requests/:requestId/review',
requireAdminRoles(['admin', 'operator']),
createJsonHandler(
(req) => reviewAdminWorkerFinanceRequest(String(req.params.requestId || ''), req.body || {}),
(req) =>
reviewAdminWorkerFinanceRequest(
String(req.params.requestId || ''),
req.body || {},
req.adminSession?.username || '',
),
{
successMessage: '资金申请已处理',
errorMessage: '处理资金申请失败',
@@ -1,4 +1,5 @@
import {
summarizeWorkerFinanceRequests,
addWorkerWalletCredit,
getWorkerFinanceRequestById,
listWorkerFinanceRequests,
@@ -169,25 +170,39 @@ export async function listAdminWorkerFinanceRequests(query: JsonObject = {}) {
const keyword = String(query.keyword || '').trim()
const createdFrom = normalizeDateString(query.createdFrom)
const createdTo = normalizeDateString(query.createdTo)
const { items, total } = await listWorkerFinanceRequests({
page,
pageSize,
const accountChannel = ['alipay', 'wechat'].includes(String(query.accountChannel || ''))
? String(query.accountChannel)
: ''
const listFilters = {
requestId,
status,
requestType,
keyword,
...(createdFrom ? { createdFrom } : {}),
...(createdTo ? { createdTo } : {}),
})
...(accountChannel ? { accountChannel } : {}),
}
const [listResult, channels] = await Promise.all([
listWorkerFinanceRequests({ page, pageSize, ...listFilters }),
summarizeWorkerFinanceRequests(listFilters),
])
const { items, total } = listResult
const summary = {
channels,
totalCount: channels.reduce((sum, item) => sum + item.count, 0),
totalAmount: channels.reduce((sum, item) => sum + item.amount, 0),
}
return {
items: items.map(mapFinanceRequest),
pagination: { page, pageSize, total },
summary,
}
}
export async function reviewAdminWorkerFinanceRequest(
requestId: number | string,
payload: JsonObject = {},
reviewedBy = '',
) {
const current = await getWorkerFinanceRequestById(requestId)
if (!current) {
@@ -204,6 +219,7 @@ export async function reviewAdminWorkerFinanceRequest(
status,
reviewedNote,
now: nowIso(),
reviewedBy: String(reviewedBy || '').trim(),
})
if (reviewed.failureReason === 'request_not_pending') {