router.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import datetime
  2. from fastapi import APIRouter, Depends, Query
  3. from typing import Any
  4. import json
  5. from datetime import datetime
  6. from alien_store.api.deps import get_contract_service
  7. from alien_store.schemas.request.contract_store import TemplatesCreate, SignUrl
  8. from alien_store.services.contract_server import ContractServer
  9. from common.esigntool.main import *
  10. import re, urllib.parse
  11. router = APIRouter()
  12. @router.get("/")
  13. async def index():
  14. return {"module": "Contract", "status": "Ok"}
  15. @router.post("/get_esign_templates")
  16. async def create_esign_templates(templates_data: TemplatesCreate, templates_server: ContractServer = Depends(get_contract_service)):
  17. # AI审核完调用 e签宝生成文件
  18. res_text = fill_in_template(templates_data.merchant_name)
  19. try:
  20. res_data = json.loads(res_text)
  21. except json.JSONDecodeError:
  22. return {"success": False, "message": "e签宝返回非 JSON", "raw": res_text}
  23. # 从返回结构提取下载链接,需与实际返回字段匹配
  24. try:
  25. contract_url = res_data["data"]["fileDownloadUrl"]
  26. file_id = res_data["data"]["fileId"]
  27. m = re.search(r'/([^/]+)\.pdf', contract_url)
  28. if m:
  29. encoded_name = m.group(1)
  30. file_name = urllib.parse.unquote(encoded_name)
  31. except Exception:
  32. return {"success": False, "message": "e签宝返回缺少 fileDownloadUrl", "raw": res_data}
  33. sign_data = create_by_file(file_id, file_name, templates_data.contact_phone, templates_data.merchant_name)
  34. sign_json = json.loads(sign_data)
  35. sing_id = sign_json["data"]["signFlowId"]
  36. result_contract = {
  37. "contract_url": contract_url,
  38. "file_name": file_name,
  39. "file_id": file_id,
  40. "status": 0,
  41. "sign_flow_id": sing_id,
  42. "sign_url": ""
  43. }
  44. updated = await templates_server.append_contract_url(templates_data, result_contract)
  45. return {"success": True, "message": "合同模板已追加/创建", "sign_flow_id": sing_id, "file_id": file_id, "contract_url": contract_url}
  46. @router.get("/contracts/{store_id}")
  47. async def list_contracts(store_id: int, templates_server: ContractServer = Depends(get_contract_service)) -> Any:
  48. """根据 store_id 查询所有合同"""
  49. rows = await templates_server.list_by_store(store_id)
  50. return rows
  51. @router.get("/get_all_templates")
  52. async def get_all_templates(
  53. page: int = Query(1, ge=1, description="页码,从1开始"),
  54. page_size: int = Query(10, ge=1, le=100, description="每页条数,默认10"),
  55. templates_server: ContractServer = Depends(get_contract_service)
  56. ) -> Any:
  57. """分页查询所有合同"""
  58. return await templates_server.list_all_paged(page, page_size)
  59. @router.post("/esign/signurl")
  60. async def get_esign_sign_url(body: SignUrl, templates_server: ContractServer = Depends(get_contract_service)):
  61. sing_flow_id = body.sign_flow_id
  62. contact_phone = body.contact_phone
  63. result = sign_url(sing_flow_id, contact_phone)
  64. try:
  65. result_json = json.loads(result)
  66. except json.JSONDecodeError:
  67. return {"success": False, "message": "e签宝返回非JSON", "raw": result}
  68. data = result_json.get("data") if isinstance(result_json, dict) else None
  69. if not data or not data.get("url"):
  70. return {"success": False, "message": "e签宝返回缺少签署链接", "raw": result_json}
  71. result_sign_url = data.get("url")
  72. await templates_server.update_sign_url(contact_phone, sing_flow_id, result_sign_url)
  73. return {"success": True, "data": {"url": result_sign_url}}
  74. @router.post("/esign/callback")
  75. async def esign_callback(payload: dict, templates_server: ContractServer = Depends(get_contract_service)) -> Any:
  76. """
  77. e签宝签署结果回调
  78. 需求:签署完成 -> 更新 signing_status=已签署,contract_url 中 status=1
  79. """
  80. sign_result = payload.get("signResult")
  81. operator = payload.get("operator") or {}
  82. sign_flow_id = payload.get("signFlowId")
  83. psn_account = operator.get("psnAccount") or {}
  84. contact_phone = psn_account.get("accountMobile")
  85. # 取回调中的毫秒时间戳,优先 operateTime,其次 timestamp
  86. ts_ms = payload.get("operateTime") or payload.get("timestamp")
  87. signing_dt = None
  88. if ts_ms:
  89. try:
  90. signing_dt = datetime.fromtimestamp(ts_ms / 1000)
  91. except Exception:
  92. signing_dt = None
  93. if sign_result == 2 and contact_phone and sign_flow_id:
  94. updated = await templates_server.mark_signed_by_phone(contact_phone, sign_flow_id, signing_dt)
  95. return {"code":"200","msg":"success"}
  96. return {"success": False, "message": "未处理: signResult!=2 或手机号/签署流程缺失"}
  97. @router.post("/esign/callback_auth")
  98. async def esign_callback_auth(payload: dict, templates_server: ContractServer = Depends(get_contract_service)):
  99. print(payload)
  100. return {"code":"200","msg":"success"}