feat(referrals): 添加邀请返利和注册确认功能

This commit is contained in:
Entropy.Xu
2026-05-16 17:37:58 +08:00
parent 328ac721ce
commit 973eb1a614
56 changed files with 6246 additions and 52 deletions

View File

@@ -8,6 +8,7 @@ export interface Announcement {
priority: number
is_pinned: boolean
is_active: boolean
requires_ack: boolean
author: {
id: string // UUID
username: string
@@ -31,6 +32,7 @@ export interface CreateAnnouncementRequest {
type?: 'info' | 'warning' | 'maintenance' | 'important'
priority?: number
is_pinned?: boolean
requires_ack?: boolean
start_time?: string
end_time?: string
}
@@ -42,6 +44,7 @@ export interface UpdateAnnouncementRequest {
priority?: number
is_active?: boolean
is_pinned?: boolean
requires_ack?: boolean
start_time?: string
end_time?: string
}
@@ -88,6 +91,11 @@ export const announcementApi = {
return response.data
},
async getRequiredUnreadAnnouncements(): Promise<AnnouncementListResponse> {
const response = await apiClient.get('/api/announcements/users/me/required-unread')
return response.data
},
// 管理员方法
// 创建公告
async createAnnouncement(data: CreateAnnouncementRequest): Promise<{ id: string; title: string; message: string }> {
@@ -106,4 +114,4 @@ export const announcementApi = {
const response = await apiClient.delete(`/api/announcements/${id}`)
return response.data
}
}
}

View File

@@ -69,6 +69,9 @@ export interface RegisterRequest {
username: string
password: string
turnstile_token?: string
invite_code?: string
privacy_policy_accepted?: boolean
privacy_policy_version?: string
}
export interface RegisterResponse {
@@ -86,6 +89,14 @@ export interface RegistrationSettingsResponse {
turnstile_enabled?: boolean
turnstile_site_key?: string | null
turnstile_required_actions?: string[]
privacy_policy?: RegistrationPrivacyPolicySettings
}
export interface RegistrationPrivacyPolicySettings {
enabled: boolean
format: 'markdown' | 'html'
content: string
version: string
}
export interface AuthSettingsResponse {

View File

@@ -0,0 +1,114 @@
import apiClient from './client'
export interface ReferralSummary {
total_invites: number
effective_invites: number
paid_reward_usd: number
pending_reward_usd: number
reversed_reward_usd: number
}
export interface ReferralDashboardResponse {
invite_code: string
invitation_link: string
summary: ReferralSummary
}
export interface ReferralRelationshipRecord {
id: string
inviter_user_id: string
inviter_username?: string | null
invitee_user_id: string
invitee_username?: string | null
invite_code_snapshot: string
first_paid_order_id?: string | null
first_paid_at_unix_secs?: number | null
source?: Record<string, unknown> | null
created_at_unix_secs: number
}
export interface ReferralRewardRecord {
id: string
referral_id: string
inviter_user_id: string
invitee_user_id: string
reward_type: string
source_order_id?: string | null
trigger_point: string
amount_usd: number
status: string
wallet_transaction_id?: string | null
idempotency_key: string
reversed_amount_usd: number
pending_reversal_amount_usd: number
admin_operator_id?: string | null
admin_note?: string | null
created_at_unix_secs: number
updated_at_unix_secs: number
}
export interface ReferralListResponse<T> {
items: T[]
total: number
limit: number
offset: number
stats: ReferralSummary
}
export interface ReferralRelationshipQuery {
inviter?: string
invitee?: string
invite_code?: string
first_paid?: boolean | null
limit?: number
offset?: number
}
export interface ReferralRewardQuery {
order_id?: string
reward_type?: string
status?: string
limit?: number
offset?: number
}
function cleanParams<T extends Record<string, unknown>>(params: T): Partial<T> {
return Object.fromEntries(
Object.entries(params).filter(([, value]) => value !== undefined && value !== null && value !== '')
) as Partial<T>
}
export const referralApi = {
async getMyReferral(): Promise<ReferralDashboardResponse> {
const response = await apiClient.get<ReferralDashboardResponse>('/api/users/me/referral')
return response.data
},
async getAdminReferrals(
params: ReferralRelationshipQuery = {}
): Promise<ReferralListResponse<ReferralRelationshipRecord>> {
const response = await apiClient.get('/api/admin/referrals', {
params: cleanParams(params as Record<string, unknown>)
})
return response.data
},
async getAdminReferralRewards(
params: ReferralRewardQuery = {}
): Promise<ReferralListResponse<ReferralRewardRecord>> {
const response = await apiClient.get('/api/admin/referral-rewards', {
params: cleanParams(params as Record<string, unknown>)
})
return response.data
},
async retryReferralReward(id: string, note?: string): Promise<{ reward: ReferralRewardRecord }> {
const response = await apiClient.post(`/api/admin/referral-rewards/${id}/retry`, { note })
return response.data
},
async voidReferralReward(id: string, note?: string): Promise<{ reward: ReferralRewardRecord }> {
const response = await apiClient.post(`/api/admin/referral-rewards/${id}/void`, { note })
return response.data
}
}