Files

96 lines
4.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""基金模型。
管理基金类型、存续期、LP 构成等信息,支撑评价指标体系的动态权重计算。
"""
import uuid
from datetime import date, datetime, timezone
from sqlalchemy import Boolean, Date, DateTime, Float, ForeignKey, Integer, String
from sqlalchemy.orm import Mapped, mapped_column
from app.core.database import Base
from app.core.types import JSONBType
class Fund(Base):
"""基金信息 — 管理基金类型和存续期。"""
__tablename__ = "funds"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
tenant_id: Mapped[str] = mapped_column(String(36), ForeignKey("tenants.id"), nullable=False, index=True)
name: Mapped[str] = mapped_column(String(200), nullable=False, comment="基金名称")
# 基金类型:angel/early_vc/growth_vc/pe/cvc/fof/distress/esg
fund_type: Mapped[str] = mapped_column(String(50), nullable=False, comment="基金类型")
# 投资策略:growth/value/empowerment/turnaround
strategy: Mapped[str] = mapped_column(String(50), nullable=False, default="growth", comment="投资策略")
# 存续期信息
established_date: Mapped[date | None] = mapped_column(Date, nullable=True, comment="基金成立日")
total_lifespan_months: Mapped[int] = mapped_column(Integer, nullable=False, default=84, comment="总存续期(月)")
investment_period_months: Mapped[int] = mapped_column(Integer, nullable=False, default=48, comment="投资期(月)")
# LP 构成 — {government: 30, market: 50, corporate: 20}
lp_composition_json: Mapped[dict | None] = mapped_column(JSONBType, nullable=True, comment="LP 构成百分比")
# 地域
primary_market: Mapped[str | None] = mapped_column(String(50), nullable=True, comment="主要市场:china_mainland/us/sea/europe")
# 状态
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, comment="是否活跃")
# 元数据
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc),
)
@property
def current_lifecycle(self) -> str:
"""根据当前日期自动计算基金存续期阶段。
返回:investment / growth / exit_preparation / liquidation
"""
if not self.established_date:
return "investment"
today = date.today()
months_elapsed = (today.year - self.established_date.year) * 12 + (today.month - self.established_date.month)
if months_elapsed < self.investment_period_months:
return "investment"
elif months_elapsed < self.total_lifespan_months - 24:
return "growth"
elif months_elapsed < self.total_lifespan_months - 12:
return "exit_preparation"
else:
return "liquidation"
class CompanyFundLink(Base):
"""企业-基金关联 — 一个企业可能被多支基金投资。"""
__tablename__ = "company_fund_links"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
company_id: Mapped[str] = mapped_column(String(36), ForeignKey("companies.id"), nullable=False, index=True)
fund_id: Mapped[str] = mapped_column(String(36), ForeignKey("funds.id"), nullable=False, index=True)
investment_date: Mapped[date | None] = mapped_column(Date, nullable=True, comment="投资日期")
investment_stage: Mapped[str | None] = mapped_column(String(50), nullable=True, comment="投资时企业阶段")
round: Mapped[str | None] = mapped_column(String(50), nullable=True, comment="轮次")
amount: Mapped[float | None] = mapped_column(Float, nullable=True, comment="投资金额(万元)")
ownership_pct: Mapped[float | None] = mapped_column(Float, nullable=True, comment="持股比例(%")
is_current: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, comment="当前是否持有")
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)