init
This commit is contained in:
89
serve/internal/database/database.go
Normal file
89
serve/internal/database/database.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/config"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
// InitDatabase 初始化数据库连接
|
||||
func InitDatabase() {
|
||||
cfg := config.GlobalConfig
|
||||
|
||||
// 构建DSN
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local&multiStatements=true",
|
||||
cfg.Database.User,
|
||||
cfg.Database.Password,
|
||||
cfg.Database.Host,
|
||||
cfg.Database.Port,
|
||||
cfg.Database.DBName,
|
||||
cfg.Database.Charset,
|
||||
)
|
||||
|
||||
// 配置GORM日志 - 使用自定义logger输出详细的SQL日志
|
||||
gormLogger := logger.New(
|
||||
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
|
||||
logger.Config{
|
||||
SlowThreshold: time.Second, // 慢SQL阈值
|
||||
LogLevel: logger.Info, // 日志级别:Info会显示所有SQL
|
||||
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound错误
|
||||
Colorful: true, // 彩色输出
|
||||
},
|
||||
)
|
||||
|
||||
// 连接数据库
|
||||
var err error
|
||||
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||
Logger: gormLogger,
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to database: %v", err)
|
||||
}
|
||||
|
||||
// 获取底层sql.DB对象进行连接池配置
|
||||
sqlDB, err := DB.DB()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get underlying sql.DB: %v", err)
|
||||
}
|
||||
|
||||
// 设置连接池参数
|
||||
sqlDB.SetMaxIdleConns(10) // 最大空闲连接数
|
||||
sqlDB.SetMaxOpenConns(100) // 最大打开连接数
|
||||
sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大生存时间
|
||||
sqlDB.SetConnMaxIdleTime(time.Minute * 30) // 连接最大空闲时间
|
||||
|
||||
// 测试连接
|
||||
if err := sqlDB.Ping(); err != nil {
|
||||
log.Fatalf("Failed to ping database: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Database connected successfully")
|
||||
}
|
||||
|
||||
// CloseDatabase 关闭数据库连接
|
||||
func CloseDatabase() {
|
||||
if DB != nil {
|
||||
sqlDB, err := DB.DB()
|
||||
if err != nil {
|
||||
log.Printf("Failed to get underlying sql.DB: %v", err)
|
||||
return
|
||||
}
|
||||
if err := sqlDB.Close(); err != nil {
|
||||
log.Printf("Failed to close database: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetDB 获取数据库实例
|
||||
func GetDB() *gorm.DB {
|
||||
return DB
|
||||
}
|
||||
172
serve/internal/database/migrate.go
Normal file
172
serve/internal/database/migrate.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AutoMigrate 自动迁移数据库表结构
|
||||
func AutoMigrate(db *gorm.DB) error {
|
||||
log.Println("开始数据库迁移...")
|
||||
|
||||
// 用户相关表
|
||||
err := db.AutoMigrate(
|
||||
&models.User{},
|
||||
&models.UserSocialLink{},
|
||||
&models.UserPreference{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 词汇相关表迁移由 SQL 脚本维护,避免与既有外键/类型冲突
|
||||
// (跳过 Vocabulary* / UserVocabularyProgress / VocabularyTest 的 AutoMigrate)
|
||||
|
||||
// 词汇书相关表(新增)
|
||||
err = db.AutoMigrate(
|
||||
&models.VocabularyBook{},
|
||||
&models.VocabularyBookWord{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 学习相关表
|
||||
err = db.AutoMigrate(
|
||||
&models.Notification{},
|
||||
&models.StudyPlan{},
|
||||
&models.StudyPlanRecord{},
|
||||
&models.ListeningMaterial{},
|
||||
&models.ListeningRecord{},
|
||||
&models.ReadingMaterial{},
|
||||
&models.ReadingRecord{},
|
||||
&models.WritingPrompt{},
|
||||
&models.WritingSubmission{},
|
||||
&models.SpeakingScenario{},
|
||||
&models.SpeakingRecord{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("数据库迁移完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateIndexes 创建额外的索引
|
||||
func CreateIndexes(db *gorm.DB) error {
|
||||
log.Println("开始创建索引...")
|
||||
|
||||
// 创建索引的辅助函数
|
||||
createIndexIfNotExists := func(indexName, tableName, columns string) {
|
||||
// 检查索引是否存在
|
||||
var count int64
|
||||
db.Raw("SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = ? AND index_name = ?", tableName, indexName).Scan(&count)
|
||||
if count == 0 {
|
||||
// 索引不存在,创建索引
|
||||
sql := "CREATE INDEX " + indexName + " ON " + tableName + "(" + columns + ")"
|
||||
result := db.Exec(sql)
|
||||
if result.Error != nil {
|
||||
log.Printf("创建索引 %s 失败: %v", indexName, result.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 用户表索引
|
||||
createIndexIfNotExists("idx_users_email_verified", "ai_users", "email_verified")
|
||||
createIndexIfNotExists("idx_users_status", "ai_users", "status")
|
||||
createIndexIfNotExists("idx_users_created_at", "ai_users", "created_at")
|
||||
|
||||
// 词汇表索引
|
||||
createIndexIfNotExists("idx_vocabulary_level", "ai_vocabulary", "level")
|
||||
createIndexIfNotExists("idx_vocabulary_frequency", "ai_vocabulary", "frequency")
|
||||
createIndexIfNotExists("idx_vocabulary_is_active", "ai_vocabulary", "is_active")
|
||||
|
||||
// 用户词汇进度索引
|
||||
createIndexIfNotExists("idx_user_vocabulary_progress_user_vocab", "ai_user_vocabulary_progress", "user_id, vocabulary_id")
|
||||
createIndexIfNotExists("idx_user_vocabulary_progress_mastery", "ai_user_vocabulary_progress", "mastery_level")
|
||||
createIndexIfNotExists("idx_user_vocabulary_progress_next_review", "ai_user_vocabulary_progress", "next_review_at")
|
||||
|
||||
// 学习记录索引
|
||||
createIndexIfNotExists("idx_listening_records_user_material", "ai_listening_records", "user_id, material_id")
|
||||
createIndexIfNotExists("idx_reading_records_user_material", "ai_reading_records", "user_id, material_id")
|
||||
createIndexIfNotExists("idx_writing_submissions_user_prompt", "ai_writing_submissions", "user_id, prompt_id")
|
||||
createIndexIfNotExists("idx_speaking_records_user_scenario", "ai_speaking_records", "user_id, scenario_id")
|
||||
|
||||
// 材料表索引
|
||||
createIndexIfNotExists("idx_listening_materials_level", "ai_listening_materials", "level")
|
||||
createIndexIfNotExists("idx_reading_materials_level", "ai_reading_materials", "level")
|
||||
createIndexIfNotExists("idx_writing_prompts_level", "ai_writing_prompts", "level")
|
||||
createIndexIfNotExists("idx_speaking_scenarios_level", "ai_speaking_scenarios", "level")
|
||||
|
||||
log.Println("索引创建完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyMergedSchemaIfNeeded 读取并执行合并后的SQL脚本,用于创建视图、触发器及扩展表
|
||||
func ApplyMergedSchemaIfNeeded(db *gorm.DB) error {
|
||||
// 检查一个扩展表是否存在,作为是否需要执行脚本的依据
|
||||
var count int64
|
||||
db.Raw("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'ai_vocabulary_books'").Scan(&count)
|
||||
if count > 0 {
|
||||
// 已存在扩展结构,跳过
|
||||
return nil
|
||||
}
|
||||
|
||||
// 读取脚本文件(从 serve 目录运行,脚本位于 ../docs/)
|
||||
candidates := []string{
|
||||
"../docs/database_schema_merged.sql",
|
||||
"../docs/database_schema.sql",
|
||||
}
|
||||
var content []byte
|
||||
var readErr error
|
||||
for _, p := range candidates {
|
||||
abs, _ := filepath.Abs(p)
|
||||
content, readErr = os.ReadFile(abs)
|
||||
if readErr == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if readErr != nil {
|
||||
log.Printf("读取数据库脚本失败: %v", readErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
sql := string(content)
|
||||
// 移除 DELIMITER 指令并将触发器结束符 // 转为 ;
|
||||
lines := strings.Split(sql, "\n")
|
||||
var cleaned []string
|
||||
for _, line := range lines {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmed, "DELIMITER") {
|
||||
continue
|
||||
}
|
||||
// 将以 // 结尾的行替换为 ;
|
||||
if strings.HasSuffix(trimmed, "//") {
|
||||
cleaned = append(cleaned, strings.TrimSuffix(line, "//")+";")
|
||||
continue
|
||||
}
|
||||
cleaned = append(cleaned, line)
|
||||
}
|
||||
cleanedSQL := strings.Join(cleaned, "\n")
|
||||
|
||||
// 关闭外键检查以避免初始化时的顺序问题
|
||||
if err := db.Exec("SET FOREIGN_KEY_CHECKS=0;").Error; err != nil {
|
||||
log.Printf("关闭外键检查失败: %v", err)
|
||||
}
|
||||
// 执行脚本(依赖 multiStatements=true)
|
||||
if err := db.Exec(cleanedSQL).Error; err != nil {
|
||||
log.Printf("执行合并SQL失败: %v", err)
|
||||
}
|
||||
// 恢复外键检查
|
||||
if err := db.Exec("SET FOREIGN_KEY_CHECKS=1;").Error; err != nil {
|
||||
log.Printf("开启外键检查失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
553
serve/internal/database/seed.go
Normal file
553
serve/internal/database/seed.go
Normal file
@@ -0,0 +1,553 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/models"
|
||||
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// stringPtr 返回字符串指针
|
||||
func stringPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
// SeedData 初始化种子数据
|
||||
func SeedData(db *gorm.DB) error {
|
||||
log.Println("开始初始化种子数据...")
|
||||
|
||||
// 检查是否已有数据
|
||||
var userCount int64
|
||||
db.Model(&models.User{}).Count(&userCount)
|
||||
if userCount > 0 {
|
||||
log.Println("数据库已有数据,跳过种子数据初始化")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 创建词汇分类
|
||||
if err := createVocabularyCategories(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建示例词汇
|
||||
if err := createSampleVocabularies(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建测试用户
|
||||
if err := createTestUsers(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建听力材料
|
||||
if err := createListeningMaterials(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建阅读材料
|
||||
if err := createReadingMaterials(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建写作提示
|
||||
if err := createWritingPrompts(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建口语场景
|
||||
if err := createSpeakingScenarios(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("种子数据初始化完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// createVocabularyCategories 创建词汇分类
|
||||
func createVocabularyCategories(db *gorm.DB) error {
|
||||
categories := []models.VocabularyCategory{
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Name: "日常生活",
|
||||
Description: stringPtr("日常生活中常用的词汇"),
|
||||
Level: "beginner",
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Name: "商务英语",
|
||||
Description: stringPtr("商务场景中使用的专业词汇"),
|
||||
Level: "intermediate",
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Name: "学术英语",
|
||||
Description: stringPtr("学术研究和论文写作中的词汇"),
|
||||
Level: "advanced",
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Name: "旅游英语",
|
||||
Description: stringPtr("旅游出行相关的实用词汇"),
|
||||
Level: "beginner",
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Name: "科技英语",
|
||||
Description: stringPtr("科技和互联网相关词汇"),
|
||||
Level: "intermediate",
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, category := range categories {
|
||||
if err := db.Create(&category).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("词汇分类创建完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// createSampleVocabularies 创建示例词汇
|
||||
func createSampleVocabularies(db *gorm.DB) error {
|
||||
// 获取第一个分类ID
|
||||
var category models.VocabularyCategory
|
||||
if err := db.First(&category).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vocabularies := []struct {
|
||||
Word string
|
||||
Phonetic string
|
||||
Level string
|
||||
Frequency int
|
||||
Definitions []models.VocabularyDefinition
|
||||
Examples []models.VocabularyExample
|
||||
}{
|
||||
{
|
||||
Word: "hello",
|
||||
Phonetic: "/həˈloʊ/",
|
||||
Level: "beginner",
|
||||
Frequency: 100,
|
||||
Definitions: []models.VocabularyDefinition{
|
||||
{
|
||||
PartOfSpeech: "interjection",
|
||||
Definition: "used as a greeting or to begin a phone conversation",
|
||||
Translation: "你好",
|
||||
SortOrder: 0,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
Examples: []models.VocabularyExample{
|
||||
{
|
||||
Example: "Hello, how are you?",
|
||||
Translation: "你好,你好吗?",
|
||||
SortOrder: 0,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Word: "world",
|
||||
Phonetic: "/wɜːrld/",
|
||||
Level: "beginner",
|
||||
Frequency: 95,
|
||||
Definitions: []models.VocabularyDefinition{
|
||||
{
|
||||
PartOfSpeech: "noun",
|
||||
Definition: "the earth, together with all of its countries and peoples",
|
||||
Translation: "世界",
|
||||
SortOrder: 0,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
Examples: []models.VocabularyExample{
|
||||
{
|
||||
Example: "The world is a beautiful place.",
|
||||
Translation: "世界是一个美丽的地方。",
|
||||
SortOrder: 0,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Word: "learn",
|
||||
Phonetic: "/lɜːrn/",
|
||||
Level: "beginner",
|
||||
Frequency: 90,
|
||||
Definitions: []models.VocabularyDefinition{
|
||||
{
|
||||
PartOfSpeech: "verb",
|
||||
Definition: "acquire knowledge of or skill in something",
|
||||
Translation: "学习",
|
||||
SortOrder: 0,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
Examples: []models.VocabularyExample{
|
||||
{
|
||||
Example: "I want to learn English.",
|
||||
Translation: "我想学习英语。",
|
||||
SortOrder: 0,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Word: "study",
|
||||
Phonetic: "/ˈstʌdi/",
|
||||
Level: "beginner",
|
||||
Frequency: 85,
|
||||
Definitions: []models.VocabularyDefinition{
|
||||
{
|
||||
PartOfSpeech: "verb",
|
||||
Definition: "devote time and attention to acquiring knowledge",
|
||||
Translation: "学习,研究",
|
||||
SortOrder: 0,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
Examples: []models.VocabularyExample{
|
||||
{
|
||||
Example: "She studies hard every day.",
|
||||
Translation: "她每天都努力学习。",
|
||||
SortOrder: 0,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Word: "practice",
|
||||
Phonetic: "/ˈpræktɪs/",
|
||||
Level: "intermediate",
|
||||
Frequency: 80,
|
||||
Definitions: []models.VocabularyDefinition{
|
||||
{
|
||||
PartOfSpeech: "verb",
|
||||
Definition: "perform an activity repeatedly to improve one's skill",
|
||||
Translation: "练习",
|
||||
SortOrder: 0,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
Examples: []models.VocabularyExample{
|
||||
{
|
||||
Example: "Practice makes perfect.",
|
||||
Translation: "熟能生巧。",
|
||||
SortOrder: 0,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, vocabData := range vocabularies {
|
||||
// 检查词汇是否已存在
|
||||
var existingVocab models.Vocabulary
|
||||
if err := db.Where("word = ?", vocabData.Word).First(&existingVocab).Error; err == nil {
|
||||
// 词汇已存在,跳过
|
||||
log.Printf("词汇 '%s' 已存在,跳过创建", vocabData.Word)
|
||||
continue
|
||||
}
|
||||
|
||||
// 创建词汇
|
||||
vocab := models.Vocabulary{
|
||||
Word: vocabData.Word,
|
||||
Phonetic: &vocabData.Phonetic,
|
||||
Level: vocabData.Level,
|
||||
Frequency: vocabData.Frequency,
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := db.Create(&vocab).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 关联分类
|
||||
if err := db.Model(&vocab).Association("Categories").Append(&category); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建定义
|
||||
for _, def := range vocabData.Definitions {
|
||||
def.VocabularyID = vocab.ID
|
||||
if err := db.Create(&def).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 创建例句
|
||||
for _, example := range vocabData.Examples {
|
||||
example.VocabularyID = vocab.ID
|
||||
if err := db.Create(&example).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("示例词汇创建完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// createTestUsers 创建测试用户
|
||||
func createTestUsers(db *gorm.DB) error {
|
||||
users := []models.User{
|
||||
{
|
||||
ID: 0, // 让数据库自动生成
|
||||
Username: "testuser",
|
||||
Email: "test@example.com",
|
||||
PasswordHash: "$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi", // password
|
||||
Nickname: stringPtr("测试用户"),
|
||||
Avatar: stringPtr("https://via.placeholder.com/150"),
|
||||
Gender: stringPtr("other"),
|
||||
BirthDate: nil,
|
||||
Location: stringPtr("北京"),
|
||||
Bio: stringPtr("这是一个测试用户"),
|
||||
EmailVerified: true,
|
||||
Status: "active",
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: 0, // 让数据库自动生成
|
||||
Username: "demo",
|
||||
Email: "demo@example.com",
|
||||
PasswordHash: "$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi", // password
|
||||
Nickname: stringPtr("演示用户"),
|
||||
Avatar: stringPtr("https://via.placeholder.com/150"),
|
||||
Gender: stringPtr("other"),
|
||||
BirthDate: nil,
|
||||
Location: stringPtr("上海"),
|
||||
Bio: stringPtr("这是一个演示用户"),
|
||||
EmailVerified: true,
|
||||
Status: "active",
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
if err := db.Create(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建用户偏好设置
|
||||
preference := models.UserPreference{
|
||||
ID: 0, // 让数据库自动生成
|
||||
UserID: user.ID,
|
||||
DailyGoal: 30,
|
||||
WeeklyGoal: 210,
|
||||
ReminderEnabled: true,
|
||||
ReminderTime: stringPtr("09:00:00"),
|
||||
DifficultyLevel: "intermediate",
|
||||
LearningMode: "casual",
|
||||
PreferredTopics: stringPtr("[\"vocabulary\", \"listening\"]"),
|
||||
NotificationSettings: stringPtr("{\"email\": true, \"push\": true}"),
|
||||
PrivacySettings: stringPtr("{\"profile_public\": false}"),
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
if err := db.Create(&preference).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("测试用户创建完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// createListeningMaterials 创建听力材料
|
||||
func createListeningMaterials(db *gorm.DB) error {
|
||||
materials := []models.ListeningMaterial{
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Title: "Daily Conversation",
|
||||
Description: stringPtr("Basic daily conversation practice"),
|
||||
Level: "beginner",
|
||||
Duration: 180, // 3 minutes
|
||||
AudioURL: "https://example.com/audio/daily-conversation.mp3",
|
||||
Transcript: stringPtr("A: Hello, how are you today? B: I'm fine, thank you. How about you?"),
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Title: "Business Meeting",
|
||||
Description: stringPtr("Business meeting discussion"),
|
||||
Level: "intermediate",
|
||||
Duration: 300, // 5 minutes
|
||||
AudioURL: "https://example.com/audio/business-meeting.mp3",
|
||||
Transcript: stringPtr("Let's discuss the quarterly report and our future plans."),
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, material := range materials {
|
||||
if err := db.Create(&material).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("听力材料创建完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// intPtr 返回整数指针
|
||||
func intPtr(i int) *int {
|
||||
return &i
|
||||
}
|
||||
|
||||
// createReadingMaterials 创建阅读材料
|
||||
func createReadingMaterials(db *gorm.DB) error {
|
||||
materials := []models.ReadingMaterial{
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Title: "The Benefits of Reading",
|
||||
Content: "Reading is one of the most beneficial activities for the human mind. It improves vocabulary, enhances critical thinking, and provides entertainment.",
|
||||
Level: "beginner",
|
||||
WordCount: 25,
|
||||
|
||||
Summary: stringPtr("这是一篇关于阅读益处的文章"),
|
||||
Source: stringPtr("Education Weekly"),
|
||||
Author: stringPtr("Reading Expert"),
|
||||
Tags: stringPtr("[\"reading\", \"education\"]"),
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Title: "Climate Change and Technology",
|
||||
Content: "Climate change represents one of the most pressing challenges of our time. Technology plays a crucial role in both contributing to and solving environmental problems.",
|
||||
Level: "intermediate",
|
||||
WordCount: 30,
|
||||
|
||||
Summary: stringPtr("这是一篇关于气候变化与科技的文章"),
|
||||
Source: stringPtr("Science Today"),
|
||||
Author: stringPtr("Climate Researcher"),
|
||||
Tags: stringPtr("[\"climate\", \"technology\"]"),
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, material := range materials {
|
||||
if err := db.Create(&material).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("阅读材料创建完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// createWritingPrompts 创建写作提示
|
||||
func createWritingPrompts(db *gorm.DB) error {
|
||||
prompts := []models.WritingPrompt{
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Title: "My Daily Routine",
|
||||
Prompt: "Describe your daily routine from morning to evening. Include what you do, when you do it, and why.",
|
||||
Level: "beginner",
|
||||
MinWords: intPtr(100),
|
||||
MaxWords: intPtr(200),
|
||||
TimeLimit: intPtr(1800), // 30 minutes
|
||||
Instructions: stringPtr("Write a clear and descriptive essay"),
|
||||
Tags: stringPtr("[\"daily\", \"routine\"]"),
|
||||
SampleAnswer: stringPtr("Every morning, I wake up at 7 AM and start my day..."),
|
||||
Rubric: stringPtr("{\"grammar\": 25, \"vocabulary\": 25, \"coherence\": 25, \"content\": 25}"),
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Title: "The Impact of Social Media",
|
||||
Prompt: "Discuss the positive and negative impacts of social media on modern society. Provide specific examples and your personal opinion.",
|
||||
Level: "intermediate",
|
||||
MinWords: intPtr(250),
|
||||
MaxWords: intPtr(400),
|
||||
TimeLimit: intPtr(2700), // 45 minutes
|
||||
Instructions: stringPtr("Write a balanced argumentative essay"),
|
||||
Tags: stringPtr("[\"social media\", \"society\"]"),
|
||||
SampleAnswer: stringPtr("Social media has transformed how we communicate..."),
|
||||
Rubric: stringPtr("{\"grammar\": 30, \"vocabulary\": 20, \"coherence\": 25, \"content\": 25}"),
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, prompt := range prompts {
|
||||
if err := db.Create(&prompt).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("写作提示创建完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// createSpeakingScenarios 创建口语场景
|
||||
func createSpeakingScenarios(db *gorm.DB) error {
|
||||
scenarios := []models.SpeakingScenario{
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Title: "Restaurant Ordering",
|
||||
Description: "You are at a restaurant and want to order food. The waiter will take your order.",
|
||||
Level: "beginner",
|
||||
Context: stringPtr("You are at a restaurant with friends"),
|
||||
Tags: stringPtr("[\"restaurant\", \"food\"]"),
|
||||
Dialogue: stringPtr("[{\"speaker\": \"waiter\", \"text\": \"Good evening, welcome to our restaurant!\"}]"),
|
||||
KeyPhrases: stringPtr("[\"I'd like to order\", \"Could I have\", \"The bill, please\"]"),
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: utils.GenerateUUID(),
|
||||
Title: "Job Interview",
|
||||
Description: "You are in a job interview for a position you really want. Answer the interviewer's questions confidently.",
|
||||
Level: "intermediate",
|
||||
Context: stringPtr("You are applying for a job"),
|
||||
Tags: stringPtr("[\"interview\", \"job\"]"),
|
||||
Dialogue: stringPtr("[{\"speaker\": \"interviewer\", \"text\": \"Tell me about yourself\"}]"),
|
||||
KeyPhrases: stringPtr("[\"I have experience in\", \"My strengths are\", \"I'm interested in\"]"),
|
||||
IsActive: true,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
if err := db.Create(&scenario).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("口语场景创建完成")
|
||||
return nil
|
||||
}
|
||||
56
serve/internal/database/user_repository.go
Normal file
56
serve/internal/database/user_repository.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/model"
|
||||
)
|
||||
|
||||
type UserRepository struct {
|
||||
// 实际项目中这里会有数据库连接
|
||||
}
|
||||
|
||||
func NewUserRepository() *UserRepository {
|
||||
return &UserRepository{}
|
||||
}
|
||||
|
||||
// 创建用户
|
||||
func (r *UserRepository) Create(user *model.User) (string, error) {
|
||||
// 实际项目中这里会执行数据库插入操作
|
||||
// 模拟生成用户ID
|
||||
user.ID = "user-123"
|
||||
return user.ID, nil
|
||||
}
|
||||
|
||||
// 根据ID获取用户
|
||||
func (r *UserRepository) GetByID(id string) (*model.User, error) {
|
||||
// 实际项目中这里会执行数据库查询操作
|
||||
if id == "user-123" {
|
||||
return &model.User{
|
||||
ID: id,
|
||||
Username: "testuser",
|
||||
Email: "test@example.com",
|
||||
Avatar: "",
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.New("用户不存在")
|
||||
}
|
||||
|
||||
// 根据邮箱获取用户
|
||||
func (r *UserRepository) GetByEmail(email string) (*model.User, error) {
|
||||
// 实际项目中这里会执行数据库查询操作
|
||||
if email == "test@example.com" {
|
||||
return &model.User{
|
||||
ID: "user-123",
|
||||
Username: "testuser",
|
||||
Email: email,
|
||||
Password: "password123", // 实际项目中密码应该是加密的
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.New("用户不存在")
|
||||
}
|
||||
|
||||
// 更新用户信息
|
||||
func (r *UserRepository) Update(user *model.User) error {
|
||||
// 实际项目中这里会执行数据库更新操作
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user