File size: 6,040 Bytes
b4f2238 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | """案件文件夹绑定 API"""
from __future__ import annotations
import logging
from typing import Any
from django.http import HttpRequest
from ninja import Router
from apps.cases.schemas import (
CaseFolderBindingCreateSchema,
CaseFolderBindingResponseSchema,
FolderBrowseEntrySchema,
FolderBrowseResponseSchema,
)
from apps.core.security import get_request_access_context
logger = logging.getLogger("apps.cases.api")
router = Router()
def _get_folder_binding_service() -> Any:
"""工厂函数:获取案件文件夹绑定服务"""
from apps.cases.services import CaseFolderBindingService # type: ignore[attr-defined]
from apps.core.dependencies import (
build_case_service_with_deps,
build_client_service,
build_contract_query_service,
build_document_service,
)
return CaseFolderBindingService(
document_service=build_document_service(),
case_service=build_case_service_with_deps(
contract_service=build_contract_query_service(),
client_service=build_client_service(),
),
)
@router.post("/{case_id}/folder-binding", response=CaseFolderBindingResponseSchema)
def create_folder_binding(request: HttpRequest, case_id: int, data: CaseFolderBindingCreateSchema) -> Any:
"""创建或更新案件文件夹绑定"""
service = _get_folder_binding_service()
ctx = get_request_access_context(request)
binding = service.create_binding_ctx(case_id=case_id, folder_path=data.folder_path, ctx=ctx)
is_accessible: bool = service.check_folder_accessible(binding.folder_path)
display_path: str = service.format_path_for_display(binding.folder_path)
logger.info(
"case_folder_binding_upsert",
extra={
"action": "case_folder_binding_upsert",
"case_id": case_id,
"folder_path": str(getattr(binding, "folder_path", "") or ""),
"display_path": str(display_path or ""),
"is_accessible": bool(is_accessible),
"user_id": getattr(getattr(ctx, "user", None), "id", None),
},
)
return CaseFolderBindingResponseSchema.from_binding(
binding,
is_accessible=is_accessible,
display_path=display_path,
)
@router.get("/{case_id}/folder-binding", response=CaseFolderBindingResponseSchema | None)
def get_folder_binding(request: HttpRequest, case_id: int) -> CaseFolderBindingResponseSchema | None:
"""获取案件文件夹绑定信息"""
service = _get_folder_binding_service()
ctx = get_request_access_context(request)
binding = service.get_binding_ctx(case_id=case_id, ctx=ctx)
if not binding:
return None
is_accessible: bool = service.check_folder_accessible(binding.folder_path)
display_path: str = service.format_path_for_display(binding.folder_path)
return CaseFolderBindingResponseSchema.from_binding(binding, is_accessible=is_accessible, display_path=display_path)
@router.delete("/{case_id}/folder-binding")
def delete_folder_binding(request: HttpRequest, case_id: int) -> dict[str, bool | str]:
"""删除案件文件夹绑定"""
service = _get_folder_binding_service()
ctx = get_request_access_context(request)
success: bool = service.delete_binding_ctx(case_id=case_id, ctx=ctx)
logger.info(
"case_folder_binding_delete",
extra={
"action": "case_folder_binding_delete",
"case_id": case_id,
"success": bool(success),
"user_id": getattr(getattr(ctx, "user", None), "id", None),
},
)
return {"success": success, "message": "文件夹绑定删除成功" if success else "未找到绑定记录"}
@router.get("/folder-browse", response=FolderBrowseResponseSchema)
def browse_folders(request: HttpRequest, path: str | None = None, include_hidden: bool = False) -> Any:
service = _get_folder_binding_service()
ctx = get_request_access_context(request)
service.require_admin(ctx)
if not path or not str(path).strip():
default_path = service.get_default_browse_path()
if default_path:
path = str(default_path)
else:
roots = service.get_browse_roots()
entries = [FolderBrowseEntrySchema(name=(p.name or str(p)), path=str(p)) for p in roots]
logger.info(
"case_folder_browse_roots",
extra={
"action": "case_folder_browse_roots",
"include_hidden": bool(include_hidden),
"roots_count": len(roots),
"user_id": getattr(getattr(ctx, "user", None), "id", None),
},
)
return FolderBrowseResponseSchema(
browsable=True,
message=None,
path=None,
parent_path=None,
entries=entries,
)
browsable, browse_message = service.is_browsable_path(str(path))
if not browsable:
return FolderBrowseResponseSchema(
browsable=False,
message=browse_message,
path=str(path).strip(),
parent_path=None,
entries=[],
)
resolved = service.resolve_under_allowed_roots(str(path))
entries = [
FolderBrowseEntrySchema(**item) for item in service.list_subdirs(str(path), include_hidden=include_hidden)
]
parent_path: str | None = service.compute_parent_path(resolved)
logger.info(
"case_folder_browse",
extra={
"action": "case_folder_browse",
"path": str(path).strip(),
"resolved_path": str(resolved),
"include_hidden": bool(include_hidden),
"entries_count": len(entries),
"user_id": getattr(getattr(ctx, "user", None), "id", None),
},
)
return FolderBrowseResponseSchema(
browsable=True,
message=None,
path=str(resolved),
parent_path=parent_path,
entries=entries,
)
|