MisakaOJ项目,第四次记录内容
该接口按情况分,需要一次性彻底删除的(包括分类下的所有题目全部删除和所有子分类的父级id置零)的情况和只删除分类本身(如果分类下有东西就停止删除)的情况要分别处理。
提示
注意,gorm的ErrRecordNotFound
错误只会在传入的是单个结构体或者结构体指针才会触发,如果传入的是切片则不会触发该错误。该结论对First
,Last
,Find
等函数都有效。
另外,Find
,First
差距很小,First
可以视作按主键排序后再加一个Limit(1)
的Find
。
go// First finds the first record ordered by primary key, matching given conditions conds
func (db *DB) First(dest interface{}, conds ...interface{}) (tx *DB) {
tx = db.Limit(1).Order(clause.OrderByColumn{
Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},
})
if len(conds) > 0 {
if exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...); len(exprs) > 0 {
tx.Statement.AddClause(clause.Where{Exprs: exprs})
}
}
tx.Statement.RaiseErrorOnNotFound = true
tx.Statement.Dest = dest
return tx.callbacks.Query().Execute(tx)
}
提示
gorm对于启用了gorm.Model
的模型,所使用的都是软删除。即删除时并不真正删除数据,而是在delete_at字段下留下删除时间。如果要真的将某条记录删除,需要这样:
goerr := DB.Unscoped().Delete(&Question{}, "identity = ?", identity).Error
if err != nil {
// 处理错误
}
Handler层:
go// RemoveCategory
// @Tags 管理员接口
// @Summary 删除分类信息
// @Param authorization header string true "authorization"
// @Param category_id query int true "category_id"
// @Param is_cascade query bool true "is_cascade"
// @Success 200 {data} json "{"code": "200", "message": ""}"
// @Router /admin/remove_category [delete]
func RemoveCategory(c *gin.Context) {
categoryId, e := strconv.Atoi(c.Query("category_id"))
if e != nil {
ErrorHandler(c, constants.ParameterParseErr.Error()+"category_id: "+e.Error())
return
}
if categoryId < 0 {
ErrorHandler(c, constants.ParameterParseErr.Error()+"category_id: less than 0")
return
}
// 把isCascade参数解析出来
var isCascade bool
isCascadeString := c.Query("is_cascade")
if isCascadeString == "" {
ErrorHandler(c, constants.ParameterMissingErr.Error()+"is_cascade")
return
}
if isCascadeString == "true" {
isCascade = true
} else if isCascadeString == "false" {
isCascade = false
} else {
ErrorHandler(c, constants.ParameterParseErr.Error()+"is_cascade")
return
}
// 先把要改的Category查出来
deleteCategory := &models.Category{}
tx := models.GetCategoryByColumn("id", categoryId)
e = tx.First(deleteCategory).Error
if e != nil {
if errors.Is(e, gorm.ErrRecordNotFound) {
ErrorHandler(c, constants.CategoryNotExistErr.Error()+"id "+string(rune(categoryId)))
return
}
ErrorHandler(c, errors.Join(constants.DataBaseQueryErr, e).Error())
return
}
// 把相联的题目和子分类全查出来
// 先查子分类
childrenCategory := make([]*models.Category, 0)
tx = models.GetCategoryByColumn("parent_id", deleteCategory.ID)
e = tx.Find(&childrenCategory).Error
// find first 等函数传入的是数组时 不会报ErrRecordNotFound
if e != nil {
ErrorHandler(c, errors.Join(constants.DataBaseQueryErr, e).Error())
return
}
// 查题目
relatedQuestion := make([]*models.Question, 0)
tx = models.GetQuestionList("", strconv.Itoa(int(deleteCategory.ID)))
e = tx.Preload("TestCases").Find(&relatedQuestion).Error
if e != nil {
ErrorHandler(c, errors.Join(constants.DataBaseQueryErr, e).Error())
return
}
//
// 开始删除
if isCascade {
// 要级联删除所有信息 相关问题全部删除 子分类的父级id置零
// 先将子分类的父级id置零
for i := range childrenCategory {
childrenCategory[i].ParentId = 0
e = models.ModifyCategory(childrenCategory[i])
if e != nil {
ErrorHandler(c, errors.Join(constants.DataBaseUpdateErr, e).Error())
return
}
}
// 再删除相关问题
for i := range relatedQuestion {
// 删除问题分类关联表里的记录
e = models.DeleteQCByQuestionId(relatedQuestion[i].ID)
if e != nil {
ErrorHandler(c, errors.Join(constants.DataBaseUpdateErr, e).Error())
return
}
// 删除对应的测试用例
e = models.DeleteTestCaseByQuestionId(relatedQuestion[i].Identity)
if e != nil {
ErrorHandler(c, errors.Join(constants.DataBaseUpdateErr, e).Error())
return
}
// 删除对应的提交记录
e = models.DeleteSubmitByQuestionId(relatedQuestion[i].Identity)
if e != nil {
ErrorHandler(c, errors.Join(constants.DataBaseUpdateErr, e).Error())
return
}
// 最后删除问题
e = models.DeleteQuestion(relatedQuestion[i])
if e != nil {
ErrorHandler(c, errors.Join(constants.DataBaseUpdateErr, e).Error())
return
}
}
} else {
// 仅删除分类时 必须检查
if len(childrenCategory) != 0 || len(relatedQuestion) != 0 {
// 不能删除
ErrorHandler(c, constants.CategoryCanNotCascadeDelErr.Error())
return
}
}
// 最后删除分类
e = models.DeleteCategory(deleteCategory)
if e != nil {
ErrorHandler(c, errors.Join(constants.DataBaseDeleteErr, e).Error())
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": "Delete Category Successfully! ",
})
}
对应的几个Model层函数,就挑两个作为示例:
go// DeleteCategory 删除分类 传入的category只能是有数据的
func DeleteCategory(category *Category) error {
return DB.Delete(category).Error
}
func DeleteQCByQuestionId(questionId uint) error {
return DB.Where("question_id = ?", questionId).Delete(&QuestionCategory{}).Error
}
本文作者:御坂19327号
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!