Files
EchoChat/backend/go-service/pkg/db/mysql.go
2026-05-18 21:23:00 +08:00

97 lines
3.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package db 提供数据库连接管理MySQL 二连接实例,用于外部 LLM 配置中心)
package db
import (
"context"
"fmt"
"time"
"github.com/echochat/backend/config"
"github.com/echochat/backend/pkg/logs"
"go.uber.org/zap"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// LLMSourceDB 包装外部 LLM 配置库连接
//
// 之所以用 wrapper 而不是直接返回 *gorm.DB
// - 主库 (*gorm.DB) 在 Wire 中已被全局注入到所有 DAO不能让 LLM 库被误用为业务库
// - Enabled=false 时仍要构造一个空 wrapper避免下游 nil 引用
// - 下游 DAO 通过 IsEnabled() 决定是否走数据库路径或返回未配置错误
type LLMSourceDB struct {
db *gorm.DB
enabled bool
}
// IsEnabled 是否真正连接了 MySQLfalse 时禁止访问 DB()
func (l *LLMSourceDB) IsEnabled() bool {
return l != nil && l.enabled && l.db != nil
}
// DB 获取底层 GORM 实例(仅 IsEnabled() == true 时返回非 nil
// 调用方应先判 IsEnabled再使用返回值。
func (l *LLMSourceDB) DB() *gorm.DB {
if !l.IsEnabled() {
return nil
}
return l.db
}
// NewLLMSourceDB 初始化外部 LLM 配置 MySQL 连接
//
// 行为约定:
// - cfg.Enabled == false跳过连接返回 enabled=false 的空 wrapper不报错保证 Wire 流程不阻塞
// - cfg.Enabled == true 但连接失败:返回 error由 main 决定是否硬失败
//
// 之所以连接失败时硬报错而不是 graceful degrade
// - 用户已显式 enabled=true预期就要用沉默降级反而难排查
// - 转写功能未启用时只需保持 enabled=false 即可
func NewLLMSourceDB(cfg *config.LLMSourceConfig) (*LLMSourceDB, error) {
funcName := "db.NewLLMSourceDB"
ctx := context.Background()
if cfg == nil || !cfg.Enabled {
logs.Info(ctx, funcName, "外部 LLM 配置库未启用,跳过 MySQL 连接(语音转写功能将不可用)")
return &LLMSourceDB{enabled: false}, nil
}
logs.Info(ctx, funcName, "正在连接外部 LLM 配置 MySQL",
zap.String("host", cfg.Host),
zap.Int("port", cfg.Port),
zap.String("dbname", cfg.DBName),
)
gdb, err := gorm.Open(mysql.Open(cfg.DSN()), &gorm.Config{
Logger: &zapGormLogger{},
})
if err != nil {
logs.Error(ctx, funcName, "MySQL 连接失败", zap.Error(err))
return nil, fmt.Errorf("连接 LLM 配置 MySQL 失败: %w", err)
}
sqlDB, err := gdb.DB()
if err != nil {
return nil, fmt.Errorf("获取 MySQL 底层 sql.DB 失败: %w", err)
}
maxIdle := cfg.MaxIdleConns
if maxIdle <= 0 {
maxIdle = 5
}
maxOpen := cfg.MaxOpenConns
if maxOpen <= 0 {
maxOpen = 20
}
sqlDB.SetMaxIdleConns(maxIdle)
sqlDB.SetMaxOpenConns(maxOpen)
sqlDB.SetConnMaxLifetime(time.Hour)
if err := sqlDB.Ping(); err != nil {
logs.Error(ctx, funcName, "MySQL Ping 失败", zap.Error(err))
return nil, fmt.Errorf("ping LLM 配置 MySQL 失败: %w", err)
}
logs.Info(ctx, funcName, "外部 LLM 配置 MySQL 连接成功")
return &LLMSourceDB{db: gdb, enabled: true}, nil
}