feat(handler,cmd): 实现阶段 5 HTTP 服务 — 处理器、入口程序、配置文件

This commit is contained in:
hhs
2026-06-10 15:18:06 +08:00
parent f532b21a7a
commit cdbaefecf8
7 changed files with 410 additions and 17 deletions

View File

@@ -1,5 +1,111 @@
package main
import (
"context"
"flag"
"log"
"os"
"ai-agent-scaffold-go/internal/config"
"ai-agent-scaffold-go/internal/handler"
"ai-agent-scaffold-go/internal/model"
"ai-agent-scaffold-go/internal/service"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
"go.uber.org/zap"
)
func main() {
// TODO: 实现启动逻辑
envPath := flag.String("env", ".env", "path to dotenv file (empty to skip)")
configPath := flag.String("config", "configs/application.yaml", "path to application.yaml")
flag.Parse()
// 1. 加载 .env 文件
loadDotenv(*envPath)
// 2. 加载应用配置
appCfg, err := config.LoadApplication(*configPath)
if err != nil {
log.Fatalf("load config: %v", err)
}
// 3. 初始化日志
logger, _ := zap.NewProduction()
if appCfg.App.Env == "local" || appCfg.App.Env == "dev" {
logger, _ = zap.NewDevelopment()
}
defer logger.Sync()
// 4. 组装 Agent
registry := model.NewInMemoryAgentRegistry()
sessions := model.NewInMemorySessionStore()
timeout, _ := appCfg.LLM.RequestTimeoutDuration()
agents, err := service.LoadAndAssemble(context.Background(), appCfg.Agent.ConfigPaths, timeout)
if err != nil {
logger.Fatal("assemble agents failed", zap.Error(err))
}
for _, agent := range agents {
if err := registry.Register(agent); err != nil {
logger.Fatal("register agent failed", zap.Error(err))
}
}
chatService := service.NewChatService(registry, sessions)
// 5. 配置 HTTP 路由
router := gin.Default()
router.Use(corsMiddleware())
router.GET("/healthz", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
handler.RegisterRoutes(router, chatService)
// 6. 启动服务
addr := appCfg.Server.Addr
logger.Info("server starting", zap.String("addr", addr), zap.Int("agents", len(agents)))
if err := router.Run(addr); err != nil {
logger.Fatal("server stopped", zap.Error(err))
}
}
// corsMiddleware 跨域中间件
func corsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
origin := c.GetHeader("Origin")
if origin != "" {
c.Header("Access-Control-Allow-Origin", origin)
c.Header("Vary", "Origin")
} else {
c.Header("Access-Control-Allow-Origin", "*")
}
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
c.Header("Access-Control-Allow-Credentials", "true")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
// loadDotenv 加载 dotenv 文件,已有环境变量不会被覆盖
func loadDotenv(path string) {
if path == "" {
return
}
if _, err := os.Stat(path); os.IsNotExist(err) {
return
}
pairs, err := godotenv.Read(path)
if err != nil {
log.Fatalf("read dotenv: %v", err)
}
for key, value := range pairs {
if _, exists := os.LookupEnv(key); !exists {
os.Setenv(key, value)
}
}
}