Files
pj0235-eai_agentplatform/eai_agentplatform/backend-go/internal/config/config.go
T
eaiadminandClaude Code 593323a934 refactor: 专员目录收敛为 6 个,重整 AI 路由与系统管理页
一次性提交当天全部改动(110 个文件)。

**未按 TOP_CODING_RULES.md G14.5「一次提交只装一件事」拆分** —— 用户明确要求
单一提交,此处如实记录,不静默忽略该冲突。

提交前验证:后端 go build / go vet / go test ./... 全绿,前端 npm run build
exit 0,/api/health 与管理员 login 均返回 200。

- 专员目录收敛为 6 个:下线 knowledge-operations / process-coordination /
  presentation-briefing / report-generation 四个专员(前后端 manifest 与 seed
  同步删除),新增 general-assistant。专员的归属关系(拥有哪些技能定义、
  哪些目录项、提示词与绑定从哪来)改由 specialists/core 的 ownership.go、
  prompt_provider.go、binding_provider.go 统一提供,seed 与 admin_handlers
  随之内置化,卸载路径统一走 uninstall.go。
- 技能:补齐 text-to-speech 的前端 manifest(后端包在 HEAD 已存在),
  skillcore/ownership.go 提供与专员对称的归属查询。
- AI 路由:ai_config.json 由 OpenRouter/Ollama 切到 LMUAI / SiliconFlow /
  llama.cpp 本地路由,ai_secrets.example.json 与部署 env 样例同步新增
  SILICONFLOW_API_KEY。
- 系统管理页重整:新增 AiAdminPage、OrganizationManagementPage,删除
  AdminOverviewPage、CompanyConfigPage,SystemConfigPage 精简,nav / router /
  config/workbench.js 同步调整。依 G05.5,开发阶段直接收口到新结构,不留旧路由。
- 新增 internal/objectrefs:统一统计对象(专员 / 技能 / xapp)的运行时引用
  (被多少 xapp、项目、任务引用),供管理页做删除前的影响面判断。
- 公众号创作专员:新增 OfficialAccountSpecialistPanel,workflow 与投递链路调整。
- XApp:考试 / 培训 Shell 扩展,XAppDirectoryPage 与 xappDefinition 同步。
- 文档:新增 GW01–GW05 工作台演进系列与 AR12 对话驱动与结构化交互架构;
  同步 AR05 / SY17 / SY23 / SY25 / PL04;TOP_CODING_RULES.md 增补 G05.5。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 09:01:28 +08:00

146 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package config
import (
"os"
"path/filepath"
"strconv"
"strings"
)
// Config 全局配置,从环境变量读取(带默认值)。
// system_config 表中的值在运行时覆盖对应字段(见 service)。
type Config struct {
Port string
DBPath string // SQLite 文件路径
JWTSecret string
JWTExpireMin int
LLMBaseURL string
LLMAPIKey string
LLMModel string
EmbedModel string
AssetRootDir string
KBDataDir string
KnowledgeSourceDir string
TrainingMaterialsDir string
// 定期备份。SQLite 是单文件、又没有任何外部主从,坏一份就是全丢,
// 所以默认开着:启动补一次,之后每 BackupIntervalHours 小时一次,保留最近 N 份。
BackupEnabled bool
BackupDir string
BackupKeep int
BackupIntervalHours int
FileMaxDoc int64
FileMaxVideo int64
ChunkThreshold int64
LibreOfficeBin string
PdftotextBin string
KingdeeBaseURL string
KingdeeAccountID string
KingdeeUsername string
KingdeePassword string
KingdeeLCID int
KingdeeTimeoutSec int
}
// Load 读取环境变量构造配置。缺失的用默认值(部署时通过 .env / systemd 注入)。
func Load() *Config {
baseDir := backendBaseDir()
assetRootDir := getenv("ASSET_ROOT_DIR", filepath.Join(appRootDir(baseDir), "assets"))
return &Config{
Port: getenv("PORT", "8080"),
DBPath: getenv("DB_PATH", filepath.Join(baseDir, "data", "eai_agentplatform.db")),
JWTSecret: getenv("JWT_SECRET", "change-this-to-a-strong-secret-in-production"),
JWTExpireMin: getenvInt("JWT_EXPIRE_MINUTES", 480),
LLMBaseURL: getenv("LLM_BASE_URL", "http://127.0.0.1:8080/v1"),
LLMAPIKey: getenv("LLM_API_KEY", "local"),
LLMModel: getenv("LLM_MODEL", "Qwen3.5-4B-Q4_K_M.gguf"),
EmbedModel: getenv("EMBED_MODEL", "bge-m3"),
AssetRootDir: assetRootDir,
KBDataDir: getenv("KB_DATA_DIR", filepath.Join(baseDir, "data", "kb_data")),
KnowledgeSourceDir: getenv("KNOWLEDGE_SOURCE_DIR", filepath.Join(assetRootDir, "knowledge", "source")),
TrainingMaterialsDir: getenv("TRAINING_MATERIALS_DIR", filepath.Join(assetRootDir, "training", "materials")),
// 备份目录必须落在 data/ 下:systemd 单元是 ProtectSystem=strict,
// 只有 data/ 可写(ReadWritePaths),换到别处会静默失败。
BackupEnabled: getenvBool("BACKUP_ENABLED", true),
BackupDir: getenv("BACKUP_DIR", filepath.Join(baseDir, "data", "backups")),
BackupKeep: getenvInt("BACKUP_KEEP", 7),
BackupIntervalHours: getenvInt("BACKUP_INTERVAL_HOURS", 24),
FileMaxDoc: getenvInt64("FILE_MAX_SIZE_DOC", 209715200),
FileMaxVideo: getenvInt64("FILE_MAX_SIZE_VIDEO", 2147483648),
ChunkThreshold: getenvInt64("CHUNK_THRESHOLD", 104857600),
LibreOfficeBin: getenv("LIBREOFFICE_BIN", "libreoffice"),
PdftotextBin: getenv("PDFTOTEXT_BIN", "pdftotext"),
KingdeeBaseURL: getenv("KINGDEE_BASE_URL", ""),
KingdeeAccountID: getenv("KINGDEE_ACCOUNT_ID", ""),
KingdeeUsername: getenv("KINGDEE_USERNAME", ""),
KingdeePassword: getenv("KINGDEE_PASSWORD", ""),
KingdeeLCID: getenvInt("KINGDEE_LCID", 2052),
KingdeeTimeoutSec: getenvInt("KINGDEE_TIMEOUT_SEC", 15),
}
}
func backendBaseDir() string {
if exe, err := os.Executable(); err == nil && exe != "" {
return filepath.Clean(filepath.Join(filepath.Dir(exe), ".."))
}
if wd, err := os.Getwd(); err == nil && wd != "" {
return wd
}
return "."
}
func appRootDir(baseDir string) string {
return filepath.Clean(filepath.Join(baseDir, ".."))
}
func getenv(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}
// getenvBool 只认显式的假值,其余(含没设)都算真 —— 默认开着的东西,
// 不该因为写了个看不懂的值就悄悄关掉。
func getenvBool(key string, def bool) bool {
v := strings.ToLower(strings.TrimSpace(os.Getenv(key)))
switch v {
case "":
return def
case "0", "false", "no", "off":
return false
default:
return true
}
}
func getenvInt(key string, def int) int {
if v := os.Getenv(key); v != "" {
if n, err := strconv.Atoi(v); err == nil {
return n
}
}
return def
}
func getenvInt64(key string, def int64) int64 {
if v := os.Getenv(key); v != "" {
if n, err := strconv.ParseInt(v, 10, 64); err == nil {
return n
}
}
return def
}