```
feat: 添加AI记忆模块和事件调度系统 - 新增AI记忆模块,支持本地记忆召回和自动写入功能 - 实现事件调度系统,支持批量处理待定事件和重试机制 - 集成心跳监控机制,跟踪API、调度器和工作节点状态 - 扩展仪表板数据统计,包含AI记忆条目和心跳概要 - 添加企业运营分析报告功能,提供财务、采购等多维度分析 - 更新配置设置,增加事件调度和AI记忆相关参数 - 优化任务队列,添加事件分发任务类型 - 扩展审计日志,记录AI记忆操作和事件调度行为 - 实现领域事件模型,支持事件持久化和状态管理 - 添加观察性服务,监控系统组件健康状况 ```
This commit is contained in:
@@ -5,10 +5,12 @@ from sqlalchemy import engine_from_config, pool
|
||||
|
||||
from app.core.config import get_settings
|
||||
from app.core.db_base import Base
|
||||
from app.modules.ai_memory import models as ai_memory_models
|
||||
from app.modules.audit import models as audit_models
|
||||
from app.modules.business import models as business_models
|
||||
from app.modules.events import models as event_models
|
||||
from app.modules.feishu import models as feishu_models
|
||||
from app.modules.observability import models as observability_models
|
||||
from app.modules.workflows import models as workflow_models
|
||||
|
||||
config = context.config
|
||||
@@ -21,10 +23,12 @@ settings = get_settings()
|
||||
|
||||
# Keep imports referenced so SQLAlchemy model classes register with Base.metadata.
|
||||
_REGISTERED_MODEL_MODULES = (
|
||||
ai_memory_models,
|
||||
audit_models,
|
||||
business_models,
|
||||
event_models,
|
||||
feishu_models,
|
||||
observability_models,
|
||||
workflow_models,
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
"""Complete V3 read-only operations foundation.
|
||||
|
||||
Revision ID: 202607090001
|
||||
Revises: 202607080002
|
||||
Create Date: 2026-07-09
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import inspect
|
||||
|
||||
|
||||
revision = "202607090001"
|
||||
down_revision = "202607080002"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
DOMAIN_EVENTS_TABLE = "domain_events"
|
||||
AI_MEMORY_ENTRIES_TABLE = "ai_memory_entries"
|
||||
SYSTEM_HEARTBEATS_TABLE = "system_heartbeats"
|
||||
|
||||
|
||||
def _table_exists(table_name: str) -> bool:
|
||||
inspector = inspect(op.get_bind())
|
||||
return table_name in inspector.get_table_names()
|
||||
|
||||
|
||||
def _column_names(table_name: str) -> set[str]:
|
||||
inspector = inspect(op.get_bind())
|
||||
if table_name not in inspector.get_table_names():
|
||||
return set()
|
||||
return {column["name"] for column in inspector.get_columns(table_name)}
|
||||
|
||||
|
||||
def _add_column_if_missing(table_name: str, column: sa.Column) -> None:
|
||||
if column.name not in _column_names(table_name):
|
||||
op.add_column(table_name, column)
|
||||
|
||||
|
||||
def _create_indexes(table_name: str, indexes: list[tuple[str, bool]]) -> None:
|
||||
for column_name, unique in indexes:
|
||||
op.create_index(
|
||||
op.f(f"ix_{table_name}_{column_name}"),
|
||||
table_name,
|
||||
[column_name],
|
||||
unique=unique,
|
||||
if_not_exists=True,
|
||||
)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
for column in [
|
||||
sa.Column("next_attempt_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("locked_until", sa.DateTime(), nullable=True),
|
||||
sa.Column("locked_by", sa.String(length=128), nullable=True),
|
||||
sa.Column("max_attempts", sa.Integer(), nullable=True),
|
||||
]:
|
||||
_add_column_if_missing(DOMAIN_EVENTS_TABLE, column)
|
||||
_create_indexes(
|
||||
DOMAIN_EVENTS_TABLE,
|
||||
[
|
||||
("next_attempt_at", False),
|
||||
("locked_until", False),
|
||||
("locked_by", False),
|
||||
],
|
||||
)
|
||||
|
||||
if not _table_exists(AI_MEMORY_ENTRIES_TABLE):
|
||||
op.create_table(
|
||||
AI_MEMORY_ENTRIES_TABLE,
|
||||
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column("code", sa.String(length=64), nullable=False),
|
||||
sa.Column("scope", sa.String(length=64), nullable=False),
|
||||
sa.Column("subject", sa.String(length=128), nullable=False),
|
||||
sa.Column("content", sa.Text(), nullable=False),
|
||||
sa.Column("summary", sa.Text(), nullable=True),
|
||||
sa.Column("tags", sa.JSON(), nullable=True),
|
||||
sa.Column("source", sa.String(length=64), nullable=False),
|
||||
sa.Column("importance", sa.Integer(), nullable=False),
|
||||
sa.Column("status", sa.String(length=32), nullable=False),
|
||||
sa.Column("actor", sa.String(length=128), nullable=False),
|
||||
sa.Column("last_used_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("expires_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
_create_indexes(
|
||||
AI_MEMORY_ENTRIES_TABLE,
|
||||
[
|
||||
("code", True),
|
||||
("scope", False),
|
||||
("subject", False),
|
||||
("source", False),
|
||||
("importance", False),
|
||||
("status", False),
|
||||
("actor", False),
|
||||
("last_used_at", False),
|
||||
("expires_at", False),
|
||||
("created_at", False),
|
||||
],
|
||||
)
|
||||
|
||||
if not _table_exists(SYSTEM_HEARTBEATS_TABLE):
|
||||
op.create_table(
|
||||
SYSTEM_HEARTBEATS_TABLE,
|
||||
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column("component", sa.String(length=128), nullable=False),
|
||||
sa.Column("instance_id", sa.String(length=128), nullable=False),
|
||||
sa.Column("status", sa.String(length=32), nullable=False),
|
||||
sa.Column("last_seen_at", sa.DateTime(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
_create_indexes(
|
||||
SYSTEM_HEARTBEATS_TABLE,
|
||||
[
|
||||
("component", False),
|
||||
("instance_id", False),
|
||||
("status", False),
|
||||
("last_seen_at", False),
|
||||
("created_at", False),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
for table_name in [SYSTEM_HEARTBEATS_TABLE, AI_MEMORY_ENTRIES_TABLE]:
|
||||
if _table_exists(table_name):
|
||||
op.drop_table(table_name)
|
||||
|
||||
event_columns = _column_names(DOMAIN_EVENTS_TABLE)
|
||||
for column_name in ["locked_by", "locked_until", "next_attempt_at"]:
|
||||
if column_name in event_columns:
|
||||
op.drop_index(op.f(f"ix_{DOMAIN_EVENTS_TABLE}_{column_name}"), table_name=DOMAIN_EVENTS_TABLE)
|
||||
op.drop_column(DOMAIN_EVENTS_TABLE, column_name)
|
||||
if "max_attempts" in _column_names(DOMAIN_EVENTS_TABLE):
|
||||
op.drop_column(DOMAIN_EVENTS_TABLE, "max_attempts")
|
||||
Reference in New Issue
Block a user