from datetime import date from decimal import Decimal from typing import Any from sqlalchemy import select from sqlalchemy.orm import Session from app.modules.business.models import FundAccount, Project, WorkTask from app.modules.business.service import serialize_model class RiskService: """Evaluate rule-based business risk signals from internal ledgers.""" def __init__(self, db: Session): self.db = db def overdue_tasks(self) -> list[dict[str, Any]]: stmt = select(WorkTask).where( WorkTask.due_date.is_not(None), WorkTask.due_date < date.today(), WorkTask.status.notin_(["完成", "已完成", "关闭"]), ) return [serialize_model(item) for item in self.db.execute(stmt).scalars()] def delayed_projects(self) -> list[dict[str, Any]]: stmt = select(Project).where( Project.due_date.is_not(None), Project.due_date < date.today(), Project.status.notin_(["验收", "已完成", "复盘", "归档", "关闭"]), ) return [serialize_model(item) for item in self.db.execute(stmt).scalars()] def over_budget_projects(self) -> list[dict[str, Any]]: stmt = select(Project).where( Project.budget_amount > 0, Project.actual_amount > Project.budget_amount, ) return [serialize_model(item) for item in self.db.execute(stmt).scalars()] def fund_risks(self) -> list[dict[str, Any]]: stmt = select(FundAccount).where(FundAccount.current_balance < FundAccount.safety_line) return [serialize_model(item) for item in self.db.execute(stmt).scalars()] def summary(self) -> dict[str, Any]: overdue_tasks = self.overdue_tasks() delayed_projects = self.delayed_projects() over_budget_projects = self.over_budget_projects() fund_risks = self.fund_risks() risk_score = ( len(overdue_tasks) * 1 + len(delayed_projects) * 3 + len(over_budget_projects) * 4 + len(fund_risks) * 5 ) if risk_score >= 15: level = "high" elif risk_score >= 5: level = "medium" else: level = "low" return { "risk_level": level, "risk_score": Decimal(risk_score), "overdue_tasks": overdue_tasks, "delayed_projects": delayed_projects, "over_budget_projects": over_budget_projects, "fund_risks": fund_risks, }