from __future__ import annotations from datetime import UTC, datetime from enum import StrEnum from typing import Annotated from annotated_types import MinLen from pydantic import BaseModel, Field from pydantic_extra_types.country import CountryAlpha2 from ulid import ULID from .ref import AccountRef class ChannelType(StrEnum): """渠道类型""" PAYMENT_CARD = "PAYMENT_CARD" E_WALLET = "E_WALLET" CASH = "CASH" class PaymentInstrumentType(StrEnum): """支付工具类型""" CREDIT = "CREDIT" DEBIT = "DEBIT" # 暂无实现的必要 # PREPAID = "PREPAID" # BNPL = "BNPL" # CONSUMER_LOAN = "CONSUMER_LOAN" class BaseChannel(BaseModel): """基础渠道模型""" id: ULID = Field(default_factory=ULID) ref_accounts: Annotated[list[AccountRef], "关联的账户引用列表", MinLen(1)] created_at: Annotated[datetime, "创建时间", Field(default_factory=lambda: datetime.now(UTC))] updated_at: Annotated[datetime, "更新时间", Field(default_factory=lambda: datetime.now(UTC))] deleted_at: Annotated[datetime | None, "软删除时间(None=未删除)", Field(default=None)] class PaymentCardChannel(BaseChannel): """支付卡渠道""" channel_type = ChannelType.PAYMENT_CARD region: Annotated[CountryAlpha2, "发卡地/清算区域"] issuer_name: Annotated[str, "发卡方名称", MinLen(1)] card_type: Annotated[PaymentInstrumentType, "卡类型"] card_suffix: Annotated[str, "卡号末尾部分,可为后4/5位或完整卡号", MinLen(1)] card_brand: Annotated[ str | None, "卡组织: UnionPay/Visa/Mastercard/JCB/AMEX/Octopus。通常可由完整卡号自动推断", Field(default=None), ] class EWalletChannel(BaseChannel): """电子钱包渠道""" channel_type = ChannelType.E_WALLET region: Annotated[CountryAlpha2, "钱包所属区域"] platform: Annotated[ str, "平台名: Alipay/WeChatPay/PayMe/AlipayHK/MPay/Octopus/AlipayMO", MinLen(1), ] platform_account_id: Annotated[str | None, "平台账户ID/手机号掩码", Field(default=None)] sub_channel: Annotated[ str | None, "子渠道: 余额/花呗/信用卡快捷/八达通App等", Field(default=None) ] sub_channel_type: Annotated[PaymentInstrumentType, "子渠道类型"] class CashChannel(BaseChannel): """现金渠道""" channel_type = ChannelType.CASH class TransferChannel(BaseChannel): """转账渠道""" channel_type = ChannelType.TRANSFER type Channel = Annotated[ PaymentCardChannel | EWalletChannel | CashChannel, Field(discriminator="channel_type"), ]