feat(distribution): 独立分销后台账号体系、多租户隔离与主站后台分销管理GUI

- 独立账号鉴权:分销后台支持独立登录页面、Bearer+Cookie 双模鉴权与租户资料接口
- 多租户隔离:分销商成员强制锁定自身站点数据范围,防止横向越权;平台超管享有全局视界
- 一键开通分销商:平台端支持一键录入站点、管理员账号、分配角色并原子化初始化佣金规则
- 主后台分销分组:在主站后台增加「分销管理」导航分组,提供站点管理、规则配置、订单监控与结算管理 GUI
- 门禁与联调:补充完整单测,全量代码门禁 check.sh 100% 通过,dev.sh 支持前后端与分销后台一键同启
This commit is contained in:
yml2213
2026-09-07 15:45:53 +08:00
parent 0c37c7e659
commit 550b42eb22
28 changed files with 3192 additions and 117 deletions
@@ -0,0 +1,246 @@
import { apiClient } from '@/shared/api/client'
import type { ApiResponse } from '@/shared/types/types'
export interface DistributionSite {
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 DistributionRule {
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 DistributionOrderPage {
items: DistributionOrder[]
total: number
page: number
page_size: number
}
export interface DistributionSettlementBatch {
id: number
site_id: number
batch_no: string
period_start: string
period_end: string
total_amount_cent: number
status: string
}
export interface DistributionSettlementItem {
id: number
order_id: number
party_role: string
amount_cent: number
status: string
}
export interface OnboardDistributionSitePayload {
site_code: string
site_name: string
domain: string
admin_username: string
admin_password: string
admin_nickname?: string
member_type?: string
display_account_enabled?: boolean
tax_enabled?: boolean
tax_rate_bps?: number
distributor_receive_ratio?: number
platform_sell_ratio?: number
service_fee_value?: number
}
export interface OnboardDistributionSiteResult {
site: DistributionSite
rule: DistributionRule
member: {
admin_id: number
username: string
nickname: string
is_superadmin: boolean
site_id?: number
site_code?: string
site_name?: string
domain?: string
member_type?: string
}
}
export async function fetchDistributionSites() {
const { data } = await apiClient.get<ApiResponse<{ items: DistributionSite[] }>>(
'/admin/distribution/sites'
)
return data.data.items ?? []
}
export async function onboardDistributionSite(payload: OnboardDistributionSitePayload) {
const { data } = await apiClient.post<ApiResponse<OnboardDistributionSiteResult>>(
'/admin/distribution/sites/onboard',
payload
)
return data.data
}
export async function updateDistributionSite(
id: number,
payload: Partial<
Pick<
DistributionSite,
'site_name' | 'domain' | 'status' | 'display_account_enabled' | 'tax_enabled' | 'tax_rate_bps'
>
>
) {
const { data } = await apiClient.put<ApiResponse<DistributionSite>>(
`/admin/distribution/sites/${id}`,
payload
)
return data.data
}
export async function fetchDistributionRules(siteId?: number, mode?: string) {
const { data } = await apiClient.get<ApiResponse<{ items: DistributionRule[] }>>(
'/admin/distribution/rules',
{
params: { site_id: siteId || undefined, mode: mode || undefined },
}
)
return data.data.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 createDistributionRule(payload: CreateRulePayload) {
const { data } = await apiClient.post<ApiResponse<DistributionRule>>(
'/admin/distribution/rules',
payload
)
return data.data
}
export async function updateDistributionRuleStatus(id: number, status: string) {
const { data } = await apiClient.put<ApiResponse<DistributionRule>>(
`/admin/distribution/rules/${id}/status`,
{ status }
)
return data.data
}
export async function fetchDistributionOrders(params: {
kind?: string
status?: string
siteID?: number
page?: number
pageSize?: number
}) {
const { data } = await apiClient.get<ApiResponse<DistributionOrderPage>>(
'/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 data.data
}
export async function fetchDistributionSettlementBatches(siteId: number) {
const { data } = await apiClient.get<ApiResponse<{ items: DistributionSettlementBatch[] }>>(
'/admin/distribution/settlement-batches',
{
params: { site_id: siteId },
}
)
return data.data.items ?? []
}
export async function createDistributionSettlementBatch(
siteId: number,
periodStart: string,
periodEnd: string
) {
const { data } = await apiClient.post<ApiResponse<DistributionSettlementBatch>>(
'/admin/distribution/settlement-batches',
{
site_id: siteId,
period_start: periodStart,
period_end: periodEnd,
}
)
return data.data
}
export async function settleDistributionBatch(id: number) {
const { data } = await apiClient.post<ApiResponse<DistributionSettlementBatch>>(
`/admin/distribution/settlement-batches/${id}/settle`
)
return data.data
}
export async function fetchDistributionSettlementItems(batchId: number) {
const { data } = await apiClient.get<ApiResponse<{ items: DistributionSettlementItem[] }>>(
`/admin/distribution/settlement-batches/${batchId}/items`
)
return data.data.items ?? []
}