134 lines
3.4 KiB
Python
134 lines
3.4 KiB
Python
from dataclasses import dataclass
|
|
from typing import Any, TypedDict
|
|
|
|
from astrbot.api import logger
|
|
|
|
from ..adapters import APIAdapterRouter
|
|
from ..api_config import APIConfigError
|
|
from ..fun_basic import load_template
|
|
from ..request import APIClientError
|
|
from ..sqlite import AsyncSQLiteDB
|
|
from .cache import JSONCacheRepository
|
|
|
|
DATA_PROCESSING_ERRORS = (
|
|
AttributeError,
|
|
IndexError,
|
|
KeyError,
|
|
OSError,
|
|
OverflowError,
|
|
TypeError,
|
|
ValueError,
|
|
)
|
|
|
|
|
|
class ServiceResult(TypedDict):
|
|
code: int
|
|
msg: str
|
|
data: Any
|
|
temp: str
|
|
icons: dict[str, Any]
|
|
|
|
|
|
def success(data: Any, *, template: str = "") -> ServiceResult:
|
|
return {
|
|
"code": 200,
|
|
"msg": "",
|
|
"data": data,
|
|
"temp": template,
|
|
"icons": {},
|
|
}
|
|
|
|
|
|
def failure(message: str) -> ServiceResult:
|
|
return {
|
|
"code": 0,
|
|
"msg": message,
|
|
"data": {},
|
|
"temp": "",
|
|
"icons": {},
|
|
}
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class ServiceContext:
|
|
router: APIAdapterRouter
|
|
database: AsyncSQLiteDB
|
|
cache: JSONCacheRepository
|
|
|
|
|
|
class BaseDomainService:
|
|
"""领域 Service 共享的请求、缓存和结果构建能力。"""
|
|
|
|
def __init__(self, context: ServiceContext):
|
|
self._router = context.router
|
|
self._database = context.database
|
|
self._cache = context.cache
|
|
|
|
@staticmethod
|
|
def new_result() -> ServiceResult:
|
|
return {
|
|
"code": 0,
|
|
"msg": "",
|
|
"data": {},
|
|
"temp": "",
|
|
"icons": {},
|
|
}
|
|
|
|
@staticmethod
|
|
def success(data: Any, *, template: str = "") -> ServiceResult:
|
|
return success(data, template=template)
|
|
|
|
@staticmethod
|
|
def failure(message: str) -> ServiceResult:
|
|
return failure(message)
|
|
|
|
async def template_result(
|
|
self,
|
|
template_name: str,
|
|
data: Any,
|
|
) -> ServiceResult:
|
|
try:
|
|
template = await load_template(template_name)
|
|
except FileNotFoundError:
|
|
logger.exception(f"模板文件不存在: {template_name}")
|
|
return failure("系统错误:模板文件不存在")
|
|
return success(data, template=template)
|
|
|
|
async def attach_template(
|
|
self,
|
|
result: ServiceResult,
|
|
template_name: str,
|
|
) -> bool:
|
|
try:
|
|
result["temp"] = await load_template(template_name)
|
|
except FileNotFoundError:
|
|
logger.exception(f"模板文件不存在: {template_name}")
|
|
result["msg"] = "系统错误:模板文件不存在"
|
|
return False
|
|
return True
|
|
|
|
async def request(
|
|
self,
|
|
config_key: str,
|
|
params: dict[str, Any] | None = None,
|
|
out_key: str | None = "data",
|
|
path_params: dict[str, Any] | None = None,
|
|
) -> Any | None:
|
|
try:
|
|
data = await self._router.request(
|
|
config_key,
|
|
params=params,
|
|
out_key=out_key,
|
|
path_params=path_params,
|
|
)
|
|
if data is None:
|
|
logger.warning(f"获取接口信息失败或返回空数据: {config_key}")
|
|
return data
|
|
except (APIConfigError, APIClientError) as exc:
|
|
logger.error(
|
|
f"API 请求失败: key={config_key}, "
|
|
f"status={getattr(exc, 'status', None)}, "
|
|
f"error={type(exc).__name__}: {exc}"
|
|
)
|
|
return None
|