From cd68e897ad5059fa996606ffd30a98fa1fec460e Mon Sep 17 00:00:00 2001 From: CaptainB Date: Mon, 3 Nov 2025 18:11:49 +0800 Subject: [PATCH] feat: add knowledge workflow and version models, serializers, and API views --- apps/knowledge/api/knowledge_workflow.py | 11 +++ apps/knowledge/models/knowledge.py | 36 ++++++++++ .../serializers/knowledge_workflow.py | 67 +++++++++++++++++++ apps/knowledge/urls.py | 2 +- apps/knowledge/views/__init__.py | 1 + apps/knowledge/views/knowledge_workflow.py | 39 +++++++++++ 6 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 apps/knowledge/api/knowledge_workflow.py create mode 100644 apps/knowledge/serializers/knowledge_workflow.py create mode 100644 apps/knowledge/views/knowledge_workflow.py diff --git a/apps/knowledge/api/knowledge_workflow.py b/apps/knowledge/api/knowledge_workflow.py new file mode 100644 index 000000000..38296bb89 --- /dev/null +++ b/apps/knowledge/api/knowledge_workflow.py @@ -0,0 +1,11 @@ +# coding=utf-8 + +from common.mixins.api_mixin import APIMixin + + +class KnowledgeWorkflowApi(APIMixin): + pass + + +class KnowledgeWorkflowVersionApi(APIMixin): + pass diff --git a/apps/knowledge/models/knowledge.py b/apps/knowledge/models/knowledge.py index dca940b48..348d67cf5 100644 --- a/apps/knowledge/models/knowledge.py +++ b/apps/knowledge/models/knowledge.py @@ -23,6 +23,7 @@ class KnowledgeType(models.IntegerChoices): WEB = 1, 'web站点类型' LARK = 2, '飞书类型' YUQUE = 3, '语雀类型' + WORKFLOW = 4, '工作流类型' class TaskType(Enum): @@ -135,6 +136,40 @@ class Knowledge(AppModelMixin): db_table = "knowledge" +class KnowledgeWorkflow(AppModelMixin): + """ + 知识库工作流表 + """ + id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id") + knowledge = models.OneToOneField(Knowledge, on_delete=models.CASCADE, verbose_name="知识库", + db_constraint=False, related_name='workflow') + workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True) + work_flow = models.JSONField(verbose_name="工作流数据", default=dict) + is_publish = models.BooleanField(verbose_name="是否发布", default=False, db_index=True) + publish_time = models.DateTimeField(verbose_name="发布时间", null=True, blank=True) + + class Meta: + db_table = "knowledge_workflow" + + +class KnowledgeWorkflowVersion(AppModelMixin): + """ + 知识库工作流版本表 - 记录工作流历史版本 + """ + id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id") + knowledge = models.ForeignKey(Knowledge, on_delete=models.CASCADE, verbose_name="知识库", db_constraint=False) + workflow = models.ForeignKey(KnowledgeWorkflow, on_delete=models.CASCADE, verbose_name="工作流", + db_constraint=False, related_name='versions') + workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True) + work_flow = models.JSONField(verbose_name="工作流数据", default=dict) + publish_user_id = models.UUIDField(verbose_name="发布者id", max_length=128, default=None, null=True) + publish_user_name = models.CharField(verbose_name="发布者名称", max_length=128, default="") + + class Meta: + db_table = "knowledge_workflow_version" + unique_together = [['knowledge', 'version']] # 同一知识库的版本号唯一 + + def get_default_status(): return Status('').__str__() @@ -162,6 +197,7 @@ class Document(AppModelMixin): class Meta: db_table = "document" + class Tag(AppModelMixin): """ 标签表 - 存储标签的key-value定义 diff --git a/apps/knowledge/serializers/knowledge_workflow.py b/apps/knowledge/serializers/knowledge_workflow.py new file mode 100644 index 000000000..d41362c28 --- /dev/null +++ b/apps/knowledge/serializers/knowledge_workflow.py @@ -0,0 +1,67 @@ +# coding=utf-8 + +from typing import Dict + +import uuid_utils.compat as uuid +from django.db import transaction +from django.db.models import QuerySet +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from common.exception.app_exception import AppApiException +from knowledge.models import KnowledgeScope, Knowledge, KnowledgeType, KnowledgeWorkflow +from knowledge.serializers.knowledge import KnowledgeModelSerializer +from system_manage.models import AuthTargetType +from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer + + +class KnowledgeWorkflowSerializer(serializers.Serializer): + class Create(serializers.Serializer): + user_id = serializers.UUIDField(required=True, label=_('user id')) + workspace_id = serializers.CharField(required=True, label=_('workspace id')) + scope = serializers.ChoiceField( + required=False, label=_('scope'), default=KnowledgeScope.WORKSPACE, choices=KnowledgeScope.choices + ) + + @transaction.atomic + def save_workflow(self, instance: Dict): + self.is_valid(raise_exception=True) + + folder_id = instance.get('folder_id', self.data.get('workspace_id')) + if QuerySet(Knowledge).filter( + workspace_id=self.data.get('workspace_id'), folder_id=folder_id, name=instance.get('name') + ).exists(): + raise AppApiException(500, _('Knowledge base name duplicate!')) + + knowledge_id = uuid.uuid7() + knowledge = Knowledge( + id=knowledge_id, + name=instance.get('name'), + desc=instance.get('desc'), + user_id=self.data.get('user_id'), + type=instance.get('type', KnowledgeType.WORKFLOW), + scope=self.data.get('scope', KnowledgeScope.WORKSPACE), + folder_id=folder_id, + workspace_id=self.data.get('workspace_id'), + embedding_model_id=instance.get('embedding_model_id'), + meta={}, + ) + knowledge.save() + # 自动资源给授权当前用户 + UserResourcePermissionSerializer(data={ + 'workspace_id': self.data.get('workspace_id'), + 'user_id': self.data.get('user_id'), + 'auth_target_type': AuthTargetType.KNOWLEDGE.value + }).auth_resource(str(knowledge_id)) + + knowledge_workflow = KnowledgeWorkflow( + id=uuid.uuid7(), + knowledge_id=knowledge_id, + workspace_id=self.data.get('workspace_id'), + work_flow=instance.get('work_flow', {}), + + ) + + knowledge_workflow.save() + + return {**KnowledgeModelSerializer(knowledge).data, 'document_list': []} diff --git a/apps/knowledge/urls.py b/apps/knowledge/urls.py index 3b41be1e9..b8f20dcaf 100644 --- a/apps/knowledge/urls.py +++ b/apps/knowledge/urls.py @@ -67,5 +67,5 @@ urlpatterns = [ path('workspace//knowledge//problem//', views.ProblemView.Page.as_view()), path('workspace//knowledge//document//', views.DocumentView.Page.as_view()), path('workspace//knowledge//', views.KnowledgeView.Page.as_view()), - + path('workspace//knowledge//workflow', views.KnowledgeWorkflowView.as_view()), ] diff --git a/apps/knowledge/views/__init__.py b/apps/knowledge/views/__init__.py index ed401ad8a..98a57a228 100644 --- a/apps/knowledge/views/__init__.py +++ b/apps/knowledge/views/__init__.py @@ -3,3 +3,4 @@ from .knowledge import * from .paragraph import * from .problem import * from .tag import * +from .knowledge_workflow import * diff --git a/apps/knowledge/views/knowledge_workflow.py b/apps/knowledge/views/knowledge_workflow.py new file mode 100644 index 000000000..1b8b47ddb --- /dev/null +++ b/apps/knowledge/views/knowledge_workflow.py @@ -0,0 +1,39 @@ +# coding=utf-8 + +from django.utils.translation import gettext_lazy as _ +from drf_spectacular.utils import extend_schema +from rest_framework.request import Request +from rest_framework.views import APIView + +from common.auth import TokenAuth +from common.auth.authentication import has_permissions +from common.constants.permission_constants import PermissionConstants, RoleConstants +from common.result import result +from knowledge.api.knowledge_workflow import KnowledgeWorkflowApi +from knowledge.serializers.knowledge_workflow import KnowledgeWorkflowSerializer + + +class KnowledgeWorkflowView(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_('Create knowledge workflow'), + summary=_('Create knowledge workflow'), + operation_id=_('Create knowledge workflow'), # type: ignore + parameters=KnowledgeWorkflowApi.get_parameters(), + responses=KnowledgeWorkflowApi.get_response(), + tags=[_('Knowledge Base')] # type: ignore + ) + @has_permissions( + PermissionConstants.KNOWLEDGE_CREATE.get_workspace_permission(), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role(), RoleConstants.USER.get_workspace_role() + ) + def post(self, request: Request, workspace_id: str): + return result.success(KnowledgeWorkflowSerializer.Create( + data={'user_id': request.user.id, 'workspace_id': workspace_id} + ).save_workflow(request.data)) + + +class KnowledgeWorkflowVersionView(APIView): + pass