From c4dd09ca1edb45232351de3eb4bb9b8deee60d62 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 <80892890+shaohuzhang1@users.noreply.github.com> Date: Thu, 4 Dec 2025 14:22:46 +0800 Subject: [PATCH] feat: Knowledge Base Workflow Execution Record (#4435) --- .../serializers/knowledge_workflow.py | 49 +++++++++-- apps/knowledge/urls.py | 1 + apps/knowledge/views/knowledge_workflow.py | 35 +++++++- ui/src/api/knowledge/knowledge.ts | 26 ++++-- .../component/list-action/index.vue | 83 +++++++++++++++++++ ui/src/views/knowledge-workflow/index.vue | 15 +++- 6 files changed, 188 insertions(+), 21 deletions(-) create mode 100644 ui/src/views/knowledge-workflow/component/list-action/index.vue diff --git a/apps/knowledge/serializers/knowledge_workflow.py b/apps/knowledge/serializers/knowledge_workflow.py index 991e1cdd0..74b3b4154 100644 --- a/apps/knowledge/serializers/knowledge_workflow.py +++ b/apps/knowledge/serializers/knowledge_workflow.py @@ -15,6 +15,7 @@ from application.flow.i_step_node import KnowledgeWorkflowPostHandler from application.flow.knowledge_workflow_manage import KnowledgeWorkflowManage from application.flow.step_node import get_node from application.serializers.application import get_mcp_tools +from common.db.search import page_search from common.exception.app_exception import AppApiException from common.utils.rsa_util import rsa_long_decrypt from common.utils.tool_code import ToolExecutor @@ -41,16 +42,46 @@ class KnowledgeWorkflowActionRequestSerializer(serializers.Serializer): knowledge_base = serializers.DictField(required=True, label=_('knowledge base data')) +class KnowledgeWorkflowActionListQuerySerializer(serializers.Serializer): + user_name = serializers.CharField(required=False, label=_('Name'), allow_blank=True, allow_null=True) + + class KnowledgeWorkflowActionSerializer(serializers.Serializer): workspace_id = serializers.CharField(required=True, label=_('workspace id')) knowledge_id = serializers.UUIDField(required=True, label=_('knowledge id')) - def action(self, instance: Dict, with_valid=True): + def get_query_set(self, instance: Dict): + query_set = QuerySet(KnowledgeAction).filter(knowledge_id=self.data.get('knowledge_id')) + if instance.get("user_name"): + query_set = query_set.filter(meta__user_name__icontains=instance.get('user_name')) + return query_set.order_by('-create_time') + + def list(self, instance: Dict, is_valid=True): + if is_valid: + self.is_valid(raise_exception=True) + KnowledgeWorkflowActionListQuerySerializer(data=instance).is_valid(raise_exception=True) + return [{'id': a.id, 'knowledge_id': a.knowledge_id, 'state': a.state, + 'details': a.details, 'meta': a.meta, 'run_time': a.run_time} for a in self.get_query_set(instance)] + + def page(self, current_page, page_size, instance: Dict, is_valid=True): + if is_valid: + self.is_valid(raise_exception=True) + KnowledgeWorkflowActionListQuerySerializer(data=instance).is_valid(raise_exception=True) + return page_search(current_page, page_size, self.get_query_set(instance), + lambda a: {'id': a.id, 'knowledge_id': a.knowledge_id, 'state': a.state, + 'details': a.details, 'meta': a.meta, 'run_time': a.run_time}) + + def action(self, instance: Dict, user, with_valid=True): if with_valid: self.is_valid(raise_exception=True) knowledge_workflow = QuerySet(KnowledgeWorkflow).filter(knowledge_id=self.data.get("knowledge_id")).first() knowledge_action_id = uuid.uuid7() - KnowledgeAction(id=knowledge_action_id, knowledge_id=self.data.get("knowledge_id"), state=State.STARTED).save() + meta = {'user_id': str(user.id), + 'user_name': user.username} + KnowledgeAction(id=knowledge_action_id, + knowledge_id=self.data.get("knowledge_id"), + state=State.STARTED, + meta=meta).save() work_flow_manage = KnowledgeWorkflowManage( Workflow.new_instance(knowledge_workflow.work_flow, WorkflowMode.KNOWLEDGE), {'knowledge_id': self.data.get("knowledge_id"), 'knowledge_action_id': knowledge_action_id, 'stream': True, @@ -59,9 +90,9 @@ class KnowledgeWorkflowActionSerializer(serializers.Serializer): KnowledgeWorkflowPostHandler(None, knowledge_action_id)) work_flow_manage.run() return {'id': knowledge_action_id, 'knowledge_id': self.data.get("knowledge_id"), 'state': State.STARTED, - 'details': {}} + 'details': {}, 'meta': meta} - def upload_document(self, instance: Dict, with_valid=True): + def upload_document(self, instance: Dict, user, with_valid=True): if with_valid: self.is_valid(raise_exception=True) knowledge_workflow = QuerySet(KnowledgeWorkflow).filter(knowledge_id=self.data.get("knowledge_id")).first() @@ -71,7 +102,10 @@ class KnowledgeWorkflowActionSerializer(serializers.Serializer): knowledge_id=self.data.get("knowledge_id")).order_by( '-create_time')[0:1].first() knowledge_action_id = uuid.uuid7() - KnowledgeAction(id=knowledge_action_id, knowledge_id=self.data.get("knowledge_id"), state=State.STARTED).save() + meta = {'user_id': str(user.id), + 'user_name': user.username} + KnowledgeAction(id=knowledge_action_id, knowledge_id=self.data.get("knowledge_id"), state=State.STARTED, + meta=meta).save() work_flow_manage = KnowledgeWorkflowManage( Workflow.new_instance(knowledge_workflow_version.work_flow, WorkflowMode.KNOWLEDGE), {'knowledge_id': self.data.get("knowledge_id"), 'knowledge_action_id': knowledge_action_id, 'stream': True, @@ -80,7 +114,7 @@ class KnowledgeWorkflowActionSerializer(serializers.Serializer): KnowledgeWorkflowPostHandler(None, knowledge_action_id)) work_flow_manage.run() return {'id': knowledge_action_id, 'knowledge_id': self.data.get("knowledge_id"), 'state': State.STARTED, - 'details': {}} + 'details': {}, 'meta': meta} class Operate(serializers.Serializer): workspace_id = serializers.CharField(required=True, label=_('workspace id')) @@ -94,7 +128,8 @@ class KnowledgeWorkflowActionSerializer(serializers.Serializer): knowledge_action = QuerySet(KnowledgeAction).filter(id=knowledge_action_id).first() return {'id': knowledge_action_id, 'knowledge_id': knowledge_action.knowledge_id, 'state': knowledge_action.state, - 'details': knowledge_action.details} + 'details': knowledge_action.details, + 'meta': knowledge_action.meta} class KnowledgeWorkflowSerializer(serializers.Serializer): diff --git a/apps/knowledge/urls.py b/apps/knowledge/urls.py index 43a77aec3..3e27fbda6 100644 --- a/apps/knowledge/urls.py +++ b/apps/knowledge/urls.py @@ -73,6 +73,7 @@ urlpatterns = [ path('workspace//knowledge//datasource///', views.KnowledgeDatasourceView.as_view()), path('workspace//knowledge//publish', views.KnowledgeWorkflowView.Publish.as_view()), path('workspace//knowledge//debug', views.KnowledgeWorkflowActionView.as_view()), + path('workspace//knowledge//action//', views.KnowledgeWorkflowActionView.Page.as_view()), path('workspace//knowledge//upload_document', views.KnowledgeWorkflowUploadDocumentView.as_view()), path('workspace//knowledge//action/', views.KnowledgeWorkflowActionView.Operate.as_view()), path('workspace//knowledge//mcp_tools', views.McpServers.as_view()), diff --git a/apps/knowledge/views/knowledge_workflow.py b/apps/knowledge/views/knowledge_workflow.py index 1bc7ae439..a78a4d588 100644 --- a/apps/knowledge/views/knowledge_workflow.py +++ b/apps/knowledge/views/knowledge_workflow.py @@ -78,14 +78,15 @@ class KnowledgeWorkflowUploadDocumentView(APIView): ) def post(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(KnowledgeWorkflowActionSerializer( - data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id}).upload_document(request.data, True)) + data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id}).upload_document(request.data, + request.user, True)) class KnowledgeWorkflowActionView(APIView): authentication_classes = [TokenAuth] @extend_schema( - methods=['GET'], + methods=['POST'], description=_('Knowledge workflow debug'), summary=_('Knowledge workflow debug'), operation_id=_('Knowledge workflow debug'), # type: ignore @@ -106,7 +107,35 @@ class KnowledgeWorkflowActionView(APIView): ) def post(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(KnowledgeWorkflowActionSerializer( - data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id}).action(request.data, True)) + data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id}).action(request.data, request.user, True)) + + class Page(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_('Page Knowledge workflow action'), + summary=_('Page Knowledge workflow action'), + operation_id=_('Page Knowledge workflow action'), # type: ignore + parameters=KnowledgeWorkflowActionApi.get_parameters(), + request=KnowledgeWorkflowActionApi.get_request(), + responses=KnowledgeWorkflowActionApi.get_response(), + tags=[_('Knowledge Base')] # type: ignore + ) + @has_permissions( + PermissionConstants.KNOWLEDGE_DOCUMENT_CREATE.get_workspace_knowledge_permission(), + PermissionConstants.KNOWLEDGE_DOCUMENT_CREATE.get_workspace_permission_workspace_manage_role(), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role(), + ViewPermission( + [RoleConstants.USER.get_workspace_role()], + [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()], + CompareConstants.AND + ), + ) + def get(self, request: Request, workspace_id: str, knowledge_id: str, current_page: int, page_size: int): + return result.success( + KnowledgeWorkflowActionSerializer(data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id}) + .page(current_page, page_size, request.data)) class Operate(APIView): authentication_classes = [TokenAuth] diff --git a/ui/src/api/knowledge/knowledge.ts b/ui/src/api/knowledge/knowledge.ts index 357e31453..98063f3fc 100644 --- a/ui/src/api/knowledge/knowledge.ts +++ b/ui/src/api/knowledge/knowledge.ts @@ -382,19 +382,19 @@ const publish: (knowledge_id: string, loading?: Ref) => Promise, ) => Promise> = (knowledge_id, data, loading) => { - return put(`${prefix.value}/${knowledge_id}/workflow`, data, undefined, loading) + return put(`${prefix.value}/${knowledge_id}/workflow`, data, undefined, loading) } - + const listKnowledgeVersion: ( knowledge_id: string, loading?: Ref, @@ -414,7 +414,18 @@ const updateKnowledgeVersion: ( loading, ) } - +const pageWorkflowAction: ( + knowledge_id: string, + page: pageRequest, + query: any, + loading?: Ref, +) => Promise> = (knowledge_id: string, page, query, loading) => { + return get( + `${prefix.value}/${knowledge_id}/action/${page.current_page}/${page.page_size}`, + query, + loading, + ) +} const getWorkflowAction: ( knowledge_id: string, knowledge_action_id: string, @@ -468,4 +479,5 @@ export default { publish, putKnowledgeWorkflow, workflowUpload, + pageWorkflowAction, } diff --git a/ui/src/views/knowledge-workflow/component/list-action/index.vue b/ui/src/views/knowledge-workflow/component/list-action/index.vue new file mode 100644 index 000000000..bce7b4c90 --- /dev/null +++ b/ui/src/views/knowledge-workflow/component/list-action/index.vue @@ -0,0 +1,83 @@ + + + diff --git a/ui/src/views/knowledge-workflow/index.vue b/ui/src/views/knowledge-workflow/index.vue index 41513ecaf..de208999d 100644 --- a/ui/src/views/knowledge-workflow/index.vue +++ b/ui/src/views/knowledge-workflow/index.vue @@ -34,8 +34,7 @@ {{ $t('common.debug') }} - + {{ $t('common.save') }} @@ -53,7 +52,10 @@ {{ $t('views.workflow.operation.toImportDoc') }} - + + + 执行记录 + {{ $t('views.workflow.setting.releaseHistory') }} @@ -127,6 +129,7 @@ + { return theme.isDefaultTheme() }) const DebugRef = ref>() - +const ListActionRef = ref>() let interval: any const workflowRef = ref() const workflowMainRef = ref() @@ -235,6 +239,9 @@ function back() { go() } } +const openListAction = () => { + ListActionRef.value?.open(id) +} function clickoutsideHistory() { if (!disablePublic.value) { showHistory.value = false