初始化
This commit is contained in:
39
internal/database/db.go
Normal file
39
internal/database/db.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// Package database 负责数据库连接初始化与 GORM 实例管理
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
|
||||
"nl-game-api-gin/internal/config"
|
||||
)
|
||||
|
||||
// DB 全局数据库实例,Init 成功后可直接使用
|
||||
var DB *gorm.DB
|
||||
|
||||
// Init 连接 MySQL 并配置连接池
|
||||
func Init() error {
|
||||
db, err := gorm.Open(mysql.Open(config.C.DSN()), &gorm.Config{
|
||||
// 只打印慢查询与错误 SQL,避免日志刷屏
|
||||
Logger: logger.Default.LogMode(logger.Warn),
|
||||
// dev 自动建表时不生成物理外键约束,与 sql/init.sql 的手写表结构保持一致
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("连接 MySQL 失败: %w", err)
|
||||
}
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取底层连接池失败: %w", err)
|
||||
}
|
||||
// 连接池参数:空闲 10、最大 100、连接最长存活 1 小时
|
||||
sqlDB.SetMaxIdleConns(10)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||
DB = db
|
||||
return nil
|
||||
}
|
||||
68
internal/database/migrate.go
Normal file
68
internal/database/migrate.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// dev 环境自动建表(仅 go run 生效)
|
||||
//
|
||||
// 安全约定:表结构迁移只允许发生在开发模式(go run 启动)。
|
||||
// go build 打包出的二进制启动时绝不会执行任何迁移——生产库的结构
|
||||
// 变更一律通过 sql/ 目录下的脚本人工执行,防止 GORM 自动改表出问题。
|
||||
//
|
||||
// dev 下也只做最保守的一件事:补建"库里不存在的表"。
|
||||
// 已存在的表一律不动(不加列、不加索引),因为手写 SQL 的索引命名
|
||||
// (uk_username 等)与 GORM 默认命名不同,全量 AutoMigrate 会重复建索引。
|
||||
package database
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"nl-game-api-gin/internal/model"
|
||||
)
|
||||
|
||||
// IsDevRun 判断当前进程是否由 go run 启动:
|
||||
// go run 会先把临时二进制编译到 go-build 缓存目录(如 %TEMP%\go-buildXXXX)再运行,
|
||||
// 而 go build 的产物路径不含该特征,因此打包后的服务永远不会命中 dev 分支。
|
||||
func IsDevRun() bool {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(exe, "go-build")
|
||||
}
|
||||
|
||||
// AutoMigrateMissing 遍历全部 GORM 模型,为缺失的表建表(含索引),已存在的表跳过。
|
||||
// 新增 model 后在下方清单登记一行,dev 启动即可自动建表;种子数据仍需执行对应 SQL 脚本。
|
||||
func AutoMigrateMissing() error {
|
||||
models := []interface{ TableName() string }{
|
||||
// 用户与登录
|
||||
&model.User{}, &model.LoginLog{},
|
||||
// 游戏与对局记录
|
||||
&model.Game{}, &model.GameRecord{}, &model.BattleRecord{},
|
||||
// 积分与签到
|
||||
&model.PointRecord{}, &model.SignIn{},
|
||||
// 商城:购物车 / 订单 / 已购游戏 / 道具
|
||||
&model.Cart{}, &model.Order{}, &model.OrderItem{}, &model.UserGame{}, &model.Prop{}, &model.UserProp{},
|
||||
// 社交:好友 / 私聊
|
||||
&model.Friend{}, &model.ChatMessage{},
|
||||
// 主题与站点配置
|
||||
&model.Theme{}, &model.SiteConfig{},
|
||||
// VIP 等级
|
||||
&model.VipLevel{},
|
||||
// 关卡进度 / 云存档 / 皮肤
|
||||
&model.GameProgress{}, &model.GameSave{},
|
||||
&model.GameSkin{}, &model.UserSkin{},
|
||||
}
|
||||
created := 0
|
||||
for _, m := range models {
|
||||
if DB.Migrator().HasTable(m) {
|
||||
continue
|
||||
}
|
||||
if err := DB.Migrator().CreateTable(m); err != nil {
|
||||
return err
|
||||
}
|
||||
created++
|
||||
log.Printf("[dev迁移] 已创建缺失表 %s(种子数据请执行 sql/ 对应脚本)", m.TableName())
|
||||
}
|
||||
if created == 0 {
|
||||
log.Println("[dev迁移] 全部表已存在,无需创建")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user