管理后台上传集成API | 首页素材装修追加提词/附件
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import UploadFile
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.enums.admin_upload import AdminUploadLogEventEnum, AdminUploadResourceTypeEnum, AdminUploadSceneEnum
|
||||
from app.enums.common import LogEventStatusEnum, LogSourceEnum
|
||||
from app.enums.upload_resource import (
|
||||
UploadResourceBindStatusEnum,
|
||||
UploadResourceCreatedByEnum,
|
||||
UploadResourceDeletePolicyEnum,
|
||||
UploadResourceDurationSourceEnum,
|
||||
UploadResourceModuleEnum,
|
||||
)
|
||||
from app.models.user import User
|
||||
from app.schemas.admin_upload import AdminUploadFileOut, AdminUploadMediaReferenceOut
|
||||
from app.services.admin_upload.storage import save_upload_file
|
||||
from app.services.operation_log_service import build_exception_detail, log_operation_event
|
||||
from app.services.upload_resource.core_service import record_external_upload_resource
|
||||
|
||||
|
||||
def _duration(value: float | None) -> float | None:
|
||||
if value is None:
|
||||
return None
|
||||
try:
|
||||
v = float(value)
|
||||
return round(v, 3) if v >= 0 else None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _module_for_scene(scene: AdminUploadSceneEnum) -> str:
|
||||
if scene == AdminUploadSceneEnum.HOME_MATERIAL_REFERENCE:
|
||||
return UploadResourceModuleEnum.HOME_MATERIAL.value
|
||||
return UploadResourceModuleEnum.ADMIN_UPLOAD.value
|
||||
|
||||
|
||||
def _role_for_type(resource_type: AdminUploadResourceTypeEnum) -> str | None:
|
||||
if resource_type == AdminUploadResourceTypeEnum.IMAGE:
|
||||
return "reference_image"
|
||||
if resource_type == AdminUploadResourceTypeEnum.VIDEO:
|
||||
return "reference_video"
|
||||
if resource_type == AdminUploadResourceTypeEnum.AUDIO:
|
||||
return "reference_audio"
|
||||
return None
|
||||
|
||||
|
||||
def _media_reference(*, url: str, resource_type: AdminUploadResourceTypeEnum, name: str | None, duration_seconds: float | None, resource_id: str) -> AdminUploadMediaReferenceOut:
|
||||
return AdminUploadMediaReferenceOut(
|
||||
url=url,
|
||||
type=resource_type.value,
|
||||
name=name,
|
||||
duration=duration_seconds,
|
||||
source="admin_upload",
|
||||
upload_resource_id=resource_id,
|
||||
display_url=url,
|
||||
preview_url=url,
|
||||
role=_role_for_type(resource_type),
|
||||
)
|
||||
|
||||
|
||||
def _log(event_type: str, *, admin: User, scene: str, resource_type: str, status: str = LogEventStatusEnum.SUCCESS.value, message: str | None = None, detail: dict[str, Any] | None = None, error: str | None = None) -> None:
|
||||
log_operation_event(
|
||||
domain="admin_upload",
|
||||
module="admin_upload",
|
||||
event_type=event_type,
|
||||
event_status=status,
|
||||
source=LogSourceEnum.API.value,
|
||||
user_id=admin.id,
|
||||
message=message,
|
||||
detail={"scene": scene, "resource_type": resource_type, **(detail or {})},
|
||||
error=error,
|
||||
)
|
||||
|
||||
|
||||
class AdminUploadService:
|
||||
async def upload_file(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
*,
|
||||
file: UploadFile,
|
||||
scene: AdminUploadSceneEnum,
|
||||
resource_type: AdminUploadResourceTypeEnum,
|
||||
admin: User,
|
||||
duration_seconds: float | None = None,
|
||||
) -> AdminUploadFileOut:
|
||||
duration = _duration(duration_seconds)
|
||||
_log(
|
||||
AdminUploadLogEventEnum.VALIDATE_STARTED.value,
|
||||
admin=admin,
|
||||
scene=scene.value,
|
||||
resource_type=resource_type.value,
|
||||
status=LogEventStatusEnum.STARTED.value,
|
||||
detail={"filename": file.filename, "content_type": file.content_type},
|
||||
)
|
||||
stored = None
|
||||
try:
|
||||
_log(
|
||||
AdminUploadLogEventEnum.VALIDATE_SUCCESS.value,
|
||||
admin=admin,
|
||||
scene=scene.value,
|
||||
resource_type=resource_type.value,
|
||||
detail={"filename": file.filename, "duration_seconds": duration},
|
||||
)
|
||||
_log(
|
||||
AdminUploadLogEventEnum.SAVE_STARTED.value,
|
||||
admin=admin,
|
||||
scene=scene.value,
|
||||
resource_type=resource_type.value,
|
||||
status=LogEventStatusEnum.STARTED.value,
|
||||
detail={"filename": file.filename},
|
||||
)
|
||||
stored = await save_upload_file(file, scene=scene, resource_type=resource_type, admin_id=admin.id)
|
||||
_log(
|
||||
AdminUploadLogEventEnum.SAVE_SUCCESS.value,
|
||||
admin=admin,
|
||||
scene=scene.value,
|
||||
resource_type=resource_type.value,
|
||||
detail={"url": stored.file_url, "storage_path": stored.storage_path, "file_size_bytes": stored.file_size_bytes},
|
||||
)
|
||||
module = _module_for_scene(scene)
|
||||
resource = await record_external_upload_resource(
|
||||
db,
|
||||
user_id=admin.id,
|
||||
module=module,
|
||||
resource_type=resource_type.value,
|
||||
resource_url=stored.file_url,
|
||||
storage_path=stored.storage_path,
|
||||
file_size_bytes=stored.file_size_bytes,
|
||||
file_name=stored.file_name,
|
||||
mime_type=file.content_type,
|
||||
duration_seconds=duration,
|
||||
duration_source=UploadResourceDurationSourceEnum.CLIENT.value if duration is not None else None,
|
||||
bind_status=UploadResourceBindStatusEnum.PENDING.value,
|
||||
delete_policy=UploadResourceDeletePolicyEnum.USER_DELETABLE.value,
|
||||
created_by=UploadResourceCreatedByEnum.API.value,
|
||||
metadata={
|
||||
"scene": scene.value,
|
||||
"original_filename": stored.original_file_name,
|
||||
"client_duration_seconds": duration_seconds,
|
||||
"source": "admin_upload",
|
||||
},
|
||||
)
|
||||
await db.refresh(resource)
|
||||
_log(
|
||||
AdminUploadLogEventEnum.DB_RECORD_SUCCESS.value,
|
||||
admin=admin,
|
||||
scene=scene.value,
|
||||
resource_type=resource_type.value,
|
||||
detail={"resource_id": resource.id, "url": stored.file_url, "module": module},
|
||||
)
|
||||
media_reference = _media_reference(
|
||||
url=stored.file_url,
|
||||
resource_type=resource_type,
|
||||
name=stored.original_file_name or stored.file_name,
|
||||
duration_seconds=resource.duration_seconds,
|
||||
resource_id=resource.id,
|
||||
)
|
||||
return AdminUploadFileOut(
|
||||
resource_id=resource.id,
|
||||
scene=scene,
|
||||
module=module,
|
||||
resource_type=resource_type,
|
||||
url=stored.file_url,
|
||||
file_name=stored.file_name,
|
||||
original_file_name=stored.original_file_name,
|
||||
file_size_bytes=stored.file_size_bytes,
|
||||
duration_seconds=resource.duration_seconds,
|
||||
media_reference=media_reference,
|
||||
)
|
||||
except Exception as exc:
|
||||
_log(
|
||||
AdminUploadLogEventEnum.FAILED.value,
|
||||
admin=admin,
|
||||
scene=scene.value,
|
||||
resource_type=resource_type.value,
|
||||
status=LogEventStatusEnum.FAILED.value,
|
||||
detail=build_exception_detail(exc, {"filename": file.filename}),
|
||||
error=str(exc),
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
admin_upload_service = AdminUploadService()
|
||||
Reference in New Issue
Block a user