```
feat(feishu): 添加飞书入站事件inbox和混合数据库协调功能 - 实现飞书入站事件持久化inbox机制,支持状态管理、租约锁定和重试退避 - 添加混合数据库基线协调工具,确保平台PostgreSQL结构安全对齐 - 增加运行组件心跳检测和readiness就绪检查机制 - 实现app_ticket事件的安全轮换和验证处理 - 添加生产环境运行编排和fail-closed安全机制 - 支持webhook快速确认和长连接独立进程处理 - 完善个人数据擦除时的待处理事件清理功能 ```
This commit is contained in:
@@ -18,6 +18,7 @@ from app.modules.feishu.constants import (
|
||||
FeishuPayloadKey,
|
||||
FeishuReceiveIdType,
|
||||
)
|
||||
from app.modules.feishu.services.reply_outbox import current_reply_outbox
|
||||
|
||||
|
||||
class FeishuService:
|
||||
@@ -30,12 +31,18 @@ class FeishuService:
|
||||
self.tenant_key = _optional_text(tenant_key) or _optional_text(
|
||||
get_settings().feishu_default_tenant_key
|
||||
)
|
||||
self.message_uuid: str | None = None
|
||||
|
||||
def set_tenant_key(self, tenant_key: str | None) -> None:
|
||||
"""Set the default tenant used by subsequent outbound operations."""
|
||||
|
||||
self.tenant_key = _optional_text(tenant_key)
|
||||
|
||||
def set_message_uuid(self, message_uuid: str | None) -> None:
|
||||
"""Set the idempotency UUID used by replies in the current command."""
|
||||
|
||||
self.message_uuid = _optional_text(message_uuid)
|
||||
|
||||
def verify_event(self, payload: dict[str, Any]) -> None:
|
||||
settings = get_settings()
|
||||
expected = settings.feishu_verification_token
|
||||
@@ -62,12 +69,26 @@ class FeishuService:
|
||||
tenant_key: str | None = None,
|
||||
record_audit: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
resolved_uuid = uuid or self.message_uuid
|
||||
resolved_tenant_key = self._resolve_tenant_key(tenant_key)
|
||||
reply_outbox = current_reply_outbox()
|
||||
if reply_outbox is not None:
|
||||
return reply_outbox.capture_text(
|
||||
text=text,
|
||||
receive_id=receive_id,
|
||||
default_receive_id=get_settings().feishu_default_chat_id,
|
||||
receive_id_type=receive_id_type,
|
||||
actor=actor,
|
||||
message_uuid=resolved_uuid,
|
||||
tenant_key=resolved_tenant_key,
|
||||
record_audit=record_audit,
|
||||
)
|
||||
result = self.client.send_text(
|
||||
text,
|
||||
receive_id,
|
||||
receive_id_type,
|
||||
uuid,
|
||||
tenant_key=self._resolve_tenant_key(tenant_key),
|
||||
resolved_uuid,
|
||||
tenant_key=resolved_tenant_key,
|
||||
)
|
||||
if record_audit:
|
||||
self.audit.log(
|
||||
@@ -79,7 +100,7 @@ class FeishuService:
|
||||
"receive_target_hash": _target_fingerprint(receive_id),
|
||||
FeishuPayloadKey.RECEIVE_ID_TYPE: receive_id_type,
|
||||
"content_length": len(text),
|
||||
FeishuPayloadKey.UUID: uuid,
|
||||
FeishuPayloadKey.UUID: resolved_uuid,
|
||||
},
|
||||
response_payload=result,
|
||||
)
|
||||
@@ -95,12 +116,25 @@ class FeishuService:
|
||||
uuid: str | None = None,
|
||||
tenant_key: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
resolved_uuid = uuid or self.message_uuid
|
||||
resolved_tenant_key = self._resolve_tenant_key(tenant_key)
|
||||
reply_outbox = current_reply_outbox()
|
||||
if reply_outbox is not None:
|
||||
return reply_outbox.capture_card(
|
||||
card=card,
|
||||
receive_id=receive_id,
|
||||
default_receive_id=get_settings().feishu_default_chat_id,
|
||||
receive_id_type=receive_id_type,
|
||||
actor=actor,
|
||||
message_uuid=resolved_uuid,
|
||||
tenant_key=resolved_tenant_key,
|
||||
)
|
||||
result = self.client.send_card(
|
||||
card,
|
||||
receive_id,
|
||||
receive_id_type,
|
||||
uuid,
|
||||
tenant_key=self._resolve_tenant_key(tenant_key),
|
||||
resolved_uuid,
|
||||
tenant_key=resolved_tenant_key,
|
||||
)
|
||||
self.audit.log(
|
||||
AuditLogCreate(
|
||||
@@ -111,7 +145,7 @@ class FeishuService:
|
||||
"receive_target_hash": _target_fingerprint(receive_id),
|
||||
FeishuPayloadKey.RECEIVE_ID_TYPE: receive_id_type,
|
||||
"card_element_count": len(card.get(FeishuPayloadKey.ELEMENTS) or []),
|
||||
FeishuPayloadKey.UUID: uuid,
|
||||
FeishuPayloadKey.UUID: resolved_uuid,
|
||||
},
|
||||
response_payload=result,
|
||||
)
|
||||
@@ -124,9 +158,17 @@ class FeishuService:
|
||||
actor: str = ActorValue.SYSTEM,
|
||||
tenant_key: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
resolved_tenant_key = self._resolve_tenant_key(tenant_key)
|
||||
reply_outbox = current_reply_outbox()
|
||||
if reply_outbox is not None:
|
||||
return reply_outbox.capture_image(
|
||||
image=image,
|
||||
actor=actor,
|
||||
tenant_key=resolved_tenant_key,
|
||||
)
|
||||
result = self.client.upload_image(
|
||||
image,
|
||||
tenant_key=self._resolve_tenant_key(tenant_key),
|
||||
tenant_key=resolved_tenant_key,
|
||||
)
|
||||
self.audit.log(
|
||||
AuditLogCreate(
|
||||
|
||||
Reference in New Issue
Block a user