package database import ( "fmt" "log/slog" "silk-server-go/internal/config" "silk-server-go/internal/model" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" ) var DB *gorm.DB func Init(cfg *config.Config) error { db, err := gorm.Open(postgres.Open(cfg.PG), &gorm.Config{ Logger: logger.Default.LogMode(logger.Warn), }) if err != nil { return err } DB = db if err := RunMigrations(db); err != nil { return err } if err := CheckSchemaVersion(db, CurrentSchemaVersion); err != nil { return err } if cfg.AllowDevAutoMigrate && cfg.AppEnv != "production" { if err := db.AutoMigrate( &model.User{}, &model.Room{}, &model.Device{}, &model.Sensor{}, &model.Threshold{}, &model.Alarm{}, &model.Camera{}, &model.VideoClip{}, &model.AuditLog{}, &model.Telemetry{}, &model.Permission{}, &model.RolePermission{}, &model.Disease{}, &model.KnowledgeArticle{}, &model.InspectionRecord{}, &model.OutboxEvent{}, &model.Notification{}, &model.DetectionTask{}, &model.Sample{}, &model.DiseaseEvent{}, &model.Tray{}, &model.Batch{}, &model.RearingRecord{}, &model.WechatBinding{}, &model.WeatherAlert{}, &model.LampTest{}, &model.LampTestStep{}, &model.Consumable{}, &model.Consultation{}, &model.SpectrumEntry{}, &model.TraceRecord{}, ); err != nil { return fmt.Errorf("开发环境 AutoMigrate 失败: %w", err) } slog.Warn("开发环境 AutoMigrate 已启用,SQL 迁移仍是生产 schema 事实来源") } // 初始化权限种子数据 seedPermissions(db) // 初始化知识库种子数据 seedKnowledge(db) slog.Info("数据库连接成功,版本化迁移完成", "schemaVersion", CurrentSchemaVersion) return nil } // seedKnowledge 初始化知识库种子数据(幂等:按名称/类型+标题判重) func seedKnowledge(db *gorm.DB) { for _, d := range model.SeedDiseases { var cnt int64 db.Model(&model.Disease{}).Where("name = ?", d.Name).Count(&cnt) if cnt == 0 { if err := db.Create(&d).Error; err != nil { slog.Warn("写入病种种子失败", "name", d.Name, "err", err) } } } for _, a := range model.SeedKnowledgeArticles { var cnt int64 db.Model(&model.KnowledgeArticle{}).Where("kind = ? AND title = ?", a.Kind, a.Title).Count(&cnt) if cnt == 0 { if err := db.Create(&a).Error; err != nil { slog.Warn("写入知识文章种子失败", "title", a.Title, "err", err) } } } } // seedPermissions 初始化权限和角色-权限映射种子数据(幂等) func seedPermissions(db *gorm.DB) { // 1. 写入权限定义(已存在则跳过) codeToID := make(map[string]string) for _, p := range model.AllPermissions { var existing model.Permission if db.Where("code = ?", p.Code).First(&existing).Error == nil { codeToID[p.Code] = existing.ID continue } desc := p.Description perm := model.Permission{ Code: p.Code, Name: p.Name, Description: &desc, } if err := db.Create(&perm).Error; err != nil { slog.Warn("写入权限种子失败", "code", p.Code, "err", err) continue } codeToID[p.Code] = perm.ID } // 2. 写入角色-权限映射(已存在则跳过) for role, codes := range model.RolePermissionMap { for _, code := range codes { pid, ok := codeToID[code] if !ok { continue } var cnt int64 db.Model(&model.RolePermission{}). Where("role = ? AND permission_id = ?", role, pid). Count(&cnt) if cnt > 0 { continue } db.Create(&model.RolePermission{Role: role, PermissionID: pid}) } } }