feat(invitation): 支持分销站点自主配置返利比例与权限隔离

- 后端新增分站专属配置及审计接口(/api/distribution/invitation-config 等),基于分站管理员严格鉴权隔离
- 分销独立后台新增「邀请返利设置」页面(Invitations.tsx),支持分站管理员自主开启/调整返利比例并查看本站流水
- 主站后台邀请接口路径补齐 /admin 前缀,解决 404/401 鉴权作用域问题
- 验证方式:backend 单元测试全数通过,distribution-admin 类型与构建检查通过
This commit is contained in:
yml2213
2026-10-10 13:47:53 +08:00
parent 984433f866
commit 7cddbe9463
8 changed files with 799 additions and 7 deletions
+82
View File
@@ -880,3 +880,85 @@ export async function testSiteSMSConfig(siteID: number, payload: TestSiteSMSConf
const resp = await api.post<ApiResponse<{ message: string }>>(`/admin/distribution/sites/${siteID}/sms-config/test`, payload)
return extract(resp)
}
export interface InvitationRebateConfig {
distribution_site_id: number
first_month_rate: number
second_month_rate: number
subsequent_rate: number
enabled: boolean
updated_at?: string
}
export interface InvitedUserItem {
id: number
inviter_id: number
inviter_phone?: string
inviter_nickname?: string
invitee_id: number
invitee_phone?: string
invitee_nickname?: string
invite_code: string
status: string
verified_at?: string
created_at: string
}
export interface RebateLedgerItem {
id: number
order_id: number
order_no: string
inviter_id: number
inviter_phone?: string
inviter_nickname?: string
invitee_id: number
invitee_phone?: string
invitee_nickname?: string
trade_role: 'buyer' | 'seller'
order_profit_basis_cent: number
single_side_basis_cent: number
rate_applied: number
days_since_verified: number
cycle_stage: string
rebate_amount_cent: number
status: string
settled_at: string
}
export async function fetchSiteInvitationConfig(siteID?: number): Promise<InvitationRebateConfig> {
const url = siteID ? `/distribution/sites/${siteID}/invitation-config` : '/distribution/invitation-config'
const resp = await api.get<ApiResponse<InvitationRebateConfig>>(url)
return extract(resp)
}
export async function updateSiteInvitationConfig(
siteID: number,
payload: {
first_month_rate: number
second_month_rate: number
subsequent_rate: number
enabled: boolean
}
): Promise<InvitationRebateConfig> {
const url = siteID ? `/distribution/sites/${siteID}/invitation-config` : '/distribution/invitation-config'
const resp = await api.put<ApiResponse<InvitationRebateConfig>>(url, payload)
return extract(resp)
}
export async function fetchSiteInvitations(
siteID: number,
params: { page: number; page_size: number; phone?: string; invite_code?: string }
): Promise<{ items: InvitedUserItem[]; total: number; page: number; page_size: number }> {
const url = siteID ? `/distribution/sites/${siteID}/invitations` : '/distribution/invitations'
const resp = await api.get<ApiResponse<{ items: InvitedUserItem[]; total: number; page: number; page_size: number }>>(url, { params })
return extract(resp)
}
export async function fetchSiteRebates(
siteID: number,
params: { page: number; page_size: number; order_no?: string; inviter_phone?: string; invitee_phone?: string }
): Promise<{ items: RebateLedgerItem[]; total: number; page: number; page_size: number }> {
const url = siteID ? `/distribution/sites/${siteID}/invitation-rebates` : '/distribution/invitation-rebates'
const resp = await api.get<ApiResponse<{ items: RebateLedgerItem[]; total: number; page: number; page_size: number }>>(url, { params })
return extract(resp)
}