fix(response):修复响应结构错误问题
This commit is contained in:
1 parent
4889477452
commit
22bdb23c8d
21 files changed
+208
-65
No files matched your search
@@ -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 删除单条调试日志请求(硬删除)
|
||||
|
||||
@@ -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 清空全部渠道错误事件(硬删除,用于错误大量堆积时快速释放数据库空间)
|
||||
|
||||
@@ -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 用量日志统计请求(复用筛选条件,不包含分页)
|
||||
|
||||
@@ -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 清空全部错误日志(硬删除,用于错误大量堆积时快速释放数据库空间)
|
||||
|
||||
+42
-8
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 用量日志统计请求(复用列表筛选条件,不含分页)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 = []
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user