feat(worker-platform): add worker withdrawal blacklist and admin actions

This commit is contained in:
yml2213
2026-09-13 11:32:42 +08:00
parent 18ac95ca27
commit 061a5d897f
15 changed files with 405 additions and 7 deletions
@@ -109,6 +109,7 @@ export default function WorkerDetailDrawer({
<Tag color={worker.status === 'active' ? 'green' : 'orange'}>
{formatWorkerStatus(worker.status)}
</Tag>
{worker.isWithdrawalBlacklisted ? <Tag color="red">提现黑名单</Tag> : null}
</Space>
) : null
}
@@ -159,6 +160,20 @@ function OverviewSection({ workerId }: { workerId: number }) {
<Descriptions.Item label="注册时间">
{formatAdminDateTime(worker.createdAt)}
</Descriptions.Item>
<Descriptions.Item label="提现权限" span={2}>
{worker.isWithdrawalBlacklisted ? (
<Space>
<Tag color="red">已加入提现黑名单</Tag>
{worker.withdrawalBlacklistReason ? (
<Typography.Text type="secondary">
原因:{worker.withdrawalBlacklistReason}
</Typography.Text>
) : null}
</Space>
) : (
<Tag color="green">正常可提现</Tag>
)}
</Descriptions.Item>
</Descriptions>
</Card>
<Card size="small" title="钱包">
@@ -1,6 +1,7 @@
import { ReloadOutlined, SearchOutlined } from '@ant-design/icons'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import {
Alert,
App,
Button,
Card,
@@ -12,6 +13,7 @@ import {
Space,
Table,
Tag,
Tooltip,
Typography,
} from 'antd'
import type { TableColumnsType } from 'antd'
@@ -27,6 +29,7 @@ import {
reviewAdminWorkerUser,
saveAdminWorkerPersonalDepositFreeAmount,
saveAdminWorkerWithdrawalAccount,
updateAdminWorkerWithdrawalBlacklist,
} from '@/services/admin'
import ImageUpload from '@/components/files/ImageUpload'
import type {
@@ -119,6 +122,7 @@ export default function WorkersPanel() {
const [keywordInput, setKeywordInput] = useState('')
const [keyword, setKeyword] = useState('')
const [workerType, setWorkerType] = useState('')
const [withdrawalBlacklisted, setWithdrawalBlacklisted] = useState('')
const [levelId, setLevelId] = useState(0)
const [targetWorkerId, setTargetWorkerId] = useState(0)
const [page, setPage] = useState(1)
@@ -128,6 +132,8 @@ export default function WorkersPanel() {
const [withdrawalAccountWorker, setWithdrawalAccountWorker] = useState<WorkerUser | null>(null)
const [resetPasswordWorker, setResetPasswordWorker] = useState<WorkerUser | null>(null)
const [freezeWorker, setFreezeWorker] = useState<WorkerUser | null>(null)
const [blacklistWorker, setBlacklistWorker] = useState<WorkerUser | null>(null)
const [submittingBlacklist, setSubmittingBlacklist] = useState(false)
const [personalDepositFreeWorker, setPersonalDepositFreeWorker] = useState<WorkerUser | null>(
null,
)
@@ -141,6 +147,7 @@ export default function WorkersPanel() {
const [withdrawalAccountForm] = Form.useForm<WithdrawalAccountFormValues>()
const [resetPasswordForm] = Form.useForm<{ password: string }>()
const [freezeForm] = Form.useForm<FreezeWorkerFormValues>()
const [blacklistForm] = Form.useForm<{ reason?: string }>()
const [personalDepositFreeForm] = Form.useForm<PersonalDepositFreeAmountFormValues>()
const withdrawalAccountChannel = Form.useWatch('accountChannel', withdrawalAccountForm)
const editingAlipayAccount = withdrawalAccounts.find(
@@ -158,6 +165,7 @@ export default function WorkersPanel() {
setKeyword('')
setStatus('')
setWorkerType('')
setWithdrawalBlacklisted('')
setLevelId(0)
setPage(1)
}, [searchParams])
@@ -168,6 +176,7 @@ export default function WorkersPanel() {
status,
keyword,
workerType,
withdrawalBlacklisted,
levelId,
targetWorkerId,
page,
@@ -178,6 +187,8 @@ export default function WorkersPanel() {
status,
keyword,
workerType,
isWithdrawalBlacklisted:
withdrawalBlacklisted === '' ? undefined : withdrawalBlacklisted === 'true',
levelId: levelId || undefined,
workerId: targetWorkerId || undefined,
includeStatistics: true,
@@ -223,6 +234,7 @@ export default function WorkersPanel() {
setKeyword('')
setStatus('')
setWorkerType('')
setWithdrawalBlacklisted('')
setLevelId(0)
setTargetWorkerId(0)
setPage(1)
@@ -235,6 +247,50 @@ export default function WorkersPanel() {
)
}
function handleAddToBlacklist(worker: WorkerUser) {
setBlacklistWorker(worker)
blacklistForm.resetFields()
}
async function submitAddToBlacklist(values: { reason?: string }) {
if (!blacklistWorker) return
setSubmittingBlacklist(true)
try {
await updateAdminWorkerWithdrawalBlacklist(blacklistWorker.workerId, {
isWithdrawalBlacklisted: true,
reason: values.reason?.trim() || '',
})
message.success('已将打手加入提现黑名单')
setBlacklistWorker(null)
blacklistForm.resetFields()
await refreshWorkers()
} catch (error) {
message.error(error instanceof Error ? error.message : '加入提现黑名单失败')
} finally {
setSubmittingBlacklist(false)
}
}
function handleRemoveFromBlacklist(worker: WorkerUser) {
modal.confirm({
title: '移出提现黑名单?',
content: `确认将打手“${worker.displayName || worker.username}”(UID: ${worker.workerId})移出提现黑名单,恢复其正常提现功能?`,
okText: '确认移出',
cancelText: '取消',
onOk: async () => {
try {
await updateAdminWorkerWithdrawalBlacklist(worker.workerId, {
isWithdrawalBlacklisted: false,
})
message.success('已移出提现黑名单,提现功能已恢复')
await refreshWorkers()
} catch (error) {
message.error(error instanceof Error ? error.message : '移出提现黑名单失败')
}
},
})
}
async function review(worker: WorkerUser, nextStatus: string) {
try {
await reviewAdminWorkerUser(worker.workerId, { status: nextStatus })
@@ -478,9 +534,22 @@ export default function WorkersPanel() {
title: '状态',
width: 130,
render: (_, row) => (
<Tag color={row.status === 'active' ? 'green' : 'orange'}>
{formatWorkerStatus(row.status)}
</Tag>
<Space direction="vertical" size={2}>
<Tag color={row.status === 'active' ? 'green' : 'orange'}>
{formatWorkerStatus(row.status)}
</Tag>
{row.isWithdrawalBlacklisted ? (
<Tooltip
title={
row.withdrawalBlacklistReason
? `原因:${row.withdrawalBlacklistReason}`
: '已加入提现黑名单,无法发起提现'
}
>
<Tag color="red">提现黑名单</Tag>
</Tooltip>
) : null}
</Space>
),
},
{
@@ -497,7 +566,7 @@ export default function WorkersPanel() {
},
{
title: '操作',
width: 340,
width: 360,
render: (_, row) => (
<Space wrap>
<Button type="primary" ghost onClick={() => setDetailWorker(row)}>
@@ -543,6 +612,13 @@ export default function WorkersPanel() {
<Button onClick={() => setCreditWorker(row)}>充值</Button>
<Button onClick={() => setResetPasswordWorker(row)}>重置密码</Button>
<Button onClick={() => void openWithdrawalAccountModal(row)}>提现信息</Button>
{row.isWithdrawalBlacklisted ? (
<Button onClick={() => handleRemoveFromBlacklist(row)}>移出提现黑名单</Button>
) : (
<Button danger onClick={() => handleAddToBlacklist(row)}>
加入提现黑名单
</Button>
)}
</Space>
),
},
@@ -607,6 +683,21 @@ export default function WorkersPanel() {
setPage(1)
}}
/>
<Select
allowClear
value={withdrawalBlacklisted || undefined}
style={{ width: 140 }}
placeholder="提现状态"
options={[
{ value: '', label: '全部提现状态' },
{ value: 'false', label: '提现正常' },
{ value: 'true', label: '提现黑名单' },
]}
onChange={(nextVal) => {
setWithdrawalBlacklisted(nextVal || '')
setPage(1)
}}
/>
<Button type="primary" icon={<SearchOutlined />} onClick={searchWorkers}>
查询
</Button>
@@ -874,6 +965,47 @@ export default function WorkersPanel() {
</Form>
</Modal>
<Modal
title="加入提现黑名单"
open={Boolean(blacklistWorker)}
confirmLoading={submittingBlacklist}
onCancel={() => {
setBlacklistWorker(null)
blacklistForm.resetFields()
}}
onOk={() => blacklistForm.submit()}
destroyOnHidden
>
<Form form={blacklistForm} layout="vertical" onFinish={submitAddToBlacklist}>
<Form.Item label="打手">
<Typography.Text strong>
{blacklistWorker?.displayName || blacklistWorker?.username || '-'}
</Typography.Text>
<Typography.Text type="secondary" style={{ marginLeft: 8 }}>
(UID: {blacklistWorker?.workerId} · 账号: {blacklistWorker?.username})
</Typography.Text>
</Form.Item>
<Alert
showIcon
type="warning"
message="加入提现黑名单后,该打手(无论是内部还是外部)将无法提交提现申请;正常接单、做单和结算不受影响。"
style={{ marginBottom: 16 }}
/>
<Form.Item
label="拉黑原因 / 备注"
name="reason"
rules={[{ max: 200, message: '原因不能超过 200 个字符' }]}
>
<Input.TextArea
autoSize={{ minRows: 3, maxRows: 6 }}
maxLength={200}
placeholder="例如:涉嫌违规刷单,暂停提现权限等待核实(打手发起提现时将提示该原因)"
showCount
/>
</Form.Item>
</Form>
</Modal>
<WorkerDetailDrawer worker={detailWorker} onClose={() => setDetailWorker(null)} />
</section>
)
@@ -61,6 +61,11 @@ export function WorkerProfileDesktopIdentity({
>
{formatWorkerStatus(worker.status)}
</Tag>
{worker.isWithdrawalBlacklisted ? (
<Tag color="red" className="worker-profile-status-tag">
提现黑名单
</Tag>
) : null}
{worker.level?.name ? (
<span className="worker-profile-level-badge">{worker.level.name}</span>
) : null}
@@ -95,6 +95,11 @@ export function WorkerProfileMobileView({
<Tag className="worker-profile-mobile-tag">
{worker.workerType === 'internal' ? '内部打手' : '外部打手'}
</Tag>
{worker.isWithdrawalBlacklisted ? (
<Tag color="red" className="worker-profile-mobile-tag">
提现黑名单
</Tag>
) : null}
</div>
<div className="worker-profile-mobile-account-text">
账号: {worker.username} {worker.phone ? `· ${worker.phone}` : ''}
@@ -156,6 +161,18 @@ export function WorkerProfileMobileView({
/>
) : null}
{worker.isWithdrawalBlacklisted ? (
<Alert
showIcon
type="error"
message={
worker.withdrawalBlacklistReason
? `提现受限:当前账号已被列入提现黑名单(原因:${worker.withdrawalBlacklistReason}),无法发起提现。如有疑问请联系客服。`
: '提现受限:当前账号已被列入提现黑名单,无法发起提现。如有疑问请联系客服。'
}
/>
) : null}
<div className="worker-profile-mobile-wallet-card">
<div className="worker-profile-mobile-wallet-top">
<div className="worker-profile-mobile-wallet-main">
@@ -220,6 +220,14 @@ export default function WorkerProfilePage() {
message.warning('当前账号尚未通过审核,暂时不能发起提现申请')
return
}
if (worker?.isWithdrawalBlacklisted) {
message.error(
worker.withdrawalBlacklistReason?.trim()
? `您当前已被限制提现(原因:${worker.withdrawalBlacklistReason.trim()}),如有疑问请联系客服`
: '您当前已被限制提现,如有疑问请联系客服',
)
return
}
if (financeConfig?.withdraw.enabled === false) {
message.warning('当前提现入口暂未开放,请联系管理员')
return
@@ -510,6 +518,18 @@ export default function WorkerProfilePage() {
/>
) : null}
{worker.isWithdrawalBlacklisted ? (
<Alert
showIcon
type="error"
message={
worker.withdrawalBlacklistReason
? `提现受限:当前账号已被列入提现黑名单(原因:${worker.withdrawalBlacklistReason}),无法发起提现。如有疑问请联系平台客服。`
: '提现受限:当前账号已被列入提现黑名单,无法发起提现。如有疑问请联系平台客服。'
}
/>
) : null}
<WorkerProfileDesktopIdentity
worker={worker}
summary={summary}
@@ -186,6 +186,16 @@ export function fetchAdminWorkerDetail(workerId: number | string) {
return apiGet<AdminWorkerDetail>(`/api/v1/admin/worker-platform/workers/${workerId}/detail`)
}
export function updateAdminWorkerWithdrawalBlacklist(
workerId: number | string,
payload: { isWithdrawalBlacklisted: boolean; reason?: string },
) {
return apiPost<{ worker: WorkerUser }>(
`/api/v1/admin/worker-platform/workers/${workerId}/withdrawal-blacklist`,
payload,
)
}
export function fetchAdminWorkerWalletLedgers(
workerId: number | string,
params?: Record<string, unknown>,
@@ -43,6 +43,12 @@ export type WorkerUser = {
createdAt: string
updatedAt: string
reviewedAt: string | null
/** 是否已被列入提现黑名单 */
isWithdrawalBlacklisted: boolean
/** 提现黑名单原因/备注 */
withdrawalBlacklistReason: string
/** 列入提现黑名单时间 */
withdrawalBlacklistedAt: string | null
/** 剩余改名卡数量,注册默认 1 张 */
renameCards: number
lastRenamedAt: string | null