create project

This commit is contained in:
2026-01-16 17:30:40 +08:00
commit effac6b017
157 changed files with 45997 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
package database
import (
"context"
"strings"
"time"
"gorm.io/gorm/logger"
)
// CustomLogger 自定义 GORM logger截断过长的 SQL 参数(如 base64 数据)
type CustomLogger struct {
logger.Interface
}
// NewCustomLogger 创建自定义 logger
func NewCustomLogger() logger.Interface {
return &CustomLogger{
Interface: logger.Default.LogMode(logger.Silent),
}
}
// Trace 重写 Trace 方法,禁用 SQL 日志输出
func (l *CustomLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
// 不输出任何 SQL 日志
// 如果需要调试,可以临时取消注释下面的代码
/*
sql, rows := fc()
sql = truncateLongValues(sql)
elapsed := time.Since(begin)
if err != nil {
l.Interface.Error(ctx, "SQL error: %v [%v] %s", err, elapsed, sql)
} else {
l.Interface.Info(ctx, "[%.3fms] [rows:%d] %s", float64(elapsed.Nanoseconds())/1e6, rows, sql)
}
*/
}
// truncateLongValues 截断 SQL 中的长字符串值
func truncateLongValues(sql string) string {
// 查找 base64 格式的数据 (data:image/...;base64,...)
if strings.Contains(sql, "data:image/") && strings.Contains(sql, ";base64,") {
parts := strings.Split(sql, "\"")
for i, part := range parts {
if strings.HasPrefix(part, "data:image/") && strings.Contains(part, ";base64,") {
if len(part) > 100 {
// 保留前50字符添加截断标记
parts[i] = part[:50] + "...[base64 data truncated]"
}
}
}
sql = strings.Join(parts, "\"")
}
// 截断其他过长的值
if len(sql) > 5000 {
// 查找 VALUES 或 SET 后的内容
if idx := strings.Index(sql, " VALUES "); idx > 0 && len(sql) > idx+5000 {
sql = sql[:idx+5000] + "...[truncated]"
} else if idx := strings.Index(sql, " SET "); idx > 0 && len(sql) > idx+3000 {
sql = sql[:idx+3000] + "...[truncated]"
} else if len(sql) > 5000 {
sql = sql[:5000] + "...[truncated]"
}
}
return sql
}
// Info 实现 Info 方法
func (l *CustomLogger) Info(ctx context.Context, msg string, data ...interface{}) {
l.Interface.Info(ctx, msg, data...)
}
// Warn 实现 Warn 方法
func (l *CustomLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
l.Interface.Warn(ctx, msg, data...)
}
// Error 实现 Error 方法
func (l *CustomLogger) Error(ctx context.Context, msg string, data ...interface{}) {
// 检查并截断 data 中的长字符串
truncatedData := make([]interface{}, len(data))
for i, d := range data {
if str, ok := d.(string); ok && len(str) > 200 {
if strings.HasPrefix(str, "data:image/") {
truncatedData[i] = str[:50] + "...[base64 data]"
} else {
truncatedData[i] = str[:200] + "..."
}
} else {
truncatedData[i] = d
}
}
l.Interface.Error(ctx, msg, truncatedData...)
}
// LogMode 实现 LogMode 方法
func (l *CustomLogger) LogMode(level logger.LogLevel) logger.Interface {
newLogger := *l
newLogger.Interface = l.Interface.LogMode(level)
return &newLogger
}

View File

@@ -0,0 +1,97 @@
package database
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/drama-generator/backend/domain/models"
"github.com/drama-generator/backend/pkg/config"
"gorm.io/driver/mysql"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
_ "modernc.org/sqlite"
)
func NewDatabase(cfg config.DatabaseConfig) (*gorm.DB, error) {
dsn := cfg.DSN()
if cfg.Type == "sqlite" {
dbDir := filepath.Dir(dsn)
if err := os.MkdirAll(dbDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create database directory: %w", err)
}
}
gormConfig := &gorm.Config{
Logger: NewCustomLogger(),
}
var db *gorm.DB
var err error
if cfg.Type == "sqlite" {
// 使用 modernc.org/sqlite 纯 Go 驱动(无需 CGO
// 添加并发优化参数WAL 模式、busy_timeout、cache
dsnWithParams := dsn + "?_journal_mode=WAL&_busy_timeout=5000&_synchronous=NORMAL&cache=shared"
db, err = gorm.Open(sqlite.Dialector{
DriverName: "sqlite",
DSN: dsnWithParams,
}, gormConfig)
} else {
db, err = gorm.Open(mysql.Open(dsn), gormConfig)
}
if err != nil {
return nil, fmt.Errorf("failed to connect to database: %w", err)
}
sqlDB, err := db.DB()
if err != nil {
return nil, fmt.Errorf("failed to get database instance: %w", err)
}
// SQLite 连接池配置(限制并发连接数)
if cfg.Type == "sqlite" {
sqlDB.SetMaxIdleConns(1)
sqlDB.SetMaxOpenConns(1) // SQLite 单写入,限制为 1
} else {
sqlDB.SetMaxIdleConns(cfg.MaxIdle)
sqlDB.SetMaxOpenConns(cfg.MaxOpen)
}
sqlDB.SetConnMaxLifetime(time.Hour)
if err := sqlDB.Ping(); err != nil {
return nil, fmt.Errorf("failed to ping database: %w", err)
}
return db, nil
}
func AutoMigrate(db *gorm.DB) error {
return db.AutoMigrate(
// 核心模型
&models.Drama{},
&models.Episode{},
&models.Character{},
&models.Scene{},
&models.Storyboard{},
// 生成相关
&models.ImageGeneration{},
&models.VideoGeneration{},
&models.VideoMerge{},
// AI配置
&models.AIServiceConfig{},
&models.AIServiceProvider{},
// 资源管理
&models.Asset{},
&models.CharacterLibrary{},
// 任务管理
&models.AsyncTask{},
)
}