分销后台前端对接与结算明细接口

- 新增 GET /admin/distribution/settlement-batches/:id/items 明细查询
- distribution-admin 重构为 react-router 多页面:仪表盘/网站/规则(启停)/订单(筛选分页)/结算(创建/明细/结算)/支付通道(绑定解绑)/账号展示(上下架)
- axios 统一 401 提示,引导回主站后台登录;金额统一整数分格式化
- npm run build(tsc 严格模式) 通过
This commit is contained in:
yml2213
2026-09-05 11:17:43 +08:00
parent ac777f5b16
commit 3162d15425
15 changed files with 1006 additions and 38 deletions
+242
View File
@@ -0,0 +1,242 @@
import axios from 'axios'
import { Modal } from 'antd'
const api = axios.create({ baseURL: '/api', withCredentials: true })
interface ApiResponse<T> { data: T; code?: number; message?: string }
let authNoticeShown = false
api.interceptors.response.use(
(resp) => resp,
(error) => {
// 后台鉴权依赖主站 HttpOnly cookie;401 说明未登录或会话过期。
if (error?.response?.status === 401 && !authNoticeShown) {
authNoticeShown = true
Modal.warning({
title: '登录状态失效',
content: '请先在主站后台登录管理员账号,再返回本页面。',
onOk: () => { authNoticeShown = false },
})
}
return Promise.reject(error)
},
)
export function extract<T>(resp: { data: ApiResponse<T> }): T {
return resp.data.data
}
export function formatMoney(cent: number | undefined | null): string {
return `¥${((cent ?? 0) / 100).toFixed(2)}`
}
export interface Site {
id: number
site_code: string
site_name: string
domain: string
status: string
display_account_enabled: boolean
tax_enabled: boolean
tax_rate_bps: number
created_at: string
}
export interface Rule {
id: number
site_id?: number
mode: string
version: number
status: string
platform_receive_ratio: number
platform_sell_ratio: number
distributor_receive_ratio: number
distributor_sell_ratio: number
distributor_a_ratio: number
distributor_b_ratio: number
service_fee_value: number
service_fee_unit: string
payment_fee_bps: number
tax_enabled: boolean
tax_rate_bps: number
effective_from: string
effective_to?: string
}
export interface DistributionOrder {
order_id: number
order_no: string
site_id: number
status: string
refund_status: string
settlement_status: string
distribution_mode: string
net_paid_amount_cent: number
payment_fee_cent: number
service_fee_cent: number
markup_amount_cent: number
tax_amount_cent: number
split_base_amount_cent: number
rounding_adjustment_cent: number
created_at: string
}
export interface OrderPage {
items: DistributionOrder[]
total: number
page: number
page_size: number
}
export interface AccountDisplay {
id: number
site_id: number
account_id: number
listing_id: number
display_status: string
}
export interface PaymentChannel {
id: number
site_id: number
config_id: number
priority: number
status: string
}
export interface SettlementBatch {
id: number
site_id: number
batch_no: string
period_start: string
period_end: string
total_amount_cent: number
status: string
}
export interface SettlementItem {
id: number
order_id: number
party_role: string
amount_cent: number
status: string
}
export async function listSites(): Promise<Site[]> {
const resp = await api.get<ApiResponse<{ items: Site[] }>>('/admin/distribution/sites')
return extract(resp).items ?? []
}
export async function listRules(siteID?: number, mode?: string): Promise<Rule[]> {
const resp = await api.get<ApiResponse<{ items: Rule[] }>>('/admin/distribution/rules', {
params: { site_id: siteID || undefined, mode: mode || undefined },
})
return extract(resp).items ?? []
}
export interface CreateRulePayload {
site_id?: number
mode: string
distributor_receive_ratio?: number
platform_sell_ratio?: number
distributor_sell_ratio?: number
platform_receive_ratio?: number
distributor_a_ratio?: number
distributor_b_ratio?: number
service_fee_value: number
service_fee_unit: string
payment_fee_bps: number
tax_enabled: boolean
tax_rate_bps: number
effective_from?: string
}
export async function createRule(payload: CreateRulePayload): Promise<Rule> {
const resp = await api.post<ApiResponse<Rule>>('/admin/distribution/rules', payload)
return extract(resp)
}
export async function updateRuleStatus(id: number, status: string): Promise<Rule> {
const resp = await api.put<ApiResponse<Rule>>(`/admin/distribution/rules/${id}/status`, { status })
return extract(resp)
}
export async function listOrders(params: {
kind?: string
status?: string
siteID?: number
page?: number
pageSize?: number
}): Promise<OrderPage> {
const resp = await api.get<ApiResponse<OrderPage>>('/admin/distribution/orders', {
params: {
kind: params.kind || undefined,
status: params.status || undefined,
site_id: params.siteID || undefined,
page: params.page || 1,
page_size: params.pageSize || 20,
},
})
return extract(resp)
}
export async function listAccountDisplays(siteID: number, status?: string): Promise<AccountDisplay[]> {
const resp = await api.get<ApiResponse<{ items: AccountDisplay[] }>>('/admin/distribution/accounts', {
params: { site_id: siteID, status: status || undefined },
})
return extract(resp).items ?? []
}
export async function updateAccountDisplay(id: number, displayStatus: string): Promise<AccountDisplay> {
const resp = await api.put<ApiResponse<AccountDisplay>>(`/admin/distribution/accounts/${id}/display`, {
display_status: displayStatus,
})
return extract(resp)
}
export async function listPaymentChannels(siteID: number): Promise<PaymentChannel[]> {
const resp = await api.get<ApiResponse<{ items: PaymentChannel[] }>>('/admin/distribution/payment-channels', {
params: { site_id: siteID },
})
return extract(resp).items ?? []
}
export async function bindPaymentChannel(siteID: number, configID: number, priority: number): Promise<PaymentChannel> {
const resp = await api.post<ApiResponse<PaymentChannel>>(
'/admin/distribution/payment-channels',
{ config_id: configID, priority },
{ params: { site_id: siteID } },
)
return extract(resp)
}
export async function unbindPaymentChannel(id: number): Promise<void> {
await api.delete(`/admin/distribution/payment-channels/${id}`)
}
export async function listSettlementBatches(siteID: number): Promise<SettlementBatch[]> {
const resp = await api.get<ApiResponse<{ items: SettlementBatch[] }>>('/admin/distribution/settlement-batches', {
params: { site_id: siteID },
})
return extract(resp).items ?? []
}
export async function createSettlementBatch(siteID: number, periodStart: string, periodEnd: string): Promise<SettlementBatch> {
const resp = await api.post<ApiResponse<SettlementBatch>>('/admin/distribution/settlement-batches', {
site_id: siteID,
period_start: periodStart,
period_end: periodEnd,
})
return extract(resp)
}
export async function settleBatch(id: number): Promise<SettlementBatch> {
const resp = await api.post<ApiResponse<SettlementBatch>>(`/admin/distribution/settlement-batches/${id}/settle`)
return extract(resp)
}
export async function listSettlementItems(batchID: number): Promise<SettlementItem[]> {
const resp = await api.get<ApiResponse<{ items: SettlementItem[] }>>(`/admin/distribution/settlement-batches/${batchID}/items`)
return extract(resp).items ?? []
}