feat: 添加公司AI管理平台基础架构

添加了完整的FastAPI后端项目结构,包括:
- 环境配置文件(.env.example)和项目说明文档(README.md、AGENTS.md)
- Dockerfile用于容器化部署
- 核心基础设施:配置管理、数据库连接、调度器、安全认证
- 模块化设计:AI代理、审批流程、审计日志、业务台账等功能模块
- 支持多数据库连接(主库和遗留系统只读库)
- AI适配器支持OpenClaw、Hermes、OpenAI兼容接口
- 飞书集成、报表生成、风险监控等企业级功能
- 完整的依赖管理和测试指南
```
This commit is contained in:
2026-06-21 21:57:28 +08:00
commit 71ca804764
68 changed files with 3662 additions and 0 deletions

View File

@@ -0,0 +1 @@
"""Report module."""

View File

@@ -0,0 +1,41 @@
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.security import require_api_key
from app.modules.reports.schemas import PushReportRequest, ReportResponse
from app.modules.reports.service import ReportService
router = APIRouter(dependencies=[Depends(require_api_key)])
@router.get("/daily-brief", response_model=ReportResponse)
def daily_brief(db: Session = Depends(get_db)) -> dict:
return ReportService(db).daily_brief()
@router.get("/project-weekly", response_model=ReportResponse)
def project_weekly(db: Session = Depends(get_db)) -> dict:
return ReportService(db).project_weekly()
@router.post("/daily-brief/push")
def push_daily_brief(payload: PushReportRequest, db: Session = Depends(get_db)) -> dict:
report = ReportService(db).daily_brief()
return ReportService(db).push_report(
report,
payload.receive_id,
payload.receive_id_type,
payload.actor,
)
@router.post("/project-weekly/push")
def push_project_weekly(payload: PushReportRequest, db: Session = Depends(get_db)) -> dict:
report = ReportService(db).project_weekly()
return ReportService(db).push_report(
report,
payload.receive_id,
payload.receive_id_type,
payload.actor,
)

View File

@@ -0,0 +1,13 @@
from pydantic import BaseModel
class ReportResponse(BaseModel):
title: str
content: str
lines: list[str]
class PushReportRequest(BaseModel):
receive_id: str | None = None
receive_id_type: str = "chat_id"
actor: str = "system"

View 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)