- 后端 handler:ResetSubscriptionQuotaRequest 新增 Monthly 字段, 验证逻辑扩展为 daily/weekly/monthly 至少一项为 true - 后端 service:AdminResetQuota 新增 resetMonthly 参数, 调用 ResetMonthlyUsage;重置后追加 subCacheL1.Wait(), 保证 ristretto Del() 的异步删除立即生效,消除重置后 /v1/usage 返回旧用量数据的竞态窗口 - 后端测试:更新存量测试用例匹配新签名,补充 TestAdminResetQuota_ResetMonthlyOnly / TestAdminResetQuota_ResetMonthlyUsageError 两个新用例 - 前端 API:resetQuota options 类型新增 monthly: boolean - 前端视图:confirmResetQuota 改为同时重置 daily/weekly/monthly - i18n:中英文确认提示文案更新,提及每月配额 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
196 lines
4.7 KiB
TypeScript
196 lines
4.7 KiB
TypeScript
/**
|
|
* Admin Subscriptions API endpoints
|
|
* Handles user subscription management for administrators
|
|
*/
|
|
|
|
import { apiClient } from '../client'
|
|
import type {
|
|
UserSubscription,
|
|
SubscriptionProgress,
|
|
AssignSubscriptionRequest,
|
|
BulkAssignSubscriptionRequest,
|
|
ExtendSubscriptionRequest,
|
|
PaginatedResponse
|
|
} from '@/types'
|
|
|
|
/**
|
|
* List all subscriptions with pagination
|
|
* @param page - Page number (default: 1)
|
|
* @param pageSize - Items per page (default: 20)
|
|
* @param filters - Optional filters (status, user_id, group_id, sort_by, sort_order)
|
|
* @returns Paginated list of subscriptions
|
|
*/
|
|
export async function list(
|
|
page: number = 1,
|
|
pageSize: number = 20,
|
|
filters?: {
|
|
status?: 'active' | 'expired' | 'revoked'
|
|
user_id?: number
|
|
group_id?: number
|
|
sort_by?: string
|
|
sort_order?: 'asc' | 'desc'
|
|
},
|
|
options?: {
|
|
signal?: AbortSignal
|
|
}
|
|
): Promise<PaginatedResponse<UserSubscription>> {
|
|
const { data } = await apiClient.get<PaginatedResponse<UserSubscription>>(
|
|
'/admin/subscriptions',
|
|
{
|
|
params: {
|
|
page,
|
|
page_size: pageSize,
|
|
...filters
|
|
},
|
|
signal: options?.signal
|
|
}
|
|
)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Get subscription by ID
|
|
* @param id - Subscription ID
|
|
* @returns Subscription details
|
|
*/
|
|
export async function getById(id: number): Promise<UserSubscription> {
|
|
const { data } = await apiClient.get<UserSubscription>(`/admin/subscriptions/${id}`)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Get subscription progress
|
|
* @param id - Subscription ID
|
|
* @returns Subscription progress with usage stats
|
|
*/
|
|
export async function getProgress(id: number): Promise<SubscriptionProgress> {
|
|
const { data } = await apiClient.get<SubscriptionProgress>(`/admin/subscriptions/${id}/progress`)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Assign subscription to user
|
|
* @param request - Assignment request
|
|
* @returns Created subscription
|
|
*/
|
|
export async function assign(request: AssignSubscriptionRequest): Promise<UserSubscription> {
|
|
const { data } = await apiClient.post<UserSubscription>('/admin/subscriptions/assign', request)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Bulk assign subscriptions to multiple users
|
|
* @param request - Bulk assignment request
|
|
* @returns Created subscriptions
|
|
*/
|
|
export async function bulkAssign(
|
|
request: BulkAssignSubscriptionRequest
|
|
): Promise<UserSubscription[]> {
|
|
const { data } = await apiClient.post<UserSubscription[]>(
|
|
'/admin/subscriptions/bulk-assign',
|
|
request
|
|
)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Extend subscription validity
|
|
* @param id - Subscription ID
|
|
* @param request - Extension request with days
|
|
* @returns Updated subscription
|
|
*/
|
|
export async function extend(
|
|
id: number,
|
|
request: ExtendSubscriptionRequest
|
|
): Promise<UserSubscription> {
|
|
const { data } = await apiClient.post<UserSubscription>(
|
|
`/admin/subscriptions/${id}/extend`,
|
|
request
|
|
)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Revoke subscription
|
|
* @param id - Subscription ID
|
|
* @returns Success confirmation
|
|
*/
|
|
export async function revoke(id: number): Promise<{ message: string }> {
|
|
const { data } = await apiClient.delete<{ message: string }>(`/admin/subscriptions/${id}`)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Reset daily, weekly, and/or monthly usage quota for a subscription
|
|
* @param id - Subscription ID
|
|
* @param options - Which windows to reset
|
|
* @returns Updated subscription
|
|
*/
|
|
export async function resetQuota(
|
|
id: number,
|
|
options: { daily: boolean; weekly: boolean; monthly: boolean }
|
|
): Promise<UserSubscription> {
|
|
const { data } = await apiClient.post<UserSubscription>(
|
|
`/admin/subscriptions/${id}/reset-quota`,
|
|
options
|
|
)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* List subscriptions by group
|
|
* @param groupId - Group ID
|
|
* @param page - Page number
|
|
* @param pageSize - Items per page
|
|
* @returns Paginated list of subscriptions in the group
|
|
*/
|
|
export async function listByGroup(
|
|
groupId: number,
|
|
page: number = 1,
|
|
pageSize: number = 20
|
|
): Promise<PaginatedResponse<UserSubscription>> {
|
|
const { data } = await apiClient.get<PaginatedResponse<UserSubscription>>(
|
|
`/admin/groups/${groupId}/subscriptions`,
|
|
{
|
|
params: { page, page_size: pageSize }
|
|
}
|
|
)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* List subscriptions by user
|
|
* @param userId - User ID
|
|
* @param page - Page number
|
|
* @param pageSize - Items per page
|
|
* @returns Paginated list of user's subscriptions
|
|
*/
|
|
export async function listByUser(
|
|
userId: number,
|
|
page: number = 1,
|
|
pageSize: number = 20
|
|
): Promise<PaginatedResponse<UserSubscription>> {
|
|
const { data } = await apiClient.get<PaginatedResponse<UserSubscription>>(
|
|
`/admin/users/${userId}/subscriptions`,
|
|
{
|
|
params: { page, page_size: pageSize }
|
|
}
|
|
)
|
|
return data
|
|
}
|
|
|
|
export const subscriptionsAPI = {
|
|
list,
|
|
getById,
|
|
getProgress,
|
|
assign,
|
|
bulkAssign,
|
|
extend,
|
|
revoke,
|
|
resetQuota,
|
|
listByGroup,
|
|
listByUser
|
|
}
|
|
|
|
export default subscriptionsAPI
|