Files
hfb_sys/frontend/src/features/admin/api/adminFinance.ts
T
2026-08-20 16:06:05 +08:00

462 lines
13 KiB
TypeScript

import { apiClient } from '@/shared/api/client'
import type { ApiResponse, PaginatedResult } from '@/shared/types/types'
// 结算差异判定阈值(分)。号主应得与钱包入账/线下结算差额绝对值达到此值即标红。
// 与后端 settlementDiffThresholdCent 保持一致,改口径时两端同步。
export const SETTLEMENT_DIFF_THRESHOLD_CENT = 5
export interface FinanceSummary {
total_flow_amount_cent: number
total_refund_amount_cent: number
pending_refund_amount_cent: number
channel_net_amount_cent: number
platform_income_amount_cent: number
normal_order_profit_amount_cent: number
pickup_profit_amount_cent: number
/** 普通(租赁)订单利润对应的已结算订单数量 */
settled_order_count: number
/** 线下提号利润对应的已完成提号笔数 */
pickup_completed_count: number
owner_should_income_amount_cent: number
owner_wallet_income_amount_cent: number
owner_effective_settlement_amount_cent: number
offline_settlement_amount_cent: number
offline_settlement_pending_amount_cent: number
settlement_diff_amount_cent: number
estimated_income_amount_cent: number
successful_pay_count: number
successful_refund_count: number
full_refund_count: number
partial_refund_count: number
pending_refund_count: number
pending_settle_order_count: number
offline_settlement_pending_count: number
financial_exception_count: number
}
export interface FinanceDailyItem {
date: string
total_flow_amount_cent: number
total_refund_amount_cent: number
pending_refund_amount_cent: number
channel_net_amount_cent: number
platform_income_amount_cent: number
owner_should_income_amount_cent: number
owner_wallet_income_amount_cent: number
owner_effective_settlement_amount_cent: number
offline_settlement_amount_cent: number
offline_settlement_pending_amount_cent: number
settlement_diff_amount_cent: number
estimated_income_amount_cent: number
successful_pay_count: number
successful_refund_count: number
full_refund_count: number
partial_refund_count: number
pending_refund_count: number
settled_order_count: number
pickup_completed_count: number
/** 正常(租赁)订单已退全款笔数(不含撞车商城退款) */
normal_full_refund_count: number
/** 线下提号取消笔数 */
pickup_cancelled_count: number
sales_count: number
paid_order_count: number
offline_settlement_pending_count: number
offline_settlement_paid_amount_cent: number
offline_settlement_paid_count: number
withdrawal_paid_amount_cent: number
withdrawal_paid_count: number
manual_paid_amount_cent: number
manual_paid_count: number
operating_expense_amount_cent: number
operating_expense_count: number
disbursement_paid_amount_cent: number
disbursement_paid_count: number
}
export interface FinancePickupSummary {
settled_amount_cent: number
profit_amount_cent: number
completed_count: number
in_progress_count: number
}
export interface FinanceMohongSummary {
flow_amount_cent: number
refund_amount_cent: number
net_amount_cent: number
paid_order_count: number
}
export interface FinanceDisbursementSummary {
paid_amount_cent: number
paid_count: number
pending_payment_amount_cent: number
pending_payment_count: number
offline_settlement_paid_amount_cent: number
offline_settlement_paid_count: number
offline_settlement_pending_amount_cent: number
offline_settlement_pending_count: number
withdrawal_amount_cent: number
withdrawal_fee_amount_cent: number
withdrawal_paid_amount_cent: number
withdrawal_paid_count: number
withdrawal_pending_amount_cent: number
withdrawal_pending_count: number
withdrawal_review_amount_cent: number
withdrawal_review_count: number
manual_paid_amount_cent: number
manual_paid_count: number
}
export interface OperatingExpenseSummary {
amount_cent: number
count: number
}
export interface FinanceDisbursementListSummary {
record_count: number
paid_amount_cent: number
paid_count: number
payment_pending_amount_cent: number
payment_pending_count: number
review_pending_amount_cent: number
review_pending_count: number
withdrawal_fee_amount_cent: number
}
export interface FinanceDisbursementItem {
source_type: 'platform_managed' | 'withdrawal' | 'manual_offline'
source_id: number
business_no: string
order_id?: number
withdrawal_id?: number
user_id: number
payee_name: string
payee_phone: string
uploader_name: string
source_channel: string
account_type: string
account_name: string
account_no: string
bank_name: string
amount_cent: number
fee_cent: number
actual_amount_cent: number
status: 'review_pending' | 'payment_pending' | 'paid' | 'rejected' | 'cancelled' | 'voided'
raw_status: string
business_created_at?: string
paid_at?: string
operator_id?: number
operator_name: string
remark: string
category: string
custom_category_name: string
voucher_url: string
voided_at?: string
voided_by?: number
voided_by_name: string
void_reason: string
}
export interface CreateManualDisbursementPayload {
category: string
custom_category_name?: string
payee_name: string
amount_cent: number
paid_at: string
remark: string
voucher_url?: string
}
export interface ManualDisbursement {
id: number
disbursement_no: string
category: string
custom_category_name: string
payee_name: string
amount_cent: number
paid_at: string
remark: string
voucher_url: string
status: 'paid' | 'voided'
created_by: number
created_by_name: string
voided_by?: number
voided_by_name: string
voided_at?: string
void_reason: string
created_at: string
}
export interface FinanceDisbursementQuery extends FinanceDateQuery {
source_type?: string
status?: string
date_type?: string
business_no?: string
keyword?: string
user_id?: string
page?: number
page_size?: number
}
export interface FinanceDisbursementList {
items: FinanceDisbursementItem[]
total: number
page: number
page_size: number
summary: FinanceDisbursementListSummary
}
export interface FinanceDashboard {
summary: FinanceSummary
daily_items: FinanceDailyItem[]
pickup_summary: FinancePickupSummary
mohong_summary: FinanceMohongSummary
disbursement_summary: FinanceDisbursementSummary
operating_expense_summary: OperatingExpenseSummary
generated_at: string
}
export interface OperatingExpense {
id: number
expense_no: string
category: string
payee_name: string
amount_cent: number
occurred_at: string
remark: string
voucher_url: string
status: 'paid' | 'voided'
created_by: number
created_by_name: string
voided_by?: number
voided_by_name: string
voided_at?: string
void_reason: string
created_at: string
}
export interface OperatingExpenseQuery extends FinanceDateQuery {
status?: string
category?: string
keyword?: string
date_type?: 'created' | 'occurred'
page?: number
page_size?: number
}
export interface OperatingExpenseListSummary {
record_count: number
paid_amount_cent: number
paid_count: number
}
export interface OperatingExpenseList {
items: OperatingExpense[]
total: number
page: number
page_size: number
summary: OperatingExpenseListSummary
}
export interface CreateOperatingExpensePayload {
category: string
payee_name: string
amount_cent: number
occurred_at: string
remark: string
voucher_url?: string
}
export interface FinanceDetail {
order_id: number
order_no: string
order_status: string
settlement_status: string
refund_status: string
handoff_mode: string
settlement_mode: string
offline_settlement_status: string
offline_settlement_amount_cent: number
renter_id: number
renter_phone: string
renter_nickname: string
owner_id: number
owner_phone: string
owner_nickname: string
order_rent_amount_cent: number
order_deposit_amount_cent: number
checkout_rent_amount_cent: number
checkout_renter_refund_cent: number
checkout_owner_income_cent: number
checkout_platform_fee_cent: number
owner_wallet_income_amount_cent: number
owner_effective_settlement_amount_cent: number
paid_amount_cent: number
refunded_amount_cent: number
refunding_amount_cent: number
failed_refund_amount_cent: number
channel_net_amount_cent: number
platform_net_amount_cent: number
settlement_diff_amount_cent: number
finance_status: string
created_at: string
settled_at?: string
offline_settled_at?: string
}
export interface FinanceDateQuery {
start_date?: string
end_date?: string
}
export interface FinanceDetailQuery extends FinanceDateQuery {
order_no?: string
user_id?: string
order_status?: string
settlement_status?: string
settlement_mode?: string
offline_settlement_status?: string
date_type?: string
page?: number
page_size?: number
}
function cleanParams(query: object) {
return Object.fromEntries(
Object.entries(query).filter(([, value]) => value !== '' && value !== undefined)
)
}
export async function fetchFinanceDashboard(query: FinanceDateQuery = {}) {
const { data } = await apiClient.get<ApiResponse<FinanceDashboard>>('/admin/finance/dashboard', {
params: cleanParams(query),
})
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,
profit_amount_cent: 0,
completed_count: 0,
in_progress_count: 0,
},
mohong_summary: data.data?.mohong_summary ?? {
flow_amount_cent: 0,
refund_amount_cent: 0,
net_amount_cent: 0,
paid_order_count: 0,
},
disbursement_summary: data.data?.disbursement_summary ?? {
paid_amount_cent: 0,
paid_count: 0,
pending_payment_amount_cent: 0,
pending_payment_count: 0,
offline_settlement_paid_amount_cent: 0,
offline_settlement_paid_count: 0,
offline_settlement_pending_amount_cent: 0,
offline_settlement_pending_count: 0,
withdrawal_amount_cent: 0,
withdrawal_fee_amount_cent: 0,
withdrawal_paid_amount_cent: 0,
withdrawal_paid_count: 0,
withdrawal_pending_amount_cent: 0,
withdrawal_pending_count: 0,
withdrawal_review_amount_cent: 0,
withdrawal_review_count: 0,
manual_paid_amount_cent: 0,
manual_paid_count: 0,
},
operating_expense_summary: data.data?.operating_expense_summary ?? {
amount_cent: 0,
count: 0,
},
}
}
export async function fetchFinanceDetails(query: FinanceDetailQuery = {}) {
const { data } = await apiClient.get<ApiResponse<PaginatedResult<FinanceDetail>>>(
'/admin/finance/details',
{ 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 fetchFinanceDisbursements(query: FinanceDisbursementQuery = {}) {
const { data } = await apiClient.get<ApiResponse<FinanceDisbursementList>>(
'/admin/finance/disbursements',
{ 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),
summary: result?.summary ?? {
record_count: 0,
paid_amount_cent: 0,
paid_count: 0,
payment_pending_amount_cent: 0,
payment_pending_count: 0,
review_pending_amount_cent: 0,
review_pending_count: 0,
withdrawal_fee_amount_cent: 0,
},
}
}
export async function fetchOperatingExpenses(query: OperatingExpenseQuery = {}) {
const { data } = await apiClient.get<ApiResponse<OperatingExpenseList>>(
'/admin/finance/operating-expenses',
{ 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),
summary: result?.summary ?? { record_count: 0, paid_amount_cent: 0, paid_count: 0 },
}
}
export async function createOperatingExpense(payload: CreateOperatingExpensePayload) {
const { data } = await apiClient.post<ApiResponse<OperatingExpense>>(
'/admin/finance/operating-expenses',
payload
)
return data.data
}
export async function voidOperatingExpense(id: number, reason: string) {
const { data } = await apiClient.post<ApiResponse<OperatingExpense>>(
`/admin/finance/operating-expenses/${id}/void`,
{ reason }
)
return data.data
}
export async function createManualDisbursement(payload: CreateManualDisbursementPayload) {
const { data } = await apiClient.post<ApiResponse<ManualDisbursement>>(
'/admin/finance/manual-disbursements',
payload
)
return data.data
}
export async function voidManualDisbursement(id: number, reason: string) {
const { data } = await apiClient.post<ApiResponse<ManualDisbursement>>(
`/admin/finance/manual-disbursements/${id}/void`,
{ reason }
)
return data.data
}