from typing import Optional from urllib.parse import quote from fastapi import APIRouter, Depends, Form, Header, HTTPException, Request, Response from sqlalchemy.orm import Session from app.core.response import success_response from app.db import get_db_session from app.services.audit_log_service import AuditLogService from app.services.auth_service import AuthService from app.services.contract_generation_service import ContractGenerationService, ExportFormat router = APIRouter(prefix="/contracts", tags=["contracts"]) ROUTE_PERMISSION_SCOPE = "contract_generation" ROUTE_PERMISSION_ACTIONS = { "list": "contracts:list", "templates": "contracts:templates:list", "generate": "contracts:generate", "preview": "contracts:preview", "detail": "contracts:detail", "preview_generated": "contracts:generated:preview", "download": "contracts:download", } def get_contract_generation_service(db: Session = Depends(get_db_session)) -> ContractGenerationService: return ContractGenerationService(db) def get_audit_log_service(db: Session = Depends(get_db_session)) -> AuditLogService: return AuditLogService(db) def require_current_user( authorization: Optional[str] = Header(default=None), db: Session = Depends(get_db_session), ): return AuthService(db).require_current_user(authorization) @router.get("") def list_generated_contracts( page: int = 1, page_size: int = 10, contract_type: Optional[str] = None, keyword: Optional[str] = None, contract_generation_service: ContractGenerationService = Depends(get_contract_generation_service), current_user: dict = Depends(require_current_user), ): return success_response( contract_generation_service.list_generated_contracts( current_user=current_user, page=page, page_size=page_size, contract_type=contract_type, keyword=keyword, ) ) @router.get("/templates") def list_available_templates( contract_type: Optional[str] = None, template_category: Optional[str] = None, industry_category: Optional[str] = None, contract_generation_service: ContractGenerationService = Depends(get_contract_generation_service), current_user: dict = Depends(require_current_user), ): kwargs = { "current_user": current_user, "contract_type": contract_type, } if template_category is not None: kwargs["template_category"] = template_category if industry_category is not None: kwargs["industry_category"] = industry_category return success_response(contract_generation_service.list_available_templates(**kwargs)) @router.post("/generate") def generate_contract( request: Request, template_id: str = Form(...), variable_values_json: str = Form(...), export_format: str = Form(default="docx"), options_json: Optional[str] = Form(default=None), contract_generation_service: ContractGenerationService = Depends(get_contract_generation_service), audit_log_service: AuditLogService = Depends(get_audit_log_service), current_user: dict = Depends(require_current_user), ): import json try: variable_values = json.loads(variable_values_json) if not isinstance(variable_values, dict): raise HTTPException(status_code=400, detail="变量值必须是JSON对象") options = {} if options_json: options = json.loads(options_json) if not isinstance(options, dict): raise HTTPException(status_code=400, detail="选项必须是JSON对象") try: export_format_enum = ExportFormat(export_format.lower()) except ValueError: raise HTTPException(status_code=400, detail=f"不支持的导出格式: {export_format}") result = contract_generation_service.generate_contract( current_user=current_user, template_id=template_id, variable_values=variable_values, export_format=export_format_enum, options=options, ) audit_log_service.record_event( tenant_id=current_user.get("tenant_id"), user_id=current_user.get("id"), action="contract_generation", resource_type="contract", resource_id=result["id"], detail_json={ "template_id": template_id, "template_name": result["template_name"], "contract_type": result["contract_type"], "template_category": result.get("template_category"), "industry_category": result.get("industry_category"), "export_format": export_format, "variables_count": len(variable_values), "file_name": result["file_name"], }, ip_address=request.client.host if request.client else None, user_agent=request.headers.get("user-agent"), ) return success_response(result) except json.JSONDecodeError: raise HTTPException(status_code=400, detail="JSON解析错误") except HTTPException: raise except Exception as exc: raise HTTPException(status_code=500, detail=f"合同生成失败: {exc}") @router.post("/preview") def preview_contract( template_id: str = Form(...), variable_values_json: str = Form(...), contract_generation_service: ContractGenerationService = Depends(get_contract_generation_service), current_user: dict = Depends(require_current_user), ): import json try: variable_values = json.loads(variable_values_json) if not isinstance(variable_values, dict): raise HTTPException(status_code=400, detail="变量值必须是JSON对象") result = contract_generation_service.preview_contract( current_user=current_user, template_id=template_id, variable_values=variable_values, ) return success_response(result) except json.JSONDecodeError: raise HTTPException(status_code=400, detail="JSON解析错误") except HTTPException: raise except Exception as exc: raise HTTPException(status_code=500, detail=f"合同预览失败: {exc}") @router.get("/{contract_id}") def get_generated_contract( contract_id: str, contract_generation_service: ContractGenerationService = Depends(get_contract_generation_service), current_user: dict = Depends(require_current_user), ): return success_response(contract_generation_service.get_generated_contract(current_user, contract_id)) @router.get("/{contract_id}/preview") def get_contract_preview( contract_id: str, contract_generation_service: ContractGenerationService = Depends(get_contract_generation_service), current_user: dict = Depends(require_current_user), ): return success_response(contract_generation_service.get_generated_contract_preview(current_user, contract_id)) @router.get("/{contract_id}/download") def download_contract( contract_id: str, request: Request, contract_generation_service: ContractGenerationService = Depends(get_contract_generation_service), audit_log_service: AuditLogService = Depends(get_audit_log_service), current_user: dict = Depends(require_current_user), ): result = contract_generation_service.get_generated_contract_download(current_user, contract_id) audit_log_service.record_event( tenant_id=current_user.get("tenant_id"), user_id=current_user.get("id"), action="download_contract", resource_type="contract", resource_id=contract_id, detail_json={ "file_name": result["file_name"], "mime_type": result["mime_type"], }, ip_address=request.client.host if request.client else None, user_agent=request.headers.get("user-agent"), ) headers = {"Content-Disposition": f"attachment; filename*=UTF-8''{quote(result['file_name'])}"} return Response(content=result["content"], media_type=result["mime_type"], headers=headers)