feat(rpm): RPM 限流模块优化

P0:
- rpm_override 嵌入 Auth Cache Snapshot,消除每请求 DB 查询 (snapshot v6→v7)
- 429 RPM 响应返回 Retry-After 头(当前分钟剩余秒数)

P1:
- ClearAll 按钮直连 DELETE API,带 loading 防重复
- 新增 GET /admin/users/:id/rpm-status 管理员 RPM 用量查询端点

优化:
- checkRPM 从级联互斥改为并行取最严,user.rpm_limit 作为全局硬上限始终生效
- Override/Group 变更后自动失效 auth cache
- fail-open 语义不变,Redis 故障不阻塞业务
This commit is contained in:
james-6-23
2026-04-23 03:33:52 +08:00
parent ef967d8f8a
commit dc5d42addc
79 changed files with 2831 additions and 140 deletions

View File

@@ -164,7 +164,8 @@ export interface GroupRateMultiplierEntry {
user_email: string
user_notes: string
user_status: string
rate_multiplier: number
rate_multiplier?: number | null
rpm_override?: number | null
}
/**
@@ -205,9 +206,7 @@ export async function clearGroupRateMultipliers(id: number): Promise<{ message:
/**
* Batch set rate multipliers for users in a group
* @param id - Group ID
* @param entries - Array of { user_id, rate_multiplier }
* @returns Success confirmation
* Only touches rate_multiplier column; preserves rpm_override on existing rows.
*/
export async function batchSetGroupRateMultipliers(
id: number,
@@ -220,6 +219,60 @@ export async function batchSetGroupRateMultipliers(
return data
}
/**
* RPM override entry for a user in a group
*/
export interface GroupRPMOverrideEntry {
user_id: number
user_name: string
user_email: string
user_notes: string
user_status: string
rpm_override: number
}
/**
* Get RPM overrides for users in a group (subset of rate-multipliers endpoint).
*/
export async function getGroupRPMOverrides(id: number): Promise<GroupRPMOverrideEntry[]> {
const { data } = await apiClient.get<GroupRateMultiplierEntry[]>(
`/admin/groups/${id}/rate-multipliers`
)
return data
.filter(e => e.rpm_override != null)
.map(e => ({
user_id: e.user_id,
user_name: e.user_name,
user_email: e.user_email,
user_notes: e.user_notes,
user_status: e.user_status,
rpm_override: e.rpm_override as number
}))
}
/**
* Batch set RPM overrides for users in a group.
* Only touches rpm_override column; preserves rate_multiplier on existing rows.
*/
export async function batchSetGroupRPMOverrides(
id: number,
entries: Array<{ user_id: number; rpm_override: number }>
): Promise<{ message: string }> {
const { data } = await apiClient.put<{ message: string }>(
`/admin/groups/${id}/rpm-overrides`,
{ entries }
)
return data
}
/**
* Clear all RPM overrides for a group (preserves rate_multiplier).
*/
export async function clearGroupRPMOverrides(id: number): Promise<{ message: string }> {
const { data } = await apiClient.delete<{ message: string }>(`/admin/groups/${id}/rpm-overrides`)
return data
}
/**
* Get usage summary (today + cumulative cost) for all groups
* @param timezone - IANA timezone string (e.g. "Asia/Shanghai")
@@ -262,6 +315,9 @@ export const groupsAPI = {
getGroupRateMultipliers,
clearGroupRateMultipliers,
batchSetGroupRateMultipliers,
getGroupRPMOverrides,
clearGroupRPMOverrides,
batchSetGroupRPMOverrides,
updateSortOrder,
getUsageSummary,
getCapacitySummary

View File

@@ -309,6 +309,7 @@ export interface SystemSettings {
// Default settings
default_balance: number;
default_concurrency: number;
default_user_rpm_limit: number;
default_subscriptions: DefaultSubscriptionSetting[];
auth_source_default_email_balance?: number;
auth_source_default_email_concurrency?: number;
@@ -482,6 +483,7 @@ export interface UpdateSettingsRequest {
totp_enabled?: boolean; // TOTP 双因素认证
default_balance?: number;
default_concurrency?: number;
default_user_rpm_limit?: number;
default_subscriptions?: DefaultSubscriptionSetting[];
auth_source_default_email_balance?: number;
auth_source_default_email_concurrency?: number;