feat(backend): 6-axis dynamic evaluation system with weight engine and template management

This commit is contained in:
selfrelease
2026-07-19 20:59:31 +08:00
parent f4ddcab2ca
commit de53a252e4
11 changed files with 2145 additions and 1 deletions
+5
View File
@@ -13,8 +13,10 @@ from app.models.customer_plan import CustomerAcquisitionPlan
from app.models.data_source import DataSource
from app.models.decision_sentinel import DecisionSentinel
from app.models.digital_twin import DigitalTwinModel
from app.models.evaluation_template import EvaluationTemplate
from app.models.exit_prediction import ExitPrediction
from app.models.financial_data import FinancialData
from app.models.fund import CompanyFundLink, Fund
from app.models.health_score import HealthScore
from app.models.hypothesis import Hypothesis
from app.models.inquiry import InquiryList
@@ -50,8 +52,11 @@ __all__ = [
"DataSource",
"DecisionSentinel",
"DigitalTwinModel",
"EvaluationTemplate",
"ExitPrediction",
"FinancialData",
"Fund",
"CompanyFundLink",
"FirmProfile",
"FundProfile",
"HealthScore",
+60
View File
@@ -0,0 +1,60 @@
"""评价模板模型。
6 轴动态评价指标体系的模板配置,支持基金类型、存续期、企业阶段、产业赛道、投资策略、投资人类型的动态组合。
"""
import uuid
from datetime import datetime, timezone
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String
from sqlalchemy.orm import Mapped, mapped_column
from app.core.database import Base
from app.core.types import JSONBType
class EvaluationTemplate(Base):
"""评价模板 — 6 轴配置单元,定义维度权重和专属指标。"""
__tablename__ = "evaluation_templates"
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="模板名称")
# 6 轴参数
fund_type: Mapped[str] = mapped_column(String(50), nullable=False, comment="基金类型:angel/early_vc/growth_vc/pe/cvc/fof/distress/esg")
fund_lifecycle: Mapped[str] = mapped_column(String(50), nullable=False, default="investment", comment="存续期阶段:investment/growth/exit_preparation/liquidation")
company_stage: Mapped[str] = mapped_column(String(50), nullable=False, default="a", comment="企业阶段:seed/a/b/c/pre_ipo")
industry: Mapped[str] = mapped_column(String(50), nullable=False, default="ai", comment="产业赛道:ai/saas/hardware/biotech/consumer/fintech/manufacturing")
strategy: Mapped[str] = mapped_column(String(50), nullable=False, default="growth", comment="投资策略:growth/value/empowerment/turnaround")
investor_type: Mapped[str] = mapped_column(String(50), nullable=False, default="investor", comment="投资人类型:gp/post_invest_lead/investor")
# 权重配置 — {dimension_key: weight},归一化后总和 = 1.0
weights_json: Mapped[dict] = mapped_column(JSONBType, nullable=False, comment="14 维度权重(归一化后)")
# 维度裁剪
enabled_dimensions: Mapped[list] = mapped_column(JSONBType, nullable=False, comment="启用的维度 key 列表")
disabled_dimensions: Mapped[list] = mapped_column(JSONBType, nullable=False, default=list, comment="禁用的维度 key 列表")
# 专属指标 — [{key, label, description, data_source}]
custom_metrics_json: Mapped[dict | None] = mapped_column(JSONBType, nullable=True, comment="赛道专属指标定义")
# 修饰因子
lp_focus_metrics: Mapped[list | None] = mapped_column(JSONBType, nullable=True, comment="LP 附加指标列表")
regional_benchmark: Mapped[str | None] = mapped_column(String(50), nullable=True, comment="地域基准标识:china_mainland/us/sea/europe")
# 元数据
is_default: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, comment="是否为该组合的默认模板")
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, comment="是否启用")
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1, comment="版本号")
created_by: Mapped[str | None] = mapped_column(String(36), nullable=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),
)
+95
View File
@@ -0,0 +1,95 @@
"""基金模型。
管理基金类型、存续期、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)
)
+11
View File
@@ -13,6 +13,7 @@ from sqlalchemy.orm import Mapped, mapped_column
from app.core.database import Base
from app.core.types import JSONBType
from app.core.types import JSONBType as _JSONB # 兼容别名
class HealthScore(Base):
@@ -47,3 +48,13 @@ class HealthScore(Base):
calculated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)
# 评价模板关联(向后兼容:旧数据为 NULL)
template_id: Mapped[str | None] = mapped_column(String(36), ForeignKey("evaluation_templates.id"), nullable=True, comment="使用的评价模板 ID")
fund_type: Mapped[str | None] = mapped_column(String(50), nullable=True, comment="冗余存储基金类型,便于查询")
fund_lifecycle: Mapped[str | None] = mapped_column(String(50), nullable=True, comment="冗余存储存续期阶段")
company_stage: Mapped[str | None] = mapped_column(String(50), nullable=True, comment="冗余存储企业阶段")
industry: Mapped[str | None] = mapped_column(String(50), nullable=True, comment="冗余存储产业赛道")
strategy: Mapped[str | None] = mapped_column(String(50), nullable=True, comment="冗余存储投资策略")
custom_metrics_result: Mapped[dict | None] = mapped_column(JSONBType, nullable=True, comment="专属指标评分结果")
lp_focus_result: Mapped[dict | None] = mapped_column(JSONBType, nullable=True, comment="LP 附加指标结果")