| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- import json
- from datetime import datetime
- from fastapi import Request
- from fastapi.datastructures import FormData
- from pydantic import BaseModel, Field, model_validator
- from app.core.logger import log
- async def parse_alipay_notify_form(request: Request) -> "AlipayNotifyBase":
- """解析支付宝通知表单数据"""
- form_data = await request.form()
- # 确保所有值都是字符串类型
- return parse_alipay_notify_form_data(form_data)
- def parse_alipay_notify_form_data(form_data: FormData) -> "AlipayNotifyBase":
- """解析支付宝通知表单数据"""
- form_dict = {}
- for key, value in form_data.items():
- if isinstance(value, str):
- form_dict[key] = value
- else:
- # 对于非字符串类型(如UploadFile),获取其字符串表示
- form_dict[key] = str(value)
- log.info(f"收到支付宝通知表单数据: {form_dict}")
- return AlipayNotifyBase(**form_dict)
- class AlipayNotifyBase(BaseModel):
- """支付宝通知基础模型"""
- notify_id: str = Field(..., description="通知ID")
- utc_timestamp: str = Field(..., description="消息发送时的服务端时间")
- msg_method: str | None = Field(None, description="消息接口名称(优先取此值,若为空则用 notify_type)")
- app_id: str = Field(..., description="消息接受方的应用id")
- version: str = Field(..., description="版本号")
- sign: str = Field(..., description="签名")
- sign_type: str = Field(..., description="签名类型")
- charset: str = Field(..., description="编码集")
- notify_type: str | None = Field(None, description="通知类型(msg_method 为空时使用)")
- biz_content: str = Field(..., description="消息报文")
- def parse_biz_content(self) -> dict:
- """解析 biz_content JSON 字符串"""
- try:
- return json.loads(self.biz_content)
- except:
- raise Exception(f"支付宝通知消息 - 解析 biz_content 失败")
- @property
- def method(self) -> str:
- """获取消息接口标识,优先 msg_method,其次 notify_type"""
- return self.msg_method or self.notify_type or ""
- class EnterpriseChangeContent(BaseModel):
- """企业变更通知内容"""
- enterprise_id: str = Field(..., description="企业id")
- out_biz_no: str = Field(..., description="外部业务号/幂等号")
- action: str = Field(..., description="变更动作")
- change_time: str = Field(..., description="变更时间")
- account_id: str | None = Field(None, description="账户id")
- remark: str | None = Field(None, description="备注")
- tenant_id: int | None = Field(None, description="租户id")
- class EmployeeChangeContent(BaseModel):
- """
- 员工变更通知内容
- {'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'}
- """
- enterprise_id: str = Field(..., description="企业id")
- employee_id: str = Field(..., description="员工id")
- action: str | None = Field(None, description="变更动作")
- change_time: str | None = Field(None, description="变更时间")
- activate: str | None = Field(None, description="激活状态")
- department_list: list | None = Field(None, description="部门列表")
- role_list: list | None = Field(None, description="角色列表")
- mobile: str | None = Field(None, description="手机号")
- email: str | None = Field(None, description="邮箱")
- open_id: str | None = Field(None, description="open_id")
- employee_name: str | None = Field(None, description="员工姓名")
- iot_face_status: str | None = Field(None, description="人脸状态状态")
- class VoucherChangeContent(BaseModel):
- """凭证变动通知内容 (alipay.commerce.ec.voucher.change.notify)"""
- # 必选字段
- account_id: str = Field(..., description="共同账户ID")
- pay_no: str = Field(..., description="交易流水号(支付宝账单号)")
- voucher_type: str = Field(..., description="凭证类型: Ticket/Multimedia/Compliance")
- notify_reason: str = Field(..., description="通知原因: INVOICE_CREATE/INVOICE_INVALID/TICKET_BIND")
- notify_desc: str = Field(..., description="通知描述")
- # 二选一
- user_id: str | None = Field(None, description="用户支付宝UID")
- open_id: str | None = Field(None, description="用户open_id")
- # 可选
- ext_infos: str | None = Field(None, description="扩展参数,JSON格式")
- enterprise_id: str | None = Field(None, description="企业ID")
- employee_id: str | None = Field(None, description="员工ID")
- voucher_id: str | None = Field(None, description="凭证ID")
- # 本地补充
- action: str | None = Field(None, description="变动动作")
- out_biz_no: str | None = Field(None, description="外部业务号")
- change_time: str | None = Field(None, description="变动时间")
- class FundChangeContent(BaseModel):
- """资金变动通知内容"""
- enterprise_id: str = Field(..., description="企业id")
- account_book_id: str = Field(..., description="资金专户号")
- out_biz_no: str = Field(..., description="外部业务号")
- change_type: str = Field(..., description="变动类型: DEPOSIT/WITHDRAW/TRANSFER")
- status: str = Field(..., description="状态")
- amount: str | None = Field(None, description="金额")
- order_no: str | None = Field(None, description="支付宝订单号")
- error_code: str | None = Field(None, description="错误码")
- error_message: str | None = Field(None, description="错误信息")
- change_time: str = Field(..., description="变动时间")
- class ConsumeChangeContent(BaseModel):
- """企业码账单消息通知内容"""
- # 必选字段
- account_id: str = Field(..., description="共同账户ID")
- enterprise_id: str = Field(..., description="企业ID")
- employee_id: str = Field(..., description="员工ID")
- pay_no: str = Field(..., description="支付宝账单号")
- consume_type: str = Field(..., description="账单类型: CONSUME(消费账单)/REFUND(退款账单)")
- gmt_biz_create: str = Field(..., description="账单创建时间,格式:yyyy-MM-dd HH:mm:ss")
- consume_amount: str = Field(..., description="账单金额,2位小数,单位:元")
- notify_reason: str = Field(..., description="通知原因")
- notify_msg: str = Field(..., description="通知描述")
-
- # 二选一字段
- user_id: str | None = Field(None, description="员工支付宝UID")
- open_id: str | None = Field(None, description="员工open_id")
-
- # 条件必选字段(退款账单才有值)
- related_pay_no: str | None = Field(None, description="关联消费账单交易流水号")
-
- # 可选字段
- gmt_recieve_pay: str | None = Field(None, description="账单支付时间,格式:yyyy-MM-dd HH:mm:ss")
- peer_pay_amount: str | None = Field(None, description="企业代付金额,2位小数,单位:元")
- expense_rule_group_id: str | None = Field(None, description="费控规则ID")
- expense_scene_code: str | None = Field(None, description="费用场景")
- expense_type: str | None = Field(None, description="费用类型")
- expense_type_sub_category: str | None = Field(None, description="费用类型子类目")
- ext_infos: str | None = Field(None, description="扩展参数,JSON格式")
- class OrderChangeContent(BaseModel):
- """订单变动通知内容
-
- 接口名: alipay.ebpp.invoice.ecorder.order.changed
- 参考文档: https://opendocs.alipay.com/apis/03iu5y
- """
- order_no: str = Field(..., description="支付宝订单号")
- biz_out_no: str = Field(..., description="商户订单号")
- enterprise_id: str = Field(..., description="企业ID")
- employee_id: str | None = Field(None, description="员工ID")
- out_biz_no: str | None = Field(None, description="外部业务号")
- action: str | None = Field(None, description="变动动作")
- change_time: str | None = Field(None, description="变动时间")
- order_status: str | None = Field(None, description="订单状态")
-
- # 补充字段
- pay_no: str | None = Field(None, description="账单号")
- order_title: str | None = Field(None, description="订单标题")
- order_amount: str | None = Field(None, description="订单金额")
- trade_no: str | None = Field(None, description="交易号")
- merchant_id: str | None = Field(None, description="商户ID")
- merchant_name: str | None = Field(None, description="商户名称")
- shop_id: str | None = Field(None, description="店铺ID")
- shop_name: str | None = Field(None, description="店铺名称")
- gmt_create: str | None = Field(None, description="创建时间")
- gmt_payment: str | None = Field(None, description="支付时间")
- fund_channel: str | None = Field(None, description="资金渠道")
- order_type: str | None = Field(None, description="订单类型")
- invoice_status: str | None = Field(None, description="发票状态")
- ext_info: str | None = Field(None, description="扩展信息")
- # 把order_no设置为pay_no的值
- @model_validator(mode="before")
- def set_order_no(cls, values):
- if not values.get("order_no") and values.get("pay_no"):
- values["order_no"] = values["pay_no"]
- return values
-
- class BillDetailQueryOut(BaseModel):
- """账单详情查询响应"""
- pay_no: str = Field(..., description="支付宝账单号")
- enterprise_id: str = Field(..., description="企业ID")
- employee_id: str = Field(..., description="员工ID")
- employee_name: str | None = Field(None, description="员工姓名")
-
- consume_amount: str = Field(..., description="账单金额")
- peer_pay_amount: str | None = Field(None, description="企业代付金额")
- employee_pay_amount: str | None = Field(None, description="员工自付金额")
-
- order_info: dict | None = Field(None, description="订单详情")
- voucher_list: list[dict] | None = Field(None, description="凭证列表")
|