feat: add document replacement functionality with file upload

This commit is contained in:
CaptainB 2025-10-20 14:41:20 +08:00
parent 76ba9d0513
commit 620d4ff996
8 changed files with 100 additions and 0 deletions

View File

@ -28,6 +28,7 @@ from common.db.search import native_search, get_dynamics_model, native_page_sear
from common.event import ListenerManagement
from common.event.common import work_thread_pool
from common.exception.app_exception import AppApiException
from common.field.common import UploadedFileField
from common.handle.impl.qa.csv_parse_qa_handle import CsvParseQAHandle
from common.handle.impl.qa.xls_parse_qa_handle import XlsParseQAHandle
from common.handle.impl.qa.xlsx_parse_qa_handle import XlsxParseQAHandle
@ -1503,6 +1504,38 @@ class DocumentSerializers(serializers.Serializer):
tag_id__in=tag_ids
).delete()
class ReplaceSourceFile(serializers.Serializer):
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
knowledge_id = serializers.UUIDField(required=True, label=_('knowledge id'))
document_id = serializers.UUIDField(required=True, label=_('document id'))
file = UploadedFileField(required=True, label=_("file"))
def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
workspace_id = self.data.get('workspace_id')
query_set = QuerySet(Knowledge).filter(id=self.data.get('knowledge_id'))
if workspace_id and workspace_id != 'None':
query_set = query_set.filter(workspace_id=workspace_id)
if not query_set.exists():
raise AppApiException(500, _('Knowledge id does not exist'))
if not QuerySet(Document).filter(
id=self.data.get('document_id'),
knowledge_id=self.data.get('knowledge_id')
).exists():
raise AppApiException(500, _('Document id does not exist'))
def replace(self):
self.is_valid(raise_exception=True)
file = self.data.get('file')
source_file = QuerySet(File).filter(source_id=self.data.get('document_id')).first()
if not source_file:
raise AppApiException(500, _('Source file not found'))
source_file.save(file.read())
return True
class FileBufferHandle:
buffer = None

View File

@ -46,6 +46,7 @@ urlpatterns = [
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/document/<str:document_id>/export', views.DocumentView.Export.as_view()),
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/document/<str:document_id>/export_zip', views.DocumentView.ExportZip.as_view()),
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/document/<str:document_id>/download_source_file', views.DocumentView.DownloadSourceFile.as_view()),
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/document/<str:document_id>/replace_source_file', views.DocumentView.ReplaceSourceFile.as_view()),
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/document/<str:document_id>/tags', views.DocumentView.Tags.as_view()),
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/document/<str:document_id>/tags/batch_delete', views.DocumentView.Tags.BatchDelete.as_view()),
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/document/<str:document_id>/paragraph', views.ParagraphView.as_view()),

View File

@ -686,6 +686,31 @@ class DocumentView(APIView):
'workspace_id': workspace_id, 'document_id': document_id, 'knowledge_id': knowledge_id
}).download_source_file()
class ReplaceSourceFile(APIView):
authentication_classes = [TokenAuth]
@extend_schema(
summary=_('Replace source file'),
operation_id=_('Replace source file'), # type: ignore
parameters=DocumentDownloadSourceAPI.get_parameters(),
responses=DocumentDownloadSourceAPI.get_response(),
tags=[_('Knowledge Base/Documentation')] # type: ignore
)
@has_permissions(
PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission(),
PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.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 post(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str):
return result.success(DocumentSerializers.ReplaceSourceFile(data={
'workspace_id': workspace_id,
'document_id': document_id,
'knowledge_id': knowledge_id,
'file': request.FILES.get('file')
}).replace())
class Tags(APIView):
authentication_classes = [TokenAuth]

View File

@ -144,6 +144,14 @@ const getDownloadSourceFile: (knowledge_id: string, document_id: string, documen
return exportFile(document_name, `${prefix.value}/${knowledge_id}/document/${document_id}/download_source_file`, {}, undefined)
}
const postReplaceSourceFile: (knowledge_id: string, document_id: string, data: any) => Promise<Result<any>> = (
knowledge_id,
document_id,
data,
) => {
return post(`${prefix.value}/${knowledge_id}/document/${document_id}/replace_source_file`, data, {}, undefined)
}
/**
*
* @param document_name
@ -608,6 +616,7 @@ export default {
putBatchCancelTask,
putCancelTask,
getDownloadSourceFile,
postReplaceSourceFile,
exportDocument,
exportDocumentZip,
putDocumentRefresh,

View File

@ -14,6 +14,7 @@ export default {
cancelGenerate: 'Cancel Generation',
export: 'Export to',
download: 'Download',
replace: 'Replace',
},
tip: {

View File

@ -14,6 +14,7 @@ export default {
cancelGenerate: '取消生成',
export: '导出',
download: '下载原文档',
replace: '替换原文档',
},
tip: {
saveMessage: '当前的更改尚未保存,确认退出吗?',

View File

@ -14,6 +14,7 @@ export default {
cancelGenerate: '取消生成',
export: '匯出',
download: '下載原文件',
replace: '替換原文件',
},
tip: {
saveMessage: '當前的更改尚未保存,確認退出嗎?',

View File

@ -494,6 +494,21 @@
</el-icon>
{{ $t('views.document.setting.download') }}
</el-dropdown-item>
<el-upload
ref="elUploadRef"
:file-list="[]"
action="#"
:auto-upload="false"
:show-file-list="false"
:on-change="(file: any, fileList: any) => replaceDocument(file, row)"
>
<el-dropdown-item v-if="permissionPrecise.doc_edit(id)">
<el-icon class="color-secondary">
<Upload />
</el-icon>
{{ $t('views.document.setting.replace') }}
</el-dropdown-item>
</el-upload>
<el-dropdown-item
@click.stop="deleteDocument(row)"
v-if="permissionPrecise.doc_delete(id)"
@ -1079,6 +1094,20 @@ function downloadDocument(row: any) {
})
}
const elUploadRef = ref()
function replaceDocument(file: any, row: any) {
const formData = new FormData()
formData.append('file', file.raw, file.name)
elUploadRef.value.clearFiles()
loadSharedApi({ type: 'document', systemType: apiType.value })
.postReplaceSourceFile(id, row.id, formData, loading)
.then(() => {
getList()
})
.catch((e: any) => {})
}
function deleteDocument(row: any) {
MsgConfirm(
`${t('views.document.delete.confirmTitle3')} ${row.name} ?`,