```
feat: 添加公司AI管理平台基础架构 添加了完整的FastAPI后端项目结构,包括: - 环境配置文件(.env.example)和项目说明文档(README.md、AGENTS.md) - Dockerfile用于容器化部署 - 核心基础设施:配置管理、数据库连接、调度器、安全认证 - 模块化设计:AI代理、审批流程、审计日志、业务台账等功能模块 - 支持多数据库连接(主库和遗留系统只读库) - AI适配器支持OpenClaw、Hermes、OpenAI兼容接口 - 飞书集成、报表生成、风险监控等企业级功能 - 完整的依赖管理和测试指南 ```
This commit is contained in:
96
app/modules/reports/service.py
Normal file
96
app/modules/reports/service.py
Normal file
@@ -0,0 +1,96 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.modules.business.models import Expense, FundAccount, Procurement, Project, WorkTask
|
||||
from app.modules.feishu.service import FeishuService
|
||||
from app.modules.risk.service import RiskService
|
||||
|
||||
|
||||
def _money(value: Decimal | int | float | None) -> str:
|
||||
"""Format a numeric value as a two-decimal money string."""
|
||||
|
||||
amount = Decimal(value or 0)
|
||||
return f"{amount:,.2f}"
|
||||
|
||||
|
||||
class ReportService:
|
||||
"""Build operational reports and push them through Feishu."""
|
||||
|
||||
def __init__(self, db: Session):
|
||||
self.db = db
|
||||
self.risks = RiskService(db)
|
||||
|
||||
def daily_brief(self) -> dict:
|
||||
project_count = int(
|
||||
self.db.execute(select(func.count()).select_from(Project)).scalar() or 0
|
||||
)
|
||||
task_count = int(self.db.execute(select(func.count()).select_from(WorkTask)).scalar() or 0)
|
||||
procurement_pending = int(
|
||||
self.db.execute(
|
||||
select(func.count())
|
||||
.select_from(Procurement)
|
||||
.where(Procurement.approval_status.in_(["草稿", "审批中", "待审批"]))
|
||||
).scalar()
|
||||
or 0
|
||||
)
|
||||
expense_pending = int(
|
||||
self.db.execute(
|
||||
select(func.count())
|
||||
.select_from(Expense)
|
||||
.where(Expense.approval_status.in_(["草稿", "审批中", "待审批"]))
|
||||
).scalar()
|
||||
or 0
|
||||
)
|
||||
fund_total = (
|
||||
self.db.execute(select(func.sum(FundAccount.current_balance))).scalar()
|
||||
or Decimal("0")
|
||||
)
|
||||
risk_summary = self.risks.summary()
|
||||
lines = [
|
||||
f"- 项目总数:{project_count}",
|
||||
f"- 任务总数:{task_count}",
|
||||
f"- 待处理采购:{procurement_pending}",
|
||||
f"- 待处理费用:{expense_pending}",
|
||||
f"- 当前账户总余额:{_money(fund_total)}",
|
||||
f"- 逾期任务:{len(risk_summary['overdue_tasks'])}",
|
||||
f"- 延期项目:{len(risk_summary['delayed_projects'])}",
|
||||
f"- 超预算项目:{len(risk_summary['over_budget_projects'])}",
|
||||
f"- 资金风险账户:{len(risk_summary['fund_risks'])}",
|
||||
f"- 综合风险等级:{risk_summary['risk_level']}",
|
||||
]
|
||||
return {"title": "每日经营晨报", "lines": lines, "content": "\n".join(lines)}
|
||||
|
||||
def project_weekly(self) -> dict:
|
||||
active = int(
|
||||
self.db.execute(
|
||||
select(func.count())
|
||||
.select_from(Project)
|
||||
.where(Project.status.notin_(["验收", "已完成", "复盘", "归档", "关闭"]))
|
||||
).scalar()
|
||||
or 0
|
||||
)
|
||||
delayed = self.risks.delayed_projects()
|
||||
over_budget = self.risks.over_budget_projects()
|
||||
lines = [
|
||||
f"- 活跃项目:{active}",
|
||||
f"- 延期项目:{len(delayed)}",
|
||||
f"- 超预算项目:{len(over_budget)}",
|
||||
"- 需要管理层关注:",
|
||||
]
|
||||
for item in delayed[:10]:
|
||||
lines.append(f" - 延期:{item.get('code')} {item.get('name')},负责人 {item.get('owner')}")
|
||||
for item in over_budget[:10]:
|
||||
lines.append(f" - 超预算:{item.get('code')} {item.get('name')}")
|
||||
return {"title": "项目周报", "lines": lines, "content": "\n".join(lines)}
|
||||
|
||||
def push_report(
|
||||
self,
|
||||
report: dict,
|
||||
receive_id: str | None,
|
||||
receive_id_type: str,
|
||||
actor: str,
|
||||
) -> dict:
|
||||
card = FeishuService.build_basic_card(report["title"], report["lines"])
|
||||
return FeishuService(self.db).send_card(card, receive_id, receive_id_type, actor)
|
||||
Reference in New Issue
Block a user