Version: 0.2.2.dev.260210
feat: 🗑️ 新增删除任务块接口 - 实现 schedules、schedule_events 与 task_items 三表的联动删除 - 保证数据一致性,避免遗留脏数据 ✅
This commit is contained in:
@@ -131,3 +131,23 @@ func (api *TaskClassHandler) UserAddTaskClassItemIntoSchedule(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, respond.Ok)
|
c.JSON(http.StatusOK, respond.Ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *TaskClassHandler) DeleteTaskClassItem(c *gin.Context) {
|
||||||
|
taskID := c.Query("task_item_id")
|
||||||
|
//将taskID转换为int
|
||||||
|
intTaskID, err := strconv.Atoi(taskID)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, respond.WrongParamType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userID := c.GetInt("user_id")
|
||||||
|
// 创建一个带 1 秒超时的上下文
|
||||||
|
ctx, cancel := context.WithTimeout(c.Request.Context(), 1*time.Second)
|
||||||
|
defer cancel() // 记得释放资源
|
||||||
|
err = api.svc.DeleteTaskClassItem(ctx, userID, intTaskID)
|
||||||
|
if err != nil {
|
||||||
|
respond.DealWithError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, respond.Ok)
|
||||||
|
}
|
||||||
|
|||||||
@@ -397,3 +397,50 @@ func (d *ScheduleDAO) SetScheduleEmbeddedTaskIDToNull(ctx context.Context, event
|
|||||||
}
|
}
|
||||||
return embeddedTaskID, nil
|
return embeddedTaskID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *ScheduleDAO) FindEmbeddedTaskIDAndDeleteIt(ctx context.Context, taskID int) (int, error) {
|
||||||
|
// 1. 先找到 schedules 表中 embedded_task_id = taskID 的记录,获取对应的 event_id
|
||||||
|
type row struct {
|
||||||
|
EventID *int `gorm:"column:event_id"`
|
||||||
|
}
|
||||||
|
var r row
|
||||||
|
err := d.db.WithContext(ctx).
|
||||||
|
Table("schedules").
|
||||||
|
Select("event_id").
|
||||||
|
Where("embedded_task_id = ?", taskID).
|
||||||
|
Order("id ASC").
|
||||||
|
Limit(1).
|
||||||
|
Scan(&r).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if r.EventID == nil {
|
||||||
|
return 0, respond.TargetTaskNotEmbeddedInAnySchedule
|
||||||
|
}
|
||||||
|
eventID := *r.EventID
|
||||||
|
|
||||||
|
// 2. 删除该 event_id 对应的课程事件(通过级联删除实现)
|
||||||
|
res := d.db.WithContext(ctx).
|
||||||
|
Table("schedule_events").
|
||||||
|
Where("id = ?", eventID).
|
||||||
|
Delete(&model.ScheduleEvent{})
|
||||||
|
if res.Error != nil {
|
||||||
|
return 0, res.Error
|
||||||
|
}
|
||||||
|
if res.RowsAffected == 0 {
|
||||||
|
return 0, respond.TargetTaskNotEmbeddedInAnySchedule
|
||||||
|
}
|
||||||
|
return eventID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ScheduleDAO) DeleteScheduleEventByTaskItemID(ctx context.Context, taskItemID int) error {
|
||||||
|
//直接找schedule_events表中type=task且rel_id=taskItemID的记录,删除它(级联删schedules)
|
||||||
|
res := d.db.WithContext(ctx).
|
||||||
|
Table("schedule_events").
|
||||||
|
Where("type = ? AND rel_id = ?", "task", taskItemID).
|
||||||
|
Delete(&model.ScheduleEvent{})
|
||||||
|
if res.Error != nil {
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package dao
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/LoveLosita/smartflow/backend/model"
|
"github.com/LoveLosita/smartflow/backend/model"
|
||||||
"github.com/LoveLosita/smartflow/backend/respond"
|
"github.com/LoveLosita/smartflow/backend/respond"
|
||||||
@@ -158,12 +159,15 @@ func (dao *TaskClassDAO) GetTaskClassItemByID(ctx context.Context, id int) (*mod
|
|||||||
|
|
||||||
func (dao *TaskClassDAO) GetTaskClassIDByTaskItemID(ctx context.Context, itemID int) (int, error) {
|
func (dao *TaskClassDAO) GetTaskClassIDByTaskItemID(ctx context.Context, itemID int) (int, error) {
|
||||||
var item model.TaskClassItem
|
var item model.TaskClassItem
|
||||||
err := dao.db.WithContext(ctx).
|
res := dao.db.WithContext(ctx).
|
||||||
Select("category_id").
|
Select("category_id").
|
||||||
Where("id = ?", itemID).
|
Where("id = ?", itemID).
|
||||||
First(&item).Error
|
First(&item)
|
||||||
if err != nil {
|
if res.Error != nil {
|
||||||
return 0, err
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return 0, respond.TaskClassItemNotFound
|
||||||
|
}
|
||||||
|
return 0, res.Error
|
||||||
}
|
}
|
||||||
return *item.CategoryID, nil
|
return *item.CategoryID, nil
|
||||||
}
|
}
|
||||||
@@ -207,3 +211,10 @@ func (dao *TaskClassDAO) IfTaskClassItemArranged(ctx context.Context, taskID int
|
|||||||
}
|
}
|
||||||
return item.EmbeddedTime != nil, nil
|
return item.EmbeddedTime != nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dao *TaskClassDAO) DeleteTaskClassItemByID(ctx context.Context, id int) error {
|
||||||
|
err := dao.db.WithContext(ctx).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Delete(&model.TaskClassItem{}).Error
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
@@ -230,4 +230,14 @@ var ( //请求相关的响应
|
|||||||
Status: "40034",
|
Status: "40034",
|
||||||
Info: "task class item already arranged",
|
Info: "task class item already arranged",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TargetTaskNotEmbeddedInAnySchedule = Response{ //目标任务未嵌入任何日程
|
||||||
|
Status: "40035",
|
||||||
|
Info: "target task not embedded in any schedule",
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskClassItemNotFound = Response{ //任务类项目未找到
|
||||||
|
Status: "40036",
|
||||||
|
Info: "task class item not found",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ func RegisterRouters(handlers *api.ApiHandlers, cache *dao.CacheDAO, limiter *pk
|
|||||||
taskClassGroup.GET("/get", handlers.TaskClassHandler.UserGetCompleteTaskClass)
|
taskClassGroup.GET("/get", handlers.TaskClassHandler.UserGetCompleteTaskClass)
|
||||||
taskClassGroup.PUT("/update", handlers.TaskClassHandler.UserUpdateTaskClass)
|
taskClassGroup.PUT("/update", handlers.TaskClassHandler.UserUpdateTaskClass)
|
||||||
taskClassGroup.POST("/insert-into-schedule", handlers.TaskClassHandler.UserAddTaskClassItemIntoSchedule)
|
taskClassGroup.POST("/insert-into-schedule", handlers.TaskClassHandler.UserAddTaskClassItemIntoSchedule)
|
||||||
|
taskClassGroup.DELETE("/delete-item", handlers.TaskClassHandler.DeleteTaskClassItem)
|
||||||
}
|
}
|
||||||
scheduleGroup := apiGroup.Group("/schedule")
|
scheduleGroup := apiGroup.Group("/schedule")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ func (ss *ScheduleService) GetUserTodaySchedule(ctx context.Context, userID int)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}*/
|
}*/
|
||||||
//2.获取当前日期
|
//2.获取当前日期
|
||||||
curTime := time.Now().Format("2006-01-02")
|
/*curTime := time.Now().Format("2006-01-02")*/
|
||||||
/*curTime := "2026-03-02" //测试数据*/
|
curTime := "2026-03-02" //测试数据
|
||||||
week, dayOfWeek, err := conv.RealDateToRelativeDate(curTime)
|
week, dayOfWeek, err := conv.RealDateToRelativeDate(curTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -242,3 +242,50 @@ func (sv *TaskClassService) AddTaskClassItemIntoSchedule(ctx context.Context, re
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sv *TaskClassService) DeleteTaskClassItem(ctx context.Context, userID int, taskItemID int) error {
|
||||||
|
//1.先验证任务块归属
|
||||||
|
taskClassID, err := sv.taskClassRepo.GetTaskClassIDByTaskItemID(ctx, taskItemID) //通过任务块ID获取所属任务类ID
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ownerID, err := sv.taskClassRepo.GetTaskClassUserIDByID(ctx, taskClassID) //通过任务类ID获取所属用户ID
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ownerID != userID {
|
||||||
|
return respond.TaskClassItemNotBelongToUser
|
||||||
|
}
|
||||||
|
//2.如果该任务块已经被安排了,先解除安排,再删除任务块(事务)
|
||||||
|
if err := sv.repoManager.Transaction(ctx, func(txM *dao.RepoManager) error {
|
||||||
|
//2.1.先检查该任务块是否已经被安排了
|
||||||
|
arranged, err := txM.TaskClass.IfTaskClassItemArranged(ctx, taskItemID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if arranged {
|
||||||
|
//2.2.如果已经被安排了,先解除安排
|
||||||
|
//先扫schedules找到该task_item_id并删除
|
||||||
|
_, txErr := txM.Schedule.FindEmbeddedTaskIDAndDeleteIt(ctx, taskItemID)
|
||||||
|
//2.3.再将task_items表的embedded_time字段设置为null
|
||||||
|
txErr = txM.TaskClass.DeleteTaskClassItemEmbeddedTime(ctx, taskItemID)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
//再删除schedule_event表中对应的事件
|
||||||
|
txErr = txM.Schedule.DeleteScheduleEventByTaskItemID(ctx, taskItemID)
|
||||||
|
if txErr != nil {
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//2.4.最后删除任务块
|
||||||
|
err = txM.TaskClass.DeleteTaskClassItemByID(ctx, taskItemID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user