Files
video-gen/video-gen-api/app/schemas/credit_product.py
T
2026-08-11 13:23:22 +08:00

135 lines
5.7 KiB
Python

from __future__ import annotations
from datetime import datetime
from typing import Literal
from pydantic import BaseModel, Field, model_validator
class CreditProductBase(BaseModel):
product_code: str = Field(..., min_length=1, max_length=64)
product_type: Literal["subscription", "credit_addon"]
name: str = Field(..., min_length=1, max_length=96)
description: str | None = Field(default=None, max_length=512)
features: list[str] = Field(default_factory=list)
tier_code: str | None = Field(default=None, max_length=32)
tier_rank: int | None = Field(default=None, ge=1, le=999)
billing_cycle: Literal["monthly", "quarterly", "yearly"] | None = None
monthly_grant_credits: float | None = Field(default=None, gt=0, le=999999999.99)
first_purchase_price: float | None = Field(default=None, ge=0, le=999999999.99)
regular_price: float | None = Field(default=None, ge=0, le=999999999.99)
activity_price: float | None = Field(default=None, ge=0, le=999999999.99)
activity_start_at: datetime | None = None
activity_end_at: datetime | None = None
renewal_enabled: bool = True
price: float = Field(default=0, ge=0, le=999999999.99)
grant_credits: float | None = Field(default=None, gt=0, le=999999999.99)
validity_months: int | None = Field(default=None, ge=1, le=36)
credit_level: Literal["promotional", "general"] = "general"
currency: str = Field(default="CNY", min_length=1, max_length=8)
is_active: bool = True
sort_order: int = Field(default=0, ge=-999999, le=999999)
@model_validator(mode="after")
def validate_product_fields(self):
if self.product_type == "subscription":
required = {
"tier_code": self.tier_code,
"tier_rank": self.tier_rank,
"billing_cycle": self.billing_cycle,
"monthly_grant_credits": self.monthly_grant_credits,
"first_purchase_price": self.first_purchase_price,
"regular_price": self.regular_price,
}
missing = [name for name, value in required.items() if value is None]
if missing:
raise ValueError(f"订阅套餐缺少字段: {', '.join(missing)}")
if self.activity_price is not None:
if not self.activity_start_at or not self.activity_end_at:
raise ValueError("配置活动价时必须同时配置活动开始和结束时间")
if self.activity_end_at <= self.activity_start_at:
raise ValueError("活动结束时间必须晚于开始时间")
else:
if self.grant_credits is None:
raise ValueError("积分增值包必须配置积分数量")
if self.price < 0:
raise ValueError("增值包价格不能小于0")
if self.validity_months is not None and not 1 <= self.validity_months <= 36:
raise ValueError("积分增值包有效期必须为1-36个月")
return self
class CreditProductCreate(CreditProductBase):
pass
class CreditProductUpdate(BaseModel):
product_code: str | None = Field(default=None, min_length=1, max_length=64)
name: str | None = Field(default=None, min_length=1, max_length=96)
description: str | None = Field(default=None, max_length=512)
features: list[str] | None = None
tier_code: str | None = Field(default=None, max_length=32)
tier_rank: int | None = Field(default=None, ge=1, le=999)
billing_cycle: Literal["monthly", "quarterly", "yearly"] | None = None
monthly_grant_credits: float | None = Field(default=None, gt=0, le=999999999.99)
first_purchase_price: float | None = Field(default=None, ge=0, le=999999999.99)
regular_price: float | None = Field(default=None, ge=0, le=999999999.99)
activity_price: float | None = Field(default=None, ge=0, le=999999999.99)
activity_start_at: datetime | None = None
activity_end_at: datetime | None = None
renewal_enabled: bool | None = None
price: float | None = Field(default=None, ge=0, le=999999999.99)
grant_credits: float | None = Field(default=None, gt=0, le=999999999.99)
validity_months: int | None = Field(default=None, ge=1, le=36)
credit_level: Literal["promotional", "general"] | None = None
currency: str | None = Field(default=None, min_length=1, max_length=8)
is_active: bool | None = None
sort_order: int | None = Field(default=None, ge=-999999, le=999999)
class CreditProductRenewalUpdate(BaseModel):
renewal_enabled: bool
class CreditProductOut(BaseModel):
id: str
product_code: str
product_type: str
name: str
description: str | None = None
features: list[str] = Field(default_factory=list)
tier_code: str | None = None
tier_rank: int | None = None
billing_cycle: str | None = None
monthly_grant_credits: float = 0
grant_count: int = 1
first_purchase_price: float = 0
regular_price: float = 0
activity_price: float | None = None
activity_start_at: datetime | None = None
activity_end_at: datetime | None = None
renewal_enabled: bool = True
grant_credits: float = 0
validity_months: int | None = None
price: float
current_price: float
target_price: float | None = None
deduction_amount: float = 0
price_type: str | None = None
credit_level: str
currency: str
is_active: bool
sort_order: int
can_purchase: bool | None = None
can_upgrade: bool = False
unavailable_reason: str | None = None
class CreditProductCatalogOut(BaseModel):
subscription_products: list[CreditProductOut]
credit_addons: list[CreditProductOut]
first_purchase_available: bool
current_subscription: dict | None = None