PMFlow/backend/app/services/integrations/feishu_integration_service/mixin_config.py

160 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
from .shared import *
class FeishuConfigMixin:
def __init__(self, db: AsyncSession):
self.db = db
# 从数据库获取飞书配置
self.app_id = None
self.app_secret = None
self.encrypt_key = None
self.verification_token = None
self.event_mode = "long_connection"
self.tenant_id = 1
@staticmethod
def _normalize_event_mode(_: Optional[str] = None) -> str:
return "long_connection"
@staticmethod
def _is_postgresql_session(db: AsyncSession) -> bool:
try:
bind = db.get_bind()
except Exception:
return False
dialect_name = str(getattr(getattr(bind, "dialect", None), "name", "")).lower()
return dialect_name.startswith("postgres")
@staticmethod
async def _notify_outbox_ready(db: AsyncSession, *, outbox_id: Optional[int]) -> None:
"""仅在 PostgreSQL 下发送 outbox ready 事件。"""
if not FeishuConfigMixin._is_postgresql_session(db):
return
try:
payload = str(int(outbox_id))[:64] if outbox_id is not None else ""
await db.execute(
text("SELECT pg_notify('feishu_outbox_ready', :payload)"),
{"payload": payload},
)
except Exception as exc:
print(f"[Feishu] outbox notify failed: outbox_id={outbox_id}, error={exc}")
async def load_config(self, tenant_id: int = 1):
"""从数据库加载飞书配置"""
self.tenant_id = tenant_id
result = await self.db.execute(
select(Integration).where(
Integration.tenant_id == tenant_id,
Integration.type == 'feishu',
Integration.is_active.is_(True),
)
)
integration = result.scalar_one_or_none()
if integration:
if isinstance(integration.config, str):
config = json.loads(integration.config) if integration.config else {}
else:
config = integration.config or {}
self.app_id = config.get('app_id')
self.app_secret = config.get('app_secret')
self.encrypt_key = None
self.verification_token = None
self.event_mode = self._normalize_event_mode(config.get('event_mode'))
else:
self.app_id = None
self.app_secret = None
self.encrypt_key = None
self.verification_token = None
self.event_mode = 'long_connection'
def is_configured(self) -> bool:
"""检查是否已配置"""
return bool(self.app_id and self.app_secret)
async def save_config(
self,
app_id: str,
app_secret: str,
encrypt_key: str,
verification_token: str,
event_mode: str = "long_connection",
tenant_id: int = 1
):
"""保存飞书配置到数据库"""
mode = self._normalize_event_mode(event_mode)
config = {
'app_id': app_id,
'app_secret': app_secret,
'encrypt_key': "",
'verification_token': "",
'event_mode': mode,
}
# 查找或创建集成记录
result = await self.db.execute(
select(Integration).where(
Integration.tenant_id == tenant_id,
Integration.type == 'feishu'
)
)
integration = result.scalar_one_or_none()
if integration:
integration.config = config
integration.is_active = True
else:
integration = Integration(
type='feishu',
config=config,
is_active=True,
tenant_id=tenant_id
)
self.db.add(integration)
await self.db.commit()
# 更新内存配置
self.app_id = app_id
self.app_secret = app_secret
self.encrypt_key = None
self.verification_token = None
self.event_mode = mode
self.tenant_id = tenant_id
async def handle_message_event(
self,
event_data: Dict[str, Any]
) -> Dict[str, Any]:
"""
处理飞书消息事件
支持的事件类型:
- message.receive_v1: 收到群聊消息
- im.message.receive_v1: 收到群聊消息(schema 2.0)
- card.action.trigger: 卡片按钮回调
"""
try:
event_type = (event_data.get('type') or '').strip()
# 飞书 schema 2.0 会返回 im.message.receive_v1
if event_type in {'message.receive_v1', 'im.message.receive_v1'}:
return await self._handle_message_receive(event_data)
if event_type in {'card.action.trigger', 'im.card.action.trigger'}:
return await self._handle_card_action_trigger(event_data)
return {
'code': 0,
'msg': f'Event type {event_type} not handled'
}
except Exception as e:
print(f"[Feishu] Handle event error: {e}")
return {
'code': 1,
'msg': str(e)
}