package router import ( "encoding/json" "fmt" "net/http" "strconv" "time" "bilidown/internal/task" "bilidown/internal/util" ) func createTask(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() if r.Method != http.MethodPost { util.Res{Success: false, Message: "不支持的请求方法"}.Write(w) return } var body []task.TaskInDB err := json.NewDecoder(r.Body).Decode(&body) if err != nil { util.Res{Success: false, Message: "参数错误"}.Write(w) return } if len(body) == 0 { util.Res{Success: false, Message: "任务列表为空"}.Write(w) return } db := util.MustGetDB() defer db.Close() // 预先获取下载目录 folder, err := util.GetCurrentFolder(db) if err != nil { util.Res{Success: false, Message: fmt.Sprintf("获取下载目录失败: %v", err)}.Write(w) return } // 批量验证参数 for _, item := range body { if !util.CheckBvidFormat(item.Bvid) { util.Res{Success: false, Message: fmt.Sprintf("bvid 格式错误: %s", item.Bvid)}.Write(w) return } if item.Title == "" || item.Owner == "" { util.Res{Success: false, Message: "标题或作者为空"}.Write(w) return } } // 批量数据库插入(单事务) tx, err := db.Begin() if err != nil { util.Res{Success: false, Message: fmt.Sprintf("开启事务失败: %v", err)}.Write(w) return } stmt, err := tx.Prepare(`INSERT INTO "task" ("bvid", "cid", "format", "title", "owner", "cover", "status", "folder", "duration", "download_type") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`) if err != nil { tx.Rollback() util.Res{Success: false, Message: fmt.Sprintf("准备语句失败: %v", err)}.Write(w) return } defer stmt.Close() tasks := make([]*task.Task, 0, len(body)) for _, item := range body { item.Folder = folder item.Status = "waiting" item.Title = util.FilterFileName(item.Title) _task := task.Task{TaskInDB: item} util.SqliteLock.Lock() result, err := stmt.Exec( item.Bvid, item.Cid, item.Format, item.Title, item.Owner, item.Cover, item.Status, item.Folder, item.Duration, item.DownloadType, ) util.SqliteLock.Unlock() if err != nil { tx.Rollback() util.Res{Success: false, Message: fmt.Sprintf("插入任务失败: %v", err)}.Write(w) return } _task.ID, _ = result.LastInsertId() _task.CreateAt = time.Now() tasks = append(tasks, &_task) } util.SqliteLock.Lock() err = tx.Commit() util.SqliteLock.Unlock() if err != nil { util.Res{Success: false, Message: fmt.Sprintf("提交事务失败: %v", err)}.Write(w) return } // 批量启动下载任务 for _, _task := range tasks { go _task.Start() } util.Res{Success: true, Message: fmt.Sprintf("成功创建 %d 个任务", len(tasks))}.Write(w) } func getActiveTask(w http.ResponseWriter, r *http.Request) { util.Res{Success: true, Data: task.GlobalTaskList}.Write(w) } func getTaskList(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { util.Res{Success: false, Message: "参数错误"}.Write(w) return } db := util.MustGetDB() defer db.Close() page, err := strconv.Atoi(r.FormValue("page")) if err != nil { page = 0 } pageSize, err := strconv.Atoi(r.FormValue("pageSize")) if err != nil { pageSize = 360 } tasks, err := task.GetTaskList(db, page, pageSize) if err != nil { util.Res{Success: false, Message: err.Error()}.Write(w) return } util.Res{Success: true, Message: "获取成功", Data: tasks}.Write(w) } func deleteTask(w http.ResponseWriter, r *http.Request) { // 支持单个删除(GET 参数 id)和批量删除(POST 参数 ids) var taskIDs []int if r.Method == http.MethodPost { defer r.Body.Close() var body struct { IDs []int `json:"ids"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { util.Res{Success: false, Message: "参数错误"}.Write(w) return } taskIDs = body.IDs } else { taskIDStr := r.FormValue("id") taskID, err := strconv.Atoi(taskIDStr) if err != nil { util.Res{Success: false, Message: "参数错误"}.Write(w) return } taskIDs = []int{taskID} } if len(taskIDs) == 0 { util.Res{Success: false, Message: "参数错误"}.Write(w) return } db := util.MustGetDB() defer db.Close() // 批量删除结果 successCount := 0 failedTasks := []struct { ID int Error string }{} for _, taskID := range taskIDs { // 只删除数据库记录,不删除已下载的视频文件 err := task.DeleteTask(db, taskID) if err != nil { failedTasks = append(failedTasks, struct { ID int Error string }{ID: taskID, Error: fmt.Sprintf("数据库删除失败: %v", err)}) continue } successCount++ } if len(failedTasks) == 0 { util.Res{Success: true, Message: fmt.Sprintf("成功删除 %d 个任务", successCount)}.Write(w) } else { util.Res{ Success: successCount > 0, Message: fmt.Sprintf("成功删除 %d 个任务,失败 %d 个", successCount, len(failedTasks)), Data: failedTasks, }.Write(w) } } func cancelTask(w http.ResponseWriter, r *http.Request) { // 支持单个取消(GET 参数 id)和批量取消(POST 参数 ids) var taskIDs []int64 if r.Method == http.MethodPost { defer r.Body.Close() var body struct { IDs []int64 `json:"ids"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { util.Res{Success: false, Message: "参数错误"}.Write(w) return } taskIDs = body.IDs } else { taskIDStr := r.FormValue("id") taskID, err := strconv.ParseInt(taskIDStr, 10, 64) if err != nil { util.Res{Success: false, Message: "参数错误"}.Write(w) return } taskIDs = []int64{taskID} } if len(taskIDs) == 0 { util.Res{Success: false, Message: "参数错误"}.Write(w) return } // 批量取消结果 successCount := 0 failedIDs := []int64{} for _, taskID := range taskIDs { if task.CancelTask(taskID) { successCount++ } else { failedIDs = append(failedIDs, taskID) } } if len(failedIDs) == 0 { util.Res{Success: true, Message: fmt.Sprintf("成功取消 %d 个任务", successCount)}.Write(w) } else { util.Res{ Success: successCount > 0, Message: fmt.Sprintf("成功取消 %d 个任务,失败 %d 个", successCount, len(failedIDs)), Data: failedIDs, }.Write(w) } }