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
```
This commit is contained in:
2026-07-08 14:08:03 +08:00
parent 92f490b97e
commit 19e59e83cc
59 changed files with 3271 additions and 247 deletions

View File

@@ -1,9 +1,9 @@
import json
from functools import lru_cache
from typing import Any
from typing import Annotated, Any
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic_settings import BaseSettings, NoDecode, SettingsConfigDict
from app.core.constants import (
ActorValue,
@@ -50,6 +50,7 @@ class Settings(BaseSettings):
feishu_verification_token: str | None = None
feishu_encrypt_key: str | None = None
feishu_default_chat_id: str | None = None
feishu_approval_approver_ids: Annotated[list[str], NoDecode] = Field(default_factory=list)
model_provider: str = DEFAULT_MODEL_PROVIDER
openclaw_base_url: str = "http://127.0.0.1:2070"
@@ -57,8 +58,8 @@ class Settings(BaseSettings):
openclaw_ws_url: str | None = None
openclaw_api_key: str | None = None
openclaw_gateway_token: str | None = None
openclaw_allowed_tools: list[str] = Field(default_factory=list)
openclaw_allowed_actions: list[str] = Field(
openclaw_allowed_tools: Annotated[list[str], NoDecode] = Field(default_factory=list)
openclaw_allowed_actions: Annotated[list[str], NoDecode] = Field(
default_factory=lambda: [DEFAULT_OPENCLAW_ACTION_JSON]
)
hermes_base_url: str = "http://127.0.0.1:2073/v1"
@@ -83,6 +84,10 @@ class Settings(BaseSettings):
legacy_project_sync_cron_minute: int = 0
legacy_task_sync_cron_hour: int = 2
legacy_task_sync_cron_minute: int = 30
official_writeback_enabled: bool = False
official_api_base_url: str | None = None
official_api_token: str | None = None
official_api_timeout_seconds: float = 10.0
@field_validator("cors_origins", mode="before")
@classmethod
@@ -99,12 +104,27 @@ class Settings(BaseSettings):
return [str(item).strip() for item in data if str(item).strip()]
return [item.strip() for item in text.split(",") if item.strip()]
@field_validator("openclaw_allowed_tools", "openclaw_allowed_actions", mode="before")
@field_validator(
"openclaw_allowed_tools",
"openclaw_allowed_actions",
"feishu_approval_approver_ids",
mode="before",
)
@classmethod
def parse_csv_list(cls, value: str | list[str]) -> list[str]:
def parse_csv_list(cls, value: str | list[str] | None) -> list[str]:
if value is None:
return []
if isinstance(value, list):
return value
return [item.strip() for item in value.split(",") if item.strip()]
return [str(item).strip() for item in value if str(item).strip()]
text = value.strip()
if not text:
return []
if text.startswith("["):
data = json.loads(text)
if not isinstance(data, list):
raise ValueError(ConfigErrorDetail.CORS_ORIGINS_FORMAT)
return [str(item).strip() for item in data if str(item).strip()]
return [item.strip() for item in text.split(",") if item.strip()]
@field_validator("masked_response_fields", mode="before")
@classmethod