Files
company-ai-platform/app/modules/business/service.py
JiuContinent 71ca804764 ```
feat: 添加公司AI管理平台基础架构

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

139 lines
4.9 KiB
Python

from datetime import date, datetime
from decimal import Decimal
from typing import Any
from fastapi import HTTPException, status
from sqlalchemy import Select, func, select
from sqlalchemy.orm import Session
from app.modules.audit.schemas import AuditLogCreate
from app.modules.audit.service import AuditService
from app.modules.approvals.service import ApprovalService
from app.modules.business.registry import HIGH_RISK_DOMAINS, get_domain_model
def serialize_model(record: Any) -> dict[str, Any]:
"""Convert a SQLAlchemy model instance into a JSON-friendly dictionary."""
data: dict[str, Any] = {}
for column in record.__table__.columns:
value = getattr(record, column.name)
if isinstance(value, (datetime, date)):
data[column.name] = value.isoformat()
elif isinstance(value, Decimal):
data[column.name] = float(value)
else:
data[column.name] = value
return data
class BusinessService:
"""Manage generic CRUD operations across registered business domains."""
def __init__(self, db: Session):
self.db = db
self.audit = AuditService(db)
def list_records(
self,
domain: str,
limit: int = 50,
offset: int = 0,
status_filter: str | None = None,
) -> tuple[int, list[dict[str, Any]]]:
model = get_domain_model(domain)
stmt: Select = select(model)
count_stmt = select(func.count()).select_from(model)
if status_filter and hasattr(model, "status"):
stmt = stmt.where(model.status == status_filter)
count_stmt = count_stmt.where(model.status == status_filter)
stmt = stmt.order_by(model.id.desc()).limit(min(limit, 500)).offset(max(offset, 0))
total = int(self.db.execute(count_stmt).scalar() or 0)
return total, [serialize_model(item) for item in self.db.execute(stmt).scalars()]
def get_record(self, domain: str, record_id: int) -> dict[str, Any]:
model = get_domain_model(domain)
record = self.db.get(model, record_id)
if record is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Record not found")
return serialize_model(record)
def create_record(
self,
domain: str,
data: dict[str, Any],
actor: str = "api",
) -> dict[str, Any]:
model = get_domain_model(domain)
allowed = {column.name for column in model.__table__.columns if column.name != "id"}
payload = {key: value for key, value in data.items() if key in allowed}
record = model(**payload)
self.db.add(record)
self.db.commit()
self.db.refresh(record)
result = serialize_model(record)
self.audit.log(
AuditLogCreate(
actor=actor,
source="api",
action=f"create:{domain}",
target_type=domain,
target_id=str(record.id),
request_payload=data,
response_payload=result,
)
)
return result
def update_record(
self,
domain: str,
record_id: int,
data: dict[str, Any],
actor: str = "api",
approval_ticket_id: str | None = None,
) -> dict[str, Any]:
if domain in HIGH_RISK_DOMAINS:
if not approval_ticket_id:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="High-risk domain update requires approval_ticket_id",
)
if not ApprovalService(self.db).is_approved_for(
approval_ticket_id,
domain,
record_id,
f"update:{domain}",
):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Approval ticket is not approved for this update",
)
model = get_domain_model(domain)
record = self.db.get(model, record_id)
if record is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Record not found",
)
allowed = {column.name for column in model.__table__.columns if column.name != "id"}
for key, value in data.items():
if key in allowed:
setattr(record, key, value)
self.db.commit()
self.db.refresh(record)
result = serialize_model(record)
self.audit.log(
AuditLogCreate(
actor=actor,
source="api",
action=f"update:{domain}",
target_type=domain,
target_id=str(record.id),
risk_level="high" if domain in HIGH_RISK_DOMAINS else "low",
request_payload={"data": data, "approval_ticket_id": approval_ticket_id},
response_payload=result,
)
)
return result