462 lines
13 KiB
TypeScript
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
|
|
}
|