11
This commit is contained in:
+35
-13
@@ -1,5 +1,6 @@
|
||||
import asyncio
|
||||
import json
|
||||
import aiofiles
|
||||
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from apscheduler.triggers.interval import IntervalTrigger
|
||||
@@ -36,17 +37,22 @@ class AsyncTask:
|
||||
async with self._file_lock:
|
||||
try:
|
||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if not self.file_path.exists():
|
||||
local_data = {}
|
||||
else:
|
||||
with open(self.file_path, 'r', encoding='utf-8') as f:
|
||||
local_data = json.load(f)
|
||||
async with aiofiles.open(self.file_path, "r", encoding="utf-8") as f:
|
||||
content = await f.read()
|
||||
local_data = json.loads(content) if content else {}
|
||||
|
||||
local_data[key] = value
|
||||
with open(self.file_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(local_data, f, ensure_ascii=False, indent=4)
|
||||
|
||||
except Exception as e:
|
||||
async with aiofiles.open(self.file_path, "w", encoding="utf-8") as f:
|
||||
await f.write(
|
||||
json.dumps(local_data, ensure_ascii=False, indent=4)
|
||||
)
|
||||
|
||||
except (OSError, json.JSONDecodeError) as e:
|
||||
logger.error(f"数据写入文件失败:{e}")
|
||||
|
||||
async def get_local_data(self, key: str, default=None):
|
||||
@@ -54,10 +60,16 @@ class AsyncTask:
|
||||
try:
|
||||
if not self.file_path.exists():
|
||||
return default
|
||||
with open(self.file_path, 'r', encoding='utf-8') as f:
|
||||
local_data = json.load(f)
|
||||
|
||||
async with aiofiles.open(self.file_path, "r", encoding="utf-8") as f:
|
||||
content = await f.read()
|
||||
if not content:
|
||||
return default
|
||||
local_data = json.loads(content)
|
||||
|
||||
return local_data.get(key, default)
|
||||
except Exception as e:
|
||||
|
||||
except (OSError, json.JSONDecodeError) as e:
|
||||
logger.error(f"读取数据文件失败:{e}")
|
||||
return default
|
||||
|
||||
@@ -65,9 +77,14 @@ class AsyncTask:
|
||||
|
||||
async def _job_common(self, fetch_func, task_key: str, namefun: str):
|
||||
state = self.tasks[task_key]
|
||||
|
||||
try:
|
||||
data = await fetch_func()
|
||||
state["state_new"] = data["status"]
|
||||
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError("fetch_func 返回数据不是 dict")
|
||||
|
||||
state["state_new"] = data.get("status")
|
||||
|
||||
if state["state_old"] != state["state_new"]:
|
||||
message_chain = MessageChain().message(data.get("data"))
|
||||
@@ -78,8 +95,15 @@ class AsyncTask:
|
||||
await self.set_local_data(task_key, state["state_new"])
|
||||
state["state_old"] = state["state_new"]
|
||||
|
||||
except asyncio.CancelledError:
|
||||
# 调度器 shutdown 时的正常路径
|
||||
raise
|
||||
|
||||
except (KeyError, TypeError, ValueError) as e:
|
||||
logger.error(f"{namefun} 数据结构异常: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{namefun}后台任务执行异常: {e}")
|
||||
logger.exception(f"{namefun} 后台任务执行异常")
|
||||
|
||||
"""===================== 初始化任务 ====================="""
|
||||
|
||||
@@ -137,13 +161,11 @@ class AsyncTask:
|
||||
logger.error(f"停止全部后台任务失败:{e}")
|
||||
|
||||
async def destroy(self):
|
||||
"""
|
||||
销毁整个调度器,适合插件卸载/重启时调用
|
||||
"""
|
||||
try:
|
||||
self.stop_all_tasks()
|
||||
if self.scheduler.running:
|
||||
self.scheduler.shutdown(wait=False)
|
||||
self.tasks.clear()
|
||||
logger.info("后台调度器已销毁")
|
||||
except Exception as e:
|
||||
logger.error(f"销毁调度器失败:{e}")
|
||||
|
||||
+7
-15
@@ -1,27 +1,19 @@
|
||||
from pathlib import Path
|
||||
from datetime import datetime,date
|
||||
import aiofiles
|
||||
|
||||
def load_template(template_name):
|
||||
async def load_template(template_name: str) -> str:
|
||||
"""
|
||||
从模板文件加载模板内容
|
||||
|
||||
Args:
|
||||
template_name: 模板文件名(不带路径)
|
||||
|
||||
Returns:
|
||||
str: 模板内容
|
||||
异步加载模板内容(非阻塞)
|
||||
"""
|
||||
# 获取模板文件路径
|
||||
plugin_dir = Path(__file__).parent.parent
|
||||
template_path = plugin_dir / "templates" / template_name
|
||||
|
||||
# 检查文件是否存在
|
||||
|
||||
if not template_path.exists():
|
||||
raise FileNotFoundError(f"模板文件不存在: {template_path}")
|
||||
|
||||
# 读取模板内容
|
||||
with open(template_path, 'r', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
|
||||
async with aiofiles.open(template_path, "r", encoding="utf-8") as f:
|
||||
return await f.read()
|
||||
|
||||
|
||||
def gold_to_string(gold_amount):
|
||||
|
||||
+21
-14
@@ -33,6 +33,13 @@ class JX3Service:
|
||||
logger.info(f"获取配置ticket成功。{self.ticket}")
|
||||
|
||||
|
||||
async def close(self):
|
||||
"""释放底层 APIClient 资源"""
|
||||
if self._api:
|
||||
await self._api.close()
|
||||
self._api = None
|
||||
|
||||
|
||||
def _init_return_data(self) -> Dict[str, Any]:
|
||||
"""初始化标准的返回数据结构"""
|
||||
return {
|
||||
@@ -96,7 +103,7 @@ class JX3Service:
|
||||
|
||||
# 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("helps.html")
|
||||
return_data["temp"] = await load_template("helps.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
@@ -198,7 +205,7 @@ class JX3Service:
|
||||
|
||||
# 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("richangyuche.html")
|
||||
return_data["temp"] = await load_template("richangyuche.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
@@ -233,7 +240,7 @@ class JX3Service:
|
||||
|
||||
# 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("xingxiashijian.html")
|
||||
return_data["temp"] = await load_template("xingxiashijian.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
@@ -361,7 +368,7 @@ class JX3Service:
|
||||
|
||||
# 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("qufuzhuangtai.html")
|
||||
return_data["temp"] = await load_template("qufuzhuangtai.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
@@ -450,7 +457,7 @@ class JX3Service:
|
||||
return return_data
|
||||
# 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("jinjia.html")
|
||||
return_data["temp"] = await load_template("jinjia.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
@@ -489,7 +496,7 @@ class JX3Service:
|
||||
|
||||
# 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("qiyuliebiao.html")
|
||||
return_data["temp"] = await load_template("qiyuliebiao.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
@@ -528,7 +535,7 @@ class JX3Service:
|
||||
|
||||
# 5. 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("wujia.html")
|
||||
return_data["temp"] = await load_template("wujia.html")
|
||||
return_data["code"] = 200
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
@@ -578,7 +585,7 @@ class JX3Service:
|
||||
|
||||
# 5. 模板渲染
|
||||
try:
|
||||
return_data["temp"] = load_template("jiaoyihang.html")
|
||||
return_data["temp"] = await load_template("jiaoyihang.html")
|
||||
return_data["code"] = 200
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
@@ -661,7 +668,7 @@ class JX3Service:
|
||||
|
||||
# 4. 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("yanhuan.html")
|
||||
return_data["temp"] = await load_template("yanhuan.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
@@ -697,7 +704,7 @@ class JX3Service:
|
||||
|
||||
# 4. 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("dilujilu.html")
|
||||
return_data["temp"] = await load_template("dilujilu.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
@@ -732,7 +739,7 @@ class JX3Service:
|
||||
|
||||
# 4. 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("tuanduizhaomu.html")
|
||||
return_data["temp"] = await load_template("tuanduizhaomu.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
@@ -772,7 +779,7 @@ class JX3Service:
|
||||
|
||||
# 4. 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("zhanji.html")
|
||||
return_data["temp"] = await load_template("zhanji.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
@@ -820,7 +827,7 @@ class JX3Service:
|
||||
|
||||
# 4. 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("juesheqiyu.html")
|
||||
return_data["temp"] = await load_template("juesheqiyu.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
@@ -853,7 +860,7 @@ class JX3Service:
|
||||
|
||||
# 4. 加载模板
|
||||
try:
|
||||
return_data["temp"] = load_template("zhengyingpaimai.html")
|
||||
return_data["temp"] = await load_template("zhengyingpaimai.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
|
||||
@@ -32,6 +32,7 @@ class APIClient:
|
||||
"""关闭 Session"""
|
||||
if self._session and not self._session.closed:
|
||||
await self._session.close()
|
||||
self._session = None
|
||||
|
||||
async def __aenter__(self):
|
||||
await self.get_session()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import json
|
||||
import shutil
|
||||
import pathlib
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
|
||||
@@ -47,26 +48,29 @@ class Jx3ApiPlugin(Star):
|
||||
"""可选择实现异步的插件初始化方法,当实例化该插件类之后会自动调用该方法。"""
|
||||
# --- 调用函数完成检查和复制 ---
|
||||
try:
|
||||
self.file_local_data = self.check_and_copy_db(
|
||||
local_data_dir=self.local_data_dir,
|
||||
db_filename="local_async.json",
|
||||
default_db_dir=self.data_file_path
|
||||
loop = asyncio.get_running_loop()
|
||||
self.file_local_data = await loop.run_in_executor(
|
||||
None,
|
||||
self.check_and_copy_db,
|
||||
self.local_data_dir,
|
||||
"local_async.json",
|
||||
self.data_file_path
|
||||
)
|
||||
except FileNotFoundError as e:
|
||||
# 处理默认文件丢失的严重错误
|
||||
logger.critical(f"插件初始化失败:{e}")
|
||||
raise # 中断初始化
|
||||
|
||||
raise
|
||||
|
||||
try:
|
||||
self.jx3fun = JX3Service(self.api_config,self.conf)
|
||||
self.jx3fun = JX3Service(self.api_config, self.conf)
|
||||
self.at = AsyncTask(self.context, self.conf, self.jx3fun)
|
||||
await self.at.init_tasks()
|
||||
except Exception as e:
|
||||
await self.at.destroy()
|
||||
logger.error(f"功能示例初始化失败: {e}")
|
||||
return
|
||||
if hasattr(self, "at"):
|
||||
await self.at.destroy()
|
||||
logger.error(f"功能模块初始化失败: {e}")
|
||||
raise
|
||||
|
||||
logger.info("jx3api异步插件初始化完成")
|
||||
logger.info("jx3api 异步插件初始化完成")
|
||||
|
||||
|
||||
def check_and_copy_db(self, local_data_dir: Union[str, Path], db_filename: str, default_db_dir: Union[str, Path]) -> pathlib.Path:
|
||||
@@ -501,8 +505,11 @@ class Jx3ApiPlugin(Star):
|
||||
|
||||
async def terminate(self):
|
||||
"""可选择实现异步的插件销毁方法,当插件被卸载/停用时会调用。"""
|
||||
# 关闭数据库连接
|
||||
|
||||
# 后台z周期进程销毁
|
||||
await self.at.destroy()
|
||||
if self.at:
|
||||
await self.at.destroy()
|
||||
self.at = None
|
||||
|
||||
if self.jx3fun:
|
||||
await self.jx3fun.close()
|
||||
self.jx3fun = None
|
||||
logger.info("jx3api插件已卸载/停用")
|
||||
+2
-1
@@ -1,2 +1,3 @@
|
||||
aiomysql
|
||||
matplotlib
|
||||
matplotlib
|
||||
aiofiles
|
||||
Reference in New Issue
Block a user