125_add_channel_monitors.sql 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. -- Migration: 125_add_channel_monitors
  2. -- 渠道监控 MVP:周期性对外部 provider/endpoint/api_key 做模型心跳测试。
  3. --
  4. -- 表结构说明:
  5. -- - channel_monitors 渠道配置表(一行 = 一个监控对象)
  6. -- - channel_monitor_histories 检测历史明细表(一次检测一个模型 = 一行)
  7. --
  8. -- 设计要点:
  9. -- - api_key_encrypted 列存放 AES-256-GCM 密文(base64),由 service 层加密。
  10. -- - extra_models 用 JSONB 存储字符串数组,便于扩展(后续可加权重等元数据)。
  11. -- - history 表通过 ON DELETE CASCADE 自动清理已删除监控的历史。
  12. -- - (enabled, last_checked_at) 索引服务于调度器扫描“到期需要检测”的监控。
  13. -- - histories 上 (monitor_id, model, checked_at DESC) 服务用户视图聚合查询;
  14. -- 单独的 (checked_at) 索引服务定期清理 30 天前数据的 DELETE。
  15. CREATE TABLE IF NOT EXISTS channel_monitors (
  16. id BIGSERIAL PRIMARY KEY,
  17. name VARCHAR(100) NOT NULL,
  18. provider VARCHAR(20) NOT NULL, -- openai / anthropic / gemini
  19. endpoint VARCHAR(500) NOT NULL, -- base origin
  20. api_key_encrypted TEXT NOT NULL, -- AES-256-GCM (base64)
  21. primary_model VARCHAR(200) NOT NULL,
  22. extra_models JSONB NOT NULL DEFAULT '[]'::jsonb,
  23. group_name VARCHAR(100) NOT NULL DEFAULT '',
  24. enabled BOOLEAN NOT NULL DEFAULT TRUE,
  25. interval_seconds INT NOT NULL,
  26. last_checked_at TIMESTAMPTZ,
  27. created_by BIGINT NOT NULL,
  28. created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  29. updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  30. CONSTRAINT channel_monitors_provider_check CHECK (provider IN ('openai', 'anthropic', 'gemini')),
  31. CONSTRAINT channel_monitors_interval_check CHECK (interval_seconds BETWEEN 15 AND 3600)
  32. );
  33. CREATE INDEX IF NOT EXISTS idx_channel_monitors_enabled_last_checked
  34. ON channel_monitors (enabled, last_checked_at);
  35. CREATE INDEX IF NOT EXISTS idx_channel_monitors_provider
  36. ON channel_monitors (provider);
  37. CREATE INDEX IF NOT EXISTS idx_channel_monitors_group_name
  38. ON channel_monitors (group_name);
  39. CREATE TABLE IF NOT EXISTS channel_monitor_histories (
  40. id BIGSERIAL PRIMARY KEY,
  41. monitor_id BIGINT NOT NULL REFERENCES channel_monitors(id) ON DELETE CASCADE,
  42. model VARCHAR(200) NOT NULL,
  43. status VARCHAR(20) NOT NULL,
  44. latency_ms INT,
  45. ping_latency_ms INT,
  46. message VARCHAR(500) NOT NULL DEFAULT '',
  47. checked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  48. CONSTRAINT channel_monitor_histories_status_check
  49. CHECK (status IN ('operational', 'degraded', 'failed', 'error'))
  50. );
  51. CREATE INDEX IF NOT EXISTS idx_channel_monitor_histories_monitor_model_checked
  52. ON channel_monitor_histories (monitor_id, model, checked_at DESC);
  53. CREATE INDEX IF NOT EXISTS idx_channel_monitor_histories_checked_at
  54. ON channel_monitor_histories (checked_at);