From 98986b0eb777f6668cb171986c977cfd9e709454 Mon Sep 17 00:00:00 2001 From: admin Date: Wed, 6 May 2026 08:12:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E9=85=8D=E7=BD=AE=E5=92=8C=E4=BA=8B=E5=8A=A1=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 数据库配置: - SQLite 开启 WAL 模式,读写可并发 - 设置 busy_timeout=5000ms 避免锁超时 - 设置 synchronous=NORMAL 平衡性能和安全 - SQLite MaxOpenConns 设为 1 避免并发写入冲突 事务修复: - auth.go: 注册流程(用户+设备+会话)加入事务 - auth.go: 密码重置(修改密码+标记验证码)加入事务 - cards.go: 卡密使用加入事务,补充 UsedAt 字段 - cards.go: 代理商批量生成(扣款+生成卡密)加入事务 - extension.go: 充值(修改余额+记录)加入事务 - extension.go: 扣费(修改余额+记录)加入事务 - storage_config.go: 设置默认存储加入事务 --- backend/internal/database/database.go | 15 ++-- backend/internal/router/admin/cards.go | 62 ++++++++++++++-- .../internal/router/admin/storage_config.go | 70 +++++++++++++------ backend/internal/router/app/auth.go | 36 +++++++--- .../internal/router/extension/extension.go | 36 ++++++++-- 5 files changed, 175 insertions(+), 44 deletions(-) diff --git a/backend/internal/database/database.go b/backend/internal/database/database.go index dd3cef1..b58d482 100644 --- a/backend/internal/database/database.go +++ b/backend/internal/database/database.go @@ -59,7 +59,7 @@ func initSQLite() { dbPath := filepath.Join(dataDir, "verification_platform.db") var errOpen error - DB, errOpen = gorm.Open(sqlite.Open(dbPath), &gorm.Config{ + DB, errOpen = gorm.Open(sqlite.Open(dbPath+"?_journal_mode=WAL&_busy_timeout=5000&_synchronous=NORMAL"), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), DisableForeignKeyConstraintWhenMigrating: true, }) @@ -70,7 +70,7 @@ func initSQLite() { if sqlDB, err := DB.DB(); err == nil { sqlDB.SetMaxIdleConns(10) - sqlDB.SetMaxOpenConns(100) + sqlDB.SetMaxOpenConns(1) sqlDB.SetConnMaxLifetime(time.Hour) } @@ -132,7 +132,7 @@ func initAutoDetect() { dataDir := config.GetDataDir() dbPath := filepath.Join(dataDir, "verification_platform.db") - DB, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{ + DB, err = gorm.Open(sqlite.Open(dbPath+"?_journal_mode=WAL&_busy_timeout=5000&_synchronous=NORMAL"), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), DisableForeignKeyConstraintWhenMigrating: true, }) @@ -143,8 +143,13 @@ func initAutoDetect() { } if sqlDB, err := DB.DB(); err == nil { - sqlDB.SetMaxIdleConns(config.GetInt("database.max_idle_conns")) - sqlDB.SetMaxOpenConns(config.GetInt("database.max_open_conns")) + if config.GetString("database.type") == "sqlite" || config.GetString("database.type") == "" { + sqlDB.SetMaxIdleConns(10) + sqlDB.SetMaxOpenConns(1) + } else { + sqlDB.SetMaxIdleConns(config.GetInt("database.max_idle_conns")) + sqlDB.SetMaxOpenConns(config.GetInt("database.max_open_conns")) + } sqlDB.SetConnMaxLifetime(time.Hour) } diff --git a/backend/internal/router/admin/cards.go b/backend/internal/router/admin/cards.go index 9be8674..11da1a3 100644 --- a/backend/internal/router/admin/cards.go +++ b/backend/internal/router/admin/cards.go @@ -618,19 +618,36 @@ func handleUseCard(c *gin.Context) { return } + tx := database.DB.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + var card model.Card - if err := database.DB.Where("card_key = ?", req.CardKey).First(&card).Error; err != nil { + if err := tx.Where("card_key = ?", req.CardKey).First(&card).Error; err != nil { + tx.Rollback() response.Error(c, 404, "卡密不存在") return } if card.Status != "unused" { + tx.Rollback() response.Error(c, 400, "卡密已被使用或已禁用") return } card.Status = "used" - database.DB.Save(&card) + now := time.Now() + card.UsedAt = &now + if err := tx.Save(&card).Error; err != nil { + tx.Rollback() + response.Error(c, 500, "卡密使用失败") + return + } + + tx.Commit() response.Success(c, gin.H{ "message": "卡密使用成功", @@ -733,13 +750,50 @@ func handleBatchGenerateCards(c *gin.Context) { return } - if err := database.DB.Model(&agentUser).Update("balance", agentUser.Balance-totalCost).Error; err != nil { + tx := database.DB.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if err := tx.Model(&agentUser).Update("balance", agentUser.Balance-totalCost).Error; err != nil { + tx.Rollback() fmt.Printf("[DEBUG] Update balance error: %v\n", err) response.Error(c, 500, "扣款失败") return } - fmt.Printf("[DEBUG] Balance updated successfully\n") + cards := make([]model.Card, 0, req.Count) + for i := 0; i < req.Count; i++ { + cardKey := req.Prefix + utils.GenerateRandomString(req.Length) + card := model.Card{ + ApplicationID: req.ApplicationID, + CardTypeID: req.CardTypeID, + CardKey: cardKey, + CreatorID: effectiveUserID, + Status: "unused", + } + cards = append(cards, card) + } + + fmt.Printf("[DEBUG] Generated %d cards, saving to database...\n", len(cards)) + + if err := tx.Create(&cards).Error; err != nil { + tx.Rollback() + fmt.Printf("[DEBUG] Database create error: %v\n", err) + response.Error(c, 500, "生成卡密失败") + return + } + + tx.Commit() + fmt.Printf("[DEBUG] Successfully created %d cards\n", len(cards)) + + response.Success(c, gin.H{ + "message": "批量生成成功", + "count": req.Count, + }) + return } cards := make([]model.Card, 0, req.Count) diff --git a/backend/internal/router/admin/storage_config.go b/backend/internal/router/admin/storage_config.go index f75c287..409f6bb 100644 --- a/backend/internal/router/admin/storage_config.go +++ b/backend/internal/router/admin/storage_config.go @@ -60,30 +60,54 @@ func handleCreateStorageConfig(c *gin.Context) { return } + var config model.StorageConfig + if req.IsDefault { - database.DB.Model(&model.StorageConfig{}).Where("is_default = ?", true).Update("is_default", false) - } - - config := model.StorageConfig{ - Name: req.Name, - Type: req.Type, - Endpoint: req.Endpoint, - Bucket: req.Bucket, - AccessKey: req.AccessKey, - SecretKey: req.SecretKey, - Region: req.Region, - PathPrefix: req.PathPrefix, - IsDefault: req.IsDefault, - Status: "active", - Remark: req.Remark, - } - - if err := database.DB.Create(&config).Error; err != nil { - c.JSON(http.StatusInternalServerError, gin.H{ - "code": 500, - "message": "创建失败", - }) - return + tx := database.DB.Begin() + tx.Model(&model.StorageConfig{}).Where("is_default = ?", true).Update("is_default", false) + config = model.StorageConfig{ + Name: req.Name, + Type: req.Type, + Endpoint: req.Endpoint, + Bucket: req.Bucket, + AccessKey: req.AccessKey, + SecretKey: req.SecretKey, + Region: req.Region, + PathPrefix: req.PathPrefix, + IsDefault: req.IsDefault, + Status: "active", + Remark: req.Remark, + } + if err := tx.Create(&config).Error; err != nil { + tx.Rollback() + c.JSON(http.StatusInternalServerError, gin.H{ + "code": 500, + "message": "创建失败", + }) + return + } + tx.Commit() + } else { + config = model.StorageConfig{ + Name: req.Name, + Type: req.Type, + Endpoint: req.Endpoint, + Bucket: req.Bucket, + AccessKey: req.AccessKey, + SecretKey: req.SecretKey, + Region: req.Region, + PathPrefix: req.PathPrefix, + IsDefault: req.IsDefault, + Status: "active", + Remark: req.Remark, + } + if err := database.DB.Create(&config).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": 500, + "message": "创建失败", + }) + return + } } c.JSON(http.StatusOK, gin.H{ diff --git a/backend/internal/router/app/auth.go b/backend/internal/router/app/auth.go index ca545da..68b568c 100644 --- a/backend/internal/router/app/auth.go +++ b/backend/internal/router/app/auth.go @@ -313,7 +313,15 @@ func handleAppRegister(c *gin.Context) { } } - if err := database.DB.Create(&user).Error; err != nil { + tx := database.DB.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if err := tx.Create(&user).Error; err != nil { + tx.Rollback() log.Printf("[DEBUG] Failed to create user: %v", err) response.Error(c, 500, "注册失败") return @@ -330,7 +338,7 @@ func handleAppRegister(c *gin.Context) { } var device model.UserDevice - if err := database.DB.Where("user_id = ? AND application_id = ? AND device_id = ?", user.ID, appModel.ID, req.DeviceID).First(&device).Error; err != nil { + if err := tx.Where("user_id = ? AND application_id = ? AND device_id = ?", user.ID, appModel.ID, req.DeviceID).First(&device).Error; err != nil { device = model.UserDevice{ UserID: user.ID, ApplicationID: appModel.ID, @@ -339,7 +347,7 @@ func handleAppRegister(c *gin.Context) { DeviceType: deviceType, Status: "active", } - if err := database.DB.Create(&device).Error; err != nil { + if err := tx.Create(&device).Error; err != nil { log.Printf("[DEBUG] Failed to create device: %v", err) } else { log.Printf("[DEBUG] Successfully created device with ID: %d, type: %s", device.ID, deviceType) @@ -353,7 +361,7 @@ func handleAppRegister(c *gin.Context) { if device.ID > 0 { var session model.DeviceSession - sessionErr := database.DB.Where("device_id = ? AND instance_id = ?", device.ID, instanceID).First(&session).Error + sessionErr := tx.Where("device_id = ? AND instance_id = ?", device.ID, instanceID).First(&session).Error if sessionErr != nil { session = model.DeviceSession{ @@ -363,18 +371,20 @@ func handleAppRegister(c *gin.Context) { InstanceID: instanceID, LastHeartbeat: &now, } - if err := database.DB.Create(&session).Error; err != nil { + if err := tx.Create(&session).Error; err != nil { log.Printf("[DEBUG] Failed to create session: %v", err) } } else { session.LastHeartbeat = &now - if err := database.DB.Save(&session).Error; err != nil { + if err := tx.Save(&session).Error; err != nil { log.Printf("[DEBUG] Failed to update session: %v", err) } } } } + tx.Commit() + service.LogVerification(c, &appModel.ID, &user.ID, "register", "用户注册: "+req.Username, req.DeviceID, nil) response.SuccessWithMessage(c, "注册成功", gin.H{ @@ -903,14 +913,24 @@ func handleAppResetPassword(c *gin.Context) { return } + tx := database.DB.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + user.Password = string(hashedPassword) - if err := database.DB.Save(&user).Error; err != nil { + if err := tx.Save(&user).Error; err != nil { + tx.Rollback() response.Error(c, 500, "密码重置失败") return } verifyCode.Used = true - database.DB.Save(&verifyCode) + tx.Save(&verifyCode) + + tx.Commit() response.Success(c, gin.H{ "message": "密码重置成功", diff --git a/backend/internal/router/extension/extension.go b/backend/internal/router/extension/extension.go index 2d8b4f4..afdea70 100644 --- a/backend/internal/router/extension/extension.go +++ b/backend/internal/router/extension/extension.go @@ -272,7 +272,15 @@ func handleRechargeUser(c *gin.Context) { return } - if err := database.DB.Save(&user).Error; err != nil { + tx := database.DB.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if err := tx.Save(&user).Error; err != nil { + tx.Rollback() service.LogVerification(c, &app.ID, &user.ID, "extension_recharge_failed", fmt.Sprintf("扩展API充值失败: 保存失败 - %s", user.Username), "", err) response.Error(c, 500, "充值失败") return @@ -286,7 +294,13 @@ func handleRechargeUser(c *gin.Context) { PaymentType: "extension_api", Remark: req.Description, } - database.DB.Create(&record) + if err := tx.Create(&record).Error; err != nil { + tx.Rollback() + response.Error(c, 500, "充值失败") + return + } + + tx.Commit() service.LogVerification(c, &app.ID, &user.ID, "extension_recharge", fmt.Sprintf("扩展API充值: 用户%s, 类型:%s, 数量:%d", user.Username, req.Type, req.Amount), "", nil) @@ -355,7 +369,15 @@ func handleDeductUser(c *gin.Context) { return } - if err := database.DB.Save(&user).Error; err != nil { + tx := database.DB.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if err := tx.Save(&user).Error; err != nil { + tx.Rollback() service.LogVerification(c, &app.ID, &user.ID, "extension_deduct_failed", fmt.Sprintf("扩展API扣费失败: 保存失败 - %s", user.Username), "", err) response.Error(c, 500, "扣除失败") return @@ -370,7 +392,13 @@ func handleDeductUser(c *gin.Context) { PaymentType: "extension_api", Remark: req.Description, } - database.DB.Create(&record) + if err := tx.Create(&record).Error; err != nil { + tx.Rollback() + response.Error(c, 500, "扣除失败") + return + } + + tx.Commit() service.LogVerification(c, &app.ID, &user.ID, "extension_deduct", fmt.Sprintf("扩展API扣费: 用户%s, 类型:%s, 数量:%d", user.Username, req.Type, req.Amount), "", nil)