展示打手提现统计
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
-- 支持资金申请列表按打手快速统计提现次数和已提现金额。
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_worker_finance_requests_worker_withdraw_stats
|
||||||
|
ON worker_finance_requests(worker_id, request_type)
|
||||||
|
INCLUDE (status, amount);
|
||||||
@@ -154,6 +154,11 @@ export type WorkerFinanceRequestRow = {
|
|||||||
worker_username?: string
|
worker_username?: string
|
||||||
worker_display_name?: string
|
worker_display_name?: string
|
||||||
worker_phone?: string
|
worker_phone?: string
|
||||||
|
worker_type?: string
|
||||||
|
worker_level_key?: string
|
||||||
|
worker_level_name?: string
|
||||||
|
worker_withdraw_request_count?: number
|
||||||
|
worker_total_withdraw_amount?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WorkerWithdrawalAccountRow = {
|
export type WorkerWithdrawalAccountRow = {
|
||||||
|
|||||||
@@ -39,9 +39,23 @@ const WORKER_FINANCE_REQUEST_SELECT = `
|
|||||||
wfr.*,
|
wfr.*,
|
||||||
wu.username AS worker_username,
|
wu.username AS worker_username,
|
||||||
wu.display_name AS worker_display_name,
|
wu.display_name AS worker_display_name,
|
||||||
wu.phone AS worker_phone
|
wu.phone AS worker_phone,
|
||||||
|
wu.worker_type,
|
||||||
|
wl.level_key AS worker_level_key,
|
||||||
|
wl.name AS worker_level_name,
|
||||||
|
withdraw_stats.withdraw_request_count AS worker_withdraw_request_count,
|
||||||
|
withdraw_stats.total_withdraw_amount AS worker_total_withdraw_amount
|
||||||
FROM worker_finance_requests wfr
|
FROM worker_finance_requests wfr
|
||||||
LEFT JOIN worker_users wu ON wu.id = wfr.worker_id
|
LEFT JOIN worker_users wu ON wu.id = wfr.worker_id
|
||||||
|
LEFT JOIN worker_levels wl ON wl.id = wu.level_id
|
||||||
|
LEFT JOIN LATERAL (
|
||||||
|
SELECT
|
||||||
|
COUNT(*)::int AS withdraw_request_count,
|
||||||
|
COALESCE(SUM(amount) FILTER (WHERE status = 'approved'), 0)::bigint AS total_withdraw_amount
|
||||||
|
FROM worker_finance_requests history_request
|
||||||
|
WHERE history_request.worker_id = wfr.worker_id
|
||||||
|
AND history_request.request_type = 'withdraw'
|
||||||
|
) withdraw_stats ON TRUE
|
||||||
`
|
`
|
||||||
|
|
||||||
export type CreateWorkerSessionInput = {
|
export type CreateWorkerSessionInput = {
|
||||||
|
|||||||
@@ -392,6 +392,11 @@ export function mapFinanceRequest(request: WorkerFinanceRequestRow | null | unde
|
|||||||
username: request.worker_username || '',
|
username: request.worker_username || '',
|
||||||
displayName: request.worker_display_name || request.worker_username || '',
|
displayName: request.worker_display_name || request.worker_username || '',
|
||||||
phone: request.worker_phone || '',
|
phone: request.worker_phone || '',
|
||||||
|
workerType: normalizeWorkerType(request.worker_type),
|
||||||
|
levelKey: request.worker_level_key || '',
|
||||||
|
levelName: request.worker_level_name || '',
|
||||||
|
withdrawRequestCount: Number(request.worker_withdraw_request_count || 0),
|
||||||
|
totalWithdrawAmount: Number(request.worker_total_withdraw_amount || 0),
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -525,6 +525,11 @@ test('支付宝提现申请保留收款二维码快照', () => {
|
|||||||
created_at: '2026-08-20T00:00:00.000Z',
|
created_at: '2026-08-20T00:00:00.000Z',
|
||||||
updated_at: '2026-08-20T00:00:00.000Z',
|
updated_at: '2026-08-20T00:00:00.000Z',
|
||||||
reviewed_at: null,
|
reviewed_at: null,
|
||||||
|
worker_type: 'internal',
|
||||||
|
worker_level_key: 'vip5',
|
||||||
|
worker_level_name: 'VIP5',
|
||||||
|
worker_withdraw_request_count: 3,
|
||||||
|
worker_total_withdraw_amount: 28_800,
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapped = mapFinanceRequest(request)
|
const mapped = mapFinanceRequest(request)
|
||||||
@@ -532,6 +537,17 @@ test('支付宝提现申请保留收款二维码快照', () => {
|
|||||||
(mapped?.payload.alipayQrCodeImage as { url?: string }).url,
|
(mapped?.payload.alipayQrCodeImage as { url?: string }).url,
|
||||||
'https://example.com/alipay-qr.png',
|
'https://example.com/alipay-qr.png',
|
||||||
)
|
)
|
||||||
|
assert.deepEqual(mapped?.worker, {
|
||||||
|
workerId: 1,
|
||||||
|
username: '',
|
||||||
|
displayName: '',
|
||||||
|
phone: '',
|
||||||
|
workerType: 'internal',
|
||||||
|
levelKey: 'vip5',
|
||||||
|
levelName: 'VIP5',
|
||||||
|
withdrawRequestCount: 3,
|
||||||
|
totalWithdrawAmount: 28_800,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('提现渠道仅支持支付宝和微信', () => {
|
test('提现渠道仅支持支付宝和微信', () => {
|
||||||
|
|||||||
@@ -201,13 +201,25 @@ export default function FinancePanel() {
|
|||||||
const requestColumns: TableColumnsType<WorkerFinanceRequest> = [
|
const requestColumns: TableColumnsType<WorkerFinanceRequest> = [
|
||||||
{
|
{
|
||||||
title: '打手',
|
title: '打手',
|
||||||
minWidth: 180,
|
minWidth: 250,
|
||||||
render: (_, row) => (
|
render: (_, row) => (
|
||||||
<div className="cell-stack">
|
<div className="cell-stack">
|
||||||
<Typography.Text strong>
|
<Typography.Text strong>
|
||||||
{row.worker?.displayName || row.worker?.username || '-'}
|
{row.worker?.displayName || row.worker?.username || '-'}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Typography.Text type="secondary">{row.worker?.username || '-'}</Typography.Text>
|
<Typography.Text type="secondary">{row.worker?.username || '-'}</Typography.Text>
|
||||||
|
{row.worker ? (
|
||||||
|
<Space size={[4, 4]} wrap>
|
||||||
|
<Tag color={row.worker.workerType === 'internal' ? 'blue' : 'default'}>
|
||||||
|
{row.worker.workerType === 'internal' ? '内部打手' : '外部打手'}
|
||||||
|
</Tag>
|
||||||
|
<Tag>{row.worker.levelName || row.worker.levelKey || '未分级'}</Tag>
|
||||||
|
<Typography.Text type="secondary">
|
||||||
|
提现 {row.worker.withdrawRequestCount} 次,累计
|
||||||
|
{formatMoney(row.worker.totalWithdrawAmount)}
|
||||||
|
</Typography.Text>
|
||||||
|
</Space>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -99,6 +99,11 @@ export type WorkerFinanceRequest = {
|
|||||||
username: string
|
username: string
|
||||||
displayName: string
|
displayName: string
|
||||||
phone: string
|
phone: string
|
||||||
|
workerType: 'internal' | 'external'
|
||||||
|
levelKey: string
|
||||||
|
levelName: string
|
||||||
|
withdrawRequestCount: number
|
||||||
|
totalWithdrawAmount: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user