- README/CHANGELOG: add v1.0.7 entry\n- Server: JWT expiry validated server-side (401 codes)\n- Web: logout/redirect on 401; proxy forwards Authorization\n- Server: bill service uses repository consistently
158 lines
4.9 KiB
Go
158 lines
4.9 KiB
Go
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("<22> 适配器模式: %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
|
||
}
|