2025-08-09 23:47:30 +08:00
|
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"encoding/json"
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/ctwj/urldb/db/entity"
|
|
|
|
|
|
"github.com/ctwj/urldb/db/repo"
|
|
|
|
|
|
"github.com/ctwj/urldb/task"
|
|
|
|
|
|
"github.com/ctwj/urldb/utils"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// TaskHandler 任务处理器
|
|
|
|
|
|
type TaskHandler struct {
|
|
|
|
|
|
repoMgr *repo.RepositoryManager
|
|
|
|
|
|
taskManager *task.TaskManager
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewTaskHandler 创建任务处理器
|
|
|
|
|
|
func NewTaskHandler(repoMgr *repo.RepositoryManager, taskManager *task.TaskManager) *TaskHandler {
|
|
|
|
|
|
return &TaskHandler{
|
|
|
|
|
|
repoMgr: repoMgr,
|
|
|
|
|
|
taskManager: taskManager,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 批量转存任务资源项
|
|
|
|
|
|
type BatchTransferResource struct {
|
|
|
|
|
|
Title string `json:"title" binding:"required"`
|
|
|
|
|
|
URL string `json:"url" binding:"required"`
|
|
|
|
|
|
CategoryID uint `json:"category_id,omitempty"`
|
2025-08-12 00:27:10 +08:00
|
|
|
|
PanID uint `json:"pan_id,omitempty"`
|
2025-08-09 23:47:30 +08:00
|
|
|
|
Tags []uint `json:"tags,omitempty"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateBatchTransferTask 创建批量转存任务
|
|
|
|
|
|
func (h *TaskHandler) CreateBatchTransferTask(c *gin.Context) {
|
|
|
|
|
|
var req struct {
|
2025-08-12 00:27:10 +08:00
|
|
|
|
Title string `json:"title" binding:"required"`
|
|
|
|
|
|
Description string `json:"description"`
|
|
|
|
|
|
Resources []BatchTransferResource `json:"resources" binding:"required,min=1"`
|
|
|
|
|
|
SelectedAccounts []uint `json:"selected_accounts,omitempty"`
|
2025-08-09 23:47:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
|
ErrorResponse(c, "参数错误: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-12 00:27:10 +08:00
|
|
|
|
utils.Info("创建批量转存任务: %s,资源数量: %d,选择账号数量: %d", req.Title, len(req.Resources), len(req.SelectedAccounts))
|
|
|
|
|
|
|
|
|
|
|
|
// 构建任务配置
|
|
|
|
|
|
taskConfig := map[string]interface{}{
|
|
|
|
|
|
"selected_accounts": req.SelectedAccounts,
|
|
|
|
|
|
}
|
|
|
|
|
|
configJSON, _ := json.Marshal(taskConfig)
|
2025-08-09 23:47:30 +08:00
|
|
|
|
|
|
|
|
|
|
// 创建任务
|
|
|
|
|
|
newTask := &entity.Task{
|
|
|
|
|
|
Title: req.Title,
|
|
|
|
|
|
Description: req.Description,
|
|
|
|
|
|
Type: "transfer",
|
|
|
|
|
|
Status: "pending",
|
|
|
|
|
|
TotalItems: len(req.Resources),
|
2025-08-12 00:27:10 +08:00
|
|
|
|
Config: string(configJSON),
|
2025-08-11 01:34:07 +08:00
|
|
|
|
CreatedAt: utils.GetCurrentTime(),
|
|
|
|
|
|
UpdatedAt: utils.GetCurrentTime(),
|
2025-08-09 23:47:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err := h.repoMgr.TaskRepository.Create(newTask)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
utils.Error("创建任务失败: %v", err)
|
|
|
|
|
|
ErrorResponse(c, "创建任务失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建任务项
|
|
|
|
|
|
for _, resource := range req.Resources {
|
|
|
|
|
|
// 构建转存输入数据
|
|
|
|
|
|
transferInput := task.TransferInput{
|
|
|
|
|
|
Title: resource.Title,
|
|
|
|
|
|
URL: resource.URL,
|
|
|
|
|
|
CategoryID: resource.CategoryID,
|
2025-08-12 00:27:10 +08:00
|
|
|
|
PanID: resource.PanID,
|
2025-08-09 23:47:30 +08:00
|
|
|
|
Tags: resource.Tags,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inputJSON, _ := json.Marshal(transferInput)
|
|
|
|
|
|
|
|
|
|
|
|
taskItem := &entity.TaskItem{
|
|
|
|
|
|
TaskID: newTask.ID,
|
|
|
|
|
|
Status: "pending",
|
|
|
|
|
|
InputData: string(inputJSON),
|
2025-08-11 01:34:07 +08:00
|
|
|
|
CreatedAt: utils.GetCurrentTime(),
|
|
|
|
|
|
UpdatedAt: utils.GetCurrentTime(),
|
2025-08-09 23:47:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err = h.repoMgr.TaskItemRepository.Create(taskItem)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
utils.Error("创建任务项失败: %v", err)
|
|
|
|
|
|
// 继续创建其他任务项
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
utils.Info("批量转存任务创建完成: %d, 共 %d 项", newTask.ID, len(req.Resources))
|
|
|
|
|
|
|
|
|
|
|
|
SuccessResponse(c, gin.H{
|
|
|
|
|
|
"task_id": newTask.ID,
|
|
|
|
|
|
"total_items": len(req.Resources),
|
|
|
|
|
|
"message": "任务创建成功",
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// StartTask 启动任务
|
|
|
|
|
|
func (h *TaskHandler) StartTask(c *gin.Context) {
|
|
|
|
|
|
taskIDStr := c.Param("id")
|
|
|
|
|
|
taskID, err := strconv.ParseUint(taskIDStr, 10, 32)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "无效的任务ID: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
utils.Info("启动任务: %d", taskID)
|
|
|
|
|
|
|
|
|
|
|
|
err = h.taskManager.StartTask(uint(taskID))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
utils.Error("启动任务失败: %v", err)
|
|
|
|
|
|
ErrorResponse(c, "启动任务失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SuccessResponse(c, gin.H{
|
|
|
|
|
|
"message": "任务启动成功",
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// StopTask 停止任务
|
|
|
|
|
|
func (h *TaskHandler) StopTask(c *gin.Context) {
|
|
|
|
|
|
taskIDStr := c.Param("id")
|
|
|
|
|
|
taskID, err := strconv.ParseUint(taskIDStr, 10, 32)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "无效的任务ID: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
utils.Info("停止任务: %d", taskID)
|
|
|
|
|
|
|
|
|
|
|
|
err = h.taskManager.StopTask(uint(taskID))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
utils.Error("停止任务失败: %v", err)
|
|
|
|
|
|
ErrorResponse(c, "停止任务失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SuccessResponse(c, gin.H{
|
|
|
|
|
|
"message": "任务停止成功",
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-10 00:54:30 +08:00
|
|
|
|
// PauseTask 暂停任务
|
|
|
|
|
|
func (h *TaskHandler) PauseTask(c *gin.Context) {
|
|
|
|
|
|
taskIDStr := c.Param("id")
|
|
|
|
|
|
taskID, err := strconv.ParseUint(taskIDStr, 10, 32)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "无效的任务ID: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
utils.Info("暂停任务: %d", taskID)
|
|
|
|
|
|
|
|
|
|
|
|
err = h.taskManager.PauseTask(uint(taskID))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
utils.Error("暂停任务失败: %v", err)
|
|
|
|
|
|
ErrorResponse(c, "暂停任务失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SuccessResponse(c, gin.H{
|
|
|
|
|
|
"message": "任务暂停成功",
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-09 23:47:30 +08:00
|
|
|
|
// GetTaskStatus 获取任务状态
|
|
|
|
|
|
func (h *TaskHandler) GetTaskStatus(c *gin.Context) {
|
|
|
|
|
|
taskIDStr := c.Param("id")
|
|
|
|
|
|
taskID, err := strconv.ParseUint(taskIDStr, 10, 32)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "无效的任务ID: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取任务详情
|
|
|
|
|
|
task, err := h.repoMgr.TaskRepository.GetByID(uint(taskID))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "任务不存在: "+err.Error(), http.StatusNotFound)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取任务项统计
|
|
|
|
|
|
stats, err := h.repoMgr.TaskItemRepository.GetStatsByTaskID(uint(taskID))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
utils.Error("获取任务项统计失败: %v", err)
|
|
|
|
|
|
stats = map[string]int{
|
|
|
|
|
|
"total": 0,
|
|
|
|
|
|
"pending": 0,
|
|
|
|
|
|
"processing": 0,
|
|
|
|
|
|
"completed": 0,
|
|
|
|
|
|
"failed": 0,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查任务是否在运行
|
|
|
|
|
|
isRunning := h.taskManager.IsTaskRunning(uint(taskID))
|
|
|
|
|
|
|
|
|
|
|
|
SuccessResponse(c, gin.H{
|
2025-08-10 13:52:41 +08:00
|
|
|
|
"id": task.ID,
|
|
|
|
|
|
"title": task.Title,
|
|
|
|
|
|
"description": task.Description,
|
|
|
|
|
|
"task_type": task.Type,
|
|
|
|
|
|
"status": task.Status,
|
|
|
|
|
|
"total_items": task.TotalItems,
|
|
|
|
|
|
"processed_items": task.ProcessedItems,
|
|
|
|
|
|
"success_items": task.SuccessItems,
|
|
|
|
|
|
"failed_items": task.FailedItems,
|
|
|
|
|
|
"is_running": isRunning,
|
|
|
|
|
|
"stats": stats,
|
|
|
|
|
|
"created_at": task.CreatedAt,
|
|
|
|
|
|
"updated_at": task.UpdatedAt,
|
2025-08-09 23:47:30 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetTasks 获取任务列表
|
|
|
|
|
|
func (h *TaskHandler) GetTasks(c *gin.Context) {
|
|
|
|
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
|
|
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
|
|
|
|
|
taskType := c.Query("task_type")
|
|
|
|
|
|
status := c.Query("status")
|
|
|
|
|
|
|
|
|
|
|
|
utils.Info("GetTasks: 获取任务列表 page=%d, pageSize=%d, taskType=%s, status=%s", page, pageSize, taskType, status)
|
|
|
|
|
|
|
|
|
|
|
|
tasks, total, err := h.repoMgr.TaskRepository.GetList(page, pageSize, taskType, status)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
utils.Error("获取任务列表失败: %v", err)
|
|
|
|
|
|
ErrorResponse(c, "获取任务列表失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
utils.Info("GetTasks: 从数据库获取到 %d 个任务", len(tasks))
|
|
|
|
|
|
|
|
|
|
|
|
// 为每个任务添加运行状态
|
|
|
|
|
|
var result []gin.H
|
|
|
|
|
|
for _, task := range tasks {
|
|
|
|
|
|
isRunning := h.taskManager.IsTaskRunning(task.ID)
|
|
|
|
|
|
utils.Info("GetTasks: 任务 %d (%s) 数据库状态: %s, TaskManager运行状态: %v", task.ID, task.Title, task.Status, isRunning)
|
|
|
|
|
|
|
|
|
|
|
|
result = append(result, gin.H{
|
|
|
|
|
|
"id": task.ID,
|
|
|
|
|
|
"title": task.Title,
|
|
|
|
|
|
"description": task.Description,
|
|
|
|
|
|
"task_type": task.Type,
|
|
|
|
|
|
"status": task.Status,
|
|
|
|
|
|
"total_items": task.TotalItems,
|
|
|
|
|
|
"processed_items": task.ProcessedItems,
|
|
|
|
|
|
"success_items": task.SuccessItems,
|
|
|
|
|
|
"failed_items": task.FailedItems,
|
|
|
|
|
|
"is_running": isRunning,
|
|
|
|
|
|
"created_at": task.CreatedAt,
|
|
|
|
|
|
"updated_at": task.UpdatedAt,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SuccessResponse(c, gin.H{
|
|
|
|
|
|
"items": result,
|
|
|
|
|
|
"total": total,
|
|
|
|
|
|
"page": page,
|
|
|
|
|
|
"size": pageSize,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetTaskItems 获取任务项列表
|
|
|
|
|
|
func (h *TaskHandler) GetTaskItems(c *gin.Context) {
|
|
|
|
|
|
taskIDStr := c.Param("id")
|
|
|
|
|
|
taskID, err := strconv.ParseUint(taskIDStr, 10, 32)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "无效的任务ID: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
2025-08-11 17:51:04 +08:00
|
|
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10000"))
|
2025-08-09 23:47:30 +08:00
|
|
|
|
status := c.Query("status")
|
|
|
|
|
|
|
|
|
|
|
|
items, total, err := h.repoMgr.TaskItemRepository.GetListByTaskID(uint(taskID), page, pageSize, status)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
utils.Error("获取任务项列表失败: %v", err)
|
|
|
|
|
|
ErrorResponse(c, "获取任务项列表失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析输入和输出数据
|
|
|
|
|
|
var result []gin.H
|
|
|
|
|
|
for _, item := range items {
|
|
|
|
|
|
itemData := gin.H{
|
|
|
|
|
|
"id": item.ID,
|
|
|
|
|
|
"status": item.Status,
|
|
|
|
|
|
"created_at": item.CreatedAt,
|
|
|
|
|
|
"updated_at": item.UpdatedAt,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析输入数据
|
|
|
|
|
|
if item.InputData != "" {
|
|
|
|
|
|
var inputData map[string]interface{}
|
|
|
|
|
|
if err := json.Unmarshal([]byte(item.InputData), &inputData); err == nil {
|
|
|
|
|
|
itemData["input"] = inputData
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析输出数据
|
|
|
|
|
|
if item.OutputData != "" {
|
|
|
|
|
|
var outputData map[string]interface{}
|
|
|
|
|
|
if err := json.Unmarshal([]byte(item.OutputData), &outputData); err == nil {
|
|
|
|
|
|
itemData["output"] = outputData
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result = append(result, itemData)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SuccessResponse(c, gin.H{
|
|
|
|
|
|
"items": result,
|
|
|
|
|
|
"total": total,
|
|
|
|
|
|
"page": page,
|
|
|
|
|
|
"size": pageSize,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DeleteTask 删除任务
|
|
|
|
|
|
func (h *TaskHandler) DeleteTask(c *gin.Context) {
|
|
|
|
|
|
taskIDStr := c.Param("id")
|
|
|
|
|
|
taskID, err := strconv.ParseUint(taskIDStr, 10, 32)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ErrorResponse(c, "无效的任务ID: "+err.Error(), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查任务是否在运行
|
|
|
|
|
|
if h.taskManager.IsTaskRunning(uint(taskID)) {
|
|
|
|
|
|
ErrorResponse(c, "任务正在运行,请先停止任务", http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除任务项
|
|
|
|
|
|
err = h.repoMgr.TaskItemRepository.DeleteByTaskID(uint(taskID))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
utils.Error("删除任务项失败: %v", err)
|
|
|
|
|
|
ErrorResponse(c, "删除任务项失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除任务
|
|
|
|
|
|
err = h.repoMgr.TaskRepository.Delete(uint(taskID))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
utils.Error("删除任务失败: %v", err)
|
|
|
|
|
|
ErrorResponse(c, "删除任务失败: "+err.Error(), http.StatusInternalServerError)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
utils.Info("任务删除成功: %d", taskID)
|
|
|
|
|
|
SuccessResponse(c, gin.H{
|
|
|
|
|
|
"message": "任务删除成功",
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|