feat(feishu): 添加飞书入站事件inbox和混合数据库协调功能

- 实现飞书入站事件持久化inbox机制,支持状态管理、租约锁定和重试退避
- 添加混合数据库基线协调工具,确保平台PostgreSQL结构安全对齐
- 增加运行组件心跳检测和readiness就绪检查机制
- 实现app_ticket事件的安全轮换和验证处理
- 添加生产环境运行编排和fail-closed安全机制
- 支持webhook快速确认和长连接独立进程处理
- 完善个人数据擦除时的待处理事件清理功能
```
This commit is contained in:
2026-07-27 17:14:37 +08:00
parent d7db84571d
commit eb8267ed18
61 changed files with 8703 additions and 183 deletions

View File

@@ -3,7 +3,7 @@ from datetime import date, datetime, timedelta
import pytest
from fastapi import HTTPException
from sqlalchemy import select
from sqlalchemy import select, text
from app.modules.ai_agent.constants import AIProviderName, AIResponseKey
from app.modules.business.constants import BusinessResponseKey, StatusValue
@@ -13,6 +13,7 @@ from fastapi.testclient import TestClient
from app.core.config import Settings, get_settings
from app.application.scheduling import create_scheduler
from app.core.database import Base, SessionLocal, engine
from app.core.database.migrations import expected_alembic_heads
from app.core.http.pagination import bounded_limit, bounded_offset
from app.core.http.responses import MaskedJSONResponse
from app.core.security import require_api_key, require_audit_api_key
@@ -89,9 +90,14 @@ from app.application.delivery import ReportDeliveryService
from app.modules.reports.chart import render_lifecycle_chart
from app.modules.reports.services import ReportService
from app.modules.feishu.service import FeishuService
from app.modules.feishu.models import FeishuEventReceipt
from app.application.feishu import FeishuCommandService
from app.application.feishu.events import FeishuEventService, _audit_event_metadata
from app.modules.feishu.constants import FeishuEventSource
from app.application.feishu.events import (
FeishuEventService,
_audit_event_metadata,
_identifier_digest,
)
from app.modules.feishu.constants import FeishuEventSource, FeishuInboundStatus
from app.modules.risk.constants import RiskEventActionValue
from app.modules.market.chart import render_market_chart
from app.modules.market.service import MarketService, TushareClient, normalize_symbol
@@ -101,6 +107,18 @@ from app.modules.workflows.models import WorkflowInstance
Base.metadata.create_all(bind=engine)
with engine.begin() as connection:
connection.execute(
text(
"CREATE TABLE IF NOT EXISTS alembic_version "
"(version_num VARCHAR(32) NOT NULL)"
)
)
connection.execute(text("DELETE FROM alembic_version"))
connection.execute(
text("INSERT INTO alembic_version (version_num) VALUES (:revision)"),
{"revision": expected_alembic_heads()[0]},
)
client = TestClient(app)
headers = {"X-API-Key": "test-key"}
audit_headers = {"X-API-Key": "test-key", "X-Audit-API-Key": "audit-key"}
@@ -183,8 +201,22 @@ def test_feishu_webhook_routes_message_event() -> None:
response = client.post("/api/v1/integrations/feishu/webhook", json=payload)
assert response.status_code == 200
data = response.json()
assert data["handled"] is True
assert data["result"]["command"] == "risk_summary"
assert data["accepted"] is True
assert data["handled"] is False
assert data["status"] == FeishuInboundStatus.PENDING
with SessionLocal() as db:
receipt = db.scalar(
select(FeishuEventReceipt).where(
FeishuEventReceipt.event_id
== _identifier_digest(
"evt-smoke-risk-001",
"event-id",
)
)
)
assert receipt is not None
assert receipt.status == FeishuInboundStatus.SUCCEEDED
assert receipt.payload is None
audit_metadata = _audit_event_metadata(payload)
assert "content" not in json.dumps(audit_metadata)
assert "test-feishu-token" not in json.dumps(audit_metadata)
@@ -192,6 +224,7 @@ def test_feishu_webhook_routes_message_event() -> None:
duplicate_response = client.post("/api/v1/integrations/feishu/webhook", json=payload)
assert duplicate_response.status_code == 200
assert duplicate_response.json()["duplicate"] is True
assert duplicate_response.json()["status"] == FeishuInboundStatus.SUCCEEDED
blocked_logs_response = client.get("/api/v1/audit/logs", headers=headers)
assert blocked_logs_response.status_code == 401
@@ -200,7 +233,11 @@ def test_feishu_webhook_routes_message_event() -> None:
assert logs_response.status_code == 200
audit_payload = json.dumps(logs_response.json(), ensure_ascii=False)
assert "test-feishu-token" not in audit_payload
assert "evt-smoke-risk-001" in audit_payload
assert "evt-smoke-risk-001" not in audit_payload
assert (
_identifier_digest("evt-smoke-risk-001", "event-id")
in audit_payload
)
def test_feishu_rule_commands_create_list_disable_and_enable(monkeypatch) -> None: