from typing import Any from fastapi import HTTPException, status from sqlalchemy.orm import Session from app.core.constants import ActorValue from app.core.config import get_settings from app.modules.audit.constants import AuditAction, AuditSource from app.modules.audit.schemas import AuditLogCreate from app.modules.audit.service import AuditService from app.modules.feishu.client import FeishuClient from app.modules.feishu.constants import FeishuPayloadKey, FeishuReceiveIdType class FeishuService: """Send Feishu messages and record audit entries for outbound actions.""" def __init__(self, db: Session): self.db = db self.audit = AuditService(db) self.client = FeishuClient() def verify_event(self, payload: dict[str, Any]) -> None: settings = get_settings() expected = settings.feishu_verification_token header = payload.get("header") or {} token = payload.get("token") or header.get("token") if not expected: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="FEISHU_VERIFICATION_TOKEN is required", ) if not token or token != expected: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid Feishu token", ) def send_text( self, text: str, receive_id: str | None = None, receive_id_type: str = FeishuReceiveIdType.CHAT_ID, actor: str = ActorValue.SYSTEM, ) -> dict[str, Any]: result = self.client.send_text(text, receive_id, receive_id_type) self.audit.log( AuditLogCreate( actor=actor, source=AuditSource.FEISHU, action=AuditAction.FEISHU_SEND_TEXT, request_payload={ FeishuPayloadKey.RECEIVE_ID: receive_id, FeishuPayloadKey.RECEIVE_ID_TYPE: receive_id_type, FeishuPayloadKey.TEXT: text, }, response_payload=result, ) ) return result def send_card( self, card: dict[str, Any], receive_id: str | None = None, receive_id_type: str = FeishuReceiveIdType.CHAT_ID, actor: str = ActorValue.SYSTEM, ) -> dict[str, Any]: result = self.client.send_card(card, receive_id, receive_id_type) self.audit.log( AuditLogCreate( actor=actor, source=AuditSource.FEISHU, action=AuditAction.FEISHU_SEND_CARD, request_payload={ FeishuPayloadKey.RECEIVE_ID: receive_id, FeishuPayloadKey.RECEIVE_ID_TYPE: receive_id_type, FeishuPayloadKey.CARD: card, }, response_payload=result, ) ) return result @staticmethod def build_basic_card(title: str, lines: list[str]) -> dict[str, Any]: return { "config": {"wide_screen_mode": True}, "header": {"title": {"tag": "plain_text", "content": title}}, "elements": [ { "tag": "div", "text": { "tag": "lark_md", "content": "\n".join(lines) or "暂无数据", }, } ], }