654 lines
26 KiB
Python
654 lines
26 KiB
Python
from datetime import datetime
|
|
from typing import Optional
|
|
from urllib.parse import quote
|
|
|
|
from fastapi import APIRouter, BackgroundTasks, Depends, Form, Header, HTTPException, Request, Response
|
|
from pydantic import BaseModel, Field
|
|
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
|
|
from app.services.tenant_service import TenantService
|
|
|
|
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",
|
|
"versions": "contracts:versions:list",
|
|
"version_detail": "contracts:versions:detail",
|
|
"version_create": "contracts:versions:create",
|
|
"version_diff": "contracts:versions:diff",
|
|
"version_download": "contracts:versions:download",
|
|
"lifecycle_update": "contracts:lifecycle:update",
|
|
"lifecycle_tasks": "contracts:lifecycle-tasks:list",
|
|
"lifecycle_task_sync": "contracts:lifecycle-tasks:sync",
|
|
"lifecycle_task_create": "contracts:lifecycle-tasks:create",
|
|
"lifecycle_task_update": "contracts:lifecycle-tasks:update",
|
|
"preview_generated": "contracts:generated:preview",
|
|
"download": "contracts:download",
|
|
}
|
|
|
|
|
|
class GeneratedContractVersionCreateRequest(BaseModel):
|
|
content: str = Field(..., min_length=1, max_length=1_000_000)
|
|
change_type: str = "editor_save"
|
|
change_summary: Optional[str] = None
|
|
source_version_id: Optional[str] = None
|
|
original_text: Optional[str] = None
|
|
replacement_text: Optional[str] = None
|
|
locate_status: str = "applied"
|
|
|
|
|
|
class GeneratedContractLifecycleUpdateRequest(BaseModel):
|
|
lifecycle_status: str = Field(..., min_length=1, max_length=32)
|
|
counterparty_name: Optional[str] = Field(default=None, max_length=120)
|
|
owner_name: Optional[str] = Field(default=None, max_length=100)
|
|
effective_at: Optional[datetime] = None
|
|
expires_at: Optional[datetime] = None
|
|
lifecycle_note: Optional[str] = Field(default=None, max_length=1000)
|
|
|
|
|
|
class ContractLifecycleTaskCreateRequest(BaseModel):
|
|
task_type: str = Field(..., min_length=1, max_length=64)
|
|
title: str = Field(..., min_length=1, max_length=200)
|
|
description: Optional[str] = Field(default=None, max_length=4000)
|
|
priority: str = Field(default="medium", max_length=32)
|
|
status_value: str = Field(default="open", max_length=32)
|
|
due_at: Optional[datetime] = None
|
|
evidence_file_id: Optional[str] = Field(default=None, max_length=64)
|
|
evidence_note: Optional[str] = Field(default=None, max_length=2000)
|
|
|
|
|
|
class ContractLifecycleTaskUpdateRequest(BaseModel):
|
|
task_type: Optional[str] = Field(default=None, max_length=64)
|
|
title: Optional[str] = Field(default=None, max_length=200)
|
|
description: Optional[str] = Field(default=None, max_length=4000)
|
|
priority: Optional[str] = Field(default=None, max_length=32)
|
|
status_value: Optional[str] = Field(default=None, max_length=32)
|
|
due_at: Optional[datetime] = None
|
|
evidence_file_id: Optional[str] = Field(default=None, max_length=64)
|
|
evidence_note: Optional[str] = Field(default=None, max_length=2000)
|
|
|
|
|
|
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 get_tenant_service(db: Session = Depends(get_db_session)) -> TenantService:
|
|
return TenantService(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,
|
|
document_type_code: Optional[str] = None,
|
|
lifecycle_status: 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,
|
|
document_type_code=document_type_code,
|
|
lifecycle_status=lifecycle_status,
|
|
keyword=keyword,
|
|
)
|
|
)
|
|
|
|
|
|
@router.get("/templates")
|
|
def list_available_templates(
|
|
contract_type: Optional[str] = None,
|
|
document_type_code: 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 document_type_code is not None:
|
|
kwargs["document_type_code"] = document_type_code
|
|
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.get("/lifecycle/tasks")
|
|
def list_contract_lifecycle_task_workbench(
|
|
page: int = 1,
|
|
page_size: int = 50,
|
|
status_value: Optional[str] = None,
|
|
task_type: 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_contract_lifecycle_task_workbench(
|
|
current_user=current_user,
|
|
page=page,
|
|
page_size=page_size,
|
|
status_value=status_value,
|
|
task_type=task_type,
|
|
)
|
|
)
|
|
|
|
|
|
@router.post("/generate")
|
|
def generate_contract(
|
|
request: Request,
|
|
background_tasks: BackgroundTasks,
|
|
template_id: Optional[str] = Form(default=None),
|
|
variable_values_json: Optional[str] = Form(default=None),
|
|
export_format: str = Form(default="docx"),
|
|
options_json: Optional[str] = Form(default=None),
|
|
document_type_code: Optional[str] = Form(default=None),
|
|
elements_json: Optional[str] = Form(default=None),
|
|
element_schema_id: Optional[str] = Form(default=None),
|
|
element_schema_version_label: Optional[str] = Form(default=None),
|
|
contract_generation_service: ContractGenerationService = Depends(get_contract_generation_service),
|
|
audit_log_service: AuditLogService = Depends(get_audit_log_service),
|
|
tenant_service: TenantService = Depends(get_tenant_service),
|
|
current_user: dict = Depends(require_current_user),
|
|
):
|
|
import json
|
|
|
|
try:
|
|
variable_values = json.loads(variable_values_json) if variable_values_json else {}
|
|
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对象")
|
|
|
|
elements_snapshot = None
|
|
if elements_json:
|
|
elements_snapshot = json.loads(elements_json)
|
|
if not isinstance(elements_snapshot, dict):
|
|
raise HTTPException(status_code=400, detail="elements_json must be a JSON object")
|
|
|
|
try:
|
|
export_format_enum = ExportFormat(export_format.lower())
|
|
except ValueError:
|
|
raise HTTPException(status_code=400, detail=f"不支持的导出格式: {export_format}")
|
|
|
|
tenant_service.assert_billing_allows(current_user, "generation")
|
|
|
|
if elements_snapshot is not None:
|
|
if not document_type_code:
|
|
raise HTTPException(status_code=400, detail="document_type_code is required when elements_json is provided")
|
|
result = contract_generation_service.generate_contract_from_elements(
|
|
current_user=current_user,
|
|
document_type_code=document_type_code,
|
|
elements=elements_snapshot,
|
|
export_format=export_format_enum,
|
|
options=options,
|
|
template_id=template_id,
|
|
variable_values=variable_values,
|
|
)
|
|
else:
|
|
if not template_id:
|
|
raise HTTPException(status_code=400, detail="template_id is required when elements_json is not provided")
|
|
if not variable_values_json:
|
|
raise HTTPException(status_code=400, detail="variable_values_json is required when elements_json is not provided")
|
|
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,
|
|
document_type_code=document_type_code,
|
|
elements_snapshot=elements_snapshot,
|
|
element_schema_id=element_schema_id,
|
|
element_schema_version_label=element_schema_version_label,
|
|
)
|
|
|
|
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": result.get("template_id") or template_id,
|
|
"template_name": result["template_name"],
|
|
"contract_type": result["contract_type"],
|
|
"document_type_code": result.get("document_type_code"),
|
|
"element_schema_id": result.get("metadata", {}).get("element_schema_id"),
|
|
"element_schema_version_label": result.get("metadata", {}).get("element_schema_version_label"),
|
|
"template_category": result.get("template_category"),
|
|
"industry_category": result.get("industry_category"),
|
|
"export_format": export_format,
|
|
"variables_count": len(result.get("variable_values") or variable_values),
|
|
"missing_field_count": len(result.get("missing_fields") or []),
|
|
"related_legal_article_count": len(result.get("related_legal_articles") or []),
|
|
"file_name": result["file_name"],
|
|
},
|
|
ip_address=request.client.host if request.client else None,
|
|
user_agent=request.headers.get("user-agent"),
|
|
)
|
|
|
|
if result.get("id"):
|
|
background_tasks.add_task(
|
|
contract_generation_service._execute_deferred_auto_review,
|
|
result["id"],
|
|
current_user["tenant_id"],
|
|
current_user,
|
|
)
|
|
|
|
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.patch("/{contract_id}/lifecycle")
|
|
def update_generated_contract_lifecycle(
|
|
contract_id: str,
|
|
payload: GeneratedContractLifecycleUpdateRequest,
|
|
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.update_generated_contract_lifecycle(
|
|
current_user=current_user,
|
|
contract_id=contract_id,
|
|
lifecycle_status=payload.lifecycle_status,
|
|
counterparty_name=payload.counterparty_name,
|
|
owner_name=payload.owner_name,
|
|
effective_at=payload.effective_at,
|
|
expires_at=payload.expires_at,
|
|
lifecycle_note=payload.lifecycle_note,
|
|
)
|
|
audit_log_service.record_event(
|
|
tenant_id=current_user.get("tenant_id"),
|
|
user_id=current_user.get("id"),
|
|
action="contract_lifecycle_update",
|
|
resource_type="contract",
|
|
resource_id=contract_id,
|
|
detail_json={
|
|
"lifecycle_status": result.get("lifecycle", {}).get("status"),
|
|
"counterparty_name": result.get("lifecycle", {}).get("counterparty_name"),
|
|
"owner_name": result.get("lifecycle", {}).get("owner_name"),
|
|
"effective_at": result.get("lifecycle", {}).get("effective_at"),
|
|
"expires_at": result.get("lifecycle", {}).get("expires_at"),
|
|
"due_status": result.get("lifecycle", {}).get("due_status"),
|
|
},
|
|
ip_address=request.client.host if request.client else None,
|
|
user_agent=request.headers.get("user-agent"),
|
|
)
|
|
return success_response(result)
|
|
|
|
|
|
@router.get("/{contract_id}/lifecycle/tasks")
|
|
def list_contract_lifecycle_tasks(
|
|
contract_id: str,
|
|
status_value: Optional[str] = None,
|
|
task_type: 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_contract_lifecycle_tasks(
|
|
current_user=current_user,
|
|
contract_id=contract_id,
|
|
status_value=status_value,
|
|
task_type=task_type,
|
|
)
|
|
)
|
|
|
|
|
|
@router.post("/{contract_id}/lifecycle/tasks/sync")
|
|
def sync_contract_lifecycle_tasks(
|
|
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.sync_contract_lifecycle_tasks(current_user=current_user, contract_id=contract_id)
|
|
audit_log_service.record_event(
|
|
tenant_id=current_user.get("tenant_id"),
|
|
user_id=current_user.get("id"),
|
|
action="contract_lifecycle_task_sync",
|
|
resource_type="contract",
|
|
resource_id=contract_id,
|
|
detail_json={
|
|
"synced": result.get("synced"),
|
|
"reason": result.get("reason"),
|
|
"total": result.get("total"),
|
|
"summary": result.get("summary"),
|
|
},
|
|
ip_address=request.client.host if request.client else None,
|
|
user_agent=request.headers.get("user-agent"),
|
|
)
|
|
return success_response(result)
|
|
|
|
|
|
@router.post("/{contract_id}/lifecycle/tasks")
|
|
def create_contract_lifecycle_task(
|
|
contract_id: str,
|
|
payload: ContractLifecycleTaskCreateRequest,
|
|
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.create_contract_lifecycle_task(
|
|
current_user=current_user,
|
|
contract_id=contract_id,
|
|
task_type=payload.task_type,
|
|
title=payload.title,
|
|
description=payload.description,
|
|
priority=payload.priority,
|
|
status_value=payload.status_value,
|
|
due_at=payload.due_at,
|
|
evidence_file_id=payload.evidence_file_id,
|
|
evidence_note=payload.evidence_note,
|
|
)
|
|
audit_log_service.record_event(
|
|
tenant_id=current_user.get("tenant_id"),
|
|
user_id=current_user.get("id"),
|
|
action="contract_lifecycle_task_create",
|
|
resource_type="contract_lifecycle_task",
|
|
resource_id=result.get("id"),
|
|
detail_json={
|
|
"contract_id": contract_id,
|
|
"task_type": result.get("task_type"),
|
|
"priority": result.get("priority"),
|
|
"status": result.get("status"),
|
|
"due_at": result.get("due_at"),
|
|
"evidence_file_id": result.get("evidence_file_id"),
|
|
},
|
|
ip_address=request.client.host if request.client else None,
|
|
user_agent=request.headers.get("user-agent"),
|
|
)
|
|
return success_response(result)
|
|
|
|
|
|
@router.put("/{contract_id}/lifecycle/tasks/{task_id}")
|
|
def update_contract_lifecycle_task(
|
|
contract_id: str,
|
|
task_id: str,
|
|
payload: ContractLifecycleTaskUpdateRequest,
|
|
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.update_contract_lifecycle_task(
|
|
current_user=current_user,
|
|
contract_id=contract_id,
|
|
task_id=task_id,
|
|
task_type=payload.task_type,
|
|
title=payload.title,
|
|
description=payload.description,
|
|
priority=payload.priority,
|
|
status_value=payload.status_value,
|
|
due_at=payload.due_at,
|
|
evidence_file_id=payload.evidence_file_id,
|
|
evidence_note=payload.evidence_note,
|
|
)
|
|
audit_log_service.record_event(
|
|
tenant_id=current_user.get("tenant_id"),
|
|
user_id=current_user.get("id"),
|
|
action="contract_lifecycle_task_update",
|
|
resource_type="contract_lifecycle_task",
|
|
resource_id=task_id,
|
|
detail_json={
|
|
"contract_id": contract_id,
|
|
"task_type": result.get("task_type"),
|
|
"priority": result.get("priority"),
|
|
"status": result.get("status"),
|
|
"due_at": result.get("due_at"),
|
|
"evidence_file_id": result.get("evidence_file_id"),
|
|
},
|
|
ip_address=request.client.host if request.client else None,
|
|
user_agent=request.headers.get("user-agent"),
|
|
)
|
|
return success_response(result)
|
|
|
|
|
|
@router.get("/{contract_id}/versions")
|
|
def list_generated_contract_versions(
|
|
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.list_generated_contract_versions(current_user, contract_id))
|
|
|
|
|
|
@router.get("/{contract_id}/versions/diff")
|
|
def diff_generated_contract_versions(
|
|
contract_id: str,
|
|
request: Request,
|
|
from_version_id: Optional[str] = None,
|
|
to_version_id: Optional[str] = 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),
|
|
):
|
|
result = contract_generation_service.diff_generated_contract_versions(
|
|
current_user=current_user,
|
|
contract_id=contract_id,
|
|
from_version_id=from_version_id,
|
|
to_version_id=to_version_id,
|
|
)
|
|
audit_log_service.record_event(
|
|
tenant_id=current_user.get("tenant_id"),
|
|
user_id=current_user.get("id"),
|
|
action="contract_version_diff",
|
|
resource_type="contract",
|
|
resource_id=contract_id,
|
|
detail_json={
|
|
"from_version": result.get("from_version", {}).get("version_label"),
|
|
"to_version": result.get("to_version", {}).get("version_label"),
|
|
"change_count": result.get("summary", {}).get("change_count"),
|
|
},
|
|
ip_address=request.client.host if request.client else None,
|
|
user_agent=request.headers.get("user-agent"),
|
|
)
|
|
return success_response(result)
|
|
|
|
|
|
@router.get("/{contract_id}/versions/{version_id}/download")
|
|
def download_generated_contract_version(
|
|
contract_id: str,
|
|
version_id: str,
|
|
request: Request,
|
|
export_format: str = "docx",
|
|
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),
|
|
):
|
|
try:
|
|
export_format_enum = ExportFormat(export_format.lower())
|
|
except ValueError:
|
|
raise HTTPException(status_code=400, detail=f"不支持的导出格式: {export_format}")
|
|
|
|
result = contract_generation_service.export_generated_contract_version(
|
|
current_user=current_user,
|
|
contract_id=contract_id,
|
|
version_id=version_id,
|
|
export_format=export_format_enum,
|
|
)
|
|
audit_log_service.record_event(
|
|
tenant_id=current_user.get("tenant_id"),
|
|
user_id=current_user.get("id"),
|
|
action="download_contract_version",
|
|
resource_type="contract_version",
|
|
resource_id=version_id,
|
|
detail_json={
|
|
"contract_id": contract_id,
|
|
"version_label": result.get("version", {}).get("version_label"),
|
|
"file_name": result["file_name"],
|
|
"mime_type": result["mime_type"],
|
|
"export_format": result.get("export_format"),
|
|
},
|
|
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)
|
|
|
|
|
|
@router.get("/{contract_id}/versions/{version_id}")
|
|
def get_generated_contract_version(
|
|
contract_id: str,
|
|
version_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_version(current_user, contract_id, version_id))
|
|
|
|
|
|
@router.post("/{contract_id}/versions")
|
|
def create_generated_contract_version(
|
|
contract_id: str,
|
|
payload: GeneratedContractVersionCreateRequest,
|
|
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.create_generated_contract_version(
|
|
current_user=current_user,
|
|
contract_id=contract_id,
|
|
content=payload.content,
|
|
change_type=payload.change_type,
|
|
change_summary=payload.change_summary,
|
|
source_version_id=payload.source_version_id,
|
|
original_text=payload.original_text,
|
|
replacement_text=payload.replacement_text,
|
|
locate_status=payload.locate_status,
|
|
)
|
|
audit_log_service.record_event(
|
|
tenant_id=current_user.get("tenant_id"),
|
|
user_id=current_user.get("id"),
|
|
action="contract_version_create",
|
|
resource_type="contract_version",
|
|
resource_id=result.get("id"),
|
|
detail_json={
|
|
"contract_id": contract_id,
|
|
"version_label": result.get("version_label"),
|
|
"change_type": result.get("change_type"),
|
|
"change_summary": result.get("change_summary"),
|
|
"content_length": len(payload.content or ""),
|
|
},
|
|
ip_address=request.client.host if request.client else None,
|
|
user_agent=request.headers.get("user-agent"),
|
|
)
|
|
return success_response(result)
|
|
|
|
|
|
@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)
|