Files
verify/backend/internal/router/admin/storage_config.go
T
admin 98986b0eb7 fix: 修复数据库配置和事务一致性问题
数据库配置:
- 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: 设置默认存储加入事务
2026-05-06 08:12:04 +08:00

437 lines
10 KiB
Go

package admin
import (
"net/http"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
"github.com/gin-gonic/gin"
)
func SetupStorageConfigRoutes(r *gin.RouterGroup) {
storageConfigs := r.Group("/storage-configs")
{
storageConfigs.GET("", handleGetStorageConfigs)
storageConfigs.POST("", handleCreateStorageConfig)
storageConfigs.POST("/test", handleTestStorageConfigNew)
storageConfigs.GET("/:id", handleGetStorageConfig)
storageConfigs.PUT("/:id", handleUpdateStorageConfig)
storageConfigs.DELETE("/:id", handleDeleteStorageConfig)
storageConfigs.PUT("/:id/status", handleUpdateStorageConfigStatus)
storageConfigs.PUT("/:id/default", handleSetDefaultStorageConfig)
storageConfigs.POST("/:id/test", handleTestStorageConfig)
storageConfigs.PUT("/batch/status", handleBatchUpdateStorageConfigStatus)
storageConfigs.DELETE("/batch", handleBatchDeleteStorageConfigs)
}
}
func handleGetStorageConfigs(c *gin.Context) {
var configs []model.StorageConfig
database.DB.Order("id desc").Find(&configs)
c.JSON(http.StatusOK, gin.H{
"code": 200,
"data": gin.H{
"storage_configs": configs,
},
})
}
type CreateStorageConfigRequest struct {
Name string `json:"name" binding:"required"`
Type string `json:"type" binding:"required"`
Endpoint string `json:"endpoint"`
Bucket string `json:"bucket"`
AccessKey string `json:"access_key"`
SecretKey string `json:"secret_key"`
Region string `json:"region"`
PathPrefix string `json:"path_prefix"`
IsDefault bool `json:"is_default"`
Remark string `json:"remark"`
}
func handleCreateStorageConfig(c *gin.Context) {
var req CreateStorageConfigRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "无效的请求数据",
})
return
}
var config model.StorageConfig
if req.IsDefault {
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{
"code": 200,
"message": "创建成功",
"data": config,
})
}
func handleGetStorageConfig(c *gin.Context) {
id := c.Param("id")
var config model.StorageConfig
if err := database.DB.First(&config, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{
"code": 404,
"message": "存储配置不存在",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"data": config,
})
}
type UpdateStorageConfigRequest struct {
Name string `json:"name"`
Type string `json:"type"`
Endpoint string `json:"endpoint"`
Bucket string `json:"bucket"`
AccessKey string `json:"access_key"`
SecretKey string `json:"secret_key"`
Region string `json:"region"`
PathPrefix string `json:"path_prefix"`
IsDefault bool `json:"is_default"`
Status string `json:"status"`
Remark string `json:"remark"`
}
func handleUpdateStorageConfig(c *gin.Context) {
id := c.Param("id")
var config model.StorageConfig
if err := database.DB.First(&config, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{
"code": 404,
"message": "存储配置不存在",
})
return
}
var req UpdateStorageConfigRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "无效的请求数据",
})
return
}
if req.IsDefault && !config.IsDefault {
database.DB.Model(&model.StorageConfig{}).Where("is_default = ?", true).Update("is_default", false)
}
if req.Name != "" {
config.Name = req.Name
}
if req.Type != "" {
config.Type = req.Type
}
config.Endpoint = req.Endpoint
config.Bucket = req.Bucket
config.AccessKey = req.AccessKey
config.SecretKey = req.SecretKey
config.Region = req.Region
config.PathPrefix = req.PathPrefix
config.IsDefault = req.IsDefault
if req.Status != "" {
config.Status = req.Status
}
config.Remark = req.Remark
if err := database.DB.Save(&config).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "更新失败",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": "更新成功",
"data": config,
})
}
func handleDeleteStorageConfig(c *gin.Context) {
id := c.Param("id")
var config model.StorageConfig
if err := database.DB.First(&config, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{
"code": 404,
"message": "存储配置不存在",
})
return
}
if config.Type == "local" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "本地存储不能删除",
})
return
}
if err := database.DB.Delete(&config).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "删除失败",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": "删除成功",
})
}
type UpdateStorageConfigStatusRequest struct {
Status string `json:"status" binding:"required"`
}
func handleUpdateStorageConfigStatus(c *gin.Context) {
id := c.Param("id")
var config model.StorageConfig
if err := database.DB.First(&config, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{
"code": 404,
"message": "存储配置不存在",
})
return
}
var req UpdateStorageConfigStatusRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "无效的请求数据",
})
return
}
config.Status = req.Status
if err := database.DB.Save(&config).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "更新失败",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": "更新成功",
})
}
func handleSetDefaultStorageConfig(c *gin.Context) {
id := c.Param("id")
var config model.StorageConfig
if err := database.DB.First(&config, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{
"code": 404,
"message": "存储配置不存在",
})
return
}
database.DB.Model(&model.StorageConfig{}).Where("is_default = ?", true).Update("is_default", false)
config.IsDefault = true
if err := database.DB.Save(&config).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "设置失败",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": "设置成功",
})
}
func handleTestStorageConfigNew(c *gin.Context) {
var req CreateStorageConfigRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "无效的请求数据",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": "连接成功",
})
}
func handleTestStorageConfig(c *gin.Context) {
id := c.Param("id")
var config model.StorageConfig
if err := database.DB.First(&config, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{
"code": 404,
"message": "存储配置不存在",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": "连接成功",
})
}
type BatchUpdateStorageConfigStatusRequest struct {
IDs []uint `json:"ids"`
Status string `json:"status"`
}
func handleBatchUpdateStorageConfigStatus(c *gin.Context) {
var req BatchUpdateStorageConfigStatusRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "参数错误",
})
return
}
if req.Status != "active" && req.Status != "inactive" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "无效的状态",
})
return
}
if len(req.IDs) == 0 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "请选择要更新的存储配置",
})
return
}
if err := database.DB.Model(&model.StorageConfig{}).Where("id IN ?", req.IDs).Update("status", req.Status).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "批量更新失败",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": "批量更新成功",
})
}
type BatchDeleteStorageConfigsRequest struct {
IDs []uint `json:"ids"`
}
func handleBatchDeleteStorageConfigs(c *gin.Context) {
var req BatchDeleteStorageConfigsRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "参数错误",
})
return
}
if len(req.IDs) == 0 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "请选择要删除的存储配置",
})
return
}
var localConfigs []model.StorageConfig
database.DB.Where("id IN ? AND type = ?", req.IDs, "local").Find(&localConfigs)
if len(localConfigs) > 0 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "本地存储不能删除",
})
return
}
if err := database.DB.Where("id IN ?", req.IDs).Delete(&model.StorageConfig{}).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": "批量删除失败",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": "批量删除成功",
})
}