feat(affiliate): add admin recharge rebate option

This commit is contained in:
Yuhao Jiang committed 2026-07-15 04:28:55 -05:00
1 parent d515c3045c
commit 736824dd7a
20 files changed
+229 -3

No files matched your search

+2
View File
@@ -376,6 +376,7 @@ export interface SystemSettings {
affiliate_rebate_freeze_hours: number;
affiliate_rebate_duration_days: number;
affiliate_rebate_per_invitee_cap: number;
affiliate_admin_recharge_enabled: boolean;
default_concurrency: number;
default_user_rpm_limit: number;
default_subscriptions: DefaultSubscriptionSetting[];
@@ -673,6 +674,7 @@ export interface UpdateSettingsRequest {
affiliate_rebate_freeze_hours?: number;
affiliate_rebate_duration_days?: number;
affiliate_rebate_per_invitee_cap?: number;
affiliate_admin_recharge_enabled?: boolean;
default_concurrency?: number;
default_user_rpm_limit?: number;
default_subscriptions?: DefaultSubscriptionSetting[];
@@ -45,6 +45,8 @@ export default {
description: 'Existing users invite new ones; the inviter earns a percentage rebate on the invitee’s recharges. Disabled by default.',
enabled: 'Enable Affiliate',
enabledHint: 'When off, the affiliate menu is hidden, the aff parameter is ignored at signup, and new recharges generate no rebate. Existing rebate balances can still be transferred.',
adminRechargeRebate: 'Rebate Admin Deposits',
adminRechargeRebateHint: 'When enabled, balance added through User Management > Deposit generates affiliate rebates. Setting a balance or withdrawing funds does not.',
rebateRate: 'Global Rebate Rate',
rebateRateHint: 'Default percentage given back to the inviter on recharges (0-100, e.g. 10 = 10%).',
freezeHours: 'Rebate Freeze Period (hours)',
@@ -45,6 +45,8 @@ export default {
description: '老用户邀请新用户注册,新用户充值后老用户按比例获得返利额度。默认关闭。',
enabled: '启用邀请返利',
enabledHint: '关闭后用户菜单中的邀请页面入口隐藏、注册时忽略邀请码、新充值不再产生返利。已有返利额度仍可转入余额。',
adminRechargeRebate: '管理员充值参与返利',
adminRechargeRebateHint: '开启后,通过“用户管理 > 充值”增加的余额会产生邀请返利;设置余额和扣款不参与返利。',
rebateRate: '全局返利比例',
rebateRateHint: '充值后返给邀请人的默认比例(0-100%,例如填写 10 表示返利 10%)。',
freezeHours: '返利冻结期(小时)',
+14
View File
@@ -5938,6 +5938,18 @@
</div>
<div v-if="form.affiliate_enabled" class="space-y-6">
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
{{ t('admin.settings.features.affiliate.adminRechargeRebate') }}
</label>
<p class="mt-0.5 text-xs text-gray-500 dark:text-gray-400">
{{ t('admin.settings.features.affiliate.adminRechargeRebateHint') }}
</p>
</div>
<Toggle v-model="form.affiliate_admin_recharge_enabled" />
</div>
<div>
<label class="input-label">
{{ t('admin.settings.features.affiliate.rebateRate') }}
@@ -8063,6 +8075,7 @@ const form = reactive<SettingsForm>({
affiliate_rebate_freeze_hours: 0,
affiliate_rebate_duration_days: 0,
affiliate_rebate_per_invitee_cap: 0,
affiliate_admin_recharge_enabled: false,
default_concurrency: 1,
default_subscriptions: [],
force_email_on_third_party_signup: false,
@@ -9418,6 +9431,7 @@ async function saveSettings() {
affiliate_rebate_freeze_hours: Math.max(0, Math.min(720, Number(form.affiliate_rebate_freeze_hours) || 0)),
affiliate_rebate_duration_days: Math.max(0, Math.min(3650, Math.floor(Number(form.affiliate_rebate_duration_days) || 0))),
affiliate_rebate_per_invitee_cap: Math.max(0, Number(form.affiliate_rebate_per_invitee_cap) || 0),
affiliate_admin_recharge_enabled: form.affiliate_admin_recharge_enabled,
default_concurrency: form.default_concurrency,
default_subscriptions: normalizedDefaultSubscriptions,
force_email_on_third_party_signup: form.force_email_on_third_party_signup,
@@ -646,6 +646,27 @@ describe("admin SettingsView payment visible method controls", () => {
expect(payload).not.toHaveProperty("payment_visible_method_wxpay_enabled");
});
it("submits the admin recharge affiliate rebate setting", async () => {
getSettings.mockResolvedValueOnce({
...baseSettingsResponse,
affiliate_enabled: true,
affiliate_admin_recharge_enabled: true,
});
const wrapper = mountView();
await flushPromises();
await wrapper.find("form").trigger("submit.prevent");
await flushPromises();
expect(updateSettings).toHaveBeenCalledTimes(1);
expect(updateSettings).toHaveBeenCalledWith(
expect.objectContaining({
affiliate_admin_recharge_enabled: true,
}),
);
});
it("submits Anthropic cache TTL injection gateway setting", async () => {
getSettings.mockResolvedValueOnce({
...baseSettingsResponse,