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