Files
group_fqcd_jr/app/api/schemas/promotion_material.py

133 lines
5.3 KiB
Python

"""产品推介材料接口请求结构。"""
from datetime import date
from typing import Any
from pydantic import BaseModel, ConfigDict, Field, field_validator
class StrictPayload(BaseModel):
model_config = ConfigDict(extra="forbid")
class PromotionTaskCreate(StrictPayload):
product_name: str = Field(min_length=1, max_length=128)
product_code: str | None = Field(default=None, max_length=32)
material_title: str = Field(min_length=1, max_length=200)
style_code: str = Field(
default="balanced_allocation",
pattern="^(steady_professional|balanced_allocation|growth_research)$",
)
output_formats: list[str] = Field(default_factory=lambda: ["pptx"], min_length=1, max_length=2)
@field_validator("output_formats")
@classmethod
def validate_output_formats(cls, value: list[str]) -> list[str]:
invalid = set(value) - {"pptx", "pdf", "poster"}
if invalid:
raise ValueError("输出格式只支持 pptx、pdf 或 poster")
if len(set(value)) != len(value):
raise ValueError("输出格式不能重复")
return value
class ProductInfo(StrictPayload):
fund_type: str = Field(min_length=1, max_length=64)
operation_mode: str = Field(min_length=1, max_length=64)
product_status: str = Field(default="new_product", max_length=64)
investment_objective: str = Field(min_length=1, max_length=2000)
benchmark: str | None = Field(default=None, max_length=500)
risk_level: str | None = Field(default=None, max_length=32)
class ManagerInfo(StrictPayload):
manager_name: str = Field(min_length=1, max_length=64)
management_company: str = Field(min_length=1, max_length=128)
registration_code: str = Field(min_length=1, max_length=64)
employment_years: str | None = Field(default=None, max_length=64)
investment_management_experience: str | None = Field(default=None, max_length=1000)
profile: str | None = Field(default=None, max_length=3000)
class TeamInfo(StrictPayload):
team_description: str = Field(min_length=1, max_length=3000)
research_capability: str | None = Field(default=None, max_length=2000)
class StrategyInfo(StrictPayload):
investment_scope: str = Field(min_length=1, max_length=3000)
strategy: str = Field(min_length=1, max_length=3000)
restrictions: str = Field(min_length=1, max_length=3000)
index_tool_attribute: str | None = Field(default=None, max_length=1000)
class FeeStructure(StrictPayload):
subscription_fee: str | None = Field(default=None, max_length=500)
purchase_fee: str | None = Field(default=None, max_length=500)
redemption_fee: str | None = Field(default=None, max_length=500)
sales_service_fee: str | None = Field(default=None, max_length=500)
management_fee: str | None = Field(default=None, max_length=500)
custody_fee: str | None = Field(default=None, max_length=500)
client_maintenance_fee: str | None = Field(default=None, max_length=500)
class RankingInfo(StrictPayload):
enabled: bool = False
institution_name: str | None = Field(default=None, max_length=128)
evaluation_period_years: str | None = Field(default=None, max_length=32)
ranking_text: str | None = Field(default=None, max_length=1000)
public_source: str | None = Field(default=None, max_length=1000)
class PerformanceInfo(StrictPayload):
as_of_date: date | None = None
history_months: int | None = Field(default=None, ge=0, le=600)
product_return: str | None = Field(default=None, max_length=64)
max_drawdown: str | None = Field(default=None, max_length=64)
volatility: str | None = Field(default=None, max_length=64)
sharpe_ratio: str | None = Field(default=None, max_length=64)
show_product_performance: bool = False
show_manager_performance: bool = False
ranking: RankingInfo = Field(default_factory=RankingInfo)
class RiskDisclosure(StrictPayload):
special_risks: list[str] = Field(default_factory=list, max_length=20)
additional_notes: str | None = Field(default=None, max_length=2000)
class PromotionInputsUpdate(StrictPayload):
product_info: ProductInfo
manager_info: ManagerInfo
team_info: TeamInfo
strategy_info: StrategyInfo
fee_structure: FeeStructure
performance_info: PerformanceInfo
risk_disclosure: RiskDisclosure = Field(default_factory=RiskDisclosure)
source_notes: dict[str, Any] = Field(default_factory=dict)
class PromotionGenerationRequest(StrictPayload):
output_formats: list[str] | None = Field(default=None, min_length=1, max_length=2)
@field_validator("output_formats")
@classmethod
def validate_generation_formats(cls, value: list[str] | None) -> list[str] | None:
if value is None:
return value
if set(value) - {"pptx", "pdf", "poster"} or len(set(value)) != len(value):
raise ValueError("输出格式只支持不重复的 pptx、pdf 或 poster")
return value
class PromotionReviewRequest(StrictPayload):
material_version_id: int = Field(gt=0)
decision: str = Field(pattern="^(approved|rejected|revision_requested)$")
comment: str | None = Field(default=None, max_length=1000)
class PromotionDeliveryRequest(StrictPayload):
material_version_id: int = Field(gt=0)
advisor_ids: list[int] = Field(min_length=1, max_length=100)
delivery_channel: str = Field(default="internal_record", max_length=32)