Files
company-ai-platform/app/modules/feishu/events.py
JiuContinent 514b14d390 ```
feat: 添加飞书集成和审计API密钥认证

- 在数据库配置中添加飞书模型导入
- 添加审计API密钥配置项和认证中间件
- 实现飞书事件重复处理防止机制
- 为审批路由添加API密钥认证
- 优化AI适配器错误处理并添加JSON解析异常捕获
- 更新测试用例以包含新的认证和事件处理逻辑
```
2026-07-06 11:34:15 +08:00

101 lines
3.5 KiB
Python

from typing import Any
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from app.core.constants import ActorValue
from app.modules.audit.constants import AuditSource
from app.modules.audit.schemas import AuditLogCreate
from app.modules.feishu.commands import FeishuCommandService
from app.modules.feishu.constants import (
FEISHU_LONG_CONNECTION_EVENT_ACTION,
FEISHU_WEBHOOK_EVENT_ACTION,
FeishuCommandKey,
FeishuPayloadKey,
)
from app.modules.feishu.models import FeishuEventReceipt
from app.modules.feishu.service import FeishuService
FEISHU_EVENT_ACTIONS = {
"webhook": FEISHU_WEBHOOK_EVENT_ACTION,
"long_connection": FEISHU_LONG_CONNECTION_EVENT_ACTION,
}
class FeishuEventService:
"""Handle Feishu message events from webhook or long connection."""
def __init__(self, db: Session):
self.db = db
self.feishu = FeishuService(db)
self.commands = FeishuCommandService(db)
def handle_event(
self,
payload: dict[str, Any],
source: str,
auto_reply: bool = True,
) -> dict[str, Any]:
self.feishu.verify_event(payload)
event_identity = _event_identity(payload, source)
if event_identity and not self._register_event(event_identity):
return {"ok": True, "handled": False, "duplicate": True}
self.feishu.audit.log(
AuditLogCreate(
actor=ActorValue.FEISHU,
source=AuditSource.FEISHU,
action=FEISHU_EVENT_ACTIONS.get(source, FEISHU_WEBHOOK_EVENT_ACTION),
target_type=source,
target_id=event_identity.get("event_key") if event_identity else None,
request_payload=payload,
response_payload={"accepted": True},
)
)
command = self.commands.extract_event_command(payload)
if not command:
return {"ok": True, "handled": False}
result = self.commands.handle_text(
command[FeishuCommandKey.TEXT],
chat_id=command[FeishuCommandKey.CHAT_ID],
actor=command[FeishuCommandKey.ACTOR],
auto_reply=auto_reply,
)
return {"ok": True, "handled": True, "result": result}
def _register_event(self, event_identity: dict[str, str | None]) -> bool:
receipt = FeishuEventReceipt(
event_key=str(event_identity["event_key"]),
source=str(event_identity["source"]),
event_id=event_identity.get("event_id"),
message_id=event_identity.get("message_id"),
)
self.db.add(receipt)
try:
self.db.flush()
except IntegrityError:
self.db.rollback()
return False
return True
def _event_identity(payload: dict[str, Any], source: str) -> dict[str, str | None] | None:
header = payload.get(FeishuPayloadKey.HEADER) or {}
event = payload.get(FeishuPayloadKey.EVENT) or {}
message = event.get(FeishuPayloadKey.MESSAGE) or {}
event_id = header.get(FeishuPayloadKey.EVENT_ID)
message_id = message.get(FeishuPayloadKey.MESSAGE_ID)
stable_id = event_id or message_id
if not stable_id:
return None
event_type = header.get(FeishuPayloadKey.EVENT_TYPE)
event_key = ":".join(
str(part)
for part in (source, event_type or FeishuPayloadKey.EVENT, stable_id)
)
return {
"event_key": event_key,
"source": source,
"event_id": str(event_id) if event_id else None,
"message_id": str(message_id) if message_id else None,
}