schemas.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import json
  2. from datetime import datetime
  3. from fastapi import Request
  4. from fastapi.datastructures import FormData
  5. from pydantic import BaseModel, Field
  6. from app.core.logger import log
  7. async def parse_alipay_notify_form(request: Request) -> "AlipayNotifyBase":
  8. """解析支付宝通知表单数据"""
  9. form_data = await request.form()
  10. # 确保所有值都是字符串类型
  11. return parse_alipay_notify_form_data(form_data)
  12. def parse_alipay_notify_form_data(form_data: FormData) -> "AlipayNotifyBase":
  13. """解析支付宝通知表单数据"""
  14. form_dict = {}
  15. for key, value in form_data.items():
  16. if isinstance(value, str):
  17. form_dict[key] = value
  18. else:
  19. # 对于非字符串类型(如UploadFile),获取其字符串表示
  20. form_dict[key] = str(value)
  21. log.info(f"收到支付宝通知表单数据: {form_dict}")
  22. return AlipayNotifyBase(**form_dict)
  23. class AlipayNotifyBase(BaseModel):
  24. """支付宝通知基础模型"""
  25. notify_id: str = Field(..., description="通知ID")
  26. utc_timestamp: str = Field(..., description="消息发送时的服务端时间")
  27. msg_method: str | None = Field(None, description="消息接口名称(优先取此值,若为空则用 notify_type)")
  28. app_id: str = Field(..., description="消息接受方的应用id")
  29. version: str = Field(..., description="版本号")
  30. sign: str = Field(..., description="签名")
  31. sign_type: str = Field(..., description="签名类型")
  32. charset: str = Field(..., description="编码集")
  33. notify_type: str | None = Field(None, description="通知类型(msg_method 为空时使用)")
  34. biz_content: str = Field(..., description="消息报文")
  35. def parse_biz_content(self) -> dict:
  36. """解析 biz_content JSON 字符串"""
  37. try:
  38. return json.loads(self.biz_content)
  39. except:
  40. raise Exception(f"支付宝通知消息 - 解析 biz_content 失败")
  41. @property
  42. def method(self) -> str:
  43. """获取消息接口标识,优先 msg_method,其次 notify_type"""
  44. return self.msg_method or self.notify_type or ""
  45. class EnterpriseChangeContent(BaseModel):
  46. """企业变更通知内容"""
  47. enterprise_id: str = Field(..., description="企业id")
  48. out_biz_no: str = Field(..., description="外部业务号/幂等号")
  49. action: str = Field(..., description="变更动作")
  50. change_time: str = Field(..., description="变更时间")
  51. account_id: str | None = Field(None, description="账户id")
  52. remark: str | None = Field(None, description="备注")
  53. tenant_id: int | None = Field(None, description="租户id")
  54. class EmployeeChangeContent(BaseModel):
  55. """
  56. 员工变更通知内容
  57. {'gmt_create': '2026-04-24 11:15:49', 'role_list': ['USER'], 'mobile': '13874410370', 'employee_name': '胡森林', 'gmt_modified': '2026-04-24 11:15:49', 'ext_info': '{}', 'enterprise_id': '2088480767913636', 'iot_face_status': '2', 'department_list': [{'department_id': '1001163032541324', 'department_name': '湖南花米惠科技有限公司'}], 'employee_id': '2300063240699375', 'activate': 'UNACTIVATED', 'action': 'EMPLOYEE_ADD', 'change_time': '2026-04-24 11:15:49'}
  58. """
  59. enterprise_id: str = Field(..., description="企业id")
  60. employee_id: str = Field(..., description="员工id")
  61. action: str | None = Field(None, description="变更动作")
  62. change_time: str | None = Field(None, description="变更时间")
  63. activate: str | None = Field(None, description="激活状态")
  64. department_list: list | None = Field(None, description="部门列表")
  65. role_list: list | None = Field(None, description="角色列表")
  66. mobile: str | None = Field(None, description="手机号")
  67. email: str | None = Field(None, description="邮箱")
  68. open_id: str | None = Field(None, description="open_id")
  69. employee_name: str | None = Field(None, description="员工姓名")
  70. iot_face_status: str | None = Field(None, description="人脸状态状态")
  71. class VoucherChangeContent(BaseModel):
  72. """凭证变动通知内容"""
  73. account_id: str = Field(..., description="账户id")
  74. voucher_id: str = Field(..., description="凭证id")
  75. enterprise_id: str = Field(..., description="企业id")
  76. out_biz_no: str = Field(..., description="外部业务号")
  77. action: str = Field(..., description="变动动作")
  78. change_time: str = Field(..., description="变动时间")
  79. class FundChangeContent(BaseModel):
  80. """资金变动通知内容"""
  81. enterprise_id: str = Field(..., description="企业id")
  82. account_book_id: str = Field(..., description="资金专户号")
  83. out_biz_no: str = Field(..., description="外部业务号")
  84. change_type: str = Field(..., description="变动类型: DEPOSIT/WITHDRAW/TRANSFER")
  85. status: str = Field(..., description="状态")
  86. amount: str | None = Field(None, description="金额")
  87. order_no: str | None = Field(None, description="支付宝订单号")
  88. error_code: str | None = Field(None, description="错误码")
  89. error_message: str | None = Field(None, description="错误信息")
  90. change_time: str = Field(..., description="变动时间")
  91. class ConsumeChangeContent(BaseModel):
  92. """企业码账单消息通知内容"""
  93. # 必选字段
  94. account_id: str = Field(..., description="共同账户ID")
  95. enterprise_id: str = Field(..., description="企业ID")
  96. employee_id: str = Field(..., description="员工ID")
  97. pay_no: str = Field(..., description="支付宝账单号")
  98. consume_type: str = Field(..., description="账单类型: CONSUME(消费账单)/REFUND(退款账单)")
  99. gmt_biz_create: str = Field(..., description="账单创建时间,格式:yyyy-MM-dd HH:mm:ss")
  100. consume_amount: str = Field(..., description="账单金额,2位小数,单位:元")
  101. notify_reason: str = Field(..., description="通知原因")
  102. notify_msg: str = Field(..., description="通知描述")
  103. # 二选一字段
  104. user_id: str | None = Field(None, description="员工支付宝UID")
  105. open_id: str | None = Field(None, description="员工open_id")
  106. # 条件必选字段(退款账单才有值)
  107. related_pay_no: str | None = Field(None, description="关联消费账单交易流水号")
  108. # 可选字段
  109. gmt_recieve_pay: str | None = Field(None, description="账单支付时间,格式:yyyy-MM-dd HH:mm:ss")
  110. peer_pay_amount: str | None = Field(None, description="企业代付金额,2位小数,单位:元")
  111. expense_rule_group_id: str | None = Field(None, description="费控规则ID")
  112. expense_scene_code: str | None = Field(None, description="费用场景")
  113. expense_type: str | None = Field(None, description="费用类型")
  114. expense_type_sub_category: str | None = Field(None, description="费用类型子类目")
  115. ext_infos: str | None = Field(None, description="扩展参数,JSON格式")