You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

225 lines
9.7 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

from dataclasses import dataclass
from datetime import datetime
from typing import Literal
from fastapi import Query
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from app.common.enums import QueueEnum
from app.core.base_params import BaseQueryParam
from app.core.base_schema import BaseSchema, TenantBySchema
class OrderCreateInternalSchema(BaseModel):
"""订单创建(内部 CRUD 用,包含所有业务字段)"""
order_no: str = Field(..., description="订单号")
tenant_id: int = Field(..., description="租户ID")
package_id: int | None = Field(default=None, description="套餐ID")
plugin_id: int | None = Field(default=None, description="插件ID")
order_type: str = Field(..., description="订单类型")
amount: int = Field(..., description="订单金额(分)")
period_count: int = Field(default=1, description="时长(月)")
pay_method: str | None = Field(default=None, description="支付方式")
pay_time: datetime | None = Field(default=None, description="支付时间")
expire_time: datetime = Field(..., description="过期时间")
status: int = Field(default=0, description="订单状态")
description: str | None = Field(default=None, description="备注")
class OrderUpdateInternalSchema(BaseModel):
"""订单更新(内部 CRUD 用)"""
status: int | None = Field(default=None, description="订单状态")
pay_method: str | None = Field(default=None, description="支付方式")
pay_time: datetime | None = Field(default=None, description="支付时间")
description: str | None = Field(default=None, description="备注")
class PaymentRecordCreateSchema(BaseModel):
"""支付记录创建"""
order_id: int = Field(..., description="订单ID")
transaction_id: str | None = Field(default=None, description="交易流水号")
pay_method: str = Field(..., description="支付方式")
amount: int = Field(..., description="支付金额(分)")
status: int = Field(default=1, description="支付状态")
raw_response: str | None = Field(default=None, description="原始响应")
pay_time: datetime | None = Field(default=None, description="支付时间")
description: str | None = Field(default=None, description="备注")
class RefundCreateSchema(BaseModel):
"""退款记录创建"""
order_id: int = Field(..., description="订单ID")
refund_no: str = Field(..., description="退款单号")
amount: int = Field(..., description="退款金额(分)")
reason: str = Field(..., description="退款原因")
refund_transaction_id: str | None = Field(default=None, description="退款交易流水号")
reviewer_id: int | None = Field(default=None, description="审核人ID")
review_time: datetime | None = Field(default=None, description="审核时间")
reject_reason: str | None = Field(default=None, description="驳回原因")
status: int = Field(default=1, description="退款状态")
description: str | None = Field(default=None, description="备注")
class RefundUpdateSchema(BaseModel):
"""退款记录更新"""
status: int | None = Field(default=None, description="退款状态")
reviewer_id: int | None = Field(default=None, description="审核人ID")
review_time: datetime | None = Field(default=None, description="审核时间")
reject_reason: str | None = Field(default=None, description="驳回原因")
refund_transaction_id: str | None = Field(default=None, description="退款交易流水号")
description: str | None = Field(default=None, description="备注")
class OrderCreateSchema(BaseModel):
"""创建订单(套餐或插件)"""
tenant_id: int = Field(..., ge=1, description="租户ID")
package_id: int | None = Field(default=None, ge=1, description="套餐ID套餐订单必填")
plugin_id: int | None = Field(default=None, ge=1, description="插件ID插件订单必填")
order_type: Literal["new", "renew", "upgrade", "downgrade", "plugin"] = Field(
...,
description="订单类型(new:新购 renew:续费 upgrade:升级 downgrade:降级 plugin:插件)",
)
pay_method: Literal["alipay", "wxpay", "free"] | None = Field(default=None, description="支付方式(留空=自动)")
@field_validator("tenant_id")
@classmethod
def positive(cls, v: int) -> int:
if v <= 0:
raise ValueError("必须为正整数")
return v
@model_validator(mode="after")
def check_target(self) -> None:
if self.order_type == "plugin":
if not self.plugin_id or self.plugin_id <= 0:
raise ValueError("插件订单必须指定 plugin_id")
else:
if not self.package_id or self.package_id <= 0:
raise ValueError("套餐订单必须指定 package_id")
class OrderOutSchema(BaseSchema, TenantBySchema):
"""订单输出"""
model_config = ConfigDict(from_attributes=True)
order_no: str = Field(..., description="订单号")
package_id: int | None = Field(default=None, description="套餐ID")
plugin_id: int | None = Field(default=None, description="插件ID")
order_type: str = Field(..., description="订单类型")
amount: int = Field(..., description="订单金额(分)")
period_count: int = Field(..., description="时长(月)")
pay_method: str | None = Field(default=None, description="支付方式")
pay_time: datetime | None = Field(default=None, description="支付时间")
expire_time: datetime = Field(..., description="过期时间")
status: int = Field(..., description="订单状态(0:待支付 1:已支付 2:已取消 3:已退款)")
description: str | None = Field(default=None, description="备注")
@dataclass
class OrderQueryParam(BaseQueryParam):
"""订单查询参数"""
tenant_id: int | None = Query(None, description="租户ID")
status: int | None = Query(None, description="订单状态(0:待支付 1:已支付 2:已取消 3:已退款)")
order_type: str | None = Query(None, description="订单类型")
order_no: str | None = Query(None, description="订单号")
def __post_init__(self) -> None:
if self.tenant_id is not None:
self.tenant_id = (QueueEnum.eq.value, self.tenant_id)
if self.status is not None:
self.status = (QueueEnum.eq.value, self.status)
if self.order_type:
self.order_type = (QueueEnum.eq.value, self.order_type)
if self.order_no:
self.order_no = (QueueEnum.like.value, self.order_no)
class PaymentCallbackSchema(BaseModel):
"""支付回调数据"""
transaction_id: str | None = Field(default=None, description="交易流水号")
amount: int = Field(..., description="支付金额(分)")
order_id: int | None = Field(default=None, description="订单ID")
raw_data: dict | None = Field(default=None, description="原始数据")
class PaymentRecordOutSchema(BaseSchema, TenantBySchema):
"""支付记录输出"""
model_config = ConfigDict(from_attributes=True)
order_id: int = Field(..., description="订单ID")
transaction_id: str | None = Field(default=None, description="交易流水号")
pay_method: str = Field(..., description="支付方式")
amount: int = Field(..., description="支付金额(分)")
pay_time: datetime | None = Field(default=None, description="支付时间")
status: int = Field(..., description="支付状态")
description: str | None = Field(default=None, description="备注")
class PaymentCreateOut(BaseModel):
"""创建支付结果"""
pay_url: str = Field(..., description="支付链接")
qr_code_url: str = Field(..., description="二维码链接")
trade_no: str = Field(..., description="交易流水号")
order_id: int = Field(..., description="订单ID")
order_no: str = Field(..., description="订单号")
amount: int = Field(..., description="支付金额(分)")
class PaymentStatusOut(BaseModel):
"""支付状态查询结果"""
exists: bool = Field(..., description="是否存在")
order_id: int | None = Field(default=None, description="订单ID")
status: int | None = Field(default=None, description="支付状态")
paid: bool = Field(default=False, description="是否已支付")
pay_method: str | None = Field(default=None, description="支付方式")
pay_time: str | None = Field(default=None, description="支付时间")
class OrderStatusMessage(BaseModel):
"""订单/退款操作结果消息"""
id: int = Field(..., description="订单/退款ID")
status: int = Field(..., description="状态")
message: str = Field(..., description="消息")
class RefundApplySchema(BaseModel):
"""退款申请"""
reason: str = Field(..., min_length=1, max_length=500, description="退款原因")
class RefundReviewSchema(BaseModel):
"""退款审核"""
reject_reason: str | None = Field(default=None, max_length=500, description="驳回原因(审核通过时可不填)")
class RefundOutSchema(BaseSchema, TenantBySchema):
"""退款记录输出"""
model_config = ConfigDict(from_attributes=True)
order_id: int = Field(..., description="订单ID")
refund_no: str = Field(..., description="退款单号")
amount: int = Field(..., description="退款金额(分)")
reason: str = Field(..., description="退款原因")
refund_transaction_id: str | None = Field(default=None, description="退款交易流水号")
reviewer_id: int | None = Field(default=None, description="审核人ID")
review_time: datetime | None = Field(default=None, description="审核时间")
reject_reason: str | None = Field(default=None, description="驳回原因")
status: int = Field(..., description="退款状态")
description: str | None = Field(default=None, description="备注")