router.py 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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
  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. # 调用 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. }
  43. list_contract = [result_contract]
  44. # contract_url 字段为字符串,存储 JSON 字符串避免类型错误
  45. result_data = templates_data.model_copy(
  46. update={"contract_url": json.dumps(list_contract, ensure_ascii=False), "seal_url": None}
  47. )
  48. return await templates_server.create_template(result_data)
  49. @router.get("/contracts/{store_id}")
  50. async def list_contracts(store_id: int, templates_server: ContractServer = Depends(get_contract_service)) -> Any:
  51. """根据 store_id 查询所有合同"""
  52. rows = await templates_server.list_by_store(store_id)
  53. return rows
  54. @router.get("/get_all_templates")
  55. async def get_all_templates(
  56. page: int = Query(1, ge=1, description="页码,从1开始"),
  57. page_size: int = Query(10, ge=1, le=100, description="每页条数,默认10"),
  58. templates_server: ContractServer = Depends(get_contract_service)
  59. ) -> Any:
  60. """分页查询所有合同"""
  61. return await templates_server.list_all_paged(page, page_size)
  62. @router.post("/esign/callback")
  63. async def esign_callback(payload: dict, templates_server: ContractServer = Depends(get_contract_service)) -> Any:
  64. """
  65. e签宝签署结果回调
  66. 需求:签署完成 -> 更新 signing_status=已签署,contract_url 中 status=1
  67. """
  68. sign_result = payload.get("signResult")
  69. operator = payload.get("operator") or {}
  70. psn_account = operator.get("psnAccount") or {}
  71. contact_phone = psn_account.get("accountMobile")
  72. # 取回调中的毫秒时间戳,优先 operateTime,其次 timestamp
  73. ts_ms = payload.get("operateTime") or payload.get("timestamp")
  74. signing_dt = None
  75. if ts_ms:
  76. try:
  77. signing_dt = datetime.fromtimestamp(ts_ms / 1000)
  78. except Exception:
  79. signing_dt = None
  80. if sign_result == 2 and contact_phone:
  81. updated = await templates_server.mark_signed_by_phone(contact_phone, signing_dt)
  82. return {"success": True, "updated": updated}
  83. return {"success": False, "message": "未处理: signResult!=2 或手机号缺失"}