package main import ( "fmt" "os" "os/signal" "syscall" "github.com/gin-gonic/gin" "billai-server/adapter" adapterHttp "billai-server/adapter/http" "billai-server/adapter/python" "billai-server/config" "billai-server/repository" repoMongo "billai-server/repository/mongo" "billai-server/router" ) func main() { // 加载配置 config.Load() // 解析路径 uploadDirAbs := config.ResolvePath(config.Global.UploadDir) outputDirAbs := config.ResolvePath(config.Global.OutputDir) pythonPathAbs := config.ResolvePath(config.Global.PythonPath) // 确保目录存在 os.MkdirAll(uploadDirAbs, 0755) os.MkdirAll(outputDirAbs, 0755) // 打印配置信息 printBanner(pythonPathAbs, uploadDirAbs, outputDirAbs) // 检查 Python 是否存在 if _, err := os.Stat(pythonPathAbs); os.IsNotExist(err) { fmt.Printf("⚠️ 警告: Python 路径不存在: %s\n", pythonPathAbs) fmt.Println(" 请在配置文件中指定正确的 Python 路径") } // 初始化适配器(外部服务交互层) initAdapters() // 初始化数据层 repo, err := initRepository() if err != nil { fmt.Printf("⚠️ 警告: 数据层初始化失败: %v\n", err) fmt.Println(" 账单数据将不会存储到数据库") os.Exit(1) } defer repo.Disconnect() // 创建路由 r := gin.Default() // 注册路由 router.Setup(r, router.Config{ OutputDir: outputDirAbs, Version: config.Global.Version, }) // 监听系统信号 go func() { quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit fmt.Println("\n🛑 正在关闭服务...") repo.Disconnect() os.Exit(0) }() // 启动服务 printAPIInfo() r.Run(":" + config.Global.Port) } // printBanner 打印启动横幅 func printBanner(pythonPath, uploadDir, outputDir string) { fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") fmt.Println("📦 BillAI 账单分析服务") fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") fmt.Printf("📁 项目根目录: %s\n", config.Global.ProjectRoot) fmt.Printf("� 适配器模式: %s\n", config.Global.AnalyzerMode) if config.Global.AnalyzerMode == "http" { fmt.Printf("🌐 分析服务: %s\n", config.Global.AnalyzerURL) } else { fmt.Printf("🐍 Python路径: %s\n", pythonPath) } fmt.Printf("📂 上传目录: %s\n", uploadDir) fmt.Printf("📂 输出目录: %s\n", outputDir) fmt.Printf("🍃 MongoDB: %s/%s\n", config.Global.MongoURI, config.Global.MongoDatabase) fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") } // printAPIInfo 打印 API 信息 func printAPIInfo() { fmt.Printf("\n🚀 服务已启动: http://localhost:%s\n", config.Global.Port) fmt.Println("📝 API 接口:") fmt.Println(" POST /api/upload - 上传并分析账单") fmt.Println(" GET /api/bills - 获取账单列表(支持分页和时间筛选)") fmt.Println(" GET /api/review - 获取需要复核的记录") fmt.Println(" GET /download/* - 下载结果文件") fmt.Println(" GET /health - 健康检查") fmt.Println() } // initAdapters 初始化适配器(外部服务交互层) // 在这里配置与外部系统的交互方式 // 支持两种模式: http (推荐) 和 subprocess func initAdapters() { var cleaner adapter.Cleaner switch config.Global.AnalyzerMode { case "http": // 使用 HTTP API 调用 Python 服务(推荐) httpCleaner := adapterHttp.NewCleaner(config.Global.AnalyzerURL) // 检查服务健康状态 if err := httpCleaner.HealthCheck(); err != nil { fmt.Printf("⚠️ 警告: Python 分析服务不可用 (%s): %v\n", config.Global.AnalyzerURL, err) fmt.Println(" 请确保分析服务已启动: cd analyzer && python server.py") } else { fmt.Printf("🌐 已连接到分析服务: %s\n", config.Global.AnalyzerURL) } cleaner = httpCleaner case "subprocess": // 使用子进程调用 Python 脚本(传统模式) pythonCleaner := python.NewCleaner() fmt.Println("🐍 使用子进程模式调用 Python") cleaner = pythonCleaner default: // 默认使用 HTTP 模式 cleaner = adapterHttp.NewCleaner(config.Global.AnalyzerURL) fmt.Printf("🌐 使用 HTTP 模式 (默认): %s\n", config.Global.AnalyzerURL) } adapter.SetCleaner(cleaner) fmt.Println("🔌 适配器初始化完成") } // initRepository 初始化数据存储层 // 在这里配置数据持久化方式 // 后续可以通过修改这里来切换不同的存储实现(如 PostgreSQL、MySQL 等) func initRepository() (repository.BillRepository, error) { // 初始化 MongoDB 存储 mongoRepo := repoMongo.NewRepository() if err := mongoRepo.Connect(); err != nil { return nil, err } repository.SetRepository(mongoRepo) fmt.Println("💾 数据层初始化完成") return mongoRepo, nil }