feat(web): 成员端同构三页与日志详情,管理端轻量筛选

成员视图改管理端壳(总览/调用日志/支付参数),行内详情弹窗含原始数据;
成员日志含错误行;共享时间范围。管理端成员/支付筛选;支付仅留类型筛选。
This commit is contained in:
2026-10-09 17:04:59 +08:00
parent 665fa088f5
commit 0269cf3386
20 changed files with 1695 additions and 602 deletions
+1
View File
@@ -65,6 +65,7 @@ export const logItem = z.object({
is_stream: z.boolean(),
billing_source: z.string().nullable(),
subscription_id: z.number().int().nullable(),
raw: z.record(z.string(), z.unknown()).optional(),
});
export const logPage = z.object({
+2
View File
@@ -37,6 +37,8 @@ export interface LogItem {
is_stream: boolean;
billing_source: BillingSource | null;
subscription_id: number | null;
/** 完整落库行(上游字段原貌)——「原始数据」视图 */
raw?: Record<string, unknown>;
}
export interface KeyItem {
+32 -2
View File
@@ -597,20 +597,39 @@ export function createRepos(db: Db) {
);
},
/** 成员视图明细分页(恒 type=2) */
/** 成员视图明细分页(可 type 筛;默认全类型) */
async listMemberPage(params: {
member_id: number;
from: Date;
to: Date;
page: number;
page_size: number;
api_key_id?: number;
billing_source?: string | "none";
subscription_id?: number;
type?: number | "error";
}) {
const conds = [
eq(apiKeys.member_id, params.member_id),
gte(usageLogs.created_at, params.from),
lt(usageLogs.created_at, params.to),
eq(usageLogs.type, 2),
];
if (params.type === "error") {
conds.push(ne(usageLogs.type, 2));
} else if (typeof params.type === "number") {
conds.push(eq(usageLogs.type, params.type));
}
if (params.api_key_id != null) {
conds.push(eq(usageLogs.api_key_id, params.api_key_id));
}
if (params.billing_source === "none") {
conds.push(isNull(usageLogs.billing_source));
} else if (params.billing_source) {
conds.push(eq(usageLogs.billing_source, params.billing_source));
}
if (params.subscription_id != null) {
conds.push(eq(usageLogs.subscription_id, params.subscription_id));
}
const totalRows = await db
.select({ n: count() })
.from(usageLogs)
@@ -621,6 +640,7 @@ export function createRepos(db: Db) {
.select({
id: usageLogs.id,
api_key_id: usageLogs.api_key_id,
dedupe_key: usageLogs.dedupe_key,
created_at: usageLogs.created_at,
token_name: apiKeys.token_name,
model_name: usageLogs.model_name,
@@ -633,6 +653,16 @@ export function createRepos(db: Db) {
is_stream: usageLogs.is_stream,
billing_source: usageLogs.billing_source,
subscription_id: usageLogs.subscription_id,
user_id: usageLogs.user_id,
username: usageLogs.username,
channel: usageLogs.channel,
channel_name: usageLogs.channel_name,
token_id: usageLogs.token_id,
group_name: usageLogs.group_name,
ip: usageLogs.ip,
request_id: usageLogs.request_id,
upstream_request_id: usageLogs.upstream_request_id,
other: usageLogs.other,
log_synced_at: apiKeys.log_synced_at,
})
.from(usageLogs)