approval/app/clients/feishu_client.py

86 lines
3.3 KiB
Python

from __future__ import annotations
import logging
import time
from typing import Any, Dict
import httpx
from app.config.settings import get_settings
logger = logging.getLogger(__name__)
_token_cache: Dict[str, Any] = {"token": "", "expires_at": 0.0}
class FeishuClient:
def __init__(self) -> None:
self.settings = get_settings()
async def _get_tenant_token(self) -> str:
if _token_cache["token"] and time.time() < _token_cache["expires_at"]:
return _token_cache["token"]
url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
payload = {
"app_id": self.settings.feishu_app_id,
"app_secret": self.settings.feishu_app_secret,
}
async with httpx.AsyncClient(timeout=self.settings.request_timeout) as client:
resp = await client.post(url, json=payload)
resp.raise_for_status()
data = resp.json()
token = data.get("tenant_access_token", "")
expire = int(data.get("expire", 0))
_token_cache["token"] = token
_token_cache["expires_at"] = time.time() + max(expire - 60, 0)
return token
async def get_approval_instance(self, instance_id: str) -> Dict[str, Any]:
token = await self._get_tenant_token()
url = f"https://open.feishu.cn/open-apis/approval/v4/instances/{instance_id}"
headers = {"Authorization": f"Bearer {token}"}
async with httpx.AsyncClient(timeout=self.settings.request_timeout) as client:
resp = await client.get(url, headers=headers)
resp.raise_for_status()
return resp.json()
async def subscribe_approval(self, approval_code: str) -> Dict[str, Any]:
token = await self._get_tenant_token()
url = (
"https://open.feishu.cn/open-apis/approval/v4/approvals/"
f"{approval_code}/subscribe"
)
headers = {"Authorization": f"Bearer {token}"}
async with httpx.AsyncClient(timeout=self.settings.request_timeout) as client:
resp = await client.post(url, headers=headers)
data = {}
try:
data = resp.json()
except Exception:
data = {}
code = data.get("code")
if resp.status_code != 200 and code != 1390007:
logger.error("订阅审批定义失败: status=%s body=%s", resp.status_code, resp.text)
resp.raise_for_status()
return data
async def unsubscribe_approval(self, approval_code: str) -> Dict[str, Any]:
token = await self._get_tenant_token()
url = (
"https://open.feishu.cn/open-apis/approval/v4/approvals/"
f"{approval_code}/unsubscribe"
)
headers = {"Authorization": f"Bearer {token}"}
async with httpx.AsyncClient(timeout=self.settings.request_timeout) as client:
resp = await client.post(url, headers=headers)
data = {}
try:
data = resp.json()
except Exception:
data = {}
if resp.status_code != 200 and data.get("code") not in (0, 1390007):
logger.error("取消订阅审批失败: status=%s body=%s", resp.status_code, resp.text)
resp.raise_for_status()
return data