package config import ( "flag" "fmt" "os" "path/filepath" "gopkg.in/yaml.v3" ) // Config 服务配置 type Config struct { Version string // 应用版本 Port string // 服务端口 ProjectRoot string // 项目根目录 PythonPath string // Python 解释器路径 CleanScript string // 清理脚本路径 UploadDir string // 上传文件目录 OutputDir string // 输出文件目录 // Analyzer 服务配置 (HTTP 模式) AnalyzerURL string // Python 分析服务 URL AnalyzerMode string // 适配器模式: http 或 subprocess // MongoDB 配置 MongoURI string // MongoDB 连接 URI MongoDatabase string // 数据库名称 MongoRawCollection string // 原始数据集合名称 MongoCleanedCollection string // 清洗后数据集合名称 } // configFile YAML 配置文件结构 type configFile struct { Version string `yaml:"version"` Server struct { Port int `yaml:"port"` } `yaml:"server"` Python struct { Path string `yaml:"path"` Script string `yaml:"script"` } `yaml:"python"` Analyzer struct { URL string `yaml:"url"` Mode string `yaml:"mode"` // http 或 subprocess } `yaml:"analyzer"` Directories struct { Upload string `yaml:"upload"` Output string `yaml:"output"` } `yaml:"directories"` MongoDB struct { URI string `yaml:"uri"` Database string `yaml:"database"` Collections struct { Raw string `yaml:"raw"` Cleaned string `yaml:"cleaned"` } `yaml:"collections"` } `yaml:"mongodb"` } // Global 全局配置实例 var Global Config // getEnvOrDefault 获取环境变量,如果不存在则返回默认值 func getEnvOrDefault(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue } // getDefaultProjectRoot 获取默认项目根目录 func getDefaultProjectRoot() string { if root := os.Getenv("BILLAI_ROOT"); root != "" { return root } exe, err := os.Executable() if err == nil { exeDir := filepath.Dir(exe) if filepath.Base(exeDir) == "server" { return filepath.Dir(exeDir) } } cwd, _ := os.Getwd() if filepath.Base(cwd) == "server" { return filepath.Dir(cwd) } return cwd } // getDefaultPythonPath 获取默认 Python 路径 func getDefaultPythonPath() string { if python := os.Getenv("BILLAI_PYTHON"); python != "" { return python } return "analyzer/venv/bin/python" } // loadConfigFile 加载 YAML 配置文件 func loadConfigFile(configPath string) *configFile { data, err := os.ReadFile(configPath) if err != nil { return nil } var cfg configFile if err := yaml.Unmarshal(data, &cfg); err != nil { fmt.Printf("⚠️ 配置文件解析失败: %v\n", err) return nil } return &cfg } // Load 加载配置 func Load() { var configFilePath string flag.StringVar(&configFilePath, "config", "config.yaml", "配置文件路径") flag.Parse() // 设置默认值 Global.Version = "0.0.1" Global.Port = getEnvOrDefault("PORT", "8080") Global.ProjectRoot = getDefaultProjectRoot() Global.PythonPath = getDefaultPythonPath() Global.CleanScript = "analyzer/clean_bill.py" Global.UploadDir = "server/uploads" Global.OutputDir = "server/outputs" // Analyzer 默认值 Global.AnalyzerURL = getEnvOrDefault("ANALYZER_URL", "http://localhost:8001") Global.AnalyzerMode = getEnvOrDefault("ANALYZER_MODE", "http") // MongoDB 默认值 Global.MongoURI = getEnvOrDefault("MONGO_URI", "mongodb://localhost:27017") Global.MongoDatabase = getEnvOrDefault("MONGO_DATABASE", "billai") Global.MongoRawCollection = getEnvOrDefault("MONGO_RAW_COLLECTION", "bills_raw") Global.MongoCleanedCollection = getEnvOrDefault("MONGO_CLEANED_COLLECTION", "bills_cleaned") // 查找配置文件 configPath := configFilePath if !filepath.IsAbs(configPath) { if _, err := os.Stat(configPath); os.IsNotExist(err) { configPath = filepath.Join("server", configFilePath) } } // 加载配置文件 if cfg := loadConfigFile(configPath); cfg != nil { fmt.Printf("📄 加载配置文件: %s\n", configPath) if cfg.Version != "" { Global.Version = cfg.Version } if cfg.Server.Port > 0 { Global.Port = fmt.Sprintf("%d", cfg.Server.Port) } if cfg.Python.Path != "" { Global.PythonPath = cfg.Python.Path } if cfg.Python.Script != "" { Global.CleanScript = cfg.Python.Script } if cfg.Directories.Upload != "" { Global.UploadDir = cfg.Directories.Upload } if cfg.Directories.Output != "" { Global.OutputDir = cfg.Directories.Output } // Analyzer 配置 if cfg.Analyzer.URL != "" { Global.AnalyzerURL = cfg.Analyzer.URL } if cfg.Analyzer.Mode != "" { Global.AnalyzerMode = cfg.Analyzer.Mode } // MongoDB 配置 if cfg.MongoDB.URI != "" { Global.MongoURI = cfg.MongoDB.URI } if cfg.MongoDB.Database != "" { Global.MongoDatabase = cfg.MongoDB.Database } if cfg.MongoDB.Collections.Raw != "" { Global.MongoRawCollection = cfg.MongoDB.Collections.Raw } if cfg.MongoDB.Collections.Cleaned != "" { Global.MongoCleanedCollection = cfg.MongoDB.Collections.Cleaned } } // 环境变量覆盖 if port := os.Getenv("PORT"); port != "" { Global.Port = port } if python := os.Getenv("BILLAI_PYTHON"); python != "" { Global.PythonPath = python } if root := os.Getenv("BILLAI_ROOT"); root != "" { Global.ProjectRoot = root } // Analyzer 环境变量覆盖 if url := os.Getenv("ANALYZER_URL"); url != "" { Global.AnalyzerURL = url } if mode := os.Getenv("ANALYZER_MODE"); mode != "" { Global.AnalyzerMode = mode } // MongoDB 环境变量覆盖 if uri := os.Getenv("MONGO_URI"); uri != "" { Global.MongoURI = uri } if db := os.Getenv("MONGO_DATABASE"); db != "" { Global.MongoDatabase = db } if rawColl := os.Getenv("MONGO_RAW_COLLECTION"); rawColl != "" { Global.MongoRawCollection = rawColl } if cleanedColl := os.Getenv("MONGO_CLEANED_COLLECTION"); cleanedColl != "" { Global.MongoCleanedCollection = cleanedColl } } // ResolvePath 解析路径(相对路径转为绝对路径) func ResolvePath(path string) string { if filepath.IsAbs(path) { return path } return filepath.Join(Global.ProjectRoot, path) }