Skip to content

批量修改

提示

当前方法不提供项目级简化。

go
type batchUpdateStruct[T] struct {
   IDs     []string `json:"ids"`
   Data    T        `json:"data"`
}

func ModifyUser(c *gin.Context) {
   core := cosy.Core[model.User](c).SetValidRules(gin.H{
      "gender": "omitempty",
      "bio": "omitempty",
      // ... 其他字段
   })
   BatchModify()
}

提示

由于安全原因,允许批量修改的字段需要在字段的 cosy Tag 中添加 batch 指令。

go
package model

type User struct {
    Model

    Name       string     `json:"name" cosy:"add:required;update:omitempty;list:fussy"`
    Password   string     `json:"-" cosy:"json:password;add:required;update:omitempty"` // hide password
    Email      string     `json:"email" cosy:"add:required;update:omitempty;list:fussy" gorm:"type:varchar(255);uniqueIndex"`
    Phone      string     `json:"phone" cosy:"add:required;update:omitempty;list:fussy" gorm:"index"`
    Avatar     string     `json:"avatar" cosy:"all:omitempty"`
    LastActive *time.Time `json:"last_active"`
    Power      int        `json:"power" cosy:"add:oneof=1 1000;update:omitempty,oneof=1 1000;list:in;batch" gorm:"default:1"`
    Status     int        `json:"status" cosy:"add:oneof=1 2 3;update:omitempty,oneof=1 2 3;list:in;batch" gorm:"default:1"`
}

生命周期

  1. 客户端提交 Json,经过 Validator 验证并过滤暂存在 ctx.Payload 中,他是一个 gin.H 类型
  2. BeforeDecode (Hook)
  3. 使用 mapstructure 将 ctx.Payload 映射到 batchUpdateStruct[T]
  4. BeforeExecute (Hook)
  5. 执行创建操作
  6. Executed (Hook)
  7. 返回响应
update

修改接口类似,我们提供三个钩子,分别是 BeforeDecodeHookBeforeExecuteHookExecutedHook

钩子名称ctx.OriginModelctx.Modelctx.Payload
BeforeDecodeHook空结构体空结构体客户端提交的数据
BeforeExecuteHook空结构体准备更新的数据客户端提交的数据
ExecutedHook空结构体准备更新的数据客户端提交的数据

事务支持

如果需要在批量更新过程中使用事务,可以使用 WithTransaction 方法:

go
func BatchModifyUser(c *gin.Context) {
   core := cosy.Core[model.User](c).
      SetValidRules(gin.H{
         "status": "omitempty,oneof=1 2 3",
         "power": "omitempty,oneof=1 1000",
         // ... 其他字段
      }).
      WithTransaction()

   core.BeforeExecuteHook(validateBatchUpdate).BatchModify()
}

使用事务后,如果在任何一个钩子中出现错误或者调用了 Abort 方法,事务将自动回滚。

在钩子函数中,你可以通过 c.Tx 获取事务对象(*gorm.DB),用于在同一事务中执行其他数据库操作:

go
func logBatchUpdate(ctx *cosy.Ctx[model.User]) {
   // 使用 ctx.Tx 执行其他数据库操作,这些操作将在同一事务中进行

   // 记录批量更新操作日志
   for _, id := range ctx.BatchEffectedIDs {
      log := model.OperationLog{
         TargetID: id,
         Action: "batch_update",
         Status: ctx.Model.Status,
      }

      err := ctx.Tx.Create(&log).Error
      if err != nil {
         ctx.AbortWithError(err) // 如果出错,中止并回滚事务
         return
      }
   }
}

中止操作

在某些情况下,你可能需要中止批量更新操作,例如在业务逻辑验证失败时。可以使用 Abort 方法来中止操作:

go
func validateBatchUpdate(ctx *cosy.Ctx[model.User]) {
   // 业务逻辑验证
   if len(ctx.BatchEffectedIDs) > 100 {
      ctx.Abort()
      ctx.JSON(http.StatusBadRequest, gin.H{"error": "一次最多只能修改100条记录"})
   }
}

func BatchModifyUser(c *gin.Context) {
   core := cosy.Core[model.User](c).
      SetValidRules(gin.H{
         "status": "omitempty,oneof=1 2 3",
         // ... 其他字段
      }).
      WithTransaction()

   core.BeforeExecuteHook(validateBatchUpdate).BatchModify()
}

如果使用了事务,调用 Abort 方法将会自动回滚事务。如果需要中止操作并返回特定错误,可以使用 AbortWithError 方法:

go
func validateBatchUpdate(ctx *cosy.Ctx[model.User]) {
   // 业务逻辑验证
   if len(ctx.BatchEffectedIDs) > 100 {
      ctx.AbortWithError(errors.New("一次最多只能修改100条记录"))
   }
}

字段保护

Cosy 会自动过滤掉 ValidRules 中不存在的字段,并且数据库更新时只会使用过滤后的字段列表作为限制条件, 如果你在 BeforeExecuteHook 中修改了 ctx.Model 的字段,但这些字段不在 ValidRules 中,那么这些字段将不会被更新。

如果需要更新这些字段,请在 BeforeExecuteHook 中使用

go
ctx.AddSelectedFields(fields ...string)

如需获取选定的字段,请在 BeforeExecuteHook 中使用

go
ctx.GetSelectedFields() string

BatchEffectedIDs

前端传递的需要修改的 ID 列表,可以在 BeforeExecuteHook 和 ExecutedHook 中使用。

go
ctx.BatchEffectedIDs []uint64