221 lines
7.9 KiB
Python
221 lines
7.9 KiB
Python
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)
|