优化资金审核与流水展示

This commit is contained in:
yml2213
2026-08-21 12:54:44 +08:00
parent 1090bd691b
commit b2a1ac3db1
8 changed files with 129 additions and 59 deletions
@@ -134,6 +134,7 @@ export type WorkerWalletLedgerRow = {
note: string note: string
payload_json: string | Record<string, unknown> payload_json: string | Record<string, unknown>
created_at: string created_at: string
related_platform_order_id?: string | null
} }
export type WorkerFinanceRequestRow = { export type WorkerFinanceRequestRow = {
@@ -539,8 +539,11 @@ export async function listWorkerWalletLedgers({
params.push(pageSize, offset) params.push(pageSize, offset)
const itemsResult = await query<WorkerWalletLedgerRow>( const itemsResult = await query<WorkerWalletLedgerRow>(
` `
SELECT * SELECT
wwl.*,
wo.platform_order_id AS related_platform_order_id
FROM worker_wallet_ledgers wwl FROM worker_wallet_ledgers wwl
LEFT JOIN work_orders wo ON wo.id = wwl.related_work_order_id
${whereClause} ${whereClause}
ORDER BY wwl.created_at DESC, wwl.id DESC ORDER BY wwl.created_at DESC, wwl.id DESC
LIMIT $${params.length - 1} OFFSET $${params.length} LIMIT $${params.length - 1} OFFSET $${params.length}
@@ -354,6 +354,7 @@ export function mapWalletLedger(ledger: WorkerWalletLedgerRow | null | undefined
balanceAfter: Number(ledger.balance_after || 0), balanceAfter: Number(ledger.balance_after || 0),
frozenAfter: Number(ledger.frozen_after || 0), frozenAfter: Number(ledger.frozen_after || 0),
relatedWorkOrderId: ledger.related_work_order_id ? Number(ledger.related_work_order_id) : null, relatedWorkOrderId: ledger.related_work_order_id ? Number(ledger.related_work_order_id) : null,
relatedPlatformOrderId: String(ledger.related_platform_order_id || '').trim() || null,
auditStatus: ledger.audit_status || '', auditStatus: ledger.audit_status || '',
note: ledger.note || '', note: ledger.note || '',
payload: safeParseJson(ledger.payload_json), payload: safeParseJson(ledger.payload_json),
@@ -8,6 +8,7 @@ import type {
WorkProductRuleRow, WorkProductRuleRow,
WorkerFinanceRequestRow, WorkerFinanceRequestRow,
WorkerUserRow, WorkerUserRow,
WorkerWalletLedgerRow,
} from '../../repositories/worker-platform/index.js' } from '../../repositories/worker-platform/index.js'
import { import {
buildWorkOrderWhere, buildWorkOrderWhere,
@@ -29,6 +30,7 @@ import {
mapWorkOrderAdmin, mapWorkOrderAdmin,
mapWorkOrderSharingWorkers, mapWorkOrderSharingWorkers,
mapFinanceRequest, mapFinanceRequest,
mapWalletLedger,
maskWorkerLeaderboardDisplayName, maskWorkerLeaderboardDisplayName,
resolveCollectSubmitTargetWorkOrder, resolveCollectSubmitTargetWorkOrder,
resolveAdminMaterialRewardAmount, resolveAdminMaterialRewardAmount,
@@ -1088,3 +1090,22 @@ test('resolveCollectSubmitTargetWorkOrder rejects unknown workOrderId', () => {
), ),
) )
}) })
test('资金流水映射展示关联的平台订单号', () => {
const ledger = {
id: 1,
worker_id: 1,
ledger_type: 'reward_settlement',
amount: 2500,
balance_after: 2500,
frozen_after: 0,
related_work_order_id: 44,
related_platform_order_id: '2623301845665410',
audit_status: 'approved',
note: '验收通过结算报酬',
payload_json: {},
created_at: '2026-08-21T00:00:00.000Z',
} satisfies WorkerWalletLedgerRow
assert.equal(mapWalletLedger(ledger)?.relatedPlatformOrderId, '2623301845665410')
})
@@ -500,6 +500,7 @@ export default function FinancePanel() {
} }
open={Boolean(reviewState)} open={Boolean(reviewState)}
destroyOnHidden destroyOnHidden
width="min(860px, calc(100vw - 32px))"
confirmLoading={reviewing} confirmLoading={reviewing}
onCancel={() => { onCancel={() => {
setReviewState(null) setReviewState(null)
@@ -509,64 +510,63 @@ export default function FinancePanel() {
> >
{reviewState ? ( {reviewState ? (
<Space direction="vertical" size={16} className="full-width"> <Space direction="vertical" size={16} className="full-width">
<Descriptions column={1} bordered size="small"> <div className="finance-review-layout">
<Descriptions.Item label="打手"> <div className="finance-review-details">
{reviewState.request.worker?.displayName || <Typography.Text strong>
reviewState.request.worker?.username || {reviewState.request.requestType === 'withdraw' ? '提现信息' : '充值信息'}
'-'} </Typography.Text>
</Descriptions.Item> <Descriptions column={1} bordered size="small">
<Descriptions.Item label="申请类型"> <Descriptions.Item label="打手">
{formatFinanceRequestType(reviewState.request.requestType)} {reviewState.request.worker?.displayName ||
</Descriptions.Item> reviewState.request.worker?.username ||
<Descriptions.Item label="金额"> '-'}
{formatMoney(reviewState.request.amount)}
</Descriptions.Item>
<Descriptions.Item label="收款信息">
{renderFinanceRequestAccountText(reviewState.request)}
</Descriptions.Item>
{reviewState.request.requestType === 'withdraw' &&
reviewState.request.accountChannel === 'alipay' ? (
<>
<Descriptions.Item label="支付宝账号">
{reviewState.request.accountNo ? (
<Typography.Text copyable>{reviewState.request.accountNo}</Typography.Text>
) : (
'-'
)}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="支付宝收款二维码"> <Descriptions.Item label="申请类型">
<ImagePreviewList {formatFinanceRequestType(reviewState.request.requestType)}
files={getAlipayWithdrawQrCode(reviewState.request)}
size={88}
/>
</Descriptions.Item> </Descriptions.Item>
</> <Descriptions.Item label="金额">
) : null} {formatMoney(reviewState.request.amount)}
{reviewState.request.requestType === 'recharge' ? (
<>
<Descriptions.Item label="付款时间">
{getRechargePaidAt(reviewState.request)
? formatAdminDateTime(getRechargePaidAt(reviewState.request))
: '-'}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="付款凭证"> <Descriptions.Item label="收款信息">
<ImagePreviewList files={getRechargeProofs(reviewState.request)} size={88} /> {renderFinanceRequestAccountText(reviewState.request)}
</Descriptions.Item> </Descriptions.Item>
</> {reviewState.request.requestType === 'withdraw' ? (
) : null} <Descriptions.Item label="收款账号">
{reviewState.request.requestType === 'withdraw' && {reviewState.request.accountNo ? (
reviewState.request.accountChannel === 'wechat' ? ( <Typography.Text copyable>{reviewState.request.accountNo}</Typography.Text>
<Descriptions.Item label="微信收款二维码"> ) : (
<ImagePreviewList '-'
files={getWechatWithdrawQrCode(reviewState.request)} )}
size={88} </Descriptions.Item>
/> ) : null}
</Descriptions.Item> {reviewState.request.requestType === 'recharge' ? (
) : null} <Descriptions.Item label="付款时间">
<Descriptions.Item label="申请备注"> {getRechargePaidAt(reviewState.request)
{reviewState.request.note || '-'} ? formatAdminDateTime(getRechargePaidAt(reviewState.request))
</Descriptions.Item> : '-'}
</Descriptions> </Descriptions.Item>
) : null}
<Descriptions.Item label="申请备注">
{reviewState.request.note || '-'}
</Descriptions.Item>
</Descriptions>
</div>
<div className="finance-review-evidence">
<Typography.Text strong>
{reviewState.request.requestType === 'withdraw'
? `${formatFinanceChannel(reviewState.request.accountChannel)}收款二维码`
: '付款凭证'}
</Typography.Text>
<ImagePreviewList
files={
reviewState.request.requestType === 'withdraw'
? getWithdrawQrCode(reviewState.request)
: getRechargeProofs(reviewState.request)
}
size={280}
/>
</div>
</div>
<Form form={reviewForm} layout="vertical" onFinish={submitFinanceReview}> <Form form={reviewForm} layout="vertical" onFinish={submitFinanceReview}>
<Form.Item label="审核备注" name="reviewedNote"> <Form.Item label="审核备注" name="reviewedNote">
@@ -420,8 +420,8 @@ export default function WorkerProfilePage() {
render: (_, row) => ( render: (_, row) => (
<div className="cell-stack"> <div className="cell-stack">
<Typography.Text>{row.note || '-'}</Typography.Text> <Typography.Text>{row.note || '-'}</Typography.Text>
{row.relatedWorkOrderId ? ( {row.relatedPlatformOrderId ? (
<Typography.Text type="secondary"> #{row.relatedWorkOrderId}</Typography.Text> <Typography.Text type="secondary"> {row.relatedPlatformOrderId}</Typography.Text>
) : null} ) : null}
</div> </div>
), ),
@@ -841,9 +841,9 @@ export default function WorkerProfilePage() {
{row.note ? ( {row.note ? (
<div className="worker-profile-record-note">: {row.note}</div> <div className="worker-profile-record-note">: {row.note}</div>
) : null} ) : null}
{row.relatedWorkOrderId ? ( {row.relatedPlatformOrderId ? (
<div className="worker-profile-record-sub"> <div className="worker-profile-record-sub">
#{row.relatedWorkOrderId} {row.relatedPlatformOrderId}
</div> </div>
) : null} ) : null}
<div className="worker-profile-record-time"> <div className="worker-profile-record-time">
+43
View File
@@ -888,6 +888,37 @@
min-width: 0; min-width: 0;
} }
.finance-review-layout {
display: grid;
grid-template-columns: minmax(0, 1fr) 304px;
gap: 20px;
align-items: start;
}
.finance-review-details,
.finance-review-evidence {
display: grid;
gap: 8px;
min-width: 0;
}
.finance-review-evidence {
justify-items: center;
padding: 12px;
border: 1px solid #edf0f5;
border-radius: 6px;
background: #f8fafc;
}
.finance-review-evidence .image-preview-list {
justify-content: center;
}
.finance-review-evidence .ant-image,
.finance-review-evidence img {
max-width: 100%;
}
.worker-orders-statistics-card { .worker-orders-statistics-card {
border-left: 3px solid #ff6a00; border-left: 3px solid #ff6a00;
} }
@@ -1444,6 +1475,18 @@
} }
@media (max-width: 720px) { @media (max-width: 720px) {
.finance-review-layout {
grid-template-columns: 1fr;
}
.finance-review-evidence {
justify-items: start;
}
.finance-review-evidence .image-preview-list {
justify-content: flex-start;
}
.users-action-card .ant-card-body { .users-action-card .ant-card-body {
padding: 14px; padding: 14px;
} }
@@ -74,6 +74,7 @@ export type WorkerWalletLedger = {
balanceAfter: number balanceAfter: number
frozenAfter: number frozenAfter: number
relatedWorkOrderId: number | null relatedWorkOrderId: number | null
relatedPlatformOrderId: string | null
auditStatus: string auditStatus: string
note: string note: string
payload: Record<string, unknown> payload: Record<string, unknown>