修复邮件发送时的收件方地址验证问题,添加域名解析检查 更新评估状态字段值从"approved"为"pending"以保持一致性 修改发票创建接口以支持无凭证上传的情况 添加用户管理接口的时间范围查询功能 更新SMTP和短信服务的默认配置
158 lines
5.3 KiB
Python
158 lines
5.3 KiB
Python
from typing import Optional, List
|
||
from pydantic import BaseModel, Field, EmailStr, field_validator, model_validator
|
||
|
||
|
||
class InvoiceHeaderCreate(BaseModel):
|
||
company_name: str = Field(..., min_length=1, max_length=128)
|
||
tax_number: str = Field(..., min_length=1, max_length=32)
|
||
register_address: Optional[str] = Field(None, min_length=1, max_length=256)
|
||
register_phone: Optional[str] = Field(None, min_length=1, max_length=32)
|
||
bank_name: Optional[str] = Field(None, min_length=1, max_length=128)
|
||
bank_account: Optional[str] = Field(None, min_length=1, max_length=64)
|
||
email: EmailStr
|
||
is_default: Optional[bool] = False
|
||
|
||
|
||
class InvoiceHeaderOut(BaseModel):
|
||
id: int
|
||
app_user_id: Optional[int] = None
|
||
company_name: str
|
||
tax_number: str
|
||
register_address: str
|
||
register_phone: str
|
||
bank_name: str
|
||
bank_account: str
|
||
email: EmailStr
|
||
class Config:
|
||
from_attributes = True
|
||
is_default: Optional[bool] = False
|
||
|
||
|
||
class InvoiceHeaderUpdate(BaseModel):
|
||
company_name: Optional[str] = Field(None, min_length=1, max_length=128)
|
||
tax_number: Optional[str] = Field(None, min_length=1, max_length=32)
|
||
register_address: Optional[str] = Field(None, max_length=256)
|
||
register_phone: Optional[str] = Field(None, max_length=32)
|
||
bank_name: Optional[str] = Field(None, max_length=128)
|
||
bank_account: Optional[str] = Field(None, max_length=64)
|
||
email: Optional[EmailStr] = None
|
||
is_default: Optional[bool] = None
|
||
|
||
|
||
class InvoiceCreate(BaseModel):
|
||
ticket_type: str = Field(..., pattern=r"^(electronic|paper)$")
|
||
invoice_type: str = Field(..., pattern=r"^(special|normal)$")
|
||
phone: str = Field(..., min_length=5, max_length=20)
|
||
email: EmailStr
|
||
company_name: str = Field(..., min_length=1, max_length=128)
|
||
tax_number: str = Field(..., min_length=1, max_length=32)
|
||
register_address: str = Field(..., max_length=256)
|
||
register_phone: str = Field(..., max_length=32)
|
||
bank_name: str = Field(..., max_length=128)
|
||
bank_account: str = Field(..., max_length=64)
|
||
app_user_id: Optional[int] = None
|
||
header_id: Optional[int] = None
|
||
wechat: Optional[str] = None
|
||
|
||
|
||
class InvoiceUpdate(BaseModel):
|
||
ticket_type: Optional[str] = Field(None, pattern=r"^(electronic|paper)$")
|
||
invoice_type: Optional[str] = Field(None, pattern=r"^(special|normal)$")
|
||
phone: Optional[str] = Field(None, min_length=5, max_length=20)
|
||
email: Optional[EmailStr] = None
|
||
company_name: Optional[str] = Field(None, min_length=1, max_length=128)
|
||
tax_number: Optional[str] = Field(None, min_length=1, max_length=32)
|
||
register_address: Optional[str] = Field(None, min_length=1, max_length=256)
|
||
register_phone: Optional[str] = Field(None, min_length=1, max_length=32)
|
||
bank_name: Optional[str] = Field(None, min_length=1, max_length=128)
|
||
bank_account: Optional[str] = Field(None, min_length=1, max_length=64)
|
||
wechat: Optional[str] = None
|
||
|
||
|
||
class InvoiceOut(BaseModel):
|
||
id: int
|
||
created_at: str
|
||
ticket_type: str
|
||
invoice_type: str
|
||
phone: str
|
||
email: EmailStr
|
||
company_name: str
|
||
tax_number: str
|
||
register_address: str
|
||
register_phone: str
|
||
bank_name: str
|
||
bank_account: str
|
||
status: str
|
||
app_user_id: Optional[int]
|
||
header_id: Optional[int]
|
||
wechat: Optional[str]
|
||
|
||
|
||
class InvoiceList(BaseModel):
|
||
items: List[InvoiceOut]
|
||
total: int
|
||
page: int
|
||
page_size: int
|
||
|
||
|
||
class UpdateStatus(BaseModel):
|
||
id: int
|
||
status: str = Field(..., pattern=r"^(pending|invoiced|rejected|refunded)$")
|
||
|
||
|
||
class UpdateType(BaseModel):
|
||
ticket_type: str = Field(..., pattern=r"^(electronic|paper)$")
|
||
invoice_type: str = Field(..., pattern=r"^(special|normal)$")
|
||
|
||
|
||
class PaymentReceiptCreate(BaseModel):
|
||
url: str = Field(..., min_length=1, max_length=512)
|
||
note: Optional[str] = Field(None, max_length=256)
|
||
extra: Optional[dict] = None
|
||
|
||
|
||
class PaymentReceiptOut(BaseModel):
|
||
id: int
|
||
url: str
|
||
note: Optional[str]
|
||
verified: bool
|
||
created_at: str
|
||
extra: Optional[dict] = None
|
||
|
||
|
||
class AppCreateInvoiceWithReceipt(BaseModel):
|
||
header_id: int
|
||
ticket_type: Optional[str] = Field(None, pattern=r"^(electronic|paper)$")
|
||
invoice_type: Optional[str] = Field(None, pattern=r"^(special|normal)$")
|
||
# 兼容前端索引字段:"0"→normal,"1"→special
|
||
invoiceTypeIndex: Optional[str] = None
|
||
receipt_url: Optional[str] = Field(None, max_length=512)
|
||
note: Optional[str] = Field(None, max_length=256)
|
||
|
||
@field_validator('ticket_type', mode='before')
|
||
@classmethod
|
||
def _default_ticket_type(cls, v):
|
||
return v or 'electronic'
|
||
|
||
@field_validator('receipt_url', mode='before')
|
||
@classmethod
|
||
def _clean_receipt_url(cls, v):
|
||
if isinstance(v, list) and v:
|
||
v = v[0]
|
||
if isinstance(v, str):
|
||
s = v.strip()
|
||
if s.startswith('`') and s.endswith('`'):
|
||
s = s[1:-1].strip()
|
||
return s or None
|
||
return v
|
||
|
||
@model_validator(mode='after')
|
||
def _coerce_invoice_type(self):
|
||
if not self.invoice_type and self.invoiceTypeIndex is not None:
|
||
mapping = {'0': 'normal', '1': 'special'}
|
||
self.invoice_type = mapping.get(str(self.invoiceTypeIndex))
|
||
# 若仍为空,默认 normal
|
||
if not self.invoice_type:
|
||
self.invoice_type = 'normal'
|
||
return self
|