router.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import datetime
  2. import logging
  3. from fastapi import APIRouter, Depends, Query
  4. from typing import Any, Union, Optional
  5. from pydantic import ValidationError
  6. from alien_store.api.deps import get_contract_service, get_contract_center_service
  7. from alien_contract.schemas.request.contract import BundleCreateRequest
  8. from alien_contract.services.contract_server import ContractCenterService
  9. from alien_store.schemas.request.contract_store import TemplatesCreate
  10. from alien_store.schemas.response.contract_store import (
  11. ModuleStatusResponse,
  12. TemplatesCreateResponse,
  13. ErrorResponse,
  14. ContractStoreResponse,
  15. PaginatedResponse,
  16. SuccessResponse
  17. )
  18. from alien_store.services.contract_server import ContractServer
  19. router = APIRouter()
  20. logger = logging.getLogger("alien_store")
  21. def _format_validation_errors(exc: ValidationError) -> list[dict[str, str]]:
  22. errors = []
  23. for err in exc.errors():
  24. loc = err.get("loc", ())
  25. field = ".".join(str(item) for item in loc if item != "body")
  26. errors.append(
  27. {
  28. "field": field or "body",
  29. "type": err.get("type", "validation_error"),
  30. "message": err.get("msg", "参数校验失败"),
  31. }
  32. )
  33. return errors
  34. @router.get("/", response_model=ModuleStatusResponse)
  35. async def index() -> ModuleStatusResponse:
  36. return ModuleStatusResponse(module="Contract", status="Ok")
  37. @router.post("/get_esign_templates", response_model=Union[TemplatesCreateResponse, ErrorResponse])
  38. async def create_esign_templates(
  39. templates_data_raw: dict[str, Any],
  40. templates_server: ContractCenterService = Depends(get_contract_center_service)
  41. ) -> Union[TemplatesCreateResponse, ErrorResponse]:
  42. """AI审核完调用 e签宝生成文件"""
  43. try:
  44. templates_data = TemplatesCreate.model_validate(templates_data_raw)
  45. except ValidationError as e:
  46. detail = _format_validation_errors(e)
  47. logger.error("get_esign_templates validation failed: %s", detail)
  48. return ErrorResponse(
  49. success=False,
  50. message="请求参数校验失败",
  51. raw={"errors": detail},
  52. )
  53. request = BundleCreateRequest(
  54. subject_type="store",
  55. subject_id=templates_data.store_id,
  56. subject_name=templates_data.store_name,
  57. business_segment=templates_data.business_segment,
  58. contact_name=templates_data.merchant_name,
  59. contact_phone=templates_data.contact_phone,
  60. ord_id=templates_data.ord_id,
  61. )
  62. result = await templates_server.create_bundle(request)
  63. if not result.get("success"):
  64. return ErrorResponse(**result)
  65. return TemplatesCreateResponse(
  66. success=True,
  67. message=result["message"],
  68. sign_flow_id=result.get("primary_sign_flow_id"),
  69. file_id=result.get("created_contracts", [{}])[0].get("file_id") if result.get("created_contracts") else None,
  70. contract_url=result.get("created_contracts", [{}])[0].get("contract_url") if result.get("created_contracts") else None,
  71. created_contracts=result.get("created_contracts"),
  72. )
  73. @router.get("/contracts/{store_id}", response_model=Union[dict, Any])
  74. async def list_contracts(
  75. store_id: int,
  76. status: Optional[int] = Query(None, description="筛选合同状态:0 未签署,1 已签署"),
  77. page: int = Query(1, ge=1, description="页码,从1开始"),
  78. page_size: int = Query(10, ge=1, le=100, description="每页条数,默认10"),
  79. templates_server: ContractServer = Depends(get_contract_service)
  80. ) -> Any:
  81. """根据 store_id 查询所有合同,支持根据 status 筛选和分页"""
  82. return await templates_server.list_contracts(store_id, status, page, page_size)
  83. @router.get("/contracts/detail/{sign_flow_id}", response_model=Union[dict, ErrorResponse])
  84. async def get_contract_detail(
  85. sign_flow_id: str,
  86. templates_server: ContractServer = Depends(get_contract_service)
  87. ) -> Union[dict, ErrorResponse]:
  88. """
  89. 根据 sign_flow_id 获取合同详情
  90. - status=0: 返回合同PDF链接(contract_url)和签署链接(sign_url)
  91. - status=1: 拉取最新下载链接并更新数据库,返回 contract_download_url
  92. """
  93. result = await templates_server.get_contract_detail(sign_flow_id)
  94. if not result.get("success", True): # get_contract_detail 返回的成功结果里没有 success 键,只有 status
  95. return ErrorResponse(**result)
  96. return result
  97. @router.get("/get_all_templates", response_model=PaginatedResponse)
  98. async def get_all_templates(
  99. page: int = Query(1, ge=1, description="页码,从1开始"),
  100. page_size: int = Query(10, ge=1, le=100, description="每页条数,默认10"),
  101. store_name: Optional[str] = Query(None, description="店铺名称(模糊查询)"),
  102. merchant_name: Optional[str] = Query(None, description="商家姓名(模糊查询)"),
  103. signing_status: Optional[str] = Query(None, description="签署状态"),
  104. business_segment: Optional[str] = Query(None, description="经营板块"),
  105. store_status: Optional[str] = Query(None, description="店铺状态:正常/禁用"),
  106. expiry_start: Optional[datetime.datetime] = Query(None, description="到期时间起"),
  107. expiry_end: Optional[datetime.datetime] = Query(None, description="到期时间止"),
  108. templates_server: ContractServer = Depends(get_contract_service)
  109. ) -> PaginatedResponse:
  110. """分页查询所有合同,支持筛选"""
  111. rows, total = await templates_server.list_all_paged(
  112. page,
  113. page_size,
  114. store_name=store_name,
  115. merchant_name=merchant_name,
  116. signing_status=signing_status,
  117. business_segment=business_segment,
  118. store_status=store_status,
  119. expiry_start=expiry_start,
  120. expiry_end=expiry_end,
  121. )
  122. total_pages = (total + page_size - 1) // page_size if total > 0 else 0
  123. items = [ContractStoreResponse(**row) for row in rows]
  124. return PaginatedResponse(
  125. items=items,
  126. total=total,
  127. page=page,
  128. page_size=page_size,
  129. total_pages=total_pages
  130. )
  131. @router.post("/esign/callback", response_model=Union[SuccessResponse, ErrorResponse])
  132. async def esign_callback(
  133. payload: dict,
  134. templates_server: ContractServer = Depends(get_contract_service)
  135. ) -> Union[SuccessResponse, ErrorResponse]:
  136. """
  137. e签宝签署结果回调
  138. 需求:签署完成 -> 更新 signing_status=已签署,contract_url 中 status=1
  139. """
  140. result = await templates_server.process_esign_callback(payload)
  141. if not result.get("success"):
  142. return ErrorResponse(**result)
  143. return SuccessResponse(code=result["code"], msg=result["msg"])
  144. # @router.post("/esign/callback_auth", response_model=SuccessResponse)
  145. # async def esign_callback_auth(
  146. # payload: dict,
  147. # templates_server: ContractServer = Depends(get_contract_service)
  148. # ) -> SuccessResponse:
  149. # logger.info(f"esign_callback_auth payload: {payload}")
  150. # return SuccessResponse(code="200", msg="success")