mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-05-05 13:40:44 +08:00
feat(affiliate): 完善邀请返利系统
- 修复返利不到账的根因:tryClaimAffiliateRebateAudit 中 PostgreSQL 参数类型推断冲突 - 补全 OAuth 注册路径(LinuxDo/OIDC/WeChat/Pending Flow)的邀请码绑定 - 前端 OAuth 注册页面传递 aff_code 参数 - 新增返利冻结期机制:可配置冻结时间,到期后自动解冻(懒解冻) - 新增返利有效期:绑定后 N 天内有效,过期不再产生返利 - 新增单人返利上限:超出上限部分精确截断 - 增强返利流程 slog 结构化日志,便于排查问题 - 已邀请用户列表增加返利明细列
This commit is contained in:
@@ -74,6 +74,26 @@ describe('oauth adoption auth api', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('posts affiliate code when completing linuxdo oauth registration', async () => {
|
||||
const { completeLinuxDoOAuthRegistration } = await import('@/api/auth')
|
||||
|
||||
await completeLinuxDoOAuthRegistration(
|
||||
'invite-code',
|
||||
{
|
||||
adoptDisplayName: true,
|
||||
adoptAvatar: false
|
||||
},
|
||||
' AFF123 '
|
||||
)
|
||||
|
||||
expect(post).toHaveBeenCalledWith('/auth/oauth/linuxdo/complete-registration', {
|
||||
invitation_code: 'invite-code',
|
||||
aff_code: 'AFF123',
|
||||
adopt_display_name: true,
|
||||
adopt_avatar: false
|
||||
})
|
||||
})
|
||||
|
||||
it('posts oidc invitation completion with adoption decisions', async () => {
|
||||
const { completeOIDCOAuthRegistration } = await import('@/api/auth')
|
||||
|
||||
@@ -134,6 +154,26 @@ describe('oauth adoption auth api', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('posts affiliate code when creating pending wechat oauth account', async () => {
|
||||
const { createPendingWeChatOAuthAccount } = await import('@/api/auth')
|
||||
|
||||
await createPendingWeChatOAuthAccount(
|
||||
'invite-code',
|
||||
{
|
||||
adoptDisplayName: false,
|
||||
adoptAvatar: true
|
||||
},
|
||||
'WXAFF'
|
||||
)
|
||||
|
||||
expect(post).toHaveBeenCalledWith('/auth/oauth/wechat/complete-registration', {
|
||||
invitation_code: 'invite-code',
|
||||
aff_code: 'WXAFF',
|
||||
adopt_display_name: false,
|
||||
adopt_avatar: true
|
||||
})
|
||||
})
|
||||
|
||||
it('classifies oauth completion results as login or bind', async () => {
|
||||
const { getOAuthCompletionKind } = await import('@/api/auth')
|
||||
|
||||
|
||||
@@ -309,6 +309,9 @@ export interface SystemSettings {
|
||||
// Default settings
|
||||
default_balance: number;
|
||||
affiliate_rebate_rate: number;
|
||||
affiliate_rebate_freeze_hours: number;
|
||||
affiliate_rebate_duration_days: number;
|
||||
affiliate_rebate_per_invitee_cap: number;
|
||||
default_concurrency: number;
|
||||
default_user_rpm_limit: number;
|
||||
default_subscriptions: DefaultSubscriptionSetting[];
|
||||
@@ -494,6 +497,9 @@ export interface UpdateSettingsRequest {
|
||||
totp_enabled?: boolean; // TOTP 双因素认证
|
||||
default_balance?: number;
|
||||
affiliate_rebate_rate?: number;
|
||||
affiliate_rebate_freeze_hours?: number;
|
||||
affiliate_rebate_duration_days?: number;
|
||||
affiliate_rebate_per_invitee_cap?: number;
|
||||
default_concurrency?: number;
|
||||
default_user_rpm_limit?: number;
|
||||
default_subscriptions?: DefaultSubscriptionSetting[];
|
||||
|
||||
@@ -564,9 +564,10 @@ export async function resetPassword(request: ResetPasswordRequest): Promise<Rese
|
||||
*/
|
||||
export async function completeLinuxDoOAuthRegistration(
|
||||
invitationCode: string,
|
||||
decision?: OAuthAdoptionDecision
|
||||
decision?: OAuthAdoptionDecision,
|
||||
affiliateCode?: string
|
||||
): Promise<OAuthTokenResponse> {
|
||||
return createPendingLinuxDoOAuthAccount(invitationCode, decision)
|
||||
return createPendingLinuxDoOAuthAccount(invitationCode, decision, affiliateCode)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -576,27 +577,32 @@ export async function completeLinuxDoOAuthRegistration(
|
||||
*/
|
||||
export async function completeOIDCOAuthRegistration(
|
||||
invitationCode: string,
|
||||
decision?: OAuthAdoptionDecision
|
||||
decision?: OAuthAdoptionDecision,
|
||||
affiliateCode?: string
|
||||
): Promise<OAuthTokenResponse> {
|
||||
return createPendingOIDCOAuthAccount(invitationCode, decision)
|
||||
return createPendingOIDCOAuthAccount(invitationCode, decision, affiliateCode)
|
||||
}
|
||||
|
||||
export async function completeWeChatOAuthRegistration(
|
||||
invitationCode: string,
|
||||
decision?: OAuthAdoptionDecision
|
||||
decision?: OAuthAdoptionDecision,
|
||||
affiliateCode?: string
|
||||
): Promise<OAuthTokenResponse> {
|
||||
return createPendingWeChatOAuthAccount(invitationCode, decision)
|
||||
return createPendingWeChatOAuthAccount(invitationCode, decision, affiliateCode)
|
||||
}
|
||||
|
||||
async function createPendingOAuthAccount(
|
||||
provider: 'linuxdo' | 'oidc' | 'wechat',
|
||||
invitationCode: string,
|
||||
decision?: OAuthAdoptionDecision
|
||||
decision?: OAuthAdoptionDecision,
|
||||
affiliateCode?: string
|
||||
): Promise<PendingOAuthCreateAccountResponse> {
|
||||
const normalizedAffiliateCode = affiliateCode?.trim()
|
||||
const { data } = await apiClient.post<PendingOAuthCreateAccountResponse>(
|
||||
`/auth/oauth/${provider}/complete-registration`,
|
||||
{
|
||||
invitation_code: invitationCode,
|
||||
...(normalizedAffiliateCode ? { aff_code: normalizedAffiliateCode } : {}),
|
||||
...serializeOAuthAdoptionDecision(decision)
|
||||
}
|
||||
)
|
||||
@@ -605,23 +611,26 @@ async function createPendingOAuthAccount(
|
||||
|
||||
export async function createPendingLinuxDoOAuthAccount(
|
||||
invitationCode: string,
|
||||
decision?: OAuthAdoptionDecision
|
||||
decision?: OAuthAdoptionDecision,
|
||||
affiliateCode?: string
|
||||
): Promise<PendingOAuthCreateAccountResponse> {
|
||||
return createPendingOAuthAccount('linuxdo', invitationCode, decision)
|
||||
return createPendingOAuthAccount('linuxdo', invitationCode, decision, affiliateCode)
|
||||
}
|
||||
|
||||
export async function createPendingOIDCOAuthAccount(
|
||||
invitationCode: string,
|
||||
decision?: OAuthAdoptionDecision
|
||||
decision?: OAuthAdoptionDecision,
|
||||
affiliateCode?: string
|
||||
): Promise<PendingOAuthCreateAccountResponse> {
|
||||
return createPendingOAuthAccount('oidc', invitationCode, decision)
|
||||
return createPendingOAuthAccount('oidc', invitationCode, decision, affiliateCode)
|
||||
}
|
||||
|
||||
export async function createPendingWeChatOAuthAccount(
|
||||
invitationCode: string,
|
||||
decision?: OAuthAdoptionDecision
|
||||
decision?: OAuthAdoptionDecision,
|
||||
affiliateCode?: string
|
||||
): Promise<PendingOAuthCreateAccountResponse> {
|
||||
return createPendingOAuthAccount('wechat', invitationCode, decision)
|
||||
return createPendingOAuthAccount('wechat', invitationCode, decision, affiliateCode)
|
||||
}
|
||||
|
||||
export async function completePendingOAuthBindLogin(
|
||||
|
||||
Reference in New Issue
Block a user