sub2api/backend/internal/service/risk_models.go
win f25dd04e0b
Some checks failed
CI / test (push) Failing after 1m31s
CI / golangci-lint (push) Failing after 3s
Security Scan / backend-security (push) Failing after 3s
Security Scan / frontend-security (push) Failing after 2s
feat(risk): 风控数据管道与风控中心
- DB Migration 081: 新增 account_behavior_hourly / account_risk_scores 表
- 行为采集:Gateway/OpenAI Gateway RecordUsage 注入 fire-and-forget CollectBehaviorAsync
- SQL 打分引擎:CTE 加权特征向量 → risk_score [0-1],UPSERT 保留 idle_override
- RiskSettings:Redis 缓存 → DB fallback → 默认值(observe 模式)
- REST API:/admin/risk/summary|accounts|accounts/:id|settings
- 前端:Pinia store + RiskControlView + 6 子组件(donut/radar/line 纯 SVG 图表)
- 侧边栏新增 Risk Control 入口(ShieldExclamationIcon)
- 反风控优化:移除 Antigravity 后台定时刷新,改为按需刷新避免 idle 封号
2026-03-28 03:07:17 +08:00

122 lines
3.7 KiB
Go

package service
import (
"encoding/json"
"time"
)
const (
RiskLevelLow = "LOW"
RiskLevelMedium = "MEDIUM"
RiskLevelHigh = "HIGH"
)
const (
RiskPhaseOff = "off"
RiskPhaseObserve = "observe"
RiskPhaseEnforce = "enforce"
)
const (
riskSettingsCacheKey = "settings:risk:v1"
)
type RiskSettings struct {
MediumThreshold float64 `json:"medium_threshold"`
HighThreshold float64 `json:"high_threshold"`
Phase string `json:"phase"`
}
func DefaultRiskSettings() *RiskSettings {
return &RiskSettings{
MediumThreshold: 0.45,
HighThreshold: 0.75,
Phase: RiskPhaseObserve,
}
}
type RiskBehaviorHourDelta struct {
APICallCount int64
StreamCount int64
TotalInputTokens int64
TotalOutputTokens int64
TotalDurationMs int64
P50DurationMs *int
}
type RiskSummary struct {
TotalAccounts int64 `json:"total_accounts"`
LowCount int64 `json:"low_count"`
MediumCount int64 `json:"medium_count"`
HighCount int64 `json:"high_count"`
AverageScore float64 `json:"average_score"`
LastScoredAt *time.Time `json:"last_scored_at,omitempty"`
Settings *RiskSettings `json:"settings"`
}
type RiskAccountFilter struct {
Page int
PageSize int
Level string
Platform string
}
type RiskAccountListItem struct {
AccountID int64 `json:"account_id"`
AccountName string `json:"account_name"`
Platform string `json:"platform"`
RiskScore float64 `json:"risk_score"`
RiskLevel string `json:"risk_level"`
RiskReasons json.RawMessage `json:"risk_reasons"`
FeatureVector json.RawMessage `json:"feature_vector"`
IdleOverride bool `json:"idle_override"`
ScoredAt time.Time `json:"scored_at"`
LastHourCalls int64 `json:"last_hour_calls"`
LastHourTokens int64 `json:"last_hour_tokens"`
}
type RiskAccountList struct {
Items []*RiskAccountListItem `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
type RiskBehaviorHour struct {
HourBucket time.Time `json:"hour_bucket"`
APICallCount int64 `json:"api_call_count"`
StreamCount int64 `json:"stream_count"`
TotalInputTokens int64 `json:"total_input_tokens"`
TotalOutputTokens int64 `json:"total_output_tokens"`
TotalDurationMs int64 `json:"total_duration_ms"`
P50DurationMs *int `json:"p50_duration_ms,omitempty"`
}
type RiskAccountDetail struct {
AccountID int64 `json:"account_id"`
AccountName string `json:"account_name"`
Platform string `json:"platform"`
RiskScore float64 `json:"risk_score"`
RiskLevel string `json:"risk_level"`
RiskReasons json.RawMessage `json:"risk_reasons"`
FeatureVector json.RawMessage `json:"feature_vector"`
IdleOverride bool `json:"idle_override"`
ScoredAt time.Time `json:"scored_at"`
ModelVersion int `json:"model_version"`
HourlyBehavior []RiskBehaviorHour `json:"hourly_behavior"`
}
type RiskScoreRecord struct {
ID int64 `json:"id"`
AccountID int64 `json:"account_id"`
RiskScore float64 `json:"risk_score"`
RiskLevel string `json:"risk_level"`
RiskReasons json.RawMessage `json:"risk_reasons"`
FeatureVector json.RawMessage `json:"feature_vector"`
ScoredAt time.Time `json:"scored_at"`
ModelVersion int `json:"model_version"`
IdleOverride bool `json:"idle_override"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}