feat(pickup): 新增管理员线下提号功能
- 新建 admin_pickups 表独立于 rental_orders,不污染订单状态机与财务统计口径 - 提号中/已完成/已取消三态:创建锁定账号,完成时给卖家钱包加可用余额并下架 listing,取消解锁账号 - 完成时 listing 置 completed,复用现有 listingLockedForOwnerMutation 拦截再上架 - 管理端提号管理页(列表/创建/完成/取消/可提号账号搜索)+ 卖家端提号记录页 - 财务仪表盘新增线下提号独立统计块,与正常订单口径分离 - 钱包流水 label、状态标签、菜单入口同步补齐 - 优化卖家服务悬浮菜单为一行四个,尺寸与配色对齐买家服务
This commit is contained in:
@@ -44,9 +44,16 @@ export interface FinanceDailyItem {
|
||||
settled_order_count: number
|
||||
}
|
||||
|
||||
export interface FinancePickupSummary {
|
||||
settled_amount_cent: number
|
||||
completed_count: number
|
||||
in_progress_count: number
|
||||
}
|
||||
|
||||
export interface FinanceDashboard {
|
||||
summary: FinanceSummary
|
||||
daily_items: FinanceDailyItem[]
|
||||
pickup_summary: FinancePickupSummary
|
||||
generated_at: string
|
||||
}
|
||||
|
||||
@@ -109,6 +116,11 @@ export async function fetchFinanceDashboard(query: FinanceDateQuery = {}) {
|
||||
return {
|
||||
...data.data,
|
||||
daily_items: Array.isArray(data.data?.daily_items) ? data.data.daily_items : [],
|
||||
pickup_summary: data.data?.pickup_summary ?? {
|
||||
settled_amount_cent: 0,
|
||||
completed_count: 0,
|
||||
in_progress_count: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import { apiClient } from '@/shared/api/client'
|
||||
import type { ApiResponse, PaginatedResult } from '@/shared/types/types'
|
||||
|
||||
export interface AdminPickup {
|
||||
id: number
|
||||
pickup_no: string
|
||||
listing_id: number
|
||||
listing_no: string
|
||||
account_id: number
|
||||
account_title: string
|
||||
server_region: string
|
||||
login_platform: string
|
||||
owner_id: number
|
||||
owner_phone: string
|
||||
admin_id: number
|
||||
platform: string
|
||||
settle_amount_cent: number
|
||||
status: string
|
||||
remark: string
|
||||
complete_remark: string
|
||||
created_at: string
|
||||
completed_at?: string
|
||||
cancelled_at?: string
|
||||
}
|
||||
|
||||
export interface AvailableListing {
|
||||
id: number
|
||||
listing_no: string
|
||||
account_id: number
|
||||
account_title: string
|
||||
server_region: string
|
||||
login_platform: string
|
||||
owner_id: number
|
||||
owner_phone: string
|
||||
}
|
||||
|
||||
export interface AdminPickupCreateRequest {
|
||||
listing_id: number
|
||||
platform?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export interface AdminPickupCompleteRequest {
|
||||
settle_amount_cent: number
|
||||
complete_remark?: string
|
||||
}
|
||||
|
||||
export interface AdminPickupListQuery {
|
||||
page?: number
|
||||
page_size?: number
|
||||
keyword?: string
|
||||
status?: string
|
||||
}
|
||||
|
||||
function cleanParams(query: object) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(query).filter(([, value]) => value !== '' && value !== undefined)
|
||||
)
|
||||
}
|
||||
|
||||
export async function fetchAdminPickups(query: AdminPickupListQuery = {}) {
|
||||
const { data } = await apiClient.get<ApiResponse<PaginatedResult<AdminPickup>>>(
|
||||
'/admin/pickups',
|
||||
{ params: cleanParams(query) }
|
||||
)
|
||||
const result = data.data
|
||||
return {
|
||||
items: Array.isArray(result?.items) ? result.items : [],
|
||||
total: Number(result?.total ?? 0),
|
||||
page: Number(result?.page ?? query.page ?? 1),
|
||||
page_size: Number(result?.page_size ?? query.page_size ?? 20),
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchAvailableListings(keyword: string, page = 1, page_size = 20) {
|
||||
const { data } = await apiClient.get<ApiResponse<PaginatedResult<AvailableListing>>>(
|
||||
'/admin/pickups/available-listings',
|
||||
{ params: cleanParams({ keyword, page, page_size }) }
|
||||
)
|
||||
const result = data.data
|
||||
return {
|
||||
items: Array.isArray(result?.items) ? result.items : [],
|
||||
total: Number(result?.total ?? 0),
|
||||
}
|
||||
}
|
||||
|
||||
export async function createAdminPickup(req: AdminPickupCreateRequest) {
|
||||
const { data } = await apiClient.post<ApiResponse<AdminPickup>>('/admin/pickups', req)
|
||||
return data.data
|
||||
}
|
||||
|
||||
export async function completeAdminPickup(id: number, req: AdminPickupCompleteRequest) {
|
||||
const { data } = await apiClient.post<ApiResponse<AdminPickup>>(
|
||||
`/admin/pickups/${id}/complete`,
|
||||
req
|
||||
)
|
||||
return data.data
|
||||
}
|
||||
|
||||
export async function cancelAdminPickup(id: number, reason: string) {
|
||||
const { data } = await apiClient.post<ApiResponse<{ cancelled: boolean }>>(
|
||||
`/admin/pickups/${id}/cancel`,
|
||||
{ reason }
|
||||
)
|
||||
return data.data
|
||||
}
|
||||
Reference in New Issue
Block a user