feat: 更新 Server 后端服务

- 更新 agent handler 和 service 层
- 新增 chat_group handler 和 service
- 删除废弃的 chat_handler
- 更新 tool 相关处理
- 更新 API 文档和依赖

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 21:26:27 +08:00
parent 237ab9f6d7
commit 71e8cc59d5
24 changed files with 5007 additions and 347 deletions

View File

@@ -3,7 +3,10 @@ package config
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/glebarez/sqlite"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
@@ -11,15 +14,31 @@ import (
"github.com/spf13/viper"
)
// 获取项目根目录
func getProjectRoot() string {
// 从当前工作目录向上查找 .env 文件
dir, _ := os.Getwd()
for i := 0; i < 5; i++ {
if _, err := os.Stat(filepath.Join(dir, ".env")); err == nil {
return dir
}
dir = filepath.Dir(dir)
}
// 默认返回当前目录
return "."
}
type Config struct {
Port string
JWTSecret string
DatabaseType string // 数据库类型: mysql 或 sqlite
DatabaseHost string
DatabasePort string
DatabaseUser string
DatabasePassword string
DatabaseName string
DatabaseURL string // 拼接后的完整连接字符串
SQLitePath string // SQLite 数据库文件路径
PythonServiceURL string
AICoreServiceAddr string // AI-Core gRPC 服务地址,如 "localhost:50051"
// 文件上传配置
@@ -36,23 +55,22 @@ type Config struct {
}
func Load() *Config {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("./config")
viper.AddConfigPath("../config")
viper.AddConfigPath("../../config")
// 重新初始化 viper避免之前的状态影响
viper.Reset()
// 默认值
// 第一步:设置默认值
viper.SetDefault("port", "8080")
viper.SetDefault("jwt_secret", "your-secret-key-change-in-production")
viper.SetDefault("python_service_url", "http://localhost:8081")
viper.SetDefault("ai_core_service_addr", "localhost:50051")
// 数据库默认配置
viper.SetDefault("database_type", "mysql")
viper.SetDefault("database_host", "localhost")
viper.SetDefault("database_port", "3306")
viper.SetDefault("database_user", "root")
viper.SetDefault("database_password", "root")
viper.SetDefault("database_name", "x_agents")
viper.SetDefault("sqlite_path", "./data/x_agents.db")
// 文件上传默认配置
viper.SetDefault("upload_mode", "local")
viper.SetDefault("upload_local_path", "resource/files")
@@ -64,30 +82,84 @@ func Load() *Config {
viper.SetDefault("minio_bucket", "x-agents")
viper.SetDefault("minio_use_ssl", false)
if err := viper.ReadInConfig(); err != nil {
log.Printf("Using default config: %v", err)
// 第二步:读取 config.yaml优先级低
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("./config")
viper.AddConfigPath("../config")
viper.AddConfigPath("../../config")
_ = viper.MergeInConfig() // 忽略错误,可能没有 config.yaml
// 第三步:读取 .env 文件(优先级最高)
projectRoot := getProjectRoot()
log.Printf("Project root: %s", projectRoot)
viper.SetConfigName(".env")
viper.SetConfigType("env")
viper.AddConfigPath(projectRoot) // 项目根目录 (X-Agents)
viper.AddConfigPath(".") // 当前目录
viper.AddConfigPath("..") // 父目录
viper.AddConfigPath("../..") // 上两级目录
viper.SetEnvPrefix("GO") // 环境变量前缀 GO_xxx (仅对环境变量生效)
viper.AutomaticEnv()
_ = viper.MergeInConfig() // 忽略错误,可能没有 .env
// 处理 .env 文件中的键名(去掉 GO_ 前缀映射)
envToConfig := map[string]string{
"GO_PORT": "port",
"GO_DATABASE_TYPE": "database_type",
"GO_DATABASE_HOST": "database_host",
"GO_DATABASE_PORT": "database_port",
"GO_DATABASE_NAME": "database_name",
"GO_DATABASE_USER": "database_user",
"GO_DATABASE_PASSWORD": "database_password",
"GO_SQLITE_PATH": "sqlite_path",
}
for envKey, configKey := range envToConfig {
if val := viper.GetString(envKey); val != "" {
viper.Set(configKey, val)
}
}
// 拼接数据库连接字符串
dbHost := viper.GetString("database_host")
dbPort := viper.GetString("database_port")
dbUser := viper.GetString("database_user")
dbPassword := viper.GetString("database_password")
dbName := viper.GetString("database_name")
databaseURL := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
dbUser, dbPassword, dbHost, dbPort, dbName)
log.Printf("Loaded config: database_type=%s, port=%s",
viper.GetString("database_type"), viper.GetString("port"))
// 获取数据库类型
dbType := viper.GetString("database_type")
var databaseURL string
if dbType == "sqlite" {
sqlitePath := viper.GetString("sqlite_path")
// 确保 SQLite 数据目录存在 (跨平台处理)
dir := filepath.Dir(sqlitePath)
if dir != "." && dir != "" {
os.MkdirAll(dir, 0755)
}
databaseURL = sqlitePath
} else {
// MySQL 连接字符串
dbHost := viper.GetString("database_host")
dbPort := viper.GetString("database_port")
dbUser := viper.GetString("database_user")
dbPassword := viper.GetString("database_password")
dbName := viper.GetString("database_name")
databaseURL = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
dbUser, dbPassword, dbHost, dbPort, dbName)
}
return &Config{
Port: viper.GetString("port"),
JWTSecret: viper.GetString("jwt_secret"),
DatabaseType: dbType,
DatabaseURL: databaseURL,
DatabaseHost: dbHost,
DatabasePort: dbPort,
DatabaseUser: dbUser,
DatabasePassword: dbPassword,
DatabaseName: dbName,
DatabaseHost: viper.GetString("database_host"),
DatabasePort: viper.GetString("database_port"),
DatabaseUser: viper.GetString("database_user"),
DatabasePassword: viper.GetString("database_password"),
DatabaseName: viper.GetString("database_name"),
SQLitePath: viper.GetString("sqlite_path"),
PythonServiceURL: viper.GetString("python_service_url"),
AICoreServiceAddr: viper.GetString("ai_core_service_addr"),
AICoreServiceAddr: viper.GetString("ai_core_service_addr"),
// 文件上传配置
UploadMode: viper.GetString("upload_mode"),
UploadLocalPath: viper.GetString("upload_local_path"),
@@ -108,9 +180,23 @@ func InitDB(cfg *Config) (*gorm.DB, error) {
return nil, fmt.Errorf("database URL is empty")
}
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
var db *gorm.DB
var err error
if cfg.DatabaseType == "sqlite" {
// SQLite 不需要创建目录逻辑,因为 Load 函数已经处理了
db, err = gorm.Open(sqlite.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
log.Printf("Using SQLite database: %s", dsn)
} else {
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
log.Printf("Using MySQL database: %s:%s/%s",
cfg.DatabaseHost, cfg.DatabasePort, cfg.DatabaseName)
}
if err != nil {
return nil, fmt.Errorf("failed to connect database: %w", err)
}