File size: 5,904 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
175
176
177
178
179
180
181
182
183
184
185
"""案件模板绑定 API

管理案件与文书模板的绑定关系.
"""

from __future__ import annotations

from typing import Any
from urllib.parse import quote

from django.http import HttpRequest, HttpResponse
from ninja import Router

from apps.cases.schemas import UnifiedGenerateRequest
from apps.cases.schemas.template_binding_schemas import (
    AvailableTemplateSchema,
    BindingsResponseSchema,
    BindTemplateRequestSchema,
    GenerateTemplateRequestSchema,
    SuccessResponseSchema,
    TemplateBindingSchema,
)

router = Router()


# ==================== Factory Function ====================


def _get_binding_service() -> Any:
    """工厂函数:获取案件模板绑定服务"""
    from apps.cases.services.template.wiring import get_case_template_binding_service

    return get_case_template_binding_service()


def _get_generation_service() -> Any:
    """工厂函数:获取案件模板生成服务"""
    from apps.cases.services import CaseTemplateGenerationService  # type: ignore[import-untyped]

    return CaseTemplateGenerationService()


def _get_unified_template_generation_service() -> Any:
    """工厂函数:获取统一模板生成服务"""
    from apps.cases.services import UnifiedTemplateGenerationService  # type: ignore[import-untyped]

    return UnifiedTemplateGenerationService()


# ==================== API Endpoints ====================


@router.get("/{case_id}/template-bindings", response=BindingsResponseSchema)
def get_case_template_bindings(request: HttpRequest, case_id: int) -> Any:
    """
    获取案件绑定的模板列表

    按模板分类(case_sub_type)分组返回.
    """
    service = _get_binding_service()
    return service.get_bindings_for_case(case_id)


@router.post("/{case_id}/template-bindings", response=TemplateBindingSchema)
def bind_template_to_case(request: HttpRequest, case_id: int, payload: BindTemplateRequestSchema) -> Any:
    """
    绑定模板到案件

    创建手动绑定记录(binding_source='manual_bound').
    """
    service = _get_binding_service()
    return service.bind_template(case_id, payload.template_id)


@router.delete("/{case_id}/template-bindings/{binding_id}", response=SuccessResponseSchema)
def unbind_template_from_case(request: HttpRequest, case_id: int, binding_id: int) -> dict[str, bool]:
    """
    解绑模板

    删除指定的绑定记录.
    """
    service = _get_binding_service()
    service.unbind_template(case_id, binding_id)
    return {"success": True}


@router.get("/{case_id}/available-templates", response=list[AvailableTemplateSchema])
def get_available_templates(request: HttpRequest, case_id: int) -> Any:
    """
    获取可绑定的模板列表

    返回所有活跃的案件模板,排除已绑定的模板.
    """
    service = _get_binding_service()
    return service.get_available_templates(case_id)


@router.post("/{case_id}/generate-template")
def generate_template_document(
    request: HttpRequest, case_id: int, payload: GenerateTemplateRequestSchema
) -> HttpResponse:
    """
    生成模板文档并返回文件流

        case_id: 案件ID
        payload.template_id: 模板ID
        payload.client_id: 当事人ID(可选,用于法定代表人身份证明书、单独授权)
        payload.client_ids: 当事人ID列表(可选,用于合并授权)
        payload.mode: 授权模式(可选): 'individual' | 'combined'

        HttpResponse: docx 文件流

    Requirements: 2.1, 2.2, 2.3, 2.4, 6.2, 6.3, 7.2, 7.3, 7.4
    """
    service = _get_generation_service()
    content, filename = service.generate_document(
        case_id=case_id,
        template_id=payload.template_id,
        client_id=payload.client_id,
        client_ids=payload.client_ids,
        mode=payload.mode,
    )

    response = HttpResponse(
        content, content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    )
    # 使用 RFC 5987 编码处理中文文件名
    response["Content-Disposition"] = f"attachment; filename*=UTF-8''{filename}"
    return response


# ==================== Helper Functions ====================


def _build_file_response(content: bytes, filename: str) -> HttpResponse:
    """
    构建文件下载响应

        content: 文件字节流
        filename: 文件名

        HttpResponse: 文件下载响应
    """
    response = HttpResponse(
        content, content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    )
    # 使用 RFC 5987 编码处理中文文件名
    encoded_filename = quote(filename)
    response["Content-Disposition"] = f"attachment; filename*=UTF-8''{encoded_filename}"
    return response


# ==================== Unified Template Generation API ====================


@router.post("/{case_id}/unified-generate")
def unified_generate_template(request: HttpRequest, case_id: int, payload: UnifiedGenerateRequest) -> HttpResponse:
    """
    统一模板生成 API(新端点)

    支持通过 template_id 或 function_code 生成文档.

        case_id: 案件ID
        payload.template_id: 模板ID(可选,优先使用)
        payload.function_code: 功能标识(可选,当 template_id 为空时使用)
        payload.client_id: 当事人ID(可选,用于法定代表人身份证明书、单独授权)
        payload.client_ids: 当事人ID列表(可选,用于合并授权)
        payload.mode: 授权模式(可选): 'individual' | 'combined'

        HttpResponse: docx 文件流

    Requirements: 1.5
    """
    service = _get_unified_template_generation_service()
    content, filename = service.generate_document(
        case_id=case_id,
        template_id=payload.template_id,
        function_code=payload.function_code,
        client_id=payload.client_id,
        client_ids=payload.client_ids,
        mode=payload.mode,
    )
    return _build_file_response(content, filename)