feat: 添加数据库迁移脚本并更新Dockerfile配置 - 在Dockerfile中添加alembic配置文件和目录的复制指令 - 更新alembic/env.py注册新的模块模型:events、workflows、writebacks - 生成完整的初始数据库schema迁移脚本,包含以下表: - approval_requests, attendance_records, audit_logs, domain_events - expenses, feishu_event_receipts, fund_accounts, legacy_sync_runs - official_writeback_runs, performance_metrics, policies, procurements - projects, report_push_runs, risk_event_actions, risk_events - standards, suppliers, work_reports, work_tasks, workflow_actions - workflow_instances等21个数据表结构定义 - 在API路由器中添加新模块的路由:events、workflows、writebacks、observability ```
75 lines
2.7 KiB
Python
75 lines
2.7 KiB
Python
from typing import Any, Protocol
|
|
|
|
import httpx
|
|
from fastapi import HTTPException, status
|
|
|
|
from app.core.config import Settings
|
|
from app.core.constants import BEARER_TOKEN_TEMPLATE, HttpHeader
|
|
from app.modules.writebacks.constants import (
|
|
WRITEBACK_DISABLED_MESSAGE,
|
|
WritebackErrorDetail,
|
|
WritebackPayloadKey,
|
|
WritebackStatus,
|
|
)
|
|
from app.modules.writebacks.models import OfficialWritebackRun
|
|
|
|
|
|
class WritebackAdapter(Protocol):
|
|
def submit(self, run: OfficialWritebackRun) -> dict[str, Any]:
|
|
"""Submit one writeback run to the configured official integration."""
|
|
|
|
|
|
class DisabledWritebackAdapter:
|
|
def submit(self, run: OfficialWritebackRun) -> dict[str, Any]:
|
|
return {
|
|
WritebackPayloadKey.STATUS: WritebackStatus.DISABLED,
|
|
WritebackPayloadKey.ERROR_MESSAGE: WRITEBACK_DISABLED_MESSAGE,
|
|
WritebackPayloadKey.CODE: run.code,
|
|
}
|
|
|
|
|
|
class HttpWritebackAdapter:
|
|
def __init__(self, settings: Settings):
|
|
self.settings = settings
|
|
|
|
def submit(self, run: OfficialWritebackRun) -> dict[str, Any]:
|
|
if not self.settings.official_api_base_url or not self.settings.official_api_token:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail=WritebackErrorDetail.OFFICIAL_API_NOT_CONFIGURED,
|
|
)
|
|
url = f"{self.settings.official_api_base_url.rstrip('/')}/writebacks/{run.domain}"
|
|
headers = {
|
|
HttpHeader.AUTHORIZATION: BEARER_TOKEN_TEMPLATE.format(
|
|
token=self.settings.official_api_token
|
|
)
|
|
}
|
|
payload = {
|
|
WritebackPayloadKey.CODE: run.code,
|
|
WritebackPayloadKey.DOMAIN: run.domain,
|
|
WritebackPayloadKey.RECORD_ID: run.record_id,
|
|
WritebackPayloadKey.ACTION: run.action,
|
|
WritebackPayloadKey.PAYLOAD: run.request_payload or {},
|
|
}
|
|
with httpx.Client(timeout=self.settings.official_api_timeout_seconds) as client:
|
|
response = client.post(url, json=payload, headers=headers)
|
|
if response.status_code >= status.HTTP_400_BAD_REQUEST:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_502_BAD_GATEWAY,
|
|
detail=response.text,
|
|
)
|
|
try:
|
|
data = response.json()
|
|
except ValueError:
|
|
data = {"text": response.text}
|
|
return {
|
|
WritebackPayloadKey.STATUS: WritebackStatus.SENT,
|
|
WritebackPayloadKey.PROVIDER_RESPONSE: data,
|
|
}
|
|
|
|
|
|
def get_writeback_adapter(settings: Settings) -> WritebackAdapter:
|
|
if settings.official_writeback_enabled:
|
|
return HttpWritebackAdapter(settings)
|
|
return DisabledWritebackAdapter()
|