// Package sysadmin 系统管理台(一期)薄聚合层:只通过小接口读取其他模块的 // 公开状态,业务包不得 import 本包。 package sysadmin import ( "context" "time" "chat-agent-system/internal/config" "github.com/jackc/pgx/v5/pgxpool" ) // StatusProvider 业务服务的数据库/schema 公开状态(*bizdev.Service 满足)。 type StatusProvider interface { DatabaseReady() bool SchemaVersion() int64 LiveSchemaVersion(ctx context.Context) int64 } // KBProber 知识库在线探针(*kb.Client 满足)。 type KBProber interface { Online() bool } // Options 装配根注入的依赖;全部为只读探针,聚合层不持有业务写路径。 type Options struct { Pool *pgxpool.Pool Store SettingsStore // 测试可直接注入;为空且 Pool 非空时使用 PG store Status StatusProvider KB KBProber TaskStoreReady func(ctx context.Context) bool FeatureValues func() map[string]string // 组合根解析后的功能开关生效值(key 为环境变量名) } // Service 系统管理台聚合服务。 type Service struct { cfg *config.Config store SettingsStore status StatusProvider kb KBProber taskStoreReady func(ctx context.Context) bool featureValues func() map[string]string specs []SettingSpec startedAt time.Time } // NewService 构建聚合服务;cfg 允许为 nil(测试)。 func NewService(cfg *config.Config, opts Options) *Service { store := opts.Store if store == nil && opts.Pool != nil { store = newPGSettingsStore(opts.Pool) } return &Service{ cfg: cfg, store: store, status: opts.Status, kb: opts.KB, taskStoreReady: opts.TaskStoreReady, featureValues: opts.FeatureValues, specs: defaultSettingSpecs(), startedAt: time.Now(), } } // Overview 系统总览,字段对齐前端 camelCase 契约。 type Overview struct { Status string `json:"status"` Env string `json:"env"` AgentBackend string `json:"agentBackend"` DatabaseReady bool `json:"databaseReady"` SchemaVersion int64 `json:"schemaVersion"` TaskStoreReady bool `json:"taskStoreReady"` KBOnline bool `json:"kbOnline"` UptimeSeconds int64 `json:"uptimeSeconds"` } // Overview 聚合 /health 同源信号。 func (s *Service) Overview(ctx context.Context) Overview { dbReady := s.status != nil && s.status.DatabaseReady() schemaVersion := int64(0) if s.status != nil { if dbReady { schemaVersion = s.status.LiveSchemaVersion(ctx) } else { schemaVersion = s.status.SchemaVersion() } } taskStoreReady := false if s.taskStoreReady != nil { taskStoreReady = s.taskStoreReady(ctx) } status := "ok" if s.cfg != nil && s.cfg.ContextDbHost != "" && !s.cfg.AllowInMemory && !dbReady { status = "degraded" } out := Overview{ Status: status, DatabaseReady: dbReady, SchemaVersion: schemaVersion, TaskStoreReady: taskStoreReady, KBOnline: s.kb != nil && s.kb.Online(), UptimeSeconds: int64(time.Since(s.startedAt).Seconds()), } if s.cfg != nil { out.Env = s.cfg.Env out.AgentBackend = s.cfg.AgentBackend } return out } // KBInfo 知识库连接视图;密码等敏感信息绝不输出。 type KBInfo struct { Online bool `json:"online"` Host string `json:"host"` Port int `json:"port"` Database string `json:"database"` User string `json:"user"` EmbeddingModel string `json:"embeddingModel"` RerankerEnabled bool `json:"rerankerEnabled"` SSLMode string `json:"sslMode"` } // KBInfo 返回 KB 连接与模型配置(脱敏)。 func (s *Service) KBInfo() KBInfo { out := KBInfo{ Online: s.kb != nil && s.kb.Online(), // KbDSN 固定 sslmode=disable,如实暴露 SSLMode: "disable", } if s.cfg != nil { out.Host = s.cfg.KbDbHost out.Port = s.cfg.KbDbPort out.Database = s.cfg.KbDbName out.User = s.cfg.KbDbUser out.EmbeddingModel = s.cfg.EmbeddingModel out.RerankerEnabled = s.cfg.RerankerEndpointURL != "" } return out }