sub2api/backend/migrations/081_create_risk_tables.sql
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

50 lines
2.3 KiB
SQL

-- +migrate Up
CREATE TABLE IF NOT EXISTS account_behavior_hourly (
id BIGSERIAL PRIMARY KEY,
account_id BIGINT NOT NULL,
hour_bucket TIMESTAMPTZ NOT NULL,
api_call_count BIGINT NOT NULL DEFAULT 0,
stream_count BIGINT NOT NULL DEFAULT 0,
total_input_tokens BIGINT NOT NULL DEFAULT 0,
total_output_tokens BIGINT NOT NULL DEFAULT 0,
total_duration_ms BIGINT NOT NULL DEFAULT 0,
p50_duration_ms INT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_account_behavior_hourly UNIQUE (account_id, hour_bucket)
);
CREATE INDEX IF NOT EXISTS idx_account_behavior_hourly_account_id ON account_behavior_hourly (account_id);
CREATE INDEX IF NOT EXISTS idx_account_behavior_hourly_hour_bucket ON account_behavior_hourly (hour_bucket DESC);
CREATE TABLE IF NOT EXISTS account_risk_scores (
id BIGSERIAL PRIMARY KEY,
account_id BIGINT NOT NULL,
risk_score DOUBLE PRECISION NOT NULL DEFAULT 0,
risk_level VARCHAR(16) NOT NULL DEFAULT 'LOW',
risk_reasons JSONB NOT NULL DEFAULT '{}',
feature_vector JSONB NOT NULL DEFAULT '{}',
scored_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
model_version INT NOT NULL DEFAULT 1,
idle_override BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_account_risk_scores_account_id UNIQUE (account_id)
);
CREATE INDEX IF NOT EXISTS idx_account_risk_scores_risk_level ON account_risk_scores (risk_level);
CREATE INDEX IF NOT EXISTS idx_account_risk_scores_risk_score ON account_risk_scores (risk_score DESC);
CREATE INDEX IF NOT EXISTS idx_account_risk_scores_scored_at ON account_risk_scores (scored_at DESC);
-- +migrate Down
DROP INDEX IF EXISTS idx_account_risk_scores_scored_at;
DROP INDEX IF EXISTS idx_account_risk_scores_risk_score;
DROP INDEX IF EXISTS idx_account_risk_scores_risk_level;
DROP TABLE IF EXISTS account_risk_scores;
DROP INDEX IF EXISTS idx_account_behavior_hourly_hour_bucket;
DROP INDEX IF EXISTS idx_account_behavior_hourly_account_id;
DROP TABLE IF EXISTS account_behavior_hourly;