feat(worker-platform): 实现打手寄卖白名单权限控制与存量发布打手平滑过渡
1. 新增数据库迁移 082_worker_consignment_whitelist.sql,worker_users 表增加 can_consign 与 consignment_activated_at 字段,存量在 consignment_submissions 提报过的打手默认自动开启; 2. 打手端提报接口 publishWorkerConsignment 增加白名单权限服务端强校验拦截(403 consignment_permission_required); 3. 管理后台打手管理表格增加「寄卖权限」实时 Switch 开关列与打手详情抽屉权限展示,支持客服即时开通或关闭打手寄卖权限; 4. 打手端我要寄卖页面在未开通权限时展示专属白名单引导卡片,一键呼起联系客服开通寄卖弹窗; 5. 补充寄卖权限拦截与路由单测,前端59项单测与后端507项单测全部通过。
This commit is contained in:
@@ -174,6 +174,18 @@ function OverviewSection({ workerId }: { workerId: number }) {
|
||||
<Tag color="green">正常可提现</Tag>
|
||||
)}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="寄卖权限" span={2}>
|
||||
<Space>
|
||||
<Tag color={worker.canConsign ? 'green' : 'default'}>
|
||||
{worker.canConsign ? '已开通寄卖' : '未开通(白名单限制)'}
|
||||
</Tag>
|
||||
{worker.consignmentActivatedAt ? (
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||
开通时间:{formatAdminDateTime(worker.consignmentActivatedAt)}
|
||||
</Typography.Text>
|
||||
) : null}
|
||||
</Space>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
<Card size="small" title="钱包">
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Modal,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Table,
|
||||
Tag,
|
||||
Tooltip,
|
||||
@@ -30,6 +31,7 @@ import {
|
||||
saveAdminWorkerPersonalDepositFreeAmount,
|
||||
saveAdminWorkerWithdrawalAccount,
|
||||
updateAdminWorkerWithdrawalBlacklist,
|
||||
updateAdminWorkerConsignmentPermission,
|
||||
} from '@/services/admin'
|
||||
import ImageUpload from '@/components/files/ImageUpload'
|
||||
import type {
|
||||
@@ -464,6 +466,27 @@ export default function WorkersPanel() {
|
||||
}
|
||||
}
|
||||
|
||||
const [updatingConsignmentWorkerId, setUpdatingConsignmentWorkerId] = useState<number | null>(
|
||||
null,
|
||||
)
|
||||
|
||||
async function handleToggleConsignmentPermission(row: WorkerUser, checked: boolean) {
|
||||
setUpdatingConsignmentWorkerId(row.workerId)
|
||||
try {
|
||||
await updateAdminWorkerConsignmentPermission(row.workerId, { canConsign: checked })
|
||||
message.success(
|
||||
checked
|
||||
? `已为打手 ${row.displayName || row.username} 开通寄卖权限`
|
||||
: `已关闭打手 ${row.displayName || row.username} 的寄卖权限`,
|
||||
)
|
||||
await queryClient.invalidateQueries({ queryKey: ['admin-workers'] })
|
||||
} catch (err: any) {
|
||||
message.error(err instanceof Error ? err.message : '更新寄卖权限失败')
|
||||
} finally {
|
||||
setUpdatingConsignmentWorkerId(null)
|
||||
}
|
||||
}
|
||||
|
||||
const columns: TableColumnsType<WorkerUser> = [
|
||||
{
|
||||
title: '打手',
|
||||
@@ -554,6 +577,19 @@ export default function WorkersPanel() {
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '寄卖权限',
|
||||
width: 120,
|
||||
render: (_, row) => (
|
||||
<Switch
|
||||
checkedChildren="已开通"
|
||||
unCheckedChildren="未开通"
|
||||
checked={row.canConsign}
|
||||
loading={updatingConsignmentWorkerId === row.workerId}
|
||||
onChange={(checked) => handleToggleConsignmentPermission(row, checked)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '冻结原因',
|
||||
width: 220,
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
ReloadOutlined,
|
||||
SafetyCertificateOutlined,
|
||||
UserOutlined,
|
||||
CustomerServiceOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import {
|
||||
@@ -36,6 +37,7 @@ import {
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import ImageUpload from '@/components/files/ImageUpload'
|
||||
import { WorkerContactModal } from '@/components/worker/WorkerContactModal'
|
||||
import {
|
||||
confirmWorkerConsignmentFriendAdded,
|
||||
fetchWorkerConsignmentPriceSuggestion,
|
||||
@@ -198,6 +200,8 @@ export default function WorkerConsignmentPage() {
|
||||
const publishDepositYuan = publishDepositFen / 100
|
||||
const availableAmount = Number(worker?.wallet?.availableAmount || 0)
|
||||
const canPublish = availableAmount >= publishDepositFen
|
||||
const canConsign = worker?.canConsign !== false
|
||||
const [contactOpen, setContactOpen] = useState(false)
|
||||
|
||||
const reviewingCount = records.filter((item) => item.group === 'reviewing').length
|
||||
const activeCount = records.filter((item) => item.group === 'active').length
|
||||
@@ -338,6 +342,11 @@ export default function WorkerConsignmentPage() {
|
||||
}
|
||||
|
||||
async function handlePublishSubmit(values: Record<string, any>) {
|
||||
if (!canConsign) {
|
||||
message.warning('您暂未开通寄卖权限,请联系客服申请开通')
|
||||
return
|
||||
}
|
||||
|
||||
if (!canPublish) {
|
||||
message.warning(`可用余额不足 ¥${publishDepositYuan.toFixed(0)},请先联系客服充值履约押金`)
|
||||
return
|
||||
@@ -579,7 +588,33 @@ export default function WorkerConsignmentPage() {
|
||||
<DollarCircleOutlined /> 发布寄卖
|
||||
</span>
|
||||
),
|
||||
children: (
|
||||
children: !canConsign ? (
|
||||
<div style={{ padding: '36px 16px', textAlign: 'center' }}>
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description={
|
||||
<div style={{ maxWidth: 420, margin: '0 auto' }}>
|
||||
<Typography.Title level={5} style={{ marginBottom: 8 }}>
|
||||
寄卖功能暂未开通
|
||||
</Typography.Title>
|
||||
<Typography.Paragraph
|
||||
type="secondary"
|
||||
style={{ fontSize: 13, marginBottom: 20 }}
|
||||
>
|
||||
为保障寄卖道具交付质量与履约安全,平台当前实行白名单审核制。请联系在线客服申请开通寄卖权限。
|
||||
</Typography.Paragraph>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<CustomerServiceOutlined />}
|
||||
onClick={() => setContactOpen(true)}
|
||||
>
|
||||
联系客服开通寄卖
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
@@ -929,6 +964,15 @@ export default function WorkerConsignmentPage() {
|
||||
/>
|
||||
</Space>
|
||||
</Modal>
|
||||
|
||||
<WorkerContactModal
|
||||
open={contactOpen}
|
||||
onClose={() => setContactOpen(false)}
|
||||
isMobile={isMobile}
|
||||
worker={worker}
|
||||
financeConfig={financeConfig}
|
||||
title="联系客服开通寄卖"
|
||||
/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -197,6 +197,16 @@ export function updateAdminWorkerWithdrawalBlacklist(
|
||||
)
|
||||
}
|
||||
|
||||
export function updateAdminWorkerConsignmentPermission(
|
||||
workerId: number | string,
|
||||
payload: { canConsign: boolean },
|
||||
) {
|
||||
return apiPost<{ worker: WorkerUser }>(
|
||||
`/api/v1/admin/worker-platform/workers/${workerId}/consignment-permission`,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchAdminWorkerWalletLedgers(
|
||||
workerId: number | string,
|
||||
params?: Record<string, unknown>,
|
||||
|
||||
@@ -54,6 +54,10 @@ export type WorkerUser = {
|
||||
lastRenamedAt: string | null
|
||||
/** 上次改名后的一年锁定期结束时间 */
|
||||
renameUnlockedAt: string | null
|
||||
/** 是否开通寄卖权限(白名单开关) */
|
||||
canConsign: boolean
|
||||
/** 开通寄卖权限时间 */
|
||||
consignmentActivatedAt: string | null
|
||||
}
|
||||
|
||||
export type WorkerUserStatistics = {
|
||||
|
||||
Reference in New Issue
Block a user