feat(task): add batch delete and cancel API support
支持批量删除和取消任务的接口: - deleteTask 接口支持 POST 方式传入 ids 数组 - cancelTask 接口支持 POST 方式传入 ids 数组 - 前端批量操作改为调用批量接口,减少请求数量 Co-Authored-By: AI
This commit is contained in:
+117
-28
@@ -138,49 +138,138 @@ func showFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func deleteTask(w http.ResponseWriter, r *http.Request) {
|
func deleteTask(w http.ResponseWriter, r *http.Request) {
|
||||||
taskIDStr := r.FormValue("id")
|
// 支持单个删除(GET 参数 id)和批量删除(POST 参数 ids)
|
||||||
taskID, err := strconv.Atoi(taskIDStr)
|
var taskIDs []int
|
||||||
if err != nil {
|
|
||||||
|
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)
|
util.Res{Success: false, Message: "参数错误"}.Write(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
db := util.MustGetDB()
|
db := util.MustGetDB()
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
_task, err := task.GetTask(db, taskID)
|
// 批量删除结果
|
||||||
if err == sql.ErrNoRows {
|
successCount := 0
|
||||||
util.Res{Success: true, Message: "数据库中没有该条记录,所以本次操作被忽略,可以算作成功。"}.Write(w)
|
failedTasks := []struct {
|
||||||
return
|
ID int
|
||||||
}
|
Error string
|
||||||
if err != nil {
|
}{}
|
||||||
util.Res{Success: false, Message: fmt.Sprintf("task.GetTask: %v", err)}.Write(w)
|
|
||||||
return
|
for _, taskID := range taskIDs {
|
||||||
}
|
_task, err := task.GetTask(db, taskID)
|
||||||
filePath := _task.FilePath()
|
if err == sql.ErrNoRows {
|
||||||
err = os.Remove(filePath)
|
// 数据库中没有该条记录,忽略
|
||||||
if err != nil && !os.IsNotExist(err) {
|
successCount++
|
||||||
util.Res{Success: false, Message: fmt.Sprintf("文件删除失败 os.Remove: %v", err)}.Write(w)
|
continue
|
||||||
return
|
}
|
||||||
|
if err != nil {
|
||||||
|
failedTasks = append(failedTasks, struct {
|
||||||
|
ID int
|
||||||
|
Error string
|
||||||
|
}{ID: taskID, Error: fmt.Sprintf("获取任务失败: %v", err)})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filePath := _task.FilePath()
|
||||||
|
err = os.Remove(filePath)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
failedTasks = append(failedTasks, struct {
|
||||||
|
ID int
|
||||||
|
Error string
|
||||||
|
}{ID: taskID, Error: fmt.Sprintf("文件删除失败: %v", err)})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
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++
|
||||||
}
|
}
|
||||||
|
|
||||||
err = task.DeleteTask(db, taskID)
|
if len(failedTasks) == 0 {
|
||||||
if err != nil {
|
util.Res{Success: true, Message: fmt.Sprintf("成功删除 %d 个任务", successCount)}.Write(w)
|
||||||
util.Res{Success: false, Message: fmt.Sprintf("task.DeleteTask: %v", err)}.Write(w)
|
} else {
|
||||||
return
|
util.Res{
|
||||||
|
Success: successCount > 0,
|
||||||
|
Message: fmt.Sprintf("成功删除 %d 个任务,失败 %d 个", successCount, len(failedTasks)),
|
||||||
|
Data: failedTasks,
|
||||||
|
}.Write(w)
|
||||||
}
|
}
|
||||||
util.Res{Success: true, Message: "删除成功"}.Write(w)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cancelTask(w http.ResponseWriter, r *http.Request) {
|
func cancelTask(w http.ResponseWriter, r *http.Request) {
|
||||||
taskIDStr := r.FormValue("id")
|
// 支持单个取消(GET 参数 id)和批量取消(POST 参数 ids)
|
||||||
taskID, err := strconv.ParseInt(taskIDStr, 10, 64)
|
var taskIDs []int64
|
||||||
if err != nil {
|
|
||||||
|
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)
|
util.Res{Success: false, Message: "参数错误"}.Write(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if task.CancelTask(taskID) {
|
|
||||||
util.Res{Success: true, Message: "任务已取消"}.Write(w)
|
// 批量取消结果
|
||||||
|
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 {
|
} else {
|
||||||
util.Res{Success: false, Message: "任务不存在或已完成"}.Write(w)
|
util.Res{
|
||||||
|
Success: successCount > 0,
|
||||||
|
Message: fmt.Sprintf("成功取消 %d 个任务,失败 %d 个", successCount, len(failedIDs)),
|
||||||
|
Data: failedIDs,
|
||||||
|
}.Write(w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,27 @@ export const deleteTask = async (id: number) => {
|
|||||||
if (!res.success) throw new Error(res.message)
|
if (!res.success) throw new Error(res.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除任务 */
|
||||||
|
export const deleteTasks = async (ids: number[]) => {
|
||||||
|
const res = await fetch('/api/deleteTask', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ ids })
|
||||||
|
}).then(res => res.json()) as ResJSON
|
||||||
|
if (!res.success) throw new Error(res.message)
|
||||||
|
}
|
||||||
|
|
||||||
export const cancelTask = async (id: number) => {
|
export const cancelTask = async (id: number) => {
|
||||||
const res = await fetch(`/api/cancelTask?id=${id}`).then(res => res.json()) as ResJSON
|
const res = await fetch(`/api/cancelTask?id=${id}`).then(res => res.json()) as ResJSON
|
||||||
if (!res.success) throw new Error(res.message)
|
if (!res.success) throw new Error(res.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量取消任务 */
|
||||||
|
export const cancelTasks = async (ids: number[]) => {
|
||||||
|
const res = await fetch('/api/cancelTask', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ ids })
|
||||||
|
}).then(res => res.json()) as ResJSON
|
||||||
|
if (!res.success) throw new Error(res.message)
|
||||||
}
|
}
|
||||||
+11
-13
@@ -1,7 +1,7 @@
|
|||||||
import van, { State } from 'vanjs-core'
|
import van, { State } from 'vanjs-core'
|
||||||
import { Route, goto, now } from 'vanjs-router'
|
import { Route, goto, now } from 'vanjs-router'
|
||||||
import { checkLogin, GLOBAL_HAS_LOGIN, GLOBAL_HIDE_PAGE, ResJSON, VanComponent } from '../mixin'
|
import { checkLogin, GLOBAL_HAS_LOGIN, GLOBAL_HIDE_PAGE, ResJSON, VanComponent } from '../mixin'
|
||||||
import { deleteTask, getActiveTask, getTaskList, showFile, cancelTask } from './data'
|
import { deleteTask, getActiveTask, getTaskList, showFile, cancelTask, deleteTasks, cancelTasks } from './data'
|
||||||
import { TaskInDB, TaskStatus } from '../work/type'
|
import { TaskInDB, TaskStatus } from '../work/type'
|
||||||
import { LoadingBox } from '../view'
|
import { LoadingBox } from '../view'
|
||||||
import { PlayerModalComp } from './playerModal'
|
import { PlayerModalComp } from './playerModal'
|
||||||
@@ -86,13 +86,14 @@ export class TaskRoute implements VanComponent {
|
|||||||
hidden: () => _that.selectedCancellable.val.length == 0,
|
hidden: () => _that.selectedCancellable.val.length == 0,
|
||||||
onclick() {
|
onclick() {
|
||||||
if (!confirm(`确定要取消选中的 ${_that.selectedCancellable.val.length} 个任务吗?`)) return
|
if (!confirm(`确定要取消选中的 ${_that.selectedCancellable.val.length} 个任务吗?`)) return
|
||||||
_that.selectedCancellable.val.forEach(t => {
|
const ids = _that.selectedCancellable.val.map(t => t.id)
|
||||||
cancelTask(t.id).then(() => {
|
cancelTasks(ids).then(() => {
|
||||||
|
_that.selectedCancellable.val.forEach(t => {
|
||||||
t.statusState.val = 'error'
|
t.statusState.val = 'error'
|
||||||
t.selected.val = false
|
t.selected.val = false
|
||||||
}).catch(error => {
|
|
||||||
alert(error.message)
|
|
||||||
})
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
alert(error.message)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, () => `批量取消 (${_that.selectedCancellable.val.length})`),
|
}, () => `批量取消 (${_that.selectedCancellable.val.length})`),
|
||||||
@@ -101,14 +102,11 @@ export class TaskRoute implements VanComponent {
|
|||||||
hidden: () => _that.selectedDeletable.val.length == 0,
|
hidden: () => _that.selectedDeletable.val.length == 0,
|
||||||
onclick() {
|
onclick() {
|
||||||
if (!confirm(`确定要删除选中的 ${_that.selectedDeletable.val.length} 个任务吗?`)) return
|
if (!confirm(`确定要删除选中的 ${_that.selectedDeletable.val.length} 个任务吗?`)) return
|
||||||
_that.selectedDeletable.val.forEach(t => {
|
const ids = _that.selectedDeletable.val.map(t => t.id)
|
||||||
t.deleting.val = true
|
deleteTasks(ids).then(() => {
|
||||||
deleteTask(t.id).then(() => {
|
_that.taskList.val = _that.taskList.val.filter(taskInDB => !ids.includes(taskInDB.id))
|
||||||
_that.taskList.val = _that.taskList.val.filter(taskInDB => taskInDB.id != t.id)
|
}).catch(error => {
|
||||||
}).catch(error => {
|
alert(error.message)
|
||||||
t.deleting.val = false
|
|
||||||
alert(error.message)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, () => `批量删除 (${_that.selectedDeletable.val.length})`),
|
}, () => `批量删除 (${_that.selectedDeletable.val.length})`),
|
||||||
|
|||||||
Reference in New Issue
Block a user