增加打手列表筛选汇总
This commit is contained in:
@@ -44,6 +44,18 @@ export type WorkerUserRow = {
|
|||||||
last_renamed_at?: string | null
|
last_renamed_at?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type WorkerUserStatistics = {
|
||||||
|
totalCount: number
|
||||||
|
statusCounts: Record<string, number>
|
||||||
|
workerTypeCounts: Record<string, number>
|
||||||
|
availableAmount: number
|
||||||
|
frozenDepositAmount: number
|
||||||
|
pendingUnfreezeAmount: number
|
||||||
|
totalCreditedAmount: number
|
||||||
|
totalSettledAmount: number
|
||||||
|
timeoutOrderCount: number
|
||||||
|
}
|
||||||
|
|
||||||
export type WorkerSessionRow = {
|
export type WorkerSessionRow = {
|
||||||
id: number
|
id: number
|
||||||
session_id: string
|
session_id: string
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { query, withTransaction } from '../../db/client.js'
|
import { query, withTransaction } from '../../db/client.js'
|
||||||
import { ensureWorkerWalletWithClient, getWorkerWalletWithClient } from './shared.js'
|
import { ensureWorkerWalletWithClient, getWorkerWalletWithClient } from './shared.js'
|
||||||
import type { CreateWorkerInput, ListInput, WorkerUserRow } from './types.js'
|
import type { CreateWorkerInput, ListInput, WorkerUserRow, WorkerUserStatistics } from './types.js'
|
||||||
import type { PoolClient } from 'pg'
|
import type { PoolClient } from 'pg'
|
||||||
|
|
||||||
const WORKER_USER_SELECT = `
|
const WORKER_USER_SELECT = `
|
||||||
@@ -265,6 +265,75 @@ export async function listWorkerUsers({
|
|||||||
return { items: itemsResult.rows, total: Number(totalResult.rows[0]?.total || 0) }
|
return { items: itemsResult.rows, total: Number(totalResult.rows[0]?.total || 0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 按筛选条件汇总打手,统计范围不受当前分页影响。 */
|
||||||
|
export async function getWorkerUserStatistics(
|
||||||
|
input: Pick<ListInput, 'status' | 'keyword' | 'workerType' | 'workerId' | 'levelId'> = {},
|
||||||
|
): Promise<WorkerUserStatistics> {
|
||||||
|
const { whereClause, params } = buildWorkerUserWhere(input)
|
||||||
|
const statisticsResult = await query<{
|
||||||
|
status: string
|
||||||
|
worker_type: string
|
||||||
|
total_count: number
|
||||||
|
available_amount: number
|
||||||
|
frozen_deposit_amount: number
|
||||||
|
pending_unfreeze_amount: number
|
||||||
|
total_credited_amount: number
|
||||||
|
total_settled_amount: number
|
||||||
|
}>(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
wu.status,
|
||||||
|
wu.worker_type,
|
||||||
|
COUNT(*)::int AS total_count,
|
||||||
|
COALESCE(SUM(COALESCE(ww.available_amount, 0)), 0)::bigint AS available_amount,
|
||||||
|
COALESCE(SUM(COALESCE(ww.frozen_deposit_amount, 0)), 0)::bigint AS frozen_deposit_amount,
|
||||||
|
COALESCE(SUM(COALESCE(ww.pending_unfreeze_amount, 0)), 0)::bigint AS pending_unfreeze_amount,
|
||||||
|
COALESCE(SUM(COALESCE(ww.total_credited_amount, 0)), 0)::bigint AS total_credited_amount,
|
||||||
|
COALESCE(SUM(COALESCE(ww.total_settled_amount, 0)), 0)::bigint AS total_settled_amount
|
||||||
|
FROM worker_users wu
|
||||||
|
LEFT JOIN worker_wallets ww ON ww.worker_id = wu.id
|
||||||
|
${whereClause}
|
||||||
|
GROUP BY wu.status, wu.worker_type
|
||||||
|
`,
|
||||||
|
params,
|
||||||
|
)
|
||||||
|
|
||||||
|
const timeoutResult = await query<{ total: number }>(
|
||||||
|
`
|
||||||
|
SELECT COUNT(*)::int AS total
|
||||||
|
FROM work_order_events e
|
||||||
|
INNER JOIN worker_users wu ON e.payload_json->>'workerId' = wu.id::text
|
||||||
|
${whereClause ? `${whereClause} AND` : 'WHERE'} e.event_type LIKE 'timeout_%'
|
||||||
|
`,
|
||||||
|
params,
|
||||||
|
)
|
||||||
|
|
||||||
|
const statistics: WorkerUserStatistics = {
|
||||||
|
totalCount: 0,
|
||||||
|
statusCounts: {},
|
||||||
|
workerTypeCounts: {},
|
||||||
|
availableAmount: 0,
|
||||||
|
frozenDepositAmount: 0,
|
||||||
|
pendingUnfreezeAmount: 0,
|
||||||
|
totalCreditedAmount: 0,
|
||||||
|
totalSettledAmount: 0,
|
||||||
|
timeoutOrderCount: Number(timeoutResult.rows[0]?.total || 0),
|
||||||
|
}
|
||||||
|
for (const row of statisticsResult.rows) {
|
||||||
|
const count = Number(row.total_count || 0)
|
||||||
|
statistics.totalCount += count
|
||||||
|
statistics.statusCounts[row.status] = (statistics.statusCounts[row.status] || 0) + count
|
||||||
|
statistics.workerTypeCounts[row.worker_type] =
|
||||||
|
(statistics.workerTypeCounts[row.worker_type] || 0) + count
|
||||||
|
statistics.availableAmount += Number(row.available_amount || 0)
|
||||||
|
statistics.frozenDepositAmount += Number(row.frozen_deposit_amount || 0)
|
||||||
|
statistics.pendingUnfreezeAmount += Number(row.pending_unfreeze_amount || 0)
|
||||||
|
statistics.totalCreditedAmount += Number(row.total_credited_amount || 0)
|
||||||
|
statistics.totalSettledAmount += Number(row.total_settled_amount || 0)
|
||||||
|
}
|
||||||
|
return statistics
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
addWorkerWalletCredit,
|
addWorkerWalletCredit,
|
||||||
ensureWorkerWallet,
|
ensureWorkerWallet,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
countTimeoutEventsByWorkerIds,
|
countTimeoutEventsByWorkerIds,
|
||||||
|
getWorkerUserStatistics,
|
||||||
getWorkerLevelById,
|
getWorkerLevelById,
|
||||||
listWorkerUsers,
|
listWorkerUsers,
|
||||||
revokeAllWorkerSessions,
|
revokeAllWorkerSessions,
|
||||||
@@ -14,6 +15,7 @@ import {
|
|||||||
hashWorkerPassword,
|
hashWorkerPassword,
|
||||||
mapWorkerUser,
|
mapWorkerUser,
|
||||||
normalizeAmountFen,
|
normalizeAmountFen,
|
||||||
|
normalizeBoolean,
|
||||||
normalizeOptionalId,
|
normalizeOptionalId,
|
||||||
normalizeReviewStatus,
|
normalizeReviewStatus,
|
||||||
normalizeSessionVersion,
|
normalizeSessionVersion,
|
||||||
@@ -29,15 +31,21 @@ export async function listAdminWorkerUsers(query: JsonObject = {}) {
|
|||||||
const pageSize = normalizePageSize(query.pageSize)
|
const pageSize = normalizePageSize(query.pageSize)
|
||||||
const workerId = normalizeOptionalId(query.workerId ?? query.worker_id) || 0
|
const workerId = normalizeOptionalId(query.workerId ?? query.worker_id) || 0
|
||||||
const levelId = normalizeOptionalId(query.levelId ?? query.level_id) || 0
|
const levelId = normalizeOptionalId(query.levelId ?? query.level_id) || 0
|
||||||
const { items, total } = await listWorkerUsers({
|
const filters = {
|
||||||
page,
|
|
||||||
pageSize,
|
|
||||||
workerId,
|
workerId,
|
||||||
levelId,
|
levelId,
|
||||||
status: String(query.status || '').trim(),
|
status: String(query.status || '').trim(),
|
||||||
keyword: String(query.keyword || '').trim(),
|
keyword: String(query.keyword || '').trim(),
|
||||||
workerType: String(query.workerType || query.worker_type || '').trim(),
|
workerType: String(query.workerType || query.worker_type || '').trim(),
|
||||||
})
|
}
|
||||||
|
const includeStatistics = normalizeBoolean(
|
||||||
|
query.includeStatistics ?? query.include_statistics,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
const [{ items, total }, statistics] = await Promise.all([
|
||||||
|
listWorkerUsers({ page, pageSize, ...filters }),
|
||||||
|
includeStatistics ? getWorkerUserStatistics(filters) : Promise.resolve(undefined),
|
||||||
|
])
|
||||||
const timeoutCounts = await countTimeoutEventsByWorkerIds(items.map((item) => Number(item.id)))
|
const timeoutCounts = await countTimeoutEventsByWorkerIds(items.map((item) => Number(item.id)))
|
||||||
return {
|
return {
|
||||||
items: items.map((item) => ({
|
items: items.map((item) => ({
|
||||||
@@ -45,6 +53,7 @@ export async function listAdminWorkerUsers(query: JsonObject = {}) {
|
|||||||
timeoutOrderCount: timeoutCounts.get(Number(item.id)) || 0,
|
timeoutOrderCount: timeoutCounts.get(Number(item.id)) || 0,
|
||||||
})),
|
})),
|
||||||
pagination: { page, pageSize, total },
|
pagination: { page, pageSize, total },
|
||||||
|
...(statistics ? { statistics } : {}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import type {
|
|||||||
AdminWorkerWithdrawalAccount,
|
AdminWorkerWithdrawalAccount,
|
||||||
UploadedFile,
|
UploadedFile,
|
||||||
WorkerUser,
|
WorkerUser,
|
||||||
|
WorkerUserStatistics,
|
||||||
} from '@/types/worker-platform'
|
} from '@/types/worker-platform'
|
||||||
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
|
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
|
||||||
import WorkerDetailDrawer from './WorkerDetailDrawer'
|
import WorkerDetailDrawer from './WorkerDetailDrawer'
|
||||||
@@ -54,6 +55,62 @@ type PersonalDepositFreeAmountFormValues = {
|
|||||||
depositFreeAmount?: number
|
depositFreeAmount?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const WORKER_STATISTICS_STATUS_ORDER = ['pending_review', 'active', 'rejected', 'disabled']
|
||||||
|
const WORKER_STATISTICS_TYPE_ORDER = ['internal', 'external']
|
||||||
|
|
||||||
|
function WorkerStatisticsBar({ statistics }: { statistics?: WorkerUserStatistics }) {
|
||||||
|
if (!statistics) return null
|
||||||
|
const statusItems = WORKER_STATISTICS_STATUS_ORDER.map((status) => ({
|
||||||
|
status,
|
||||||
|
count: Number(statistics.statusCounts?.[status] || 0),
|
||||||
|
}))
|
||||||
|
const otherStatusItems = Object.entries(statistics.statusCounts || {})
|
||||||
|
.filter(([status]) => !WORKER_STATISTICS_STATUS_ORDER.includes(status))
|
||||||
|
.map(([status, count]) => ({ status, count: Number(count || 0) }))
|
||||||
|
const typeItems = WORKER_STATISTICS_TYPE_ORDER.map((workerType) => ({
|
||||||
|
workerType,
|
||||||
|
count: Number(statistics.workerTypeCounts?.[workerType] || 0),
|
||||||
|
}))
|
||||||
|
const otherTypeItems = Object.entries(statistics.workerTypeCounts || {})
|
||||||
|
.filter(([workerType]) => !WORKER_STATISTICS_TYPE_ORDER.includes(workerType))
|
||||||
|
.map(([workerType, count]) => ({ workerType, count: Number(count || 0) }))
|
||||||
|
const workerTypeLabels: Record<string, string> = {
|
||||||
|
internal: '内部打手',
|
||||||
|
external: '外部打手',
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card size="small" bordered={false} className="worker-orders-statistics-card">
|
||||||
|
<div className="worker-orders-statistics-summary">
|
||||||
|
本次筛选统计共 <strong>{statistics.totalCount}</strong> 名,余额合计{' '}
|
||||||
|
<strong>{formatMoney(statistics.availableAmount)}</strong>,冻结押金合计{' '}
|
||||||
|
<strong>{formatMoney(statistics.frozenDepositAmount)}</strong>,待解冻合计{' '}
|
||||||
|
<strong>{formatMoney(statistics.pendingUnfreezeAmount)}</strong>,累计结算{' '}
|
||||||
|
<strong>{formatMoney(statistics.totalSettledAmount)}</strong>,累计充值{' '}
|
||||||
|
<strong>{formatMoney(statistics.totalCreditedAmount)}</strong>。
|
||||||
|
</div>
|
||||||
|
<div className="worker-orders-statistics-statuses">
|
||||||
|
其中:
|
||||||
|
{[...statusItems, ...otherStatusItems].map(({ status, count }, index) => (
|
||||||
|
<span key={`status-${status}`}>
|
||||||
|
{index > 0 ? ',' : ''}
|
||||||
|
{formatWorkerStatus(status)}
|
||||||
|
{count}名
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
{[...typeItems, ...otherTypeItems].map(({ workerType, count }, index) => (
|
||||||
|
<span key={`type-${workerType}`}>
|
||||||
|
{index === 0 ? ',' : ''}
|
||||||
|
{workerTypeLabels[workerType] || workerType}
|
||||||
|
{count}名
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
<span>,超时订单{statistics.timeoutOrderCount}单</span>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function WorkersPanel() {
|
export default function WorkersPanel() {
|
||||||
const { message, modal } = App.useApp()
|
const { message, modal } = App.useApp()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
@@ -123,6 +180,7 @@ export default function WorkersPanel() {
|
|||||||
workerType,
|
workerType,
|
||||||
levelId: levelId || undefined,
|
levelId: levelId || undefined,
|
||||||
workerId: targetWorkerId || undefined,
|
workerId: targetWorkerId || undefined,
|
||||||
|
includeStatistics: true,
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
}),
|
}),
|
||||||
@@ -491,97 +549,102 @@ export default function WorkersPanel() {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<section className="platform-panel-stack">
|
||||||
title="打手"
|
<Card
|
||||||
extra={
|
title="打手"
|
||||||
<Space wrap className="filter-form">
|
extra={
|
||||||
<Input
|
<Space wrap className="filter-form">
|
||||||
allowClear
|
<Input
|
||||||
value={keywordInput}
|
allowClear
|
||||||
placeholder="账号 / 昵称 / 手机号"
|
value={keywordInput}
|
||||||
style={{ width: 220 }}
|
placeholder="账号 / 昵称 / 手机号"
|
||||||
onChange={(event) => setKeywordInput(event.target.value)}
|
style={{ width: 220 }}
|
||||||
onPressEnter={searchWorkers}
|
onChange={(event) => setKeywordInput(event.target.value)}
|
||||||
/>
|
onPressEnter={searchWorkers}
|
||||||
<Select
|
/>
|
||||||
allowClear
|
<Select
|
||||||
value={status}
|
allowClear
|
||||||
style={{ width: 150 }}
|
value={status}
|
||||||
options={[
|
style={{ width: 150 }}
|
||||||
{ value: '', label: '全部状态' },
|
options={[
|
||||||
{ value: 'pending_review', label: '待处理' },
|
{ value: '', label: '全部状态' },
|
||||||
{ value: 'active', label: '已启用' },
|
{ value: 'pending_review', label: '待处理' },
|
||||||
{ value: 'rejected', label: '已冻结' },
|
{ value: 'active', label: '已启用' },
|
||||||
{ value: 'disabled', label: '已停用' },
|
{ value: 'rejected', label: '已冻结' },
|
||||||
]}
|
{ value: 'disabled', label: '已停用' },
|
||||||
onChange={(nextStatus) => {
|
]}
|
||||||
setStatus(nextStatus || '')
|
onChange={(nextStatus) => {
|
||||||
setPage(1)
|
setStatus(nextStatus || '')
|
||||||
}}
|
setPage(1)
|
||||||
/>
|
}}
|
||||||
<Select
|
/>
|
||||||
allowClear
|
<Select
|
||||||
value={workerType || undefined}
|
allowClear
|
||||||
style={{ width: 140 }}
|
value={workerType || undefined}
|
||||||
placeholder="全部类型"
|
style={{ width: 140 }}
|
||||||
options={[
|
placeholder="全部类型"
|
||||||
{ value: 'external', label: '外部打手' },
|
options={[
|
||||||
{ value: 'internal', label: '内部打手' },
|
{ value: 'external', label: '外部打手' },
|
||||||
]}
|
{ value: 'internal', label: '内部打手' },
|
||||||
onChange={(nextType) => {
|
]}
|
||||||
setWorkerType(nextType || '')
|
onChange={(nextType) => {
|
||||||
setPage(1)
|
setWorkerType(nextType || '')
|
||||||
}}
|
setPage(1)
|
||||||
/>
|
}}
|
||||||
<Select
|
/>
|
||||||
allowClear
|
<Select
|
||||||
value={levelId || undefined}
|
allowClear
|
||||||
style={{ width: 140 }}
|
value={levelId || undefined}
|
||||||
placeholder="全部等级"
|
style={{ width: 140 }}
|
||||||
loading={levelsQuery.isLoading}
|
placeholder="全部等级"
|
||||||
options={(levelsQuery.data?.data.items || []).map((item) => ({
|
loading={levelsQuery.isLoading}
|
||||||
value: item.levelId,
|
options={(levelsQuery.data?.data.items || []).map((item) => ({
|
||||||
label: item.name,
|
value: item.levelId,
|
||||||
}))}
|
label: item.name,
|
||||||
onChange={(nextLevelId) => {
|
}))}
|
||||||
setLevelId(nextLevelId || 0)
|
onChange={(nextLevelId) => {
|
||||||
setPage(1)
|
setLevelId(nextLevelId || 0)
|
||||||
}}
|
setPage(1)
|
||||||
/>
|
}}
|
||||||
<Button type="primary" icon={<SearchOutlined />} onClick={searchWorkers}>
|
/>
|
||||||
查询
|
<Button type="primary" icon={<SearchOutlined />} onClick={searchWorkers}>
|
||||||
</Button>
|
查询
|
||||||
<Button onClick={clearWorkerFilters}>清空筛选</Button>
|
</Button>
|
||||||
<Button
|
<Button onClick={clearWorkerFilters}>清空筛选</Button>
|
||||||
icon={<ReloadOutlined />}
|
<Button
|
||||||
loading={workersQuery.isFetching}
|
icon={<ReloadOutlined />}
|
||||||
onClick={() => workersQuery.refetch()}
|
loading={workersQuery.isFetching}
|
||||||
>
|
onClick={() => workersQuery.refetch()}
|
||||||
刷新
|
>
|
||||||
</Button>
|
刷新
|
||||||
</Space>
|
</Button>
|
||||||
}
|
</Space>
|
||||||
bordered={false}
|
}
|
||||||
>
|
bordered={false}
|
||||||
{targetWorkerId ? (
|
>
|
||||||
<Typography.Text type="secondary">已定位打手 #{targetWorkerId}</Typography.Text>
|
{targetWorkerId ? (
|
||||||
) : null}
|
<Typography.Text type="secondary">已定位打手 #{targetWorkerId}</Typography.Text>
|
||||||
<Table<WorkerUser>
|
) : null}
|
||||||
rowKey="workerId"
|
</Card>
|
||||||
loading={workersQuery.isLoading}
|
<WorkerStatisticsBar statistics={workersQuery.data?.data.statistics} />
|
||||||
dataSource={workersQuery.data?.data.items || []}
|
<Card title="打手列表" bordered={false}>
|
||||||
columns={columns}
|
<Table<WorkerUser>
|
||||||
pagination={buildAdminTablePagination({
|
rowKey="workerId"
|
||||||
current: workersPagination?.page || page,
|
loading={workersQuery.isLoading}
|
||||||
pageSize: workersPagination?.pageSize || pageSize,
|
dataSource={workersQuery.data?.data.items || []}
|
||||||
total: workersPagination?.total || 0,
|
columns={columns}
|
||||||
onChange: (nextPage, nextPageSize) => {
|
pagination={buildAdminTablePagination({
|
||||||
setPage(nextPage)
|
current: workersPagination?.page || page,
|
||||||
setPageSize(nextPageSize)
|
pageSize: workersPagination?.pageSize || pageSize,
|
||||||
},
|
total: workersPagination?.total || 0,
|
||||||
})}
|
onChange: (nextPage, nextPageSize) => {
|
||||||
scroll={{ x: 1000 }}
|
setPage(nextPage)
|
||||||
/>
|
setPageSize(nextPageSize)
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
scroll={{ x: 1000 }}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
title="冻结打手账号"
|
title="冻结打手账号"
|
||||||
@@ -812,6 +875,6 @@ export default function WorkersPanel() {
|
|||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<WorkerDetailDrawer worker={detailWorker} onClose={() => setDetailWorker(null)} />
|
<WorkerDetailDrawer worker={detailWorker} onClose={() => setDetailWorker(null)} />
|
||||||
</Card>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import type {
|
|||||||
WorkerAnnouncementConfig,
|
WorkerAnnouncementConfig,
|
||||||
WorkerLevel,
|
WorkerLevel,
|
||||||
WorkerListResponse,
|
WorkerListResponse,
|
||||||
|
WorkerUserListResponse,
|
||||||
WorkerUser,
|
WorkerUser,
|
||||||
WorkerWalletLedger,
|
WorkerWalletLedger,
|
||||||
} from '@/types/worker-platform'
|
} from '@/types/worker-platform'
|
||||||
@@ -173,7 +174,7 @@ export function saveAdminWorkerLevel(payload: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function fetchAdminWorkerUsers(params?: Record<string, unknown>) {
|
export function fetchAdminWorkerUsers(params?: Record<string, unknown>) {
|
||||||
return apiGet<WorkerListResponse<WorkerUser>>('/api/v1/admin/worker-platform/workers', params)
|
return apiGet<WorkerUserListResponse>('/api/v1/admin/worker-platform/workers', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchAdminWorkerDetail(workerId: number | string) {
|
export function fetchAdminWorkerDetail(workerId: number | string) {
|
||||||
|
|||||||
@@ -50,6 +50,18 @@ export type WorkerUser = {
|
|||||||
renameUnlockedAt: string | null
|
renameUnlockedAt: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type WorkerUserStatistics = {
|
||||||
|
totalCount: number
|
||||||
|
statusCounts: Record<string, number>
|
||||||
|
workerTypeCounts: Record<string, number>
|
||||||
|
availableAmount: number
|
||||||
|
frozenDepositAmount: number
|
||||||
|
pendingUnfreezeAmount: number
|
||||||
|
totalCreditedAmount: number
|
||||||
|
totalSettledAmount: number
|
||||||
|
timeoutOrderCount: number
|
||||||
|
}
|
||||||
|
|
||||||
export type WorkerSessionDevice = {
|
export type WorkerSessionDevice = {
|
||||||
sessionId: string
|
sessionId: string
|
||||||
deviceId: string
|
deviceId: string
|
||||||
@@ -677,6 +689,10 @@ export type WorkerListResponse<T> = {
|
|||||||
pagination: Pagination
|
pagination: Pagination
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type WorkerUserListResponse = WorkerListResponse<WorkerUser> & {
|
||||||
|
statistics?: WorkerUserStatistics
|
||||||
|
}
|
||||||
|
|
||||||
export type WorkerOrderOverviewItem = {
|
export type WorkerOrderOverviewItem = {
|
||||||
status: string
|
status: string
|
||||||
orderCount: number
|
orderCount: number
|
||||||
|
|||||||
Reference in New Issue
Block a user