feat(打手): 支持个人追加免押额度
This commit is contained in:
@@ -355,6 +355,7 @@ function applyWorkerDepositRule(queryClient: ReturnType<typeof useQueryClient>,
|
||||
const requiredDepositAmount = Number(data.requiredDepositAmount || 0)
|
||||
return {
|
||||
...data,
|
||||
depositFreeAmount,
|
||||
freezeDepositAmount: Math.max(0, requiredDepositAmount - depositFreeAmount),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
fetchAdminWorkerWithdrawalAccounts,
|
||||
resetAdminWorkerPassword,
|
||||
reviewAdminWorkerUser,
|
||||
saveAdminWorkerPersonalDepositFreeAmount,
|
||||
saveAdminWorkerWithdrawalAccount,
|
||||
} from '@/services/admin'
|
||||
import ImageUpload from '@/components/files/ImageUpload'
|
||||
@@ -49,6 +50,10 @@ type FreezeWorkerFormValues = {
|
||||
freezeReason: string
|
||||
}
|
||||
|
||||
type PersonalDepositFreeAmountFormValues = {
|
||||
depositFreeAmount?: number
|
||||
}
|
||||
|
||||
export default function WorkersPanel() {
|
||||
const { message, modal } = App.useApp()
|
||||
const queryClient = useQueryClient()
|
||||
@@ -66,6 +71,9 @@ 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 [personalDepositFreeWorker, setPersonalDepositFreeWorker] = useState<WorkerUser | null>(
|
||||
null,
|
||||
)
|
||||
const [detailWorker, setDetailWorker] = useState<WorkerUser | null>(null)
|
||||
const autoOpenedDetailRef = useRef(0)
|
||||
const [withdrawalAccounts, setWithdrawalAccounts] = useState<AdminWorkerWithdrawalAccount[]>([])
|
||||
@@ -76,6 +84,7 @@ export default function WorkersPanel() {
|
||||
const [withdrawalAccountForm] = Form.useForm<WithdrawalAccountFormValues>()
|
||||
const [resetPasswordForm] = Form.useForm<{ password: string }>()
|
||||
const [freezeForm] = Form.useForm<FreezeWorkerFormValues>()
|
||||
const [personalDepositFreeForm] = Form.useForm<PersonalDepositFreeAmountFormValues>()
|
||||
const withdrawalAccountChannel = Form.useWatch('accountChannel', withdrawalAccountForm)
|
||||
const editingAlipayAccount = withdrawalAccounts.find(
|
||||
(account) => account.accountChannel === 'alipay',
|
||||
@@ -266,6 +275,21 @@ export default function WorkersPanel() {
|
||||
}
|
||||
}
|
||||
|
||||
async function submitPersonalDepositFreeAmount(values: PersonalDepositFreeAmountFormValues) {
|
||||
if (!personalDepositFreeWorker) return
|
||||
try {
|
||||
await saveAdminWorkerPersonalDepositFreeAmount(personalDepositFreeWorker.workerId, {
|
||||
depositFreeAmount: values.depositFreeAmount ?? null,
|
||||
})
|
||||
message.success('个人免押额度已更新')
|
||||
setPersonalDepositFreeWorker(null)
|
||||
personalDepositFreeForm.resetFields()
|
||||
await refreshWorkers()
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : '更新个人免押额度失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function openWithdrawalAccountModal(worker: WorkerUser) {
|
||||
setWithdrawalAccountWorker(worker)
|
||||
setWithdrawalAccounts([])
|
||||
@@ -341,6 +365,22 @@ export default function WorkersPanel() {
|
||||
),
|
||||
},
|
||||
{ title: '等级', width: 140, render: (_, row) => row.level?.name || '-' },
|
||||
{
|
||||
title: '免押额度',
|
||||
width: 280,
|
||||
render: (_, row) =>
|
||||
row.personalDepositFreeAmount !== null ? (
|
||||
<Typography.Text>
|
||||
{formatMoney(row.effectiveDepositFreeAmount)}(等级 {formatMoney(
|
||||
row.levelDepositFreeAmount,
|
||||
)} + 个人 {formatMoney(row.personalDepositFreeAmount)})
|
||||
</Typography.Text>
|
||||
) : (
|
||||
<Typography.Text type="secondary">
|
||||
{formatMoney(row.effectiveDepositFreeAmount)}(等级)
|
||||
</Typography.Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '邀请人',
|
||||
width: 160,
|
||||
@@ -428,6 +468,20 @@ export default function WorkersPanel() {
|
||||
>
|
||||
调等级
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setPersonalDepositFreeWorker(row)
|
||||
personalDepositFreeForm.setFieldsValue({
|
||||
// 接口金额使用“分”,输入框按“元”编辑。
|
||||
depositFreeAmount:
|
||||
row.personalDepositFreeAmount === null
|
||||
? undefined
|
||||
: Number(row.personalDepositFreeAmount) / 100,
|
||||
})
|
||||
}}
|
||||
>
|
||||
免押额度
|
||||
</Button>
|
||||
<Button onClick={() => setCreditWorker(row)}>充值</Button>
|
||||
<Button onClick={() => setResetPasswordWorker(row)}>重置密码</Button>
|
||||
<Button onClick={() => void openWithdrawalAccountModal(row)}>提现信息</Button>
|
||||
@@ -563,6 +617,39 @@ export default function WorkersPanel() {
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="设置个人免押额度"
|
||||
open={Boolean(personalDepositFreeWorker)}
|
||||
onCancel={() => {
|
||||
setPersonalDepositFreeWorker(null)
|
||||
personalDepositFreeForm.resetFields()
|
||||
}}
|
||||
onOk={() => personalDepositFreeForm.submit()}
|
||||
destroyOnHidden
|
||||
>
|
||||
<Form
|
||||
form={personalDepositFreeForm}
|
||||
layout="vertical"
|
||||
onFinish={submitPersonalDepositFreeAmount}
|
||||
>
|
||||
<Form.Item label="打手">
|
||||
<Typography.Text>
|
||||
{personalDepositFreeWorker?.displayName || personalDepositFreeWorker?.username || '-'}
|
||||
</Typography.Text>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="个人免押额度"
|
||||
name="depositFreeAmount"
|
||||
tooltip="该额度会与等级免押额度相加;留空或填 0 表示不追加个人额度。"
|
||||
>
|
||||
<InputNumber min={0} step={10} addonAfter="元" className="full-width" />
|
||||
</Form.Item>
|
||||
<Typography.Text type="secondary">
|
||||
个人额度会叠加等级额度,合计额度以内的订单或拼单份额无需冻结押金;超过部分才会冻结。
|
||||
</Typography.Text>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="重置打手密码"
|
||||
open={Boolean(resetPasswordWorker)}
|
||||
|
||||
@@ -365,6 +365,16 @@ export function reviewAdminWorkerUser(
|
||||
)
|
||||
}
|
||||
|
||||
export function saveAdminWorkerPersonalDepositFreeAmount(
|
||||
workerId: number,
|
||||
payload: { depositFreeAmount: number | null },
|
||||
) {
|
||||
return apiPost<{ worker: WorkerUser }>(
|
||||
`/api/v1/admin/worker-platform/workers/${workerId}/personal-deposit-free-amount`,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function resetAdminWorkerPassword(workerId: number, payload: { password: string }) {
|
||||
return apiPost<{ worker: WorkerUser; reloginRequired: boolean }>(
|
||||
`/api/v1/admin/worker-platform/workers/${workerId}/reset-password`,
|
||||
|
||||
@@ -9,6 +9,12 @@ export type WorkerUser = {
|
||||
freezeReason: string
|
||||
frozenAt: string | null
|
||||
inviteCode: string
|
||||
/** 个人追加免押额度;null 表示未追加。 */
|
||||
personalDepositFreeAmount: number | null
|
||||
/** 打手等级带来的免押额度。 */
|
||||
levelDepositFreeAmount: number
|
||||
/** 当前实际生效的免押额度。 */
|
||||
effectiveDepositFreeAmount: number
|
||||
inviter: null | {
|
||||
workerId: number
|
||||
username: string
|
||||
@@ -561,6 +567,8 @@ export type WorkOrder = {
|
||||
canResetGiftFlow?: boolean
|
||||
}
|
||||
rewardAmount: number
|
||||
/** 当前打手生效的免押额度,仅打手端订单数据返回。 */
|
||||
depositFreeAmount?: number
|
||||
requiredDepositAmount?: number
|
||||
depositThresholdAmount?: number
|
||||
freezeDepositAmount?: number
|
||||
|
||||
@@ -2,6 +2,7 @@ type HallProduct = {
|
||||
productName?: string
|
||||
rewardAmount?: number
|
||||
freezeDepositAmount?: number
|
||||
depositFreeAmount?: number
|
||||
sharing?: {
|
||||
enabled: boolean
|
||||
totalQuantity: number
|
||||
@@ -41,11 +42,15 @@ export function resolveHallRewardAmount(order: HallProduct): number {
|
||||
return resolveHallRemainingQuantity(order) * Number(order.sharing.unitReward || 0)
|
||||
}
|
||||
|
||||
/** 拼单卡片保证金按剩余份数占总份数的比例展示。 */
|
||||
/** 拼单卡片按当前剩余份数计算个人免押后的实际保证金。 */
|
||||
export function resolveHallDepositAmount(order: HallProduct): number {
|
||||
if (!order.sharing?.enabled) return Number(order.freezeDepositAmount || 0)
|
||||
const totalQuantity = Math.max(1, Number(order.sharing.totalQuantity) || 1)
|
||||
return Math.round(
|
||||
(Number(order.freezeDepositAmount || 0) * resolveHallRemainingQuantity(order)) / totalQuantity,
|
||||
const depositFreeAmount = Math.max(0, Number(order.depositFreeAmount || 0))
|
||||
const requiredDepositAmount = Number(order.freezeDepositAmount || 0) + depositFreeAmount
|
||||
return Math.max(
|
||||
0,
|
||||
Math.round((requiredDepositAmount * resolveHallRemainingQuantity(order)) / totalQuantity) -
|
||||
depositFreeAmount,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user