Files
Mutual_Fund/service/advisor/dashboard.py

87 lines
3.1 KiB
Python

"""驾驶舱聚合(首页数据与待办聚合,PRD §4.1)。
口径说明(E1 最小口径):总 AUM=名下客户「持有中」持仓市值之和;有效客户=signed;
流失率=closed/总客户;报告产出/发送=advisor_report 计数。回访完成率等精细口径 P2 后置。
"""
from __future__ import annotations
from sqlalchemy.ext.asyncio import AsyncSession
from common_const import (
CUSTOMER_REL_STATUS_CLOSED,
CUSTOMER_REL_STATUS_SIGNED,
REPORT_SEND_STATUS_DRAFT,
REPORT_SEND_STATUS_SENT,
TODO_STATUS_PENDING,
)
from model.sys_user import SysUser
from repositories.advisor_report import AdvisorReportRepo
from repositories.advisor_todo import AdvisorTodoRepo
from repositories.advisor_visit_record import AdvisorVisitRecordRepo
from repositories.customer_relation import CustomerRelationRepo
def _todo_summary(todo) -> dict:
return {
"id": todo.id,
"todo_type": todo.todo_type,
"customer_id": todo.customer_id,
"priority": todo.priority,
"status": todo.status,
"due_at": todo.due_at.isoformat() if todo.due_at else None,
}
async def get_dashboard(db: AsyncSession, user: SysUser) -> dict:
relation_repo = CustomerRelationRepo(db)
todo_repo = AdvisorTodoRepo(db)
report_repo = AdvisorReportRepo(db)
visit_repo = AdvisorVisitRecordRepo(db)
pending = await todo_repo.count_by_advisor(advisor_id=user.id, status=TODO_STATUS_PENDING)
pending_items = await todo_repo.list_by_advisor(
advisor_id=user.id, status=TODO_STATUS_PENDING, limit=10, offset=0
)
total = await relation_repo.count_customer_rows(advisor_id=user.id)
signed = await relation_repo.count_customer_rows(
advisor_id=user.id, status=CUSTOMER_REL_STATUS_SIGNED
)
closed = await relation_repo.count_customer_rows(
advisor_id=user.id, status=CUSTOMER_REL_STATUS_CLOSED
)
aum = await relation_repo.sum_holdings_value(user.id)
sent = await report_repo.count_by_advisor(
advisor_id=user.id, send_status=REPORT_SEND_STATUS_SENT
)
draft = await report_repo.count_by_advisor(
advisor_id=user.id, send_status=REPORT_SEND_STATUS_DRAFT
)
visits = await visit_repo.count_by_advisor(advisor_id=user.id)
# 客户分层:C1-C5 各档人数(未知等级归入 unknown)
levels = {k: 0 for k in ("C1", "C2", "C3", "C4", "C5")}
unknown = 0
for level, cnt in await relation_repo.risk_level_distribution(user.id):
if level in levels:
levels[level] = cnt
else:
unknown += cnt
return {
"todos": {
"pending": pending,
"items": [_todo_summary(todo) for todo in pending_items],
},
"overview": {
"total_customers": total,
"signed_customers": signed,
"closed_customers": closed,
"churn_rate": round(closed / total, 4) if total else 0.0,
"total_aum": f"{aum:.2f}",
"reports_sent": sent,
"reports_draft": draft,
"visits_count": visits,
},
"customer_levels": {**levels, "unknown": unknown},
}