File size: 6,040 Bytes
f8b03c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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,
    )