diff --git a/apps/backend/src/db/migrations/049_worker_alipay_withdrawal_qr_code.sql b/apps/backend/src/db/migrations/049_worker_alipay_withdrawal_qr_code.sql new file mode 100644 index 00000000..a978fdf9 --- /dev/null +++ b/apps/backend/src/db/migrations/049_worker_alipay_withdrawal_qr_code.sql @@ -0,0 +1,5 @@ +-- 支付宝提现账户保存收款二维码;历史账户保留空值,需补充后才能继续提现。 +ALTER TABLE worker_withdrawal_accounts + ADD COLUMN IF NOT EXISTS alipay_qr_code_json JSONB NOT NULL DEFAULT '{}'::jsonb; + +COMMENT ON COLUMN worker_withdrawal_accounts.alipay_qr_code_json IS '支付宝收款二维码文件信息'; diff --git a/apps/backend/src/repositories/worker-platform/types.ts b/apps/backend/src/repositories/worker-platform/types.ts index 770f30b8..fbc49c3a 100644 --- a/apps/backend/src/repositories/worker-platform/types.ts +++ b/apps/backend/src/repositories/worker-platform/types.ts @@ -161,6 +161,7 @@ export type WorkerWithdrawalAccountRow = { account_channel: string account_name: string account_no: string + alipay_qr_code_json: string | Record wechat_qr_code_json: string | Record created_at: string } diff --git a/apps/backend/src/repositories/worker-platform/worker-repo.ts b/apps/backend/src/repositories/worker-platform/worker-repo.ts index 8a123e57..b193afa4 100644 --- a/apps/backend/src/repositories/worker-platform/worker-repo.ts +++ b/apps/backend/src/repositories/worker-platform/worker-repo.ts @@ -654,14 +654,16 @@ export async function createWorkerWithdrawalAccount(input: { accountChannel: string accountName: string accountNo: string + alipayQrCodeJson: string wechatQrCodeJson: string now: string }): Promise { const result = await query( ` INSERT INTO worker_withdrawal_accounts ( - worker_id, account_channel, account_name, account_no, wechat_qr_code_json, created_at - ) VALUES ($1, $2, $3, $4, $5::jsonb, $6) + worker_id, account_channel, account_name, account_no, + alipay_qr_code_json, wechat_qr_code_json, created_at + ) VALUES ($1, $2, $3, $4, $5::jsonb, $6::jsonb, $7) ON CONFLICT (worker_id, account_channel) DO NOTHING RETURNING * `, @@ -670,6 +672,7 @@ export async function createWorkerWithdrawalAccount(input: { input.accountChannel, input.accountName, input.accountNo, + input.alipayQrCodeJson, input.wechatQrCodeJson, input.now, ], @@ -682,18 +685,21 @@ export async function upsertWorkerWithdrawalAccount(input: { accountChannel: string accountName: string accountNo: string + alipayQrCodeJson: string wechatQrCodeJson: string now: string }): Promise { const result = await query( ` INSERT INTO worker_withdrawal_accounts ( - worker_id, account_channel, account_name, account_no, wechat_qr_code_json, created_at - ) VALUES ($1, $2, $3, $4, $5::jsonb, $6) + worker_id, account_channel, account_name, account_no, + alipay_qr_code_json, wechat_qr_code_json, created_at + ) VALUES ($1, $2, $3, $4, $5::jsonb, $6::jsonb, $7) ON CONFLICT (worker_id, account_channel) DO UPDATE SET account_name = EXCLUDED.account_name, account_no = EXCLUDED.account_no, + alipay_qr_code_json = EXCLUDED.alipay_qr_code_json, wechat_qr_code_json = EXCLUDED.wechat_qr_code_json RETURNING * `, @@ -702,6 +708,7 @@ export async function upsertWorkerWithdrawalAccount(input: { input.accountChannel, input.accountName, input.accountNo, + input.alipayQrCodeJson, input.wechatQrCodeJson, input.now, ], diff --git a/apps/backend/src/routes/worker.ts b/apps/backend/src/routes/worker.ts index 23dceac4..113a927a 100644 --- a/apps/backend/src/routes/worker.ts +++ b/apps/backend/src/routes/worker.ts @@ -7,6 +7,7 @@ import { changeWorkerPassword, createWorkerRechargeRequest, createWorkerWithdrawalAccount, + saveWorkerAlipayWithdrawalQrCode, createWorkerWithdrawRequest, getWorkerProfile, getWorkerSessionSummary, @@ -326,6 +327,19 @@ router.post( ), ) +router.put( + '/profile/withdrawal-account/alipay-qr-code', + requireActiveWorker, + createRouteHandler( + (req) => saveWorkerAlipayWithdrawalQrCode(req.body || {}, getRequiredWorkerSession(req)), + { + successMessage: '支付宝收款二维码已保存', + errorMessage: '保存支付宝收款二维码失败', + scope: '[worker/profile/withdrawal-account/alipay-qr-code]', + }, + ), +) + router.post( '/profile/withdraw-requests', requireActiveWorker, diff --git a/apps/backend/src/services/worker-platform/admin-service.ts b/apps/backend/src/services/worker-platform/admin-service.ts index cf60179f..6d1e2388 100644 --- a/apps/backend/src/services/worker-platform/admin-service.ts +++ b/apps/backend/src/services/worker-platform/admin-service.ts @@ -1256,7 +1256,16 @@ export async function saveAdminWorkerWithdrawalAccount( const current = currentAccounts.find((item) => item.account_channel === accountChannel) const accountName = String(payload.accountName || payload.realName || '').trim() const accountNo = - accountChannel === 'alipay' ? String(payload.accountNo || payload.account || '').trim() : '' + accountChannel === 'alipay' + ? String(payload.accountNo || payload.account || current?.account_no || '').trim() + : '' + const uploadedAlipayQrCode = normalizeProofFiles( + payload.alipayQrCodeImages || payload.alipayQrCodes, + )[0] + const alipayQrCodeImage = + accountChannel === 'alipay' + ? uploadedAlipayQrCode || safeParseJson(current?.alipay_qr_code_json) + : {} const uploadedWechatQrCode = normalizeProofFiles( payload.wechatQrCodeImages || payload.wechatQrCodes, )[0] @@ -1271,12 +1280,18 @@ export async function saveAdminWorkerWithdrawalAccount( errorCode: 'admin_worker_withdraw_account_name_required', }) } - if (accountChannel === 'alipay' && !accountNo) { + if (accountChannel === 'alipay' && !current && !accountNo) { throw createHttpError('请填写支付宝账号', { statusCode: 400, errorCode: 'admin_worker_withdraw_alipay_account_required', }) } + if (accountChannel === 'alipay' && !String(alipayQrCodeImage.url || '').trim()) { + throw createHttpError('请上传支付宝收款二维码', { + statusCode: 400, + errorCode: 'admin_worker_withdraw_alipay_qr_required', + }) + } if (accountChannel === 'wechat' && !String(wechatQrCodeImage.url || '').trim()) { throw createHttpError('请上传微信收款二维码', { statusCode: 400, @@ -1289,6 +1304,7 @@ export async function saveAdminWorkerWithdrawalAccount( accountChannel, accountName, accountNo, + alipayQrCodeJson: JSON.stringify(alipayQrCodeImage), wechatQrCodeJson: JSON.stringify(wechatQrCodeImage), now: nowIso(), }) @@ -1414,11 +1430,13 @@ export async function reviewAdminWorkerFinanceRequest( } function mapAdminWorkerWithdrawalAccount(account: WorkerWithdrawalAccountRow) { + const alipayQrCodeImage = refreshUploadedFileUrls(safeParseJson(account.alipay_qr_code_json)) const wechatQrCodeImage = refreshUploadedFileUrls(safeParseJson(account.wechat_qr_code_json)) return { accountChannel: account.account_channel, accountName: account.account_name, accountNo: account.account_channel === 'alipay' ? account.account_no : '', + alipayQrCodeImage: String(alipayQrCodeImage.url || '').trim() ? alipayQrCodeImage : null, wechatQrCodeImage: String(wechatQrCodeImage.url || '').trim() ? wechatQrCodeImage : null, createdAt: account.created_at, } diff --git a/apps/backend/src/services/worker-platform/mappers.ts b/apps/backend/src/services/worker-platform/mappers.ts index b375968c..f0af71de 100644 --- a/apps/backend/src/services/worker-platform/mappers.ts +++ b/apps/backend/src/services/worker-platform/mappers.ts @@ -118,7 +118,10 @@ export function canWorkerAutoAcceptWholeOrder(input: { worker: WorkerUserRow hasPersonalSharingShare: boolean }): boolean { - return !input.hasPersonalSharingShare && resolveWorkerPermissions(input.worker).autoAcceptWithoutEvidence + return ( + !input.hasPersonalSharingShare && + resolveWorkerPermissions(input.worker).autoAcceptWithoutEvidence + ) } export function resolveVisibleDelaySeconds(levelKey: string, configured?: unknown): number { @@ -361,6 +364,10 @@ export function mapWalletLedger(ledger: WorkerWalletLedgerRow | null | undefined export function mapFinanceRequest(request: WorkerFinanceRequestRow | null | undefined) { if (!request) return null const payload = safeParseJson(request.payload_json) + const alipayQrCodeImage = refreshUploadedFileUrls(payload.alipayQrCodeImage) + if (String(alipayQrCodeImage.url || '').trim()) { + payload.alipayQrCodeImage = alipayQrCodeImage + } const wechatQrCodeImage = refreshUploadedFileUrls(payload.wechatQrCodeImage) if (String(wechatQrCodeImage.url || '').trim()) { payload.wechatQrCodeImage = wechatQrCodeImage diff --git a/apps/backend/src/services/worker-platform/worker-platform-service.test.ts b/apps/backend/src/services/worker-platform/worker-platform-service.test.ts index 429f0d54..f7c6ec3e 100644 --- a/apps/backend/src/services/worker-platform/worker-platform-service.test.ts +++ b/apps/backend/src/services/worker-platform/worker-platform-service.test.ts @@ -6,6 +6,7 @@ import type { WorkOrderShareRow, WorkProductRuleMappingRow, WorkProductRuleRow, + WorkerFinanceRequestRow, WorkerUserRow, } from '../../repositories/worker-platform/index.js' import { @@ -26,6 +27,7 @@ import { mapWorkOrderForWorker, mapWorkOrderAdmin, mapWorkOrderSharingWorkers, + mapFinanceRequest, maskWorkerLeaderboardDisplayName, resolveCollectSubmitTargetWorkOrder, resolveAdminMaterialRewardAmount, @@ -505,6 +507,33 @@ test('微信提现金额超过 100 元时被拒绝', () => { ) }) +test('支付宝提现申请保留收款二维码快照', () => { + const request: WorkerFinanceRequestRow = { + id: 1, + worker_id: 1, + request_type: 'withdraw', + status: 'pending', + amount: 10_000, + account_channel: 'alipay', + account_name: '测试用户', + account_no: 'test@example.com', + note: '', + reviewed_note: '', + payload_json: { + alipayQrCodeImage: { url: 'https://example.com/alipay-qr.png' }, + }, + created_at: '2026-08-20T00:00:00.000Z', + updated_at: '2026-08-20T00:00:00.000Z', + reviewed_at: null, + } + + const mapped = mapFinanceRequest(request) + assert.equal( + (mapped?.payload.alipayQrCodeImage as { url?: string }).url, + 'https://example.com/alipay-qr.png', + ) +}) + test('提现渠道仅支持支付宝和微信', () => { assert.equal(normalizeWithdrawChannel('alipay'), 'alipay') assert.equal(normalizeWithdrawChannel('wechat'), 'wechat') diff --git a/apps/backend/src/services/worker-platform/worker-service.ts b/apps/backend/src/services/worker-platform/worker-service.ts index 06eef685..4fe73906 100644 --- a/apps/backend/src/services/worker-platform/worker-service.ts +++ b/apps/backend/src/services/worker-platform/worker-service.ts @@ -57,6 +57,7 @@ import { listWorkOrderShares, listWorkOrderSharesByOrderIds, listWorkerLevels, + upsertWorkerWithdrawalAccount, getWorkerCancelRequestRisk, listDueDepositUnfreezes, listOverdueWorkOrders, @@ -1186,7 +1187,14 @@ export async function createWorkerWithdrawRequest( }) } assertWithdrawChannelAllowedForAmount(amount, accountChannel) + const alipayQrCodeImage = resolveAlipayWithdrawalQrCode(withdrawalAccount) const wechatQrCodeImage = resolveWechatWithdrawalQrCode(withdrawalAccount) + if (accountChannel === 'alipay' && !alipayQrCodeImage) { + throw createHttpError('支付宝提现信息缺少收款二维码,请先补充后再提现', { + statusCode: 409, + errorCode: 'worker_withdrawal_alipay_qr_required', + }) + } if (accountChannel === 'wechat' && !wechatQrCodeImage) { throw createHttpError('微信提现信息缺少收款二维码,请联系客服处理', { statusCode: 409, @@ -1217,6 +1225,7 @@ export async function createWorkerWithdrawRequest( source: 'worker_profile_withdraw', username: worker.username, phone: worker.phone || '', + alipayQrCodeImage, wechatQrCodeImage, }), now: nowIso(), @@ -1261,6 +1270,10 @@ export async function createWorkerWithdrawalAccount( const accountName = String(payload.accountName || payload.realName || '').trim() const accountNo = accountChannel === 'alipay' ? String(payload.accountNo || payload.account || '').trim() : '' + const alipayQrCodeImage = + accountChannel === 'alipay' + ? normalizeProofFiles(payload.alipayQrCodeImages || payload.alipayQrCodes)[0] || null + : null const wechatQrCodeImage = accountChannel === 'wechat' ? normalizeProofFiles(payload.wechatQrCodeImages || payload.wechatQrCodes)[0] || null @@ -1277,6 +1290,12 @@ export async function createWorkerWithdrawalAccount( errorCode: 'worker_withdraw_account_no_required', }) } + if (accountChannel === 'alipay' && !alipayQrCodeImage) { + throw createHttpError('请上传支付宝收款二维码', { + statusCode: 400, + errorCode: 'worker_withdraw_alipay_qr_required', + }) + } if (accountChannel === 'wechat' && !wechatQrCodeImage) { throw createHttpError('请上传微信收款二维码', { statusCode: 400, @@ -1289,6 +1308,7 @@ export async function createWorkerWithdrawalAccount( accountChannel, accountName, accountNo, + alipayQrCodeJson: JSON.stringify(alipayQrCodeImage || {}), wechatQrCodeJson: JSON.stringify(wechatQrCodeImage || {}), now: nowIso(), }) @@ -1303,24 +1323,81 @@ export async function createWorkerWithdrawalAccount( } } +/** 历史支付宝账户只允许补充一次收款二维码,不允许修改账户姓名、账号或二维码。 */ +export async function saveWorkerAlipayWithdrawalQrCode( + payload: JsonObject = {}, + session: WorkerSession, +) { + requireActiveWorkerSession(session) + const worker = await getRequiredWorker(session.workerId) + const current = await getWorkerWithdrawalAccount(worker.id, 'alipay') + if (!current) { + throw createHttpError('请先添加支付宝提现信息', { + statusCode: 409, + errorCode: 'worker_withdrawal_alipay_account_required', + }) + } + if (resolveAlipayWithdrawalQrCode(current)) { + throw createHttpError('支付宝收款二维码已补充,后续修改请联系客服处理', { + statusCode: 409, + errorCode: 'worker_withdrawal_alipay_qr_already_set', + }) + } + const alipayQrCodeImage = normalizeProofFiles( + payload.alipayQrCodeImages || payload.alipayQrCodes, + )[0] + if (!alipayQrCodeImage) { + throw createHttpError('请上传支付宝收款二维码', { + statusCode: 400, + errorCode: 'worker_withdraw_alipay_qr_required', + }) + } + const saved = await upsertWorkerWithdrawalAccount({ + workerId: worker.id, + accountChannel: 'alipay', + accountName: current.account_name, + accountNo: current.account_no, + alipayQrCodeJson: JSON.stringify(alipayQrCodeImage), + wechatQrCodeJson: JSON.stringify(safeParseJson(current.wechat_qr_code_json)), + now: nowIso(), + }) + if (!saved) { + throw createHttpError('保存支付宝收款二维码失败', { + statusCode: 500, + errorCode: 'worker_withdrawal_alipay_qr_save_failed', + }) + } + return { withdrawalAccount: mapWithdrawalAccount(saved) } +} + function mapWithdrawalAccount(account: { account_channel: string account_name: string account_no: string + alipay_qr_code_json: string | Record wechat_qr_code_json: string | Record created_at: string }) { + const alipayQrCodeImage = resolveAlipayWithdrawalQrCode(account) const wechatQrCodeImage = resolveWechatWithdrawalQrCode(account) return { accountChannel: account.account_channel, accountName: account.account_name, accountNoMasked: account.account_channel === 'alipay' ? maskWithdrawalAccountNo(account.account_no) : '', + alipayQrCodeImage, wechatQrCodeImage, createdAt: account.created_at, } } +function resolveAlipayWithdrawalQrCode(account: { + alipay_qr_code_json: string | Record +}) { + const file = refreshUploadedFileUrls(safeParseJson(account.alipay_qr_code_json)) + return String(file.url || '').trim() ? file : null +} + function resolveWechatWithdrawalQrCode(account: { wechat_qr_code_json: string | Record }) { diff --git a/apps/frontend/src/pages/admin/panels/FinancePanel.tsx b/apps/frontend/src/pages/admin/panels/FinancePanel.tsx index 9f0212c0..1a085702 100644 --- a/apps/frontend/src/pages/admin/panels/FinancePanel.tsx +++ b/apps/frontend/src/pages/admin/panels/FinancePanel.tsx @@ -235,9 +235,7 @@ export default function FinancePanel() { minWidth: 140, render: (_, row) => ( ), @@ -516,13 +514,21 @@ export default function FinancePanel() { {reviewState.request.requestType === 'withdraw' && reviewState.request.accountChannel === 'alipay' ? ( - - {reviewState.request.accountNo ? ( - {reviewState.request.accountNo} - ) : ( - '-' - )} - + <> + + {reviewState.request.accountNo ? ( + {reviewState.request.accountNo} + ) : ( + '-' + )} + + + + + ) : null} {reviewState.request.requestType === 'recharge' ? ( <> @@ -697,6 +703,30 @@ function getWechatWithdrawQrCode(request: WorkerFinanceRequest): UploadedFile[] ] } +function getWithdrawQrCode(request: WorkerFinanceRequest): UploadedFile[] { + if (request.accountChannel === 'alipay') return getAlipayWithdrawQrCode(request) + return getWechatWithdrawQrCode(request) +} + +function getAlipayWithdrawQrCode(request: WorkerFinanceRequest): UploadedFile[] { + if (request.requestType !== 'withdraw' || request.accountChannel !== 'alipay') return [] + const payload = asRecord(request.payload) + const file = asRecord(payload.alipayQrCodeImage) + const url = String(file.url || '').trim() + if (!url) return [] + return [ + { + url, + mediumUrl: String(file.mediumUrl || '').trim(), + thumbnailUrl: String(file.thumbnailUrl || '').trim(), + objectKey: String(file.objectKey || '').trim(), + filename: String(file.filename || '').trim(), + contentType: String(file.contentType || '').trim(), + size: Number(file.size || 0), + }, + ] +} + function getRechargePaidAt(request: WorkerFinanceRequest): string { const payload = asRecord(request.payload) return String(payload.paidAt || '').trim() diff --git a/apps/frontend/src/pages/admin/panels/WorkersPanel.tsx b/apps/frontend/src/pages/admin/panels/WorkersPanel.tsx index c946a820..84889956 100644 --- a/apps/frontend/src/pages/admin/panels/WorkersPanel.tsx +++ b/apps/frontend/src/pages/admin/panels/WorkersPanel.tsx @@ -39,6 +39,7 @@ type WithdrawalAccountFormValues = { accountChannel?: string accountName?: string accountNo?: string + alipayQrCodeImages?: UploadedFile[] wechatQrCodeImages?: UploadedFile[] } @@ -69,6 +70,9 @@ export default function WorkersPanel() { const [resetPasswordForm] = Form.useForm<{ password: string }>() const [freezeForm] = Form.useForm() const withdrawalAccountChannel = Form.useWatch('accountChannel', withdrawalAccountForm) + const editingAlipayAccount = withdrawalAccounts.find( + (account) => account.accountChannel === 'alipay', + ) const workersQuery = useQuery({ queryKey: ['admin-worker-platform-workers', status, keyword, workerType, page, pageSize], @@ -225,6 +229,7 @@ export default function WorkersPanel() { accountChannel, accountName: account?.accountName || '', accountNo: account?.accountNo || '', + alipayQrCodeImages: account?.alipayQrCodeImage ? [account.alipayQrCodeImage] : [], wechatQrCodeImages: account?.wechatQrCodeImage ? [account.wechatQrCodeImage] : [], }) } @@ -240,6 +245,7 @@ export default function WorkersPanel() { { accountName: String(values.accountName || '').trim(), accountNo: String(values.accountNo || '').trim(), + alipayQrCodeImages: values.alipayQrCodeImages || [], wechatQrCodeImages: values.wechatQrCodeImages || [], }, ) @@ -559,13 +565,28 @@ export default function WorkersPanel() { ) : ( - - - + <> + + + + + + + )} diff --git a/apps/frontend/src/pages/worker/WorkerProfilePage.tsx b/apps/frontend/src/pages/worker/WorkerProfilePage.tsx index bbec9e13..4193623a 100644 --- a/apps/frontend/src/pages/worker/WorkerProfilePage.tsx +++ b/apps/frontend/src/pages/worker/WorkerProfilePage.tsx @@ -49,6 +49,7 @@ import { deleteWorkerSession, fetchWorkerWalletLedgers, logoutWorker, + saveWorkerAlipayWithdrawalQrCode, } from '@/services/worker' import type { UploadedFile, @@ -80,9 +81,14 @@ type WithdrawalAccountFormValues = { accountChannel?: string accountName?: string accountNo?: string + alipayQrCodeImages?: UploadedFile[] wechatQrCodeImages?: UploadedFile[] } +type AlipayQrCodeFormValues = { + alipayQrCodeImages?: UploadedFile[] +} + type PasswordFormValues = { currentPassword?: string newPassword?: string @@ -106,6 +112,7 @@ export default function WorkerProfilePage() { const [rechargeOpen, setRechargeOpen] = useState(false) const [withdrawOpen, setWithdrawOpen] = useState(false) const [withdrawalAccountOpen, setWithdrawalAccountOpen] = useState(false) + const [alipayQrCodeOpen, setAlipayQrCodeOpen] = useState(false) const [passwordOpen, setPasswordOpen] = useState(false) const [contactOpen, setContactOpen] = useState(false) const [devicesOpen, setDevicesOpen] = useState(false) @@ -116,6 +123,7 @@ export default function WorkerProfilePage() { const [rechargeForm] = Form.useForm() const [withdrawForm] = Form.useForm() const [withdrawalAccountForm] = Form.useForm() + const [alipayQrCodeForm] = Form.useForm() const [passwordForm] = Form.useForm() const selectedWithdrawChannel = Form.useWatch('accountChannel', withdrawForm) const selectedWithdrawalAccountChannel = Form.useWatch('accountChannel', withdrawalAccountForm) @@ -141,6 +149,9 @@ export default function WorkerProfilePage() { const selectedWithdrawalAccount = withdrawalAccounts.find( (account) => account.accountChannel === selectedWithdrawChannel, ) + const alipayWithdrawalAccount = withdrawalAccounts.find( + (account) => account.accountChannel === 'alipay', + ) const canUseFinanceActions = worker?.status === 'active' async function copyInviteCode() { @@ -233,6 +244,11 @@ export default function WorkerProfilePage() { openWithdrawalAccountModal() return } + if (alipayWithdrawalAccount && !alipayWithdrawalAccount.alipayQrCodeImage) { + message.warning('请先补充支付宝收款二维码') + openAlipayQrCodeModal() + return + } withdrawForm.setFieldValue('accountChannel', withdrawalAccounts[0].accountChannel) setWithdrawOpen(true) } @@ -249,6 +265,19 @@ export default function WorkerProfilePage() { setWithdrawalAccountOpen(true) } + function openAlipayQrCodeModal() { + if (!alipayWithdrawalAccount) { + openWithdrawalAccountModal() + return + } + alipayQrCodeForm.setFieldsValue({ + alipayQrCodeImages: alipayWithdrawalAccount.alipayQrCodeImage + ? [alipayWithdrawalAccount.alipayQrCodeImage] + : [], + }) + setAlipayQrCodeOpen(true) + } + async function submitRecharge(values: RechargeFormValues) { setSubmittingRecharge(true) try { @@ -301,6 +330,7 @@ export default function WorkerProfilePage() { accountChannel: String(values.accountChannel || ''), accountName: String(values.accountName || '').trim(), accountNo: String(values.accountNo || '').trim(), + alipayQrCodeImages: values.alipayQrCodeImages || [], wechatQrCodeImages: values.wechatQrCodeImages || [], }) message.success('提现信息已添加,该方式后续修改请联系客服处理') @@ -316,6 +346,20 @@ export default function WorkerProfilePage() { } } + async function submitAlipayQrCode(values: AlipayQrCodeFormValues) { + try { + await saveWorkerAlipayWithdrawalQrCode({ + alipayQrCodeImages: values.alipayQrCodeImages || [], + }) + message.success('支付宝收款二维码已保存') + setAlipayQrCodeOpen(false) + alipayQrCodeForm.resetFields() + await refreshAll() + } catch (error) { + message.error(error instanceof Error ? error.message : '保存支付宝收款二维码失败') + } + } + async function submitPasswordChange(values: PasswordFormValues) { setSubmittingPassword(true) try { @@ -673,6 +717,22 @@ export default function WorkerProfilePage() { 邀请好友 + {alipayWithdrawalAccount && !alipayWithdrawalAccount.alipayQrCodeImage ? ( + + ) : null} + ) : null} @@ -1234,10 +1303,14 @@ export default function WorkerProfilePage() { {account.accountChannel === 'wechat' ? '已上传收款二维码' - : account.accountNoMasked} + : `${account.accountNoMasked} / ${ + account.alipayQrCodeImage + ? '已上传收款二维码' + : '未上传收款二维码' + }`} - 已锁定,修改请联系客服 + 收款人姓名和账号已锁定 ))} @@ -1533,7 +1606,7 @@ export default function WorkerProfilePage() {
@@ -1570,17 +1643,54 @@ export default function WorkerProfilePage() { ) : ( - - - + <> + + + + + + + )} + { + setAlipayQrCodeOpen(false) + alipayQrCodeForm.resetFields() + }} + onOk={() => alipayQrCodeForm.submit()} + > + +
+ + + +
+
+ ( '/api/v1/worker/profile/change-password', diff --git a/apps/frontend/src/types/worker-platform.ts b/apps/frontend/src/types/worker-platform.ts index fba07244..67622d3d 100644 --- a/apps/frontend/src/types/worker-platform.ts +++ b/apps/frontend/src/types/worker-platform.ts @@ -197,6 +197,7 @@ export type WorkerWithdrawalAccount = { accountChannel: string accountName: string accountNoMasked: string + alipayQrCodeImage: UploadedFile | null wechatQrCodeImage: UploadedFile | null createdAt: string | null } @@ -205,6 +206,7 @@ export type AdminWorkerWithdrawalAccount = { accountChannel: string accountName: string accountNo: string + alipayQrCodeImage: UploadedFile | null wechatQrCodeImage: UploadedFile | null createdAt: string }