176 lines
6.7 KiB
Python
176 lines
6.7 KiB
Python
"""文档服务实现。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import mimetypes
|
|
import time
|
|
from pathlib import Path
|
|
|
|
from sqlalchemy import text
|
|
|
|
from fastapi_common.fastapi_common_sqlalchemy.database import GetAsyncSession
|
|
from fastapi_common.fastapi_common_web.domain.responses import StatusCodeEnum
|
|
from fastapi_common.fastapi_common_web.exception.LeauditException import LeauditException
|
|
from fastapi_common.fastapi_common_storage.oss_path_utils import OssPathUtils
|
|
|
|
from fastapi_modules.fastapi_leaudit.domian.vo.documentVo import DocumentUploadVO
|
|
from fastapi_modules.fastapi_leaudit.models import LeauditDocument, LeauditDocumentFile
|
|
from fastapi_modules.fastapi_leaudit.services import IAuditService, IDocumentService, IOssService
|
|
from fastapi_modules.fastapi_leaudit.services.impl.auditServiceImpl import AuditServiceImpl
|
|
from fastapi_modules.fastapi_leaudit.services.impl.ossServiceImpl import OssServiceImpl
|
|
|
|
|
|
class DocumentServiceImpl(IDocumentService):
|
|
"""文档服务实现。"""
|
|
|
|
def __init__(
|
|
self,
|
|
OssService: IOssService | None = None,
|
|
AuditService: IAuditService | None = None,
|
|
) -> None:
|
|
self.OssService = OssService or OssServiceImpl()
|
|
self.AuditService = AuditService or AuditServiceImpl()
|
|
|
|
async def Upload(
|
|
self,
|
|
FileName: str,
|
|
FileContent: bytes,
|
|
ContentType: str | None,
|
|
TypeId: int | None = None,
|
|
TypeCode: str | None = None,
|
|
BizDocumentId: int | None = None,
|
|
Region: str = "default",
|
|
FileRole: str = "primary",
|
|
CreatedBy: int | None = None,
|
|
AutoRun: bool = False,
|
|
) -> DocumentUploadVO:
|
|
"""上传文档并建立 LeAudit document/file 记录。"""
|
|
if not FileName:
|
|
raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "上传文件名不能为空")
|
|
if not FileContent:
|
|
raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "上传文件内容不能为空")
|
|
if not TypeId and not TypeCode:
|
|
raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "typeId 与 typeCode 至少传一个")
|
|
|
|
normalizedRegion = (Region or "default").strip() or "default"
|
|
normalizedFileRole = (FileRole or "primary").strip() or "primary"
|
|
fileExt = Path(FileName).suffix.lstrip(".").lower() or None
|
|
mimeType = ContentType or mimetypes.guess_type(FileName)[0] or "application/octet-stream"
|
|
fileSha256 = hashlib.sha256(FileContent).hexdigest()
|
|
fileSize = len(FileContent)
|
|
|
|
async with GetAsyncSession() as Session:
|
|
if TypeId is not None and TypeCode is not None:
|
|
typeResult = await Session.execute(
|
|
text(
|
|
"""
|
|
SELECT id, code
|
|
FROM leaudit_document_types
|
|
WHERE id = :type_id
|
|
AND code = :type_code
|
|
AND deleted_at IS NULL
|
|
LIMIT 1
|
|
"""
|
|
),
|
|
{"type_id": TypeId, "type_code": TypeCode},
|
|
)
|
|
elif TypeId is not None:
|
|
typeResult = await Session.execute(
|
|
text(
|
|
"""
|
|
SELECT id, code
|
|
FROM leaudit_document_types
|
|
WHERE id = :type_id
|
|
AND deleted_at IS NULL
|
|
LIMIT 1
|
|
"""
|
|
),
|
|
{"type_id": TypeId},
|
|
)
|
|
else:
|
|
typeResult = await Session.execute(
|
|
text(
|
|
"""
|
|
SELECT id, code
|
|
FROM leaudit_document_types
|
|
WHERE code = :type_code
|
|
AND deleted_at IS NULL
|
|
LIMIT 1
|
|
"""
|
|
),
|
|
{"type_code": TypeCode},
|
|
)
|
|
typeRow = typeResult.mappings().first()
|
|
if not typeRow:
|
|
raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "文档类型不存在或已停用")
|
|
|
|
resolvedTypeId = int(typeRow["id"])
|
|
resolvedTypeCode = str(typeRow["code"])
|
|
resolvedBizDocumentId = BizDocumentId or int(time.time() * 1000)
|
|
|
|
document = await LeauditDocument.upsert_by_biz_id(
|
|
Session,
|
|
bizDocumentId=resolvedBizDocumentId,
|
|
typeId=resolvedTypeId,
|
|
region=normalizedRegion,
|
|
processingStatus="waiting",
|
|
)
|
|
|
|
versionCount = await LeauditDocumentFile.count_by_document(Session, document.Id)
|
|
versionNo = f"v{versionCount + 1}"
|
|
objectKey = OssPathUtils.BuildBusinessDocKey(
|
|
Region=normalizedRegion,
|
|
TypeCode=resolvedTypeCode,
|
|
DocumentId=document.Id,
|
|
Version=versionNo,
|
|
FileRole=normalizedFileRole,
|
|
FileName=FileName,
|
|
)
|
|
ossUrl = await self.OssService.UploadBytes(
|
|
ObjectKey=objectKey,
|
|
Content=FileContent,
|
|
ContentType=mimeType,
|
|
)
|
|
|
|
await LeauditDocumentFile.deactivate_active_by_document(Session, document.Id)
|
|
documentFile = LeauditDocumentFile(
|
|
documentId=document.Id,
|
|
fileRole=normalizedFileRole,
|
|
fileName=FileName,
|
|
fileExt=fileExt,
|
|
mimeType=mimeType,
|
|
fileSize=fileSize,
|
|
sha256=fileSha256,
|
|
localPath=None,
|
|
ossUrl=ossUrl,
|
|
storageProvider="minio",
|
|
isActive=True,
|
|
createdBy=CreatedBy,
|
|
)
|
|
Session.add(documentFile)
|
|
await Session.flush()
|
|
await Session.commit()
|
|
await Session.refresh(document)
|
|
await Session.refresh(documentFile)
|
|
|
|
run = None
|
|
processingStatus = document.processingStatus or "waiting"
|
|
if AutoRun:
|
|
run = await self.AuditService.Run(DocumentId=document.Id)
|
|
processingStatus = "running" if run.status in {"pending", "running"} else run.status
|
|
|
|
return DocumentUploadVO(
|
|
documentId=document.Id,
|
|
bizDocumentId=document.bizDocumentId,
|
|
fileId=documentFile.Id,
|
|
typeId=resolvedTypeId,
|
|
typeCode=resolvedTypeCode,
|
|
region=normalizedRegion,
|
|
fileName=documentFile.fileName,
|
|
ossUrl=ossUrl,
|
|
processingStatus=processingStatus,
|
|
autoRunTriggered=AutoRun,
|
|
run=run,
|
|
)
|