feat(distribution): 补齐退款加价回退、本站自卖与开通默认规则
退款时压缩卖方加价,人工/自动批次仅完结单可入账,号主收入进主站钱包;仲裁后重算释放并支持作废未入账 DS。开通时四种模式按默认生效,对外口径合并为 35/40/25,不再单独展示通道费。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -115,6 +115,7 @@ export interface Site {
|
||||
display_account_enabled: boolean
|
||||
tax_enabled: boolean
|
||||
tax_rate_bps: number
|
||||
settlement_hold_days?: number
|
||||
logo_url?: string
|
||||
favicon_url?: string
|
||||
banners_json?: string
|
||||
@@ -302,9 +303,7 @@ export interface OnboardSitePayload {
|
||||
display_account_enabled?: boolean
|
||||
tax_enabled?: boolean
|
||||
tax_rate_bps?: number
|
||||
distributor_receive_ratio?: number
|
||||
platform_sell_ratio?: number
|
||||
service_fee_value?: number
|
||||
settlement_hold_days?: number
|
||||
}
|
||||
|
||||
export interface OnboardSiteResult {
|
||||
@@ -440,6 +439,11 @@ export async function settleBatch(id: number): Promise<SettlementBatch> {
|
||||
return extract(resp)
|
||||
}
|
||||
|
||||
export async function cancelSettlementBatch(id: number): Promise<SettlementBatch> {
|
||||
const resp = await api.post<ApiResponse<SettlementBatch>>(`/admin/distribution/settlement-batches/${id}/cancel`)
|
||||
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 ?? []
|
||||
@@ -509,6 +513,7 @@ export interface SiteWallet {
|
||||
pending_release_cent: number
|
||||
total_settled_cent: number
|
||||
total_withdrawn_cent: number
|
||||
settlement_hold_days?: number
|
||||
}
|
||||
|
||||
export async function fetchSiteWallet(siteID: number): Promise<SiteWallet> {
|
||||
|
||||
@@ -1030,13 +1030,7 @@ export default function Orders({ kind }: { kind: 'all' | 'sold' | 'receive' }) {
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
|
||||
<span style={{ color: '#595959' }}>3. 减:支付通道手续费(千分之五):</span>
|
||||
<span style={{ color: '#cf1322' }}>
|
||||
- ¥{((selectedOrder.payment_fee_cent || 0) / 100).toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
|
||||
<span style={{ color: '#595959' }}>4. 可分润主站提价利润池:</span>
|
||||
<span style={{ color: '#595959' }}>3. 可分润主站提价利润池:</span>
|
||||
<strong>¥{((selectedOrder.split_base_amount_cent || 0) / 100).toFixed(2)}</strong>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ export default function Rules() {
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 13 }}>
|
||||
租客通过本分销站点租赁主站共享号源池账号,获得扣除号主底价及通道费后的<strong>主站提价利润池 40% 分成</strong>。
|
||||
租客通过本分销站点租赁主站共享号源池账号,获得扣除号主底价后的<strong>主站提价利润池 40% 分成</strong>。
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
</div>
|
||||
@@ -200,7 +200,7 @@ export default function Rules() {
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 13 }}>
|
||||
号主本金 100% 刚兑保障,支付通道手续费固定为 0.5%,订单履约验收后系统自动打款至商户钱包。
|
||||
号主本金 100% 刚兑保障。利润按收号 / 卖出 / 服务费切分,订单完成并过冷却期后系统自动释放入账至商户钱包。
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
</div>
|
||||
@@ -270,12 +270,6 @@ export default function Rules() {
|
||||
width: 110,
|
||||
render: (_: unknown, r: Rule) => <span>{r.service_fee_value}%</span>,
|
||||
},
|
||||
{
|
||||
title: '通道手续费',
|
||||
width: 110,
|
||||
dataIndex: 'payment_fee_bps',
|
||||
render: (v: number) => <span>{(v / 100).toFixed(2)}%</span>,
|
||||
},
|
||||
{
|
||||
title: '生效时间',
|
||||
dataIndex: 'effective_from',
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
listSettlementItems,
|
||||
listSites,
|
||||
settleBatch,
|
||||
cancelSettlementBatch,
|
||||
getMe,
|
||||
type SettlementBatch,
|
||||
type SettlementItem,
|
||||
@@ -41,9 +42,11 @@ import { useMember } from '../MemberContext'
|
||||
import type { Dayjs } from 'dayjs'
|
||||
|
||||
const STATUS_LABELS: Record<string, { label: string; color: string }> = {
|
||||
settled: { label: '已打款结算', color: 'success' },
|
||||
draft: { label: '待审核出账', color: 'warning' },
|
||||
settled: { label: '已释放入账', color: 'success' },
|
||||
draft: { label: '待释放入账', color: 'warning' },
|
||||
pending: { label: '待释放入账', color: 'warning' },
|
||||
processing: { label: '结算处理中', color: 'processing' },
|
||||
cancelled: { label: '已作废', color: 'default' },
|
||||
}
|
||||
|
||||
const PARTY_ROLE_LABELS: Record<string, string> = {
|
||||
@@ -132,16 +135,34 @@ export default function Settlements() {
|
||||
|
||||
async function handleSettle(batch: SettlementBatch) {
|
||||
Modal.confirm({
|
||||
title: '确认打款结算?',
|
||||
content: `即将对批次【${batch.batch_no}】执行结算操作,打款金额 ${formatMoney(batch.total_amount_cent)} 将计入商户可用余额。`,
|
||||
title: '立即释放入账?',
|
||||
content: `即将把批次【${batch.batch_no}】的 ${formatMoney(batch.total_amount_cent)} 转入商户可提现余额,人工批次会绕过冷却期。`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
await settleBatch(batch.id)
|
||||
message.success(`批次 ${batch.batch_no} 已成功结算入账!`)
|
||||
message.success(`批次 ${batch.batch_no} 已释放入账`)
|
||||
if (siteID) void load(siteID)
|
||||
} catch (error: unknown) {
|
||||
const respMessage = (error as { response?: { data?: { message?: string } } })?.response?.data?.message
|
||||
message.error(respMessage ?? '结算失败')
|
||||
message.error(respMessage ?? '释放失败')
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function handleCancel(batch: SettlementBatch) {
|
||||
Modal.confirm({
|
||||
title: '作废未入账批次?',
|
||||
content: `作废【${batch.batch_no}】后,其中订单可重新进入自动释放或新的人工批次。`,
|
||||
okButtonProps: { danger: true },
|
||||
onOk: async () => {
|
||||
try {
|
||||
await cancelSettlementBatch(batch.id)
|
||||
message.success(`批次 ${batch.batch_no} 已作废`)
|
||||
if (siteID) void load(siteID)
|
||||
} catch (error: unknown) {
|
||||
const respMessage = (error as { response?: { data?: { message?: string } } })?.response?.data?.message
|
||||
message.error(respMessage ?? '作废失败')
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -170,7 +191,7 @@ export default function Settlements() {
|
||||
</Typography.Title>
|
||||
<Typography.Text type="secondary">
|
||||
{isSuperadmin
|
||||
? '系统按站点自动结算入账并生成批次;也可手工创建批次立即结算'
|
||||
? '冷却期满后系统按站点按天自动释放入账;也可手工创建批次立即入账,或作废未入账草稿以免挡住自动释放'
|
||||
: '订单完成冷却期结束后系统自动释放入账,此处查看对账批次与入账明细'}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
@@ -288,7 +309,7 @@ export default function Settlements() {
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 160,
|
||||
width: 240,
|
||||
render: (_: unknown, row: SettlementBatch) => (
|
||||
<Space>
|
||||
<Button
|
||||
@@ -299,14 +320,24 @@ export default function Settlements() {
|
||||
>
|
||||
查看明细
|
||||
</Button>
|
||||
{isSuperadmin && row.status !== 'settled' && (
|
||||
{isSuperadmin && row.status !== 'settled' && row.status !== 'cancelled' && (
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
style={{ color: '#52c41a' }}
|
||||
onClick={() => void handleSettle(row)}
|
||||
>
|
||||
确认打款结算
|
||||
立即释放入账
|
||||
</Button>
|
||||
)}
|
||||
{isSuperadmin && row.status !== 'settled' && row.status !== 'cancelled' && (
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
onClick={() => void handleCancel(row)}
|
||||
>
|
||||
作废
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
|
||||
@@ -43,12 +43,10 @@ export default function Sites({ isSuperadmin = true }: { isSuperadmin?: boolean
|
||||
form.resetFields()
|
||||
form.setFieldsValue({
|
||||
member_type: 'operator',
|
||||
distributor_receive_ratio: 35,
|
||||
platform_sell_ratio: 40,
|
||||
service_fee_value: 25,
|
||||
display_account_enabled: true,
|
||||
tax_enabled: false,
|
||||
tax_rate_bps: 100,
|
||||
settlement_hold_days: 7,
|
||||
})
|
||||
setModalOpen(true)
|
||||
}
|
||||
@@ -67,9 +65,7 @@ export default function Sites({ isSuperadmin = true }: { isSuperadmin?: boolean
|
||||
display_account_enabled: values.display_account_enabled,
|
||||
tax_enabled: values.tax_enabled,
|
||||
tax_rate_bps: values.tax_enabled ? values.tax_rate_bps : 0,
|
||||
distributor_receive_ratio: Number(values.distributor_receive_ratio),
|
||||
platform_sell_ratio: Number(values.platform_sell_ratio),
|
||||
service_fee_value: Number(values.service_fee_value),
|
||||
settlement_hold_days: Number(values.settlement_hold_days ?? 7),
|
||||
}
|
||||
const res = await onboardSite(payload)
|
||||
message.success('分销站点及分销商管理员开通成功!')
|
||||
@@ -254,31 +250,18 @@ export default function Sites({ isSuperadmin = true }: { isSuperadmin?: boolean
|
||||
|
||||
<Divider style={{ margin: '16px 0' }} />
|
||||
|
||||
<Typography.Title level={5}>3. 分销比例与佣金规则初始化</Typography.Title>
|
||||
<Typography.Title level={5}>3. 结算冷却期与展示</Typography.Title>
|
||||
<Typography.Paragraph type="secondary" style={{ marginTop: -8 }}>
|
||||
开通后四种分成模式均按默认生效:收号 35% / 卖出 40% / 服务费 25%(本站自卖 75% + 25%)。之后可在规则页调整。
|
||||
</Typography.Paragraph>
|
||||
<Space direction="horizontal" style={{ width: '100%' }} size="middle">
|
||||
<Form.Item
|
||||
label="分销商收号分成 (%)"
|
||||
name="distributor_receive_ratio"
|
||||
tooltip="卖出后利润的 35%"
|
||||
label="结算冷却期 (天)"
|
||||
name="settlement_hold_days"
|
||||
tooltip="订单完成后进入冷却期,期满自动释放到可提现余额;0 表示完成即释放"
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
<InputNumber min={0} max={100} step={1} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="平台卖号分成 (%)"
|
||||
name="platform_sell_ratio"
|
||||
tooltip="卖出后利润的 40%"
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
<InputNumber min={0} max={100} step={1} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="平台服务费 (%)"
|
||||
name="service_fee_value"
|
||||
tooltip="卖出后利润的 25%"
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
<InputNumber min={0} max={100} step={1} style={{ width: '100%' }} />
|
||||
<InputNumber min={0} max={365} step={1} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Space>
|
||||
|
||||
@@ -330,10 +313,8 @@ export default function Sites({ isSuperadmin = true }: { isSuperadmin?: boolean
|
||||
<Descriptions.Item label="所属角色">
|
||||
{successResult.member.member_type}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="初始规则版本">
|
||||
收号分成: {successResult.rule.distributor_receive_ratio}% · 卖号分成:{' '}
|
||||
{successResult.rule.platform_sell_ratio}% · 服务费:{' '}
|
||||
{successResult.rule.service_fee_value}%
|
||||
<Descriptions.Item label="初始分成规则">
|
||||
四种模式均已按默认生效:收号 35% / 卖出 40% / 服务费 25%(本站自卖 75% + 25%),可到规则页调整
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<div style={{ marginTop: 16, textAlign: 'right' }}>
|
||||
|
||||
@@ -167,6 +167,9 @@ export default function Wallet() {
|
||||
</Typography.Title>
|
||||
<Typography.Text type="secondary">
|
||||
查看分销结算收益到账情况,申请提取收益并实时追踪财务打款流水。
|
||||
{wallet?.settlement_hold_days != null
|
||||
? ` 当前站点冷却期 ${wallet.settlement_hold_days} 天。`
|
||||
: ''}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<Space>
|
||||
@@ -208,7 +211,7 @@ export default function Wallet() {
|
||||
<Col flex="1">
|
||||
<Card bordered={false} style={{ background: '#fafafa' }}>
|
||||
<Statistic
|
||||
title="待释放收益 (订单完成冷却中)"
|
||||
title="待释放收益 (冷却期内)"
|
||||
value={((wallet?.pending_release_cent ?? 0) / 100).toFixed(2)}
|
||||
prefix={<HourglassOutlined style={{ color: '#1677ff' }} />}
|
||||
suffix="元"
|
||||
|
||||
Reference in New Issue
Block a user