diff --git a/api/admin/v1/channel_debug_log.go b/api/admin/v1/channel_debug_log.go index 915e890..06ce149 100644 --- a/api/admin/v1/channel_debug_log.go +++ b/api/admin/v1/channel_debug_log.go @@ -1,6 +1,10 @@ package v1 -import "github.com/gogf/gf/v2/frame/g" +import ( + "encoding/json" + + "github.com/gogf/gf/v2/frame/g" +) // 渠道调试日志:调试开关开启的渠道,per-attempt 记录客户端↔系统↔上游四段完整报文。 // 端点全部为 GET/DELETE,路径在 /channels/ 下,自动命中 rbac 前缀规则 @@ -50,9 +54,14 @@ type ChannelDebugLogDetailReq struct { ID int64 `json:"id" in:"path" v:"required" dc:"记录ID"` } -// ChannelDebugLogDetailRes 渠道调试日志详情响应 +// ChannelDebugLogDetailRes 渠道调试日志详情响应:详情对象平铺进统一响应的 data 字段 +// (json:"-" + 自定义 MarshalJSON),避免统一包装后再多包一层 data type ChannelDebugLogDetailRes struct { - Data map[string]any `json:"data"` + Data map[string]any `json:"-"` +} + +func (r *ChannelDebugLogDetailRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } // ChannelDebugLogDeleteReq 删除单条调试日志请求(硬删除) diff --git a/api/admin/v1/channel_error.go b/api/admin/v1/channel_error.go index 66a8292..4ac6d00 100644 --- a/api/admin/v1/channel_error.go +++ b/api/admin/v1/channel_error.go @@ -1,6 +1,10 @@ package v1 -import "github.com/gogf/gf/v2/frame/g" +import ( + "encoding/json" + + "github.com/gogf/gf/v2/frame/g" +) // === 渠道错误监控 === @@ -61,8 +65,14 @@ type ChannelErrorCategoriesReq struct { g.Meta `path:"/monitor/channel-errors/categories" method:"get" mime:"json" tags:"管理后台-监控" summary:"错误分类选项"` } +// ChannelErrorCategoriesRes 分类选项平铺进统一响应的 data 字段(json:"-" + 自定义 +// MarshalJSON),避免统一包装后再多包一层 data type ChannelErrorCategoriesRes struct { - Data []map[string]string `json:"data"` + Data []map[string]string `json:"-"` +} + +func (r *ChannelErrorCategoriesRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } // ChannelErrorClearReq 清空全部渠道错误事件(硬删除,用于错误大量堆积时快速释放数据库空间) diff --git a/api/admin/v1/dashboard.go b/api/admin/v1/dashboard.go index 67adf09..ca17aed 100644 --- a/api/admin/v1/dashboard.go +++ b/api/admin/v1/dashboard.go @@ -1,6 +1,8 @@ package v1 import ( + "encoding/json" + "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" ) @@ -142,6 +144,54 @@ type AdminUsageLogListReq struct { AdminUsageLogFilter `json:",inline"` // 嵌入共用筛选条件 } +// AdminUsageLogListItem 用量日志列表项:仅表格列与悬浮明细(Token / 费用分解)所需字段。 +// 纯关联 ID(user_id / project_id / channel_id)与排查大字段(billing_snapshot、 +// user_agent、error_message、request_id 等)不进列表,统一经详情接口按需获取。 +type AdminUsageLogListItem struct { + Id int64 `json:"id"` + TenantId int64 `json:"tenant_id"` + TenantName string `json:"tenant_name"` + Username string `json:"username"` + ProjectName string `json:"project_name"` + ApiKeyId int64 `json:"api_key_id"` + ApiKeyName string `json:"api_key_name"` + ChannelName string `json:"channel_name"` + ModelName string `json:"model_name"` + UpstreamModel string `json:"upstream_model"` + RequestType int `json:"request_type"` + BillingMode string `json:"billing_mode"` + InputTokens int `json:"input_tokens"` + OutputTokens int `json:"output_tokens"` + CacheCreationTokens int `json:"cache_creation_tokens"` + CacheReadTokens int `json:"cache_read_tokens"` + CacheCreation5mTokens int `json:"cache_creation_5m_tokens"` + CacheCreation1hTokens int `json:"cache_creation_1h_tokens"` + ReasoningTokens int `json:"reasoning_tokens"` + AudioInputTokens int `json:"audio_input_tokens"` + AudioOutputTokens int `json:"audio_output_tokens"` + ImageOutputTokens int `json:"image_output_tokens"` + InputCost float64 `json:"input_cost"` + OutputCost float64 `json:"output_cost"` + CacheCreationCost float64 `json:"cache_creation_cost"` + CacheReadCost float64 `json:"cache_read_cost"` + TotalCost float64 `json:"total_cost"` + ActualCost float64 `json:"actual_cost"` + RateMultiplier float64 `json:"rate_multiplier"` + LatencyMs int `json:"latency_ms"` + FirstTokenMs int `json:"first_token_ms"` + Status string `json:"status"` + RetryIndex int `json:"retry_index"` + CreatedAt *gtime.Time `json:"created_at"` +} + +type AdminUsageLogListRes struct { + List []*AdminUsageLogListItem `json:"list"` + Total int `json:"total"` + Page int `json:"page"` + PageSize int `json:"page_size"` +} + +// AdminUsageLogItem 用量日志全量字段结构体(详情接口专用,列表用上方的精简 ListItem) type AdminUsageLogItem struct { Id int64 `json:"id"` TenantId int64 `json:"tenant_id"` @@ -204,13 +254,6 @@ type AdminUsageLogItem struct { CreatedAt *gtime.Time `json:"created_at"` } -type AdminUsageLogListRes struct { - List []*AdminUsageLogItem `json:"list"` - Total int `json:"total"` - Page int `json:"page"` - PageSize int `json:"page_size"` -} - // AdminUsageLogDetailReq 用量日志详情(列表只返回表格展示字段, // billing_snapshot / billing_summary / user_agent / error_message 等大字段经此接口按需获取) type AdminUsageLogDetailReq struct { @@ -218,8 +261,14 @@ type AdminUsageLogDetailReq struct { Id int64 `json:"id" in:"path" v:"min:1" dc:"用量记录ID"` } +// AdminUsageLogDetailRes 详情对象平铺进统一响应的 data 字段(json:"-" + 自定义 MarshalJSON, +// 与 RequestAuditLogDetailRes 同一模式),避免统一包装后再多包一层 data type AdminUsageLogDetailRes struct { - Data *AdminUsageLogItem `json:"data"` + Data *AdminUsageLogItem `json:"-"` +} + +func (r *AdminUsageLogDetailRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } // AdminUsageLogSummaryReq 用量日志统计请求(复用筛选条件,不包含分页) diff --git a/api/admin/v1/error_log.go b/api/admin/v1/error_log.go index d88c9e9..d81b7af 100644 --- a/api/admin/v1/error_log.go +++ b/api/admin/v1/error_log.go @@ -58,8 +58,14 @@ type ErrorLogStatsReq struct { g.Meta `path:"/error-logs/stats" method:"get" mime:"json" tags:"管理后台-系统错误" summary:"错误统计"` } +// ErrorLogStatsRes 统计对象平铺进统一响应的 data 字段(json:"-" + 自定义 MarshalJSON), +// 避免统一包装后再多包一层 data type ErrorLogStatsRes struct { - Data map[string]any `json:"data"` + Data map[string]any `json:"-"` +} + +func (r *ErrorLogStatsRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } // ErrorLogClearReq 清空全部错误日志(硬删除,用于错误大量堆积时快速释放数据库空间) diff --git a/api/admin/v1/monitor.go b/api/admin/v1/monitor.go index 65b9920..584d326 100644 --- a/api/admin/v1/monitor.go +++ b/api/admin/v1/monitor.go @@ -1,6 +1,10 @@ package v1 -import "github.com/gogf/gf/v2/frame/g" +import ( + "encoding/json" + + "github.com/gogf/gf/v2/frame/g" +) // === 监控仪表盘 === @@ -9,8 +13,14 @@ type MonitorDashboardReq struct { Minutes int `json:"minutes" in:"query" d:"5"` } +// MonitorDashboardRes 数据对象平铺进统一响应的 data 字段(json:"-" + 自定义 MarshalJSON), +// 避免统一包装后再多包一层 data(monitor 包下带 Data 的 Res 均为此模式) type MonitorDashboardRes struct { - Data map[string]any `json:"data"` + Data map[string]any `json:"-"` +} + +func (r *MonitorDashboardRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } type MonitorTrafficReq struct { @@ -19,7 +29,11 @@ type MonitorTrafficReq struct { } type MonitorTrafficRes struct { - Data any `json:"data"` + Data any `json:"-"` +} + +func (r *MonitorTrafficRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } type MonitorTrafficFlowReq struct { @@ -30,7 +44,11 @@ type MonitorTrafficFlowReq struct { } type MonitorTrafficFlowRes struct { - Data any `json:"data"` + Data any `json:"-"` +} + +func (r *MonitorTrafficFlowRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } type MonitorModelPerformanceReq struct { @@ -102,7 +120,11 @@ type MonitorLatencyReq struct { } type MonitorLatencyRes struct { - Data map[string]any `json:"data"` + Data map[string]any `json:"-"` +} + +func (r *MonitorLatencyRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } type MonitorSystemReq struct { @@ -111,7 +133,11 @@ type MonitorSystemReq struct { } type MonitorSystemRes struct { - Data map[string]any `json:"data"` + Data map[string]any `json:"-"` +} + +func (r *MonitorSystemRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } // MonitorDispatchReq 渠道调度引擎指标请求 @@ -176,7 +202,11 @@ type MonitorRealtimeReq struct { } type MonitorRealtimeRes struct { - Data any `json:"data"` + Data any `json:"-"` +} + +func (r *MonitorRealtimeRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } // === 告警规则 === @@ -202,7 +232,11 @@ type AlertOptionsReq struct { } type AlertOptionsRes struct { - Data map[string]any `json:"data"` + Data map[string]any `json:"-"` +} + +func (r *AlertOptionsRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } type AlertRuleCreateReq struct { diff --git a/api/admin/v1/plan.go b/api/admin/v1/plan.go index d9a0fa1..bc0fe00 100644 --- a/api/admin/v1/plan.go +++ b/api/admin/v1/plan.go @@ -1,6 +1,8 @@ package v1 import ( + "encoding/json" + "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" ) @@ -59,8 +61,14 @@ type PlanDetailReq struct { Id int64 `json:"id" in:"path" v:"required|min:1"` } +// PlanDetailRes 详情对象平铺进统一响应的 data 字段(json:"-" + 自定义 MarshalJSON), +// 避免统一包装后再多包一层 data type PlanDetailRes struct { - Data *PlanItem `json:"data"` + Data *PlanItem `json:"-"` +} + +func (r *PlanDetailRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } type PlanUpdateReq struct { diff --git a/api/tenant/v1/audit_tenant.go b/api/tenant/v1/audit_tenant.go index f5c1745..945e331 100644 --- a/api/tenant/v1/audit_tenant.go +++ b/api/tenant/v1/audit_tenant.go @@ -1,6 +1,8 @@ package v1 import ( + "encoding/json" + "github.com/gogf/gf/v2/frame/g" ) @@ -62,6 +64,12 @@ type TenantRequestAuditLogDetailReq struct { Id int64 `json:"id" in:"path" v:"required|min:1"` } +// TenantRequestAuditLogDetailRes 详情对象平铺进统一响应的 data 字段(json:"-" + 自定义 +// MarshalJSON),避免统一包装后再多包一层 data(与 admin 端审计详情接口同一模式) type TenantRequestAuditLogDetailRes struct { - Data map[string]any `json:"data"` + Data map[string]any `json:"-"` +} + +func (r *TenantRequestAuditLogDetailRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } diff --git a/api/tenant/v1/billing_tenant.go b/api/tenant/v1/billing_tenant.go index c4b9cae..039ea1b 100644 --- a/api/tenant/v1/billing_tenant.go +++ b/api/tenant/v1/billing_tenant.go @@ -1,6 +1,10 @@ package v1 -import "github.com/gogf/gf/v2/frame/g" +import ( + "encoding/json" + + "github.com/gogf/gf/v2/frame/g" +) // === 租户用量与账单 === @@ -31,8 +35,14 @@ type TenantUsageLogDetailReq struct { Id int64 `json:"id" in:"path" v:"min:1" dc:"用量记录ID"` } +// TenantUsageLogDetailRes 详情对象平铺进统一响应的 data 字段(json:"-" + 自定义 +// MarshalJSON),避免统一包装后再多包一层 data(与 admin 端详情接口同一模式) type TenantUsageLogDetailRes struct { - Data map[string]any `json:"data"` + Data map[string]any `json:"-"` +} + +func (r *TenantUsageLogDetailRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } // TenantUsageLogsSummaryReq 用量日志统计请求(复用列表筛选条件,不含分页) diff --git a/api/tenant/v1/project_tenant.go b/api/tenant/v1/project_tenant.go index 355b344..ce1b738 100644 --- a/api/tenant/v1/project_tenant.go +++ b/api/tenant/v1/project_tenant.go @@ -1,6 +1,8 @@ package v1 import ( + "encoding/json" + "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" ) @@ -124,8 +126,14 @@ type TenantProjectUsageStatsReq struct { Id int64 `json:"id" in:"path" v:"required|min:1"` } +// TenantProjectUsageStatsRes 统计对象平铺进统一响应的 data 字段(json:"-" + 自定义 +// MarshalJSON),避免统一包装后再多包一层 data type TenantProjectUsageStatsRes struct { - Data map[string]any `json:"data"` + Data map[string]any `json:"-"` +} + +func (r *TenantProjectUsageStatsRes) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Data) } type TenantProjectUsageLogsReq struct { diff --git a/internal/logic/admin/audit.go b/internal/logic/admin/audit.go index 5aaba2e..572270e 100644 --- a/internal/logic/admin/audit.go +++ b/internal/logic/admin/audit.go @@ -355,7 +355,8 @@ func (s *sAdmin) ListRequestAuditLogs(ctx context.Context, req *v1.RequestAuditL } where := strings.Join(conditions, " AND ") - // 列表白名单:表格列 + EnrichAuditRecords 回填名称所需的关联 ID。 + // 列表白名单:表格列 + EnrichAuditRecords 回填名称所需的关联键。 + // user_id / project_id 仅作关联查询键(username / project_name 据此回填),表格不展示,出参剔除; // request_id / query_params / user_agent 等列表不展示的字段经详情接口获取。 auditFields := "id, tenant_id, user_id, api_key_id, project_id, method, path, model_name, status_code, client_ip, latency_ms, first_token_ms, audit_level, created_at" @@ -363,6 +364,10 @@ func (s *sAdmin) ListRequestAuditLogs(ctx context.Context, req *v1.RequestAuditL if err != nil { return nil, err } + for _, item := range items { + delete(item, "user_id") + delete(item, "project_id") + } return &v1.RequestAuditLogListRes{ List: items, diff --git a/internal/logic/admin/dashboard.go b/internal/logic/admin/dashboard.go index dbb3533..a781032 100644 --- a/internal/logic/admin/dashboard.go +++ b/internal/logic/admin/dashboard.go @@ -455,19 +455,22 @@ func buildUsageLogFilter(f v1.AdminUsageLogFilter) (where string, args []any) { const usageLogJoinedFrom = "bil_usage_logs u LEFT JOIN tnt_users t ON u.user_id = t.id AND u.tenant_id = t.tenant_id LEFT JOIN tnt_projects p ON u.project_id = p.id LEFT JOIN tnt_tenants tn ON u.tenant_id = tn.id LEFT JOIN api_keys ak ON u.api_key_id = ak.id" // usageLogListFields 列表展示字段白名单:表格列 + 悬浮明细(Token / 费用分解)所需。 -// 大字段(billing_snapshot、billing_summary、user_agent、error_message 等)只进详情接口, +// 纯关联 ID(user_id / project_id / channel_id)列表不展示(名称经 JOIN 回填, +// tenant_id / api_key_id 保留作名称为空时的兜底展示),只进详情接口; +// 大字段(billing_snapshot、billing_summary、user_agent、error_message 等)同样只进详情, // 禁止改回全字段 —— 列表每页最多 100 行,快照 JSONB 会成倍放大响应体积。 -const usageLogListFields = `u.id, u.tenant_id, COALESCE(tn.name, '') AS tenant_name, u.user_id, COALESCE(t.username, '') AS username, - u.project_id, COALESCE(p.name, '') AS project_name, u.api_key_id, COALESCE(ak.name, '') AS api_key_name, - u.channel_id, u.channel_name, u.model_name, u.upstream_model, u.request_type, u.billing_mode, +const usageLogListFields = `u.id, u.tenant_id, COALESCE(tn.name, '') AS tenant_name, COALESCE(t.username, '') AS username, + COALESCE(p.name, '') AS project_name, u.api_key_id, COALESCE(ak.name, '') AS api_key_name, + u.channel_name, u.model_name, u.upstream_model, u.request_type, u.billing_mode, u.input_tokens, u.output_tokens, u.cache_creation_tokens, u.cache_read_tokens, u.cache_creation_5m_tokens, u.cache_creation_1h_tokens, u.reasoning_tokens, u.audio_input_tokens, u.audio_output_tokens, u.image_output_tokens, u.input_cost, u.output_cost, u.cache_creation_cost, u.cache_read_cost, u.total_cost, u.actual_cost, u.rate_multiplier, u.latency_ms, u.first_token_ms, u.status, u.retry_index, u.created_at` -// usageLogDetailFields 详情字段:列表白名单 + 仅详情展示的大字段(错误信息、UA、计费快照等) +// usageLogDetailFields 详情字段:列表白名单 + 纯关联 ID + 仅详情展示的大字段(错误信息、UA、计费快照等) const usageLogDetailFields = usageLogListFields + `, + u.user_id, u.project_id, u.channel_id, u.channel_type, u.requested_model, u.relay_mode, u.currency, u.billing_source, u.error_message, u.client_ip, u.user_agent, u.service_tier, u.reasoning_effort, u.stream_end_reason, u.image_count, u.image_size, u.pre_deduct_amount, u.refund_amount, u.supplement_amount, @@ -498,9 +501,9 @@ func (s *sAdmin) GetAllUsageLogs(ctx context.Context, req *v1.AdminUsageLogListR return nil, err } - logs := make([]*v1.AdminUsageLogItem, 0, len(result)) + logs := make([]*v1.AdminUsageLogListItem, 0, len(result)) for _, row := range result { - item := &v1.AdminUsageLogItem{} + item := &v1.AdminUsageLogListItem{} if err := row.Struct(item); err != nil { continue } diff --git a/tests/integration/admin/plan_test.go b/tests/integration/admin/plan_test.go index 0c83c0c..6f6d667 100644 --- a/tests/integration/admin/plan_test.go +++ b/tests/integration/admin/plan_test.go @@ -45,21 +45,19 @@ func TestPlanCRUD(t *testing.T) { detailResp.AssertSuccess(t) var detail struct { - Data struct { - Id int64 `json:"id"` - Name string `json:"name"` - Identifier string `json:"identifier"` - Status string `json:"status"` - } `json:"data"` + Id int64 `json:"id"` + Name string `json:"name"` + Identifier string `json:"identifier"` + Status string `json:"status"` } detailResp.DecodeData(t, &detail) - if detail.Data.Id != planID { - t.Fatalf("expected plan id=%d, got %d", planID, detail.Data.Id) + if detail.Id != planID { + t.Fatalf("expected plan id=%d, got %d", planID, detail.Id) } t.Logf("Plan detail: name=%s, identifier=%s, status=%s", - detail.Data.Name, detail.Data.Identifier, detail.Data.Status) + detail.Name, detail.Identifier, detail.Status) // Update updateResp := client.Put(fmt.Sprintf("/api/admin/plans/%d", planID), map[string]any{ @@ -76,15 +74,13 @@ func TestPlanCRUD(t *testing.T) { verifyResp.AssertSuccess(t) var verifyData struct { - Data struct { - Description string `json:"description"` - } `json:"data"` + Description string `json:"description"` } verifyResp.DecodeData(t, &verifyData) - if verifyData.Data.Description != "updated by integration test" { + if verifyData.Description != "updated by integration test" { t.Fatalf("expected description 'updated by integration test', got %q", - verifyData.Data.Description) + verifyData.Description) } t.Logf("Plan update verified") @@ -105,13 +101,11 @@ func TestPlanToggleRecommend(t *testing.T) { detailResp.AssertSuccess(t) var detail struct { - Data struct { - IsRecommended bool `json:"is_recommended"` - } `json:"data"` + IsRecommended bool `json:"is_recommended"` } detailResp.DecodeData(t, &detail) - t.Logf("Plan %d is_recommended=%v after first toggle", planID, detail.Data.IsRecommended) + t.Logf("Plan %d is_recommended=%v after first toggle", planID, detail.IsRecommended) // Toggle again to revert resp2 := client.Put(fmt.Sprintf("/api/admin/plans/%d/toggle-recommend", planID), nil) diff --git a/web/admin/src/views/ai/ChannelDetailPage.vue b/web/admin/src/views/ai/ChannelDetailPage.vue index c2e266f..85e163a 100644 --- a/web/admin/src/views/ai/ChannelDetailPage.vue +++ b/web/admin/src/views/ai/ChannelDetailPage.vue @@ -612,7 +612,7 @@ async function openDebugDetail(row: any) { debugDetail.value = null try { const res: any = await request.get(`/admin/channels/${channelId}/debug-logs/${row.id}`) - debugDetail.value = res.data?.data?.data || null + debugDetail.value = res.data?.data || null } catch { debugDetail.value = null } finally { diff --git a/web/admin/src/views/monitor/ChannelErrorsPage.vue b/web/admin/src/views/monitor/ChannelErrorsPage.vue index 40d54ff..293fba0 100644 --- a/web/admin/src/views/monitor/ChannelErrorsPage.vue +++ b/web/admin/src/views/monitor/ChannelErrorsPage.vue @@ -128,7 +128,7 @@ const columns: TableColumnData[] = [ async function fetchCategories() { try { const res = await request.get('/admin/monitor/channel-errors/categories') - categoryOptions.value = res.data?.data?.data || [] + categoryOptions.value = res.data?.data || [] } catch { categoryOptions.value = [] } diff --git a/web/admin/src/views/monitor/ErrorLogsPage.vue b/web/admin/src/views/monitor/ErrorLogsPage.vue index 30af0fd..1a3e002 100644 --- a/web/admin/src/views/monitor/ErrorLogsPage.vue +++ b/web/admin/src/views/monitor/ErrorLogsPage.vue @@ -142,7 +142,7 @@ async function fetchStats() { statsLoading.value = true try { const res = await request.get('/admin/error-logs/stats') - stats.value = res.data?.data?.data + stats.value = res.data?.data } catch { // error auto-shown } finally { diff --git a/web/admin/src/views/monitor/MonitorPage.vue b/web/admin/src/views/monitor/MonitorPage.vue index 9b47e00..25c9332 100644 --- a/web/admin/src/views/monitor/MonitorPage.vue +++ b/web/admin/src/views/monitor/MonitorPage.vue @@ -173,7 +173,7 @@ function updateCharts() { async function fetchDashboard() { try { const res = await request.get('/admin/monitor/dashboard', { params: { minutes: timeRange.value } }) - dashboardData.value = res.data?.data?.data || {} + dashboardData.value = res.data?.data || {} } catch { // error auto-shown by Axios interceptor } @@ -182,7 +182,7 @@ async function fetchDashboard() { async function fetchSystemHistory() { try { const res = await request.get('/admin/monitor/system', { params: { minutes: timeRange.value } }) - systemHistory.value = res.data?.data?.data?.history || [] + systemHistory.value = res.data?.data?.history || [] } catch { // keep previous data } @@ -191,7 +191,7 @@ async function fetchSystemHistory() { async function fetchTraffic() { try { const res = await request.get('/admin/monitor/traffic', { params: { minutes: timeRange.value } }) - trafficData.value = res.data?.data?.data || [] + trafficData.value = res.data?.data || [] } catch { // keep previous data } diff --git a/web/admin/src/views/monitor/RealtimeMonitorPage.vue b/web/admin/src/views/monitor/RealtimeMonitorPage.vue index 8465abd..2749d37 100644 --- a/web/admin/src/views/monitor/RealtimeMonitorPage.vue +++ b/web/admin/src/views/monitor/RealtimeMonitorPage.vue @@ -25,7 +25,7 @@ const gcPauseOption = shallowRef({}) async function fetchRealtime() { try { const res = await request.get('/admin/monitor/realtime') - data.value = res.data?.data?.data || {} + data.value = res.data?.data || {} lastUpdated.value = new Date() updateCharts() } catch { diff --git a/web/admin/src/views/monitor/TrafficFlowPage.vue b/web/admin/src/views/monitor/TrafficFlowPage.vue index d42e667..0017517 100644 --- a/web/admin/src/views/monitor/TrafficFlowPage.vue +++ b/web/admin/src/views/monitor/TrafficFlowPage.vue @@ -95,7 +95,7 @@ async function fetchFlow() { params.end_date = dateRange.value[1] } const res = await request.get('/admin/monitor/traffic-flow', { params }) - const payload = res.data?.data?.data || {} + const payload = res.data?.data || {} nodes.value = payload.nodes || [] links.value = payload.links || [] if (nodes.value.length > 0) { diff --git a/web/tenant/src/components/common/ApiKeyEditModal.vue b/web/tenant/src/components/common/ApiKeyEditModal.vue index 539b5e3..02889a0 100644 --- a/web/tenant/src/components/common/ApiKeyEditModal.vue +++ b/web/tenant/src/components/common/ApiKeyEditModal.vue @@ -245,7 +245,7 @@ async function handleSubmit() { ? `/tenant/projects/${props.projectId}/api-keys` : '/tenant/api-keys' const res: any = await request.post(url, body) - createdKey.value = (res.data?.data?.data || res.data?.data)?.key || '' + createdKey.value = res.data?.data?.key || '' } else { const body: any = { name: form.name, diff --git a/web/tenant/src/views/service/RequestAuditLogsPage.vue b/web/tenant/src/views/service/RequestAuditLogsPage.vue index 9e1c3e2..62ae5ca 100644 --- a/web/tenant/src/views/service/RequestAuditLogsPage.vue +++ b/web/tenant/src/views/service/RequestAuditLogsPage.vue @@ -189,7 +189,7 @@ async function fetchDetail(id: number) { detailRecord.value = null try { const res: any = await request.get(`/tenant/audit/request-logs/${id}`) - detailRecord.value = res.data?.data?.data || null + detailRecord.value = res.data?.data || null } catch { detailRecord.value = null } finally { diff --git a/web/tenant/src/views/team/ProjectDetailPage.vue b/web/tenant/src/views/team/ProjectDetailPage.vue index 4d08c24..f3f3d77 100644 --- a/web/tenant/src/views/team/ProjectDetailPage.vue +++ b/web/tenant/src/views/team/ProjectDetailPage.vue @@ -329,8 +329,7 @@ async function fetchUsageStats() { usageLoading.value = true try { const res: any = await request.get(`/tenant/projects/${projectId.value}/usage-stats`) - const raw = res.data?.data - usageStats.value = raw?.data || raw + usageStats.value = res.data?.data || null } catch { usageStats.value = null } finally {