From 985e61209d7ead8157c233e5a5a83e3bdd9d1497 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 22 Aug 2026 09:57:28 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B5=84=E9=87=91=E7=94=B3=E8=AF=B7=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=B8=A0=E9=81=93=E7=AD=9B=E9=80=89=E5=90=88=E8=AE=A1?= =?UTF-8?q?=E4=B8=8E=E5=AE=A1=E6=A0=B8=E7=95=99=E7=97=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/repositories/worker-platform/types.ts | 2 + .../worker-platform/worker-finance-repo.ts | 60 +++++++++++++++- .../worker-platform/worker-repo.ts | 1 + .../routes/admin/worker-platform/finance.ts | 7 +- .../admin-worker-finance-service.ts | 24 +++++-- .../src/pages/admin/panels/FinancePanel.tsx | 70 ++++++++++++++++++- .../src/services/admin/worker-platform.ts | 13 ++-- apps/frontend/src/styles/worker.css | 9 +++ apps/frontend/src/utils/admin-display.ts | 48 +++++++++++++ apps/frontend/src/utils/admin-options.ts | 53 +++++++++++++- 10 files changed, 273 insertions(+), 14 deletions(-) diff --git a/apps/backend/src/repositories/worker-platform/types.ts b/apps/backend/src/repositories/worker-platform/types.ts index cb8b90c1..8351c0e4 100644 --- a/apps/backend/src/repositories/worker-platform/types.ts +++ b/apps/backend/src/repositories/worker-platform/types.ts @@ -471,6 +471,8 @@ export type FinanceRequestListInput = { /** 申请时间区间(YYYY-MM-DD,上海时区,含起止日);后台列表筛选使用。 */ createdFrom?: string createdTo?: string + /** 收款渠道筛选:alipay / wechat */ + accountChannel?: string } export type CreateWorkerInput = { diff --git a/apps/backend/src/repositories/worker-platform/worker-finance-repo.ts b/apps/backend/src/repositories/worker-platform/worker-finance-repo.ts index c2fbefe1..0e192eb1 100644 --- a/apps/backend/src/repositories/worker-platform/worker-finance-repo.ts +++ b/apps/backend/src/repositories/worker-platform/worker-finance-repo.ts @@ -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> { + 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( diff --git a/apps/backend/src/repositories/worker-platform/worker-repo.ts b/apps/backend/src/repositories/worker-platform/worker-repo.ts index 1a261af8..cd9cec74 100644 --- a/apps/backend/src/repositories/worker-platform/worker-repo.ts +++ b/apps/backend/src/repositories/worker-platform/worker-repo.ts @@ -272,6 +272,7 @@ export { listWorkerFinanceRequests, listWorkerWithdrawalAccounts, reviewWorkerFinanceRequest, + summarizeWorkerFinanceRequests, upsertWorkerWithdrawalAccount, } from './worker-finance-repo.js' diff --git a/apps/backend/src/routes/admin/worker-platform/finance.ts b/apps/backend/src/routes/admin/worker-platform/finance.ts index 454afce1..507f3c7c 100644 --- a/apps/backend/src/routes/admin/worker-platform/finance.ts +++ b/apps/backend/src/routes/admin/worker-platform/finance.ts @@ -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: '处理资金申请失败', diff --git a/apps/backend/src/services/worker-platform/admin-worker-finance-service.ts b/apps/backend/src/services/worker-platform/admin-worker-finance-service.ts index 2a0136ff..0d38c3b6 100644 --- a/apps/backend/src/services/worker-platform/admin-worker-finance-service.ts +++ b/apps/backend/src/services/worker-platform/admin-worker-finance-service.ts @@ -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') { diff --git a/apps/frontend/src/pages/admin/panels/FinancePanel.tsx b/apps/frontend/src/pages/admin/panels/FinancePanel.tsx index 38cadee8..1787dfba 100644 --- a/apps/frontend/src/pages/admin/panels/FinancePanel.tsx +++ b/apps/frontend/src/pages/admin/panels/FinancePanel.tsx @@ -72,6 +72,7 @@ export default function FinancePanel() { const [keywordInput, setKeywordInput] = useState('') const [keyword, setKeyword] = useState('') const [createdRange, setCreatedRange] = useState<[string, string] | null>(null) + const [accountChannel, setAccountChannel] = useState('') const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE) const [savingConfig, setSavingConfig] = useState(false) @@ -114,6 +115,7 @@ export default function FinancePanel() { pageSize, createdRange ? createdRange[0] : '', createdRange ? createdRange[1] : '', + accountChannel, ], queryFn: () => fetchAdminWorkerFinanceRequests({ @@ -286,7 +288,9 @@ export default function FinancePanel() {
{formatAdminDateTime(row.createdAt)} - {row.reviewedAt ? `审核:${formatAdminDateTime(row.reviewedAt)}` : '待审核'} + {row.reviewedAt + ? `审核:${formatAdminDateTime(row.reviewedAt)}${resolveFinanceReviewer(row) ? `(${resolveFinanceReviewer(row)})` : ''}` + : '待审核'}
), @@ -472,6 +476,19 @@ export default function FinancePanel() { setPage(1) }} /> +