diff --git a/api/admin/v1/dashboard.go b/api/admin/v1/dashboard.go index a8f3a31..beff1e6 100644 --- a/api/admin/v1/dashboard.go +++ b/api/admin/v1/dashboard.go @@ -101,15 +101,16 @@ type AdminDashboardRecentAlertsRes struct { // === 用量日志 === type AdminUsageLogListReq struct { - g.Meta `path:"/usage-logs" method:"get" mime:"json" tags:"管理后台-用量" summary:"用量日志列表"` - Page int `json:"page" d:"1" dc:"页码"` - PageSize int `json:"page_size" d:"20" v:"between:1,100" dc:"每页数量"` - TenantID int64 `json:"tenant_id" dc:"租户ID"` - Username string `json:"username" dc:"用户名(模糊匹配)"` - Model string `json:"model" dc:"模型名称"` - Status string `json:"status" dc:"状态"` - StartDate string `json:"start_date" dc:"开始日期"` - EndDate string `json:"end_date" dc:"结束日期"` + g.Meta `path:"/usage-logs" method:"get" mime:"json" tags:"管理后台-用量" summary:"用量日志列表"` + Page int `json:"page" d:"1" dc:"页码"` + PageSize int `json:"page_size" d:"20" v:"between:1,100" dc:"每页数量"` + TenantID int64 `json:"tenant_id" dc:"租户ID"` + Username string `json:"username" dc:"用户名(模糊匹配)"` + Model string `json:"model" dc:"模型名称"` + Status string `json:"status" dc:"状态"` + RequestType int `json:"request_type" dc:"请求类型: 1=同步, 2=流式, 3=异步, 4=WebSocket"` + StartDate string `json:"start_date" dc:"开始日期"` + EndDate string `json:"end_date" dc:"结束日期"` } type AdminUsageLogItem struct { @@ -301,14 +302,15 @@ type AdminWalletSetWarningThresholdRes struct{} // AdminUsageLogExportReq 导出用量日志请求 type AdminUsageLogExportReq struct { - g.Meta `path:"/usage-logs/export" method:"get" mime:"json" tags:"管理后台-用量" summary:"导出用量日志"` - Format string `json:"format" in:"query" d:"csv" v:"in:csv,xlsx" dc:"导出格式:csv / xlsx"` - TenantID int64 `json:"tenant_id" in:"query" dc:"租户ID"` - Username string `json:"username" in:"query" dc:"用户名(模糊匹配)"` - Model string `json:"model" in:"query" dc:"模型名称"` - Status string `json:"status" in:"query" dc:"状态"` - StartDate string `json:"start_date" in:"query" dc:"开始日期"` - EndDate string `json:"end_date" in:"query" dc:"结束日期"` + g.Meta `path:"/usage-logs/export" method:"get" mime:"json" tags:"管理后台-用量" summary:"导出用量日志"` + Format string `json:"format" in:"query" d:"csv" v:"in:csv,xlsx" dc:"导出格式:csv / xlsx"` + TenantID int64 `json:"tenant_id" in:"query" dc:"租户ID"` + Username string `json:"username" in:"query" dc:"用户名(模糊匹配)"` + Model string `json:"model" in:"query" dc:"模型名称"` + Status string `json:"status" in:"query" dc:"状态"` + RequestType int `json:"request_type" in:"query" dc:"请求类型: 1=同步, 2=流式, 3=异步, 4=WebSocket"` + StartDate string `json:"start_date" in:"query" dc:"开始日期"` + EndDate string `json:"end_date" in:"query" dc:"结束日期"` } type AdminUsageLogExportRes struct{} diff --git a/internal/logic/admin/dashboard.go b/internal/logic/admin/dashboard.go index 43542f7..9c1211c 100644 --- a/internal/logic/admin/dashboard.go +++ b/internal/logic/admin/dashboard.go @@ -218,6 +218,10 @@ func (s *sAdmin) GetAllUsageLogs(ctx context.Context, req *v1.AdminUsageLogListR conditions = append(conditions, "u.status = ?") args = append(args, req.Status) } + if req.RequestType > 0 { + conditions = append(conditions, "u.request_type = ?") + args = append(args, req.RequestType) + } if req.StartDate != "" { conditions = append(conditions, "u.created_at >= ?") args = append(args, req.StartDate+" 00:00:00") @@ -592,6 +596,10 @@ func (s *sAdmin) ExportUsageLogs(ctx context.Context, req *v1.AdminUsageLogExpor conditions = append(conditions, "u.status = ?") args = append(args, req.Status) } + if req.RequestType > 0 { + conditions = append(conditions, "u.request_type = ?") + args = append(args, req.RequestType) + } if req.StartDate != "" { conditions = append(conditions, "u.created_at >= ?") args = append(args, req.StartDate+" 00:00:00") diff --git a/web/admin/src/views/monitor/ChannelErrorsPage.vue b/web/admin/src/views/monitor/ChannelErrorsPage.vue index 6cde8c6..45ebec0 100644 --- a/web/admin/src/views/monitor/ChannelErrorsPage.vue +++ b/web/admin/src/views/monitor/ChannelErrorsPage.vue @@ -14,7 +14,7 @@ const pagination = reactive({ current: 1, pageSize: 20 }) const filterChannelId = ref(undefined) const filterCategory = ref('') const filterStatusCode = ref(undefined) -const filterDateRange = ref([]) +const filterDateRange = ref(undefined) const filterKeyword = ref('') // Stats @@ -156,7 +156,7 @@ async function fetchData() { if (filterCategory.value) params.error_category = filterCategory.value if (filterStatusCode.value) params.status_code = filterStatusCode.value if (filterKeyword.value) params.keyword = filterKeyword.value - if (filterDateRange.value?.length === 2) { + if (filterDateRange.value && filterDateRange.value.length === 2) { params.start_date = filterDateRange.value[0] params.end_date = filterDateRange.value[1] } @@ -182,7 +182,7 @@ function handleReset() { filterChannelId.value = undefined filterCategory.value = '' filterStatusCode.value = undefined - filterDateRange.value = [] + filterDateRange.value = undefined filterKeyword.value = '' pagination.current = 1 fetchData() @@ -269,11 +269,11 @@ onMounted(() => { @input="(val: string) => filterStatusCode = val ? Number(val) : undefined" /> diff --git a/web/admin/src/views/monitor/CronJobsPage.vue b/web/admin/src/views/monitor/CronJobsPage.vue index 3507a1c..0935bb2 100644 --- a/web/admin/src/views/monitor/CronJobsPage.vue +++ b/web/admin/src/views/monitor/CronJobsPage.vue @@ -30,6 +30,8 @@ const jobNameMap: Record = { usage_log_cleanup: '用量日志清理', oauth_token_refresh: 'OAuth 令牌刷新', cron_execution_cleanup: '执行记录清理', + prededuct_orphan_cleanup: '预扣孤儿清理', + plugin_example_daily_cleanup: '插件示例数据清理', } function formatDuration(ms: number): string { diff --git a/web/admin/src/views/security/RequestAuditLogsPage.vue b/web/admin/src/views/security/RequestAuditLogsPage.vue index 78b9276..f73b366 100644 --- a/web/admin/src/views/security/RequestAuditLogsPage.vue +++ b/web/admin/src/views/security/RequestAuditLogsPage.vue @@ -20,7 +20,7 @@ const pagination = reactive({ pageSizeOptions: [10, 20, 50], }) const filter = reactive({ - tenant_id: '', + tenant_id: null as number | null, api_key_id: '', username: '', request_id: '', @@ -30,6 +30,21 @@ const filter = reactive({ date_range: [] as string[], }) +const tenantOptions = ref<{ label: string; value: number }[]>([]) + +async function fetchTenantOptions(keyword: string) { + try { + const res: any = await request.get('/admin/tenants', { + params: { page: 1, page_size: 20, keyword, status: 'active' }, + }) + const list = res.data?.data?.list || res.data?.list || [] + tenantOptions.value = list.map((t: any) => ({ + label: `${t.name}(${t.code})`, + value: t.id, + })) + } catch { /* ignore */ } +} + const detailLoading = ref(false) const showDetail = ref(false) const detailRecord = ref(null) @@ -180,7 +195,7 @@ async function fetchData() { page: pagination.current, page_size: pagination.pageSize, } - if (filter.tenant_id) params.tenant_id = parseInt(filter.tenant_id) + if (filter.tenant_id) params.tenant_id = filter.tenant_id if (filter.api_key_id) params.api_key_id = parseInt(filter.api_key_id) if (filter.username) params.username = filter.username if (filter.method) params.method = filter.method @@ -229,7 +244,7 @@ function handleFilter() { } function handleReset() { - filter.tenant_id = '' + filter.tenant_id = null filter.api_key_id = '' filter.username = '' filter.request_id = '' @@ -242,6 +257,7 @@ function handleReset() { } onMounted(() => { + fetchTenantOptions('') const route = useRoute() if (route.query.request_id) { filter.request_id = String(route.query.request_id) @@ -259,12 +275,17 @@ onMounted(() => { - {
{{ detailRecord.task_id }} - - - 查看任务 - + 查看任务
diff --git a/web/admin/src/views/security/TenantLoginHistoryPage.vue b/web/admin/src/views/security/TenantLoginHistoryPage.vue index 6bcef66..1214879 100644 --- a/web/admin/src/views/security/TenantLoginHistoryPage.vue +++ b/web/admin/src/views/security/TenantLoginHistoryPage.vue @@ -36,7 +36,23 @@ const methodOptions = [ { label: '恢复码', value: 'backup_code' }, ] +const tenantOptions = ref<{ label: string; value: number }[]>([]) + +async function fetchTenantOptions(keyword: string) { + try { + const res: any = await request.get('/admin/tenants', { + params: { page: 1, page_size: 20, keyword, status: 'active' }, + }) + const list = res.data?.data?.list || res.data?.list || [] + tenantOptions.value = list.map((t: any) => ({ + label: `${t.name}(${t.code})`, + value: t.id, + })) + } catch { /* ignore */ } +} + onMounted(() => { + fetchTenantOptions('') fetchData() }) @@ -122,8 +138,17 @@ function onDateChange(dateString: string | undefined, type: 'start' | 'end') { - - + + diff --git a/web/admin/src/views/tenant/MembersPage.vue b/web/admin/src/views/tenant/MembersPage.vue index 862d5f1..95b18b8 100644 --- a/web/admin/src/views/tenant/MembersPage.vue +++ b/web/admin/src/views/tenant/MembersPage.vue @@ -21,7 +21,21 @@ const pagination = reactive({ const filterStatus = ref(null) const filterRole = ref(null) const filterSearch = ref('') -const filterTenantId = ref('') +const filterTenantId = ref(null) +const filterTenantOptions = ref<{ label: string; value: number }[]>([]) + +async function fetchFilterTenantOptions(keyword: string) { + try { + const res: any = await request.get('/admin/tenants', { + params: { page: 1, page_size: 20, keyword, status: 'active' }, + }) + const list = res.data?.data?.list || res.data?.list || [] + filterTenantOptions.value = list.map((t: any) => ({ + label: `${t.name}(${t.code})`, + value: t.id, + })) + } catch { /* ignore */ } +} // Create member const showCreateModal = ref(false) @@ -255,6 +269,7 @@ function handleFilter() { } onMounted(() => { + fetchFilterTenantOptions('') fetchData() }) @@ -287,6 +302,18 @@ const { exporting, exportFile } = useExport({ + - 搜索 diff --git a/web/admin/src/views/tenant/UsageLogsPage.vue b/web/admin/src/views/tenant/UsageLogsPage.vue index b361258..8916e47 100644 --- a/web/admin/src/views/tenant/UsageLogsPage.vue +++ b/web/admin/src/views/tenant/UsageLogsPage.vue @@ -18,29 +18,73 @@ const pagination = reactive({ pageSizeOptions: [10, 20, 50, 100], }) -const filterTenantId = ref('') +const filterTenantId = ref(undefined) const filterUsername = ref('') -const filterModel = ref('') +const filterModel = ref(undefined) const filterStatus = ref(undefined) const filterRequestType = ref(undefined) const filterDateRange = ref([]) +const tenantOptions = ref<{ label: string; value: number }[]>([]) +const modelOptions = ref<{ label: string; value: string }[]>([]) + const statusOptions = [ - { label: '全部状态', value: '' }, { label: '成功', value: 'success' }, - { label: '失败', value: 'failed' }, - { label: '中断', value: 'interrupted' }, + { label: '失败', value: 'error' }, { label: '超时', value: 'timeout' }, + { label: '已取消', value: 'cancelled' }, ] const requestTypeOptions = [ - { label: '全部类型', value: '' }, { label: '同步', value: '1' }, { label: '流式', value: '2' }, { label: '异步', value: '3' }, { label: 'WebSocket', value: '4' }, ] +let tenantSearchTimer: ReturnType | null = null +let modelSearchTimer: ReturnType | null = null + +async function fetchTenantOptions(keyword = '') { + try { + const res: any = await request.get('/admin/tenants/select', { + params: { page: 1, page_size: 50, keyword } + }) + const list = res.data?.data?.list || [] + tenantOptions.value = list.map((t: any) => ({ + label: `${t.name}(${t.code})`, + value: t.id, + })) + } catch { + tenantOptions.value = [] + } +} + +function handleTenantSearch(value: string) { + if (tenantSearchTimer) clearTimeout(tenantSearchTimer) + tenantSearchTimer = setTimeout(() => fetchTenantOptions(value), 300) +} + +async function fetchModelOptions(search = '') { + try { + const res: any = await request.get('/admin/models', { + params: { page: 1, page_size: 50, status: 'active', search } + }) + const list = res.data?.data?.list || [] + modelOptions.value = list.map((m: any) => ({ + label: m.model_name, + value: m.model_name, + })) + } catch { + modelOptions.value = [] + } +} + +function handleModelSearch(value: string) { + if (modelSearchTimer) clearTimeout(modelSearchTimer) + modelSearchTimer = setTimeout(() => fetchModelOptions(value), 300) +} + const statusTagColor: Record = { success: 'green', failed: 'red', @@ -158,7 +202,7 @@ async function fetchData() { page: pagination.current, page_size: pagination.pageSize, } - if (filterTenantId.value) params.tenant_id = parseInt(filterTenantId.value) + if (filterTenantId.value) params.tenant_id = filterTenantId.value if (filterUsername.value) params.username = filterUsername.value if (filterModel.value) params.model = filterModel.value if (filterStatus.value) params.status = filterStatus.value @@ -186,9 +230,9 @@ function handleFilter() { } function handleReset() { - filterTenantId.value = '' + filterTenantId.value = undefined filterUsername.value = '' - filterModel.value = '' + filterModel.value = undefined filterStatus.value = undefined filterRequestType.value = undefined filterDateRange.value = [] @@ -197,6 +241,8 @@ function handleReset() { } onMounted(() => { + fetchTenantOptions() + fetchModelOptions() fetchData() }) @@ -231,13 +277,18 @@ const { exporting, exportFile } = useExport({ - + - +