// Package handler provides HTTP handlers for various endpoints. package handler import ( "net/http" "time" "modelRT/database" "modelRT/logger" "modelRT/network" "modelRT/orm" "github.com/gin-gonic/gin" "github.com/gofrs/uuid" "gorm.io/gorm" ) // AsyncTaskCancelHandler handles cancellation of an async task // @Summary 取消异步任务 // @Description 取消指定ID的异步任务(如果任务尚未开始执行) // @Tags AsyncTask // @Accept json // @Produce json // @Param task_id path string true "任务ID" // @Success 200 {object} network.SuccessResponse "任务取消成功" // @Failure 400 {object} network.FailureResponse "请求参数错误或任务无法取消" // @Failure 404 {object} network.FailureResponse "任务不存在" // @Failure 500 {object} network.FailureResponse "服务器内部错误" // @Router /task/async/{task_id}/cancel [post] func AsyncTaskCancelHandler(c *gin.Context) { ctx := c.Request.Context() // Parse task ID from path parameter taskIDStr := c.Param("task_id") if taskIDStr == "" { logger.Error(ctx, "task_id parameter is required") c.JSON(http.StatusOK, network.FailureResponse{ Code: http.StatusBadRequest, Msg: "task_id parameter is required", }) return } taskID, err := uuid.FromString(taskIDStr) if err != nil { logger.Error(ctx, "invalid task ID format", "task_id", taskIDStr, "error", err) c.JSON(http.StatusOK, network.FailureResponse{ Code: http.StatusBadRequest, Msg: "invalid task ID format", }) return } pgClient := database.GetPostgresDBClient() if pgClient == nil { logger.Error(ctx, "database connection not found in context") c.JSON(http.StatusOK, network.FailureResponse{ Code: http.StatusInternalServerError, Msg: "database connection error", }) return } // Query task from database asyncTask, err := database.GetAsyncTaskByID(ctx, pgClient, taskID) if err != nil { if err == gorm.ErrRecordNotFound { logger.Error(ctx, "async task not found", "task_id", taskID) c.JSON(http.StatusOK, network.FailureResponse{ Code: http.StatusNotFound, Msg: "task not found", }) return } logger.Error(ctx, "failed to query async task from database", "error", err) c.JSON(http.StatusOK, network.FailureResponse{ Code: http.StatusInternalServerError, Msg: "failed to query task", }) return } // Check if task can be cancelled (only SUBMITTED tasks can be cancelled) if asyncTask.Status != orm.AsyncTaskStatusSubmitted { logger.Error(ctx, "task cannot be cancelled", "task_id", taskID, "status", asyncTask.Status) c.JSON(http.StatusOK, network.FailureResponse{ Code: http.StatusBadRequest, Msg: "task cannot be cancelled (already running or completed)", }) return } // Update task status to failed with cancellation reason timestamp := time.Now().Unix() err = database.FailAsyncTask(ctx, pgClient, taskID, timestamp) if err != nil { logger.Error(ctx, "failed to cancel async task", "task_id", taskID, "error", err) c.JSON(http.StatusOK, network.FailureResponse{ Code: http.StatusInternalServerError, Msg: "failed to cancel task", }) return } // Update task result with cancellation error err = database.UpdateAsyncTaskResultWithError(ctx, pgClient, taskID, 40003, "task cancelled by user", orm.JSONMap{ "cancelled_at": timestamp, "cancelled_by": "user", }) if err != nil { logger.Error(ctx, "failed to update task result with cancellation error", "task_id", taskID, "error", err) // Continue anyway since task is already marked as failed } c.JSON(http.StatusOK, network.SuccessResponse{ Code: 2000, Msg: "task cancelled successfully", }) }