Files
Agent-System/internal/sysadmin/service.go
T
weichen f1404d5bd2 feat(sysadmin): add system admin aggregation API and system_settings store
- new internal/sysadmin package (overview/kb/features/settings) with small
  port interfaces; business packages stay unaware of sysadmin
- new routes /api/v1/admin/system/{overview,kb,features,settings} guarded
  by requireSystemAdmin
- goose 00049: system_settings table (key/jsonb value, updated_by/at)
- SettingSpec whitelist registry with DB > env > default precedence
- register sysadmin in module-boundaries.yaml (archtest rules)
2026-09-17 06:56:46 +00:00

143 lines
4.1 KiB
Go

// 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
}