Skip to content

Commit 89206c9

Browse files
authored
feat: Batch cancel tasks (#1896)
1 parent 4550f72 commit 89206c9

File tree

7 files changed

+136
-5
lines changed

7 files changed

+136
-5
lines changed

apps/dataset/serializers/document_serializers.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,23 @@ def get_buffer(self, file):
7777
return self.buffer
7878

7979

80+
class BatchCancelInstanceSerializer(serializers.Serializer):
81+
id_list = serializers.ListField(required=True, child=serializers.UUIDField(required=True),
82+
error_messages=ErrMessage.char("id列表"))
83+
type = serializers.IntegerField(required=True, error_messages=ErrMessage.integer(
84+
"任务类型"))
85+
86+
def is_valid(self, *, raise_exception=False):
87+
super().is_valid(raise_exception=True)
88+
_type = self.data.get('type')
89+
try:
90+
TaskType(_type)
91+
except Exception as e:
92+
raise AppApiException(500, '任务类型不支持')
93+
94+
8095
class CancelInstanceSerializer(serializers.Serializer):
81-
type = serializers.IntegerField(required=True, error_messages=ErrMessage.boolean(
96+
type = serializers.IntegerField(required=True, error_messages=ErrMessage.integer(
8297
"任务类型"))
8398

8499
def is_valid(self, *, raise_exception=False):
@@ -1064,6 +1079,28 @@ def batch_delete(self, instance: Dict, with_valid=True):
10641079
delete_embedding_by_document_list(document_id_list)
10651080
return True
10661081

1082+
def batch_cancel(self, instance: Dict, with_valid=True):
1083+
if with_valid:
1084+
self.is_valid(raise_exception=True)
1085+
BatchCancelInstanceSerializer(data=instance).is_valid(raise_exception=True)
1086+
document_id_list = instance.get("id_list")
1087+
ListenerManagement.update_status(QuerySet(Paragraph).annotate(
1088+
reversed_status=Reverse('status'),
1089+
task_type_status=Substr('reversed_status', TaskType(instance.get('type')).value,
1090+
1),
1091+
).filter(task_type_status__in=[State.PENDING.value, State.STARTED.value]).filter(
1092+
document_id__in=document_id_list).values('id'),
1093+
TaskType(instance.get('type')),
1094+
State.REVOKE)
1095+
ListenerManagement.update_status(QuerySet(Document).annotate(
1096+
reversed_status=Reverse('status'),
1097+
task_type_status=Substr('reversed_status', TaskType(instance.get('type')).value,
1098+
1),
1099+
).filter(task_type_status__in=[State.PENDING.value, State.STARTED.value]).filter(
1100+
id__in=document_id_list).values('id'),
1101+
TaskType(instance.get('type')),
1102+
State.REVOKE)
1103+
10671104
def batch_edit_hit_handling(self, instance: Dict, with_valid=True):
10681105
if with_valid:
10691106
BatchSerializer(data=instance).is_valid(model=Document, raise_exception=True)

apps/dataset/swagger_api/document_api.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,17 @@ def get_request_body_api():
3737
description="1|2|3 1:向量化|2:生成问题|3:同步文档")
3838
}
3939
)
40+
41+
class BatchCancel(ApiMixin):
42+
@staticmethod
43+
def get_request_body_api():
44+
return openapi.Schema(
45+
type=openapi.TYPE_OBJECT,
46+
properties={
47+
'id_list': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_STRING),
48+
title="文档id列表",
49+
description="文档id列表"),
50+
'type': openapi.Schema(type=openapi.TYPE_INTEGER, title="任务类型",
51+
description="1|2|3 1:向量化|2:生成问题|3:同步文档", default=1)
52+
}
53+
)

apps/dataset/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
path('dataset/<str:dataset_id>/document/<str:document_id>/sync', views.Document.SyncWeb.as_view()),
4242
path('dataset/<str:dataset_id>/document/<str:document_id>/refresh', views.Document.Refresh.as_view()),
4343
path('dataset/<str:dataset_id>/document/<str:document_id>/cancel_task', views.Document.CancelTask.as_view()),
44+
path('dataset/<str:dataset_id>/document/cancel_task/_batch',
45+
views.Document.CancelTask.Batch.as_view()),
4446
path('dataset/<str:dataset_id>/document/<str:document_id>/paragraph', views.Paragraph.as_view()),
4547
path('dataset/<str:dataset_id>/document/batch_generate_related', views.Document.BatchGenerateRelated.as_view()),
4648
path(

apps/dataset/views/document.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,24 @@ def put(self, request: Request, dataset_id: str, document_id: str):
238238
request.data
239239
))
240240

241+
class Batch(APIView):
242+
authentication_classes = [TokenAuth]
243+
244+
@action(methods=['PUT'], detail=False)
245+
@swagger_auto_schema(operation_summary="批量取消任务",
246+
operation_id="批量取消任务",
247+
request_body=DocumentApi.BatchCancel.get_request_body_api(),
248+
manual_parameters=DocumentSerializers.Create.get_request_params_api(),
249+
responses=result.get_default_response(),
250+
tags=["知识库/文档"]
251+
)
252+
@has_permissions(
253+
lambda r, k: Permission(group=Group.DATASET, operate=Operate.MANAGE,
254+
dynamic_tag=k.get('dataset_id')))
255+
def put(self, request: Request, dataset_id: str):
256+
return result.success(
257+
DocumentSerializers.Batch(data={'dataset_id': dataset_id}).batch_cancel(request.data))
258+
241259
class Refresh(APIView):
242260
authentication_classes = [TokenAuth]
243261

ui/src/api/document.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,14 @@ const cancelTask: (
359359
)
360360
}
361361

362+
const batchCancelTask: (
363+
dataset_id: string,
364+
data: any,
365+
loading?: Ref<boolean>
366+
) => Promise<Result<boolean>> = (dataset_id, data, loading) => {
367+
return put(`${prefix}/${dataset_id}/document/cancel_task/_batch`, data, undefined, loading)
368+
}
369+
362370
export default {
363371
postSplitDocument,
364372
getDocument,
@@ -383,5 +391,6 @@ export default {
383391
batchRefresh,
384392
batchGenerateRelated,
385393
cancelTask,
386-
exportDocumentZip
394+
exportDocumentZip,
395+
batchCancelTask
387396
}

ui/src/styles/element-plus.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
--el-text-color-regular: #1f2329;
77
--el-color-info: #8f959e !important;
88
--el-disabled-bg-color: #eff0f1;
9-
--el-disabled-border-color: #bbbfc4;
109
--el-text-color-primary: #1f2329;
1110
}
1211

ui/src/views/document/index.vue

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<LayoutContainer header="文档">
2+
<LayoutContainer header="文档" class="document-main">
33
<div class="main-calc-height">
44
<div class="p-24">
55
<div class="flex-between">
@@ -403,12 +403,25 @@
403403
</el-table-column>
404404
</app-table>
405405
</div>
406+
406407
<ImportDocumentDialog ref="ImportDocumentDialogRef" :title="title" @refresh="refresh" />
407408
<SyncWebDialog ref="SyncWebDialogRef" @refresh="refresh" />
408409
<!-- 选择知识库 -->
409410
<SelectDatasetDialog ref="SelectDatasetDialogRef" @refresh="refreshMigrate" />
410411
<GenerateRelatedDialog ref="GenerateRelatedDialogRef" @refresh="refresh" />
411412
</div>
413+
<div class="mul-operation w-full flex" v-if="multipleSelection.length !== 0">
414+
<el-button :disabled="multipleSelection.length === 0" @click="cancelTaskHandle(1)">
415+
取消向量化
416+
</el-button>
417+
<el-button :disabled="multipleSelection.length === 0" @click="cancelTaskHandle(2)">
418+
取消生成
419+
</el-button>
420+
<el-text type="info" class="secondary ml-24">
421+
已选 {{ multipleSelection.length }} 项
422+
</el-text>
423+
<el-button class="ml-16" type="primary" link @click="clearSelection"> 清空 </el-button>
424+
</div>
412425
</LayoutContainer>
413426
</template>
414427
<script setup lang="ts">
@@ -478,6 +491,7 @@ const multipleSelection = ref<any[]>([])
478491
const title = ref('')
479492
480493
const SelectDatasetDialogRef = ref()
494+
481495
const exportDocument = (document: any) => {
482496
documentApi.exportDocument(document.name, document.dataset_id, document.id, loading).then(() => {
483497
MsgSuccess('导出成功')
@@ -490,6 +504,28 @@ const exportDocumentZip = (document: any) => {
490504
MsgSuccess('导出成功')
491505
})
492506
}
507+
508+
function cancelTaskHandle(val: any) {
509+
const arr: string[] = []
510+
multipleSelection.value.map((v) => {
511+
if (v) {
512+
arr.push(v.id)
513+
}
514+
})
515+
const obj = {
516+
id_list: arr,
517+
type: val
518+
}
519+
documentApi.batchCancelTask(id, obj, loading).then(() => {
520+
MsgSuccess('批量取消成功')
521+
multipleTableRef.value?.clearSelection()
522+
})
523+
}
524+
525+
function clearSelection() {
526+
multipleTableRef.value?.clearSelection()
527+
}
528+
493529
function openDatasetDialog(row?: any) {
494530
const arr: string[] = []
495531
if (row) {
@@ -813,4 +849,20 @@ onBeforeUnmount(() => {
813849
closeInterval()
814850
})
815851
</script>
816-
<style lang="scss" scoped></style>
852+
<style lang="scss" scoped>
853+
.document-main {
854+
box-sizing: border-box;
855+
.mul-operation {
856+
position: fixed;
857+
margin-left: var(--sidebar-width);
858+
bottom: 0;
859+
right: 24px;
860+
width: calc(100% - var(--sidebar-width) - 48px);
861+
padding: 16px 24px;
862+
box-sizing: border-box;
863+
background: #ffffff;
864+
z-index: 22;
865+
box-shadow: 0px -2px 4px 0px rgba(31, 35, 41, 0.08);
866+
}
867+
}
868+
</style>

0 commit comments

Comments
 (0)