feat(workbench): complete company view — health trend, major events, milestones, team, synergy + dashboard click-through
This commit is contained in:
@@ -18,16 +18,25 @@ from app.schemas.company import (
|
||||
AgreementBrief,
|
||||
BoardMeetingBrief,
|
||||
HealthScoreBrief,
|
||||
HealthScoreHistoryPoint,
|
||||
MajorEventBrief,
|
||||
MilestoneBrief,
|
||||
ReportBrief,
|
||||
RiskBrief,
|
||||
SynergyBrief,
|
||||
TeamMemberBrief,
|
||||
WeakSignalBrief,
|
||||
)
|
||||
from app.models.agreement import InvestmentAgreement
|
||||
from app.models.board import BoardMeeting
|
||||
from app.models.financial_data import FinancialData
|
||||
from app.models.health_score import HealthScore
|
||||
from app.models.major_event import MajorEvent
|
||||
from app.models.milestone import MilestoneTree
|
||||
from app.models.report import MonthlyReport
|
||||
from app.models.risk import RiskEvent
|
||||
from app.models.synergy import SynergyOpportunity
|
||||
from app.models.talent import TeamMember
|
||||
from app.models.weak_signal import WeakSignal
|
||||
|
||||
router = APIRouter(prefix="/companies", tags=["companies"])
|
||||
@@ -224,14 +233,102 @@ async def get_company_detail(
|
||||
latest_fin = latest_fin_result.scalar_one_or_none()
|
||||
latest_financial = latest_fin.data_json if latest_fin else None
|
||||
|
||||
# 健康度历史趋势(最近 12 条)
|
||||
history_result = await db.execute(
|
||||
select(HealthScore)
|
||||
.where(HealthScore.company_id == company_id)
|
||||
.order_by(HealthScore.calculated_at.asc())
|
||||
.limit(12)
|
||||
)
|
||||
history_scores = history_result.scalars().all()
|
||||
health_score_history = [
|
||||
HealthScoreHistoryPoint(
|
||||
period=s.calculated_at.strftime("%Y-%m"),
|
||||
total_score=s.total_score,
|
||||
calculated_at=s.calculated_at,
|
||||
)
|
||||
for s in history_scores
|
||||
]
|
||||
|
||||
# 重大事项(最近 10 条)
|
||||
events_result = await db.execute(
|
||||
select(MajorEvent)
|
||||
.where(MajorEvent.company_id == company_id)
|
||||
.order_by(MajorEvent.created_at.desc())
|
||||
.limit(10)
|
||||
)
|
||||
events = events_result.scalars().all()
|
||||
major_events = [
|
||||
MajorEventBrief(
|
||||
id=e.id, event_type=e.event_type, title=e.title,
|
||||
description=e.description, severity=e.severity,
|
||||
status=e.status, occurred_at=e.occurred_at,
|
||||
) for e in events
|
||||
]
|
||||
|
||||
# 里程碑
|
||||
milestone_result = await db.execute(
|
||||
select(MilestoneTree)
|
||||
.where(MilestoneTree.company_id == company_id)
|
||||
.order_by(MilestoneTree.target_date.desc().nulls_last())
|
||||
.limit(20)
|
||||
)
|
||||
milestones = milestone_result.scalars().all()
|
||||
milestone_briefs = [
|
||||
MilestoneBrief(
|
||||
id=m.id, name=m.name, status=m.status, is_current=m.is_current,
|
||||
target_date=m.target_date, actual_date=m.actual_date,
|
||||
description=m.description,
|
||||
) for m in milestones
|
||||
]
|
||||
|
||||
# 团队成员
|
||||
team_result = await db.execute(
|
||||
select(TeamMember)
|
||||
.where(TeamMember.company_id == company_id)
|
||||
.order_by(TeamMember.is_key_person.desc(), TeamMember.joined_at.desc())
|
||||
.limit(20)
|
||||
)
|
||||
team_members_data = team_result.scalars().all()
|
||||
team_members = [
|
||||
TeamMemberBrief(
|
||||
id=t.id, name=t.name, role=t.role, is_key_person=t.is_key_person,
|
||||
stability_score=t.stability_score, joined_at=t.joined_at,
|
||||
) for t in team_members_data
|
||||
]
|
||||
|
||||
# 协同机会(涉及该企业的)
|
||||
synergy_result = await db.execute(
|
||||
select(SynergyOpportunity)
|
||||
.where(
|
||||
(SynergyOpportunity.company_a_id == company_id) |
|
||||
(SynergyOpportunity.company_b_id == company_id)
|
||||
)
|
||||
.order_by(SynergyOpportunity.created_at.desc())
|
||||
.limit(10)
|
||||
)
|
||||
synergies = synergy_result.scalars().all()
|
||||
synergy_briefs = [
|
||||
SynergyBrief(
|
||||
id=s.id, type=s.type, title=s.title,
|
||||
description=s.description, status=s.status,
|
||||
match_reason=s.match_reason,
|
||||
) for s in synergies
|
||||
]
|
||||
|
||||
return success(data=CompanyDetailResponse(
|
||||
company=CompanyResponse.model_validate(company, from_attributes=True),
|
||||
health_score=health_brief,
|
||||
health_score_history=health_score_history,
|
||||
recent_reports=report_briefs,
|
||||
open_risks=risk_briefs,
|
||||
recent_weak_signals=signal_briefs,
|
||||
active_agreements=agreement_briefs,
|
||||
recent_board_meetings=meeting_briefs,
|
||||
major_events=major_events,
|
||||
milestones=milestone_briefs,
|
||||
team_members=team_members,
|
||||
synergy_opportunities=synergy_briefs,
|
||||
financial_data_count=fin_count,
|
||||
latest_financial=latest_financial,
|
||||
))
|
||||
|
||||
@@ -130,15 +130,74 @@ class BoardMeetingBrief(BaseModel):
|
||||
meeting_at: datetime | None = None
|
||||
|
||||
|
||||
class MajorEventBrief(BaseModel):
|
||||
"""重大事项摘要。"""
|
||||
|
||||
id: str
|
||||
event_type: str
|
||||
title: str
|
||||
description: str | None = None
|
||||
severity: str = "medium"
|
||||
status: str = "identified"
|
||||
occurred_at: datetime | None = None
|
||||
|
||||
|
||||
class MilestoneBrief(BaseModel):
|
||||
"""里程碑摘要。"""
|
||||
|
||||
id: str
|
||||
name: str
|
||||
status: str = "planned"
|
||||
is_current: bool = False
|
||||
target_date: datetime | None = None
|
||||
actual_date: datetime | None = None
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class TeamMemberBrief(BaseModel):
|
||||
"""团队成员摘要。"""
|
||||
|
||||
id: str
|
||||
name: str
|
||||
role: str | None = None
|
||||
is_key_person: bool = False
|
||||
stability_score: float | None = None
|
||||
joined_at: datetime | None = None
|
||||
|
||||
|
||||
class SynergyBrief(BaseModel):
|
||||
"""协同机会摘要。"""
|
||||
|
||||
id: str
|
||||
type: str
|
||||
title: str
|
||||
description: str | None = None
|
||||
status: str = "discovered"
|
||||
match_reason: str | None = None
|
||||
|
||||
|
||||
class HealthScoreHistoryPoint(BaseModel):
|
||||
"""健康度历史数据点。"""
|
||||
|
||||
period: str
|
||||
total_score: float
|
||||
calculated_at: datetime
|
||||
|
||||
|
||||
class CompanyDetailResponse(BaseModel):
|
||||
"""企业详情聚合响应 — 工作台使用。"""
|
||||
|
||||
company: CompanyResponse
|
||||
health_score: HealthScoreBrief | None = None
|
||||
health_score_history: list[HealthScoreHistoryPoint] = []
|
||||
recent_reports: list[ReportBrief] = []
|
||||
open_risks: list[RiskBrief] = []
|
||||
recent_weak_signals: list[WeakSignalBrief] = []
|
||||
active_agreements: list[AgreementBrief] = []
|
||||
recent_board_meetings: list[BoardMeetingBrief] = []
|
||||
major_events: list[MajorEventBrief] = []
|
||||
milestones: list[MilestoneBrief] = []
|
||||
team_members: list[TeamMemberBrief] = []
|
||||
synergy_opportunities: list[SynergyBrief] = []
|
||||
financial_data_count: int = 0
|
||||
latest_financial: dict | None = None
|
||||
|
||||
Reference in New Issue
Block a user