133 lines
5.3 KiB
Python
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)
|