🐛解决退出时数据库重复关闭导致卡进程的BUG

This commit is contained in:
2025-12-02 11:06:13 +08:00
parent a6691595e5
commit 78762eceae
8 changed files with 192 additions and 28 deletions
+7
View File
@@ -57,6 +57,13 @@ __plugin_meta__ = PluginMetadata(
help="我的农场返回图片的清晰度, [low, medium, hight, original]",
default_value="low",
),
RegisterConfig(
key="动态背景",
value=True,
help="是否开启根据季节和节日变化的动态背景",
type=bool,
default_value=True,
),
RegisterConfig(
key="签到图片样式",
value="text",
+28 -5
View File
@@ -35,10 +35,10 @@ class CSqlManager(ABC):
if cls.m_pDB is None:
cls.m_pDB = await aiosqlite.connect(config.g_sDBFilePath)
cls.m_pDB.row_factory = aiosqlite.Row
logger.info("真寻农场数据库连接初始化成功")
logger.debug("真寻农场数据库连接初始化成功")
return True
else:
logger.info("真寻农场数据库连接已存在,跳过初始化")
logger.debug("真寻农场数据库连接已存在,跳过初始化")
return True
except Exception as e:
logger.error(f"真寻农场数据库连接初始化失败: {e}")
@@ -46,10 +46,33 @@ class CSqlManager(ABC):
@classmethod
async def cleanup(cls):
if cls.m_pDB:
await cls.m_pDB.close()
try:
if not cls.isConnectionValid():
logger.warning("真寻农场数据库连接已关闭或无效,跳过清理")
return
if hasattr(cls, "m_pDB") and cls.m_pDB:
await cls.m_pDB.close()
cls.m_pDB = None
cls._initialized = False
logger.debug("真寻农场数据库连接已安全关闭")
else:
logger.debug("真寻农场数据库连接已不存在,无需关闭")
except Exception as e:
logger.error(f"真寻农场数据库清理过程中出错: {e}")
cls.m_pDB = None
logger.info("真寻农场数据库连接已关闭")
cls._initialized = False
@classmethod
def isConnectionValid(cls) -> bool:
if not hasattr(cls, "m_pDB") or cls.m_pDB is None:
return False
try:
return hasattr(cls.m_pDB, "_conn") and cls.m_pDB._conn is not None
except:
return False
@classmethod
def getDB(cls) -> aiosqlite.Connection:
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

+4
View File
@@ -1,3 +1,6 @@
from . import utils
def getConfig():
from . import config
@@ -34,4 +37,5 @@ __all__ = [
"getJsonManager",
"getRequestManager",
"getToolManager",
"utils",
]
+88 -23
View File
@@ -4,8 +4,9 @@ from zhenxun.utils._build_image import BuildImage
from zhenxun.utils.image_utils import ImageTemplate
from zhenxun.utils.platform import PlatformUtils
from ...core import CPlayer
from ...core.dbService import g_pDBService
from .. import config, getJsonManager, getToolManager
from .. import config, getJsonManager, getToolManager, utils
class CDarwFarm:
@@ -19,7 +20,9 @@ class CDarwFarm:
Returns:
bytes: 返回绘制结果
"""
img, soilSize, soilPos, grass = await cls.createBaseCanvas()
dynamic = Config.get_config("zhenxun_plugin_farm", "动态背景")
img, soilSize, soilPos, grass = await cls.createBaseCanvas(dynamic)
player = await getToolManager().getPlayerByUid(uid)
if not player:
@@ -48,20 +51,44 @@ class CDarwFarm:
isFirstExpansion = False
await cls.drawUserInfo(img, player, uid)
await cls.applyDefinitionResize(img)
return img.pic2bytes()
@classmethod
async def createBaseCanvas(cls) -> tuple[BuildImage, tuple, dict, BuildImage]:
async def createBaseCanvas(
cls, dynamic: bool
) -> tuple[BuildImage, tuple, dict, BuildImage]:
"""创建基础画布并返回 (img, soilSize, soilPos, grass)
Returns:
tuple: (主画布, 土地尺寸, 土地坐标映射, 草地图)
"""
img = BuildImage(
background=config.g_sResourcePath / "background/background.jpg"
)
if dynamic:
season = getToolManager().getSeason()
backgroundPath = config.g_sResourcePath / "background/decorateIcon"
# 节日优先级更高
if season == utils.Season.SPRING:
bgFile = backgroundPath / "decorate_spring_1.jpg"
elif season == utils.Season.SUMMER:
bgFile = backgroundPath / "decorate_summer_1.jpg"
elif season == utils.Season.AUTUMN:
bgFile = backgroundPath / "decorate_autumn_1.jpg"
elif season == utils.Season.WINTER:
bgFile = backgroundPath / "decorate_winter_1.jpg"
else:
bgFile = backgroundPath / "decorate_default_1.jpg"
img = BuildImage(background=bgFile)
else:
img = BuildImage(
background=config.g_sResourcePath
/ "background/decorateIcon/decorate_default_1jpg"
)
soil = await getJsonManager().getSoil()
soilSize = soil["size"]
@@ -76,17 +103,17 @@ class CDarwFarm:
async def drawUnlockedTile(
cls, img: BuildImage, uid: str, soilIndex: int, x: int, y: int, soilSize
) -> bool:
"""绘制已解锁土地格(包含土地贴图与作物),返回该格是否可收获。
"""绘制已解锁土地格(包含土地贴图与作物),返回该格是否可收获
Args:
img (BuildImage): 主画布。
uid (str): 用户UID。
soilIndex (int): 土地索引(从1开始)。
x (int), y (int): 贴图位置。
soilSize: 土地贴图尺寸([w,h] 或 (w,h))。
img (BuildImage): 主画布
uid (str): 用户UID
soilIndex (int): 土地索引(从1开始)
x (int), y (int): 贴图位置
soilSize: 土地贴图尺寸([w,h] 或 (w,h))
Returns:
bool: 是否有可收获作物(isRipe)。
bool: 是否有可收获作物(isRipe)
"""
soilUrl = "soil/普通土地.png"
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex)
@@ -128,10 +155,10 @@ class CDarwFarm:
grass: BuildImage,
isFirstExpansion: bool,
) -> bool:
"""绘制未解锁格子并在首次出现时绘制扩建图标。
"""绘制未解锁格子并在首次出现时绘制扩建图标
Returns:
bool: 是否在此格绘制了扩建图标(仅首次返回 True)。
bool: 是否在此格绘制了扩建图标(仅首次返回 True)
"""
await img.paste(grass, (x, y))
@@ -153,7 +180,7 @@ class CDarwFarm:
@classmethod
async def pasteRipeOnce(cls, img: BuildImage, x: int, y: int, soilSize):
"""首次发现成熟作物时贴上成熟提示图(ripe)。"""
"""首次发现成熟作物时贴上成熟提示图(ripe)"""
ripe = BuildImage(background=config.g_sResourcePath / "background/ripe.png")
await img.paste(
ripe,
@@ -161,22 +188,32 @@ class CDarwFarm:
)
@classmethod
async def drawUserInfo(cls, img: BuildImage, player, uid: str):
"""绘制左上角用户信息(头像、框、昵称、经验、等级、货币等)。
async def drawUserInfo(cls, img: BuildImage, player: CPlayer, uid: str):
"""绘制左上角用户信息(头像、框、昵称、经验、等级、货币等)
TODO 需要详细的绘制头像框
Args:
img (BuildImage): 主画布。
player: 用户对象(包含 user 字段)。
uid (str): 用户UID。
img (BuildImage): 主画布
player(CPlayer): 用户
uid (str): 用户Uid
"""
# 背景板
background = BuildImage(
background=config.g_sResourcePath / "background/userInfo.png"
)
await img.paste(background, (150, 30))
# 头像
image = await PlatformUtils.get_user_avatar(uid, "qq")
if image:
avatar = BuildImage(background=image)
await avatar.resize(0, 140, 150)
await img.paste(avatar, (125, 85))
frame = BuildImage(background=config.g_sResourcePath / "background/frame.png")
# 头像框
frame = BuildImage(
background=config.g_sResourcePath / "background/frame/frame.png"
)
await img.paste(frame, (75, 44))
nameImg = await BuildImage.build_text_image(
@@ -214,9 +251,37 @@ class CDarwFarm:
)
await img.paste(bondsImg, (570, 255))
@classmethod
async def drawUserDecorateIcon(cls, img: BuildImage, player: CPlayer) -> bool:
"""绘制用户装饰图标
Args:
img (BuildImage): 主画布
player (CPlayer): 用户
Returns:
bool: 是否成功绘制
"""
decorateIconId = player.user.get("decorateIcon", 0)
if decorateIconId <= 0:
return False
iconPath = (
config.g_sResourcePath / f"decorateIcon/DecorateIcon{decorateIconId}.png"
)
if not iconPath.exists():
return False
icon = BuildImage(background=iconPath)
await icon.resize(0, 100, 100)
await img.paste(icon, (x, y))
return True
@classmethod
async def applyDefinitionResize(cls, img: BuildImage):
"""根据配置调整绘制清晰度(original/medium/hight/其他)。"""
"""根据配置调整绘制清晰度(original/medium/hight/其他)"""
definition = Config.get_config("zhenxun_plugin_farm", "绘制农场清晰度")
if definition == "medium":
await img.resize(0.6)
+43
View File
@@ -5,11 +5,13 @@ from zoneinfo import ZoneInfo
from jinja2 import Template
from playwright.async_api import async_playwright
from zhdate import ZhDate
from zhenxun.services.log import logger
from zhenxun.utils.message import MessageUtils
from ..core import CPlayer, g_pUserPool
from . import utils
class CToolManager:
@@ -110,6 +112,47 @@ class CToolManager:
tz = ZoneInfo("Asia/Shanghai")
return datetime.now(tz)
@classmethod
def getSeason(cls) -> utils.Season:
month = cls.dateTime().month
if month in [3, 4, 5]:
return utils.Season.SPRING
elif month in [6, 7, 8]:
return utils.Season.SUMMER
elif month in [9, 10, 11]:
return utils.Season.AUTUMN
elif month in [12, 1, 2]:
return utils.Season.WINTER
else:
return utils.Season.NO
@classmethod
def getFestival(cls) -> utils.Festival:
now = cls.dateTime()
lunarDate = ZhDate.from_datetime(now)
lunarMonth = lunarDate.lunar_month
lunarDay = lunarDate.lunar_day
festivalChecks = [
(1, lambda: lunarMonth == 8 and lunarDay == 15), # 中秋节
(2, lambda: (now.month, now.day) == (10, 1)), # 国庆节
(3, lambda: lunarMonth == 1 and lunarDay == 15), # 元宵节
(4, lambda: lunarMonth == 5 and lunarDay == 5), # 端午节
(5, lambda: lunarMonth == 1 and lunarDay == 1), # 春节
(6, lambda: (now.month, now.day) == (10, 31)), # 万圣节
(7, lambda: (now.month, now.day) == (12, 25)), # 圣诞节
(8, lambda: (now.month, now.day) == (1, 1)), # 新年
(9, lambda: (now.month, now.day) == (2, 14)), # 情人节
]
for code, checkFunc in festivalChecks:
if checkFunc():
return utils.Festival(code)
return utils.Festival.NO
@classmethod
def renderHtmlToFile(
cls, path: Path | str, context: dict, output: Path | str
+22
View File
@@ -0,0 +1,22 @@
from enum import Enum
class Festival(Enum):
NO = 0 # 无节日
MIDAUTUMN = 1 # 中秋节
NATIONALDAY = 2 # 国庆节
LANTERNFESTIVAL = 3 # 元宵节
DRAGONBOAT = 4 # 端午节
SPRINGFESTIVAL = 5 # 春节
HALLOWEEN = 6 # 万圣节
CHRISTMAS = 7 # 圣诞节
NEWYEAR = 8 # 新年
VALENTINE = 9 # 情人节
class Season(Enum):
NO = 0 # 无季节
SPRING = 1 # 春季
SUMMER = 2 # 夏季
AUTUMN = 3 # 秋季
WINTER = 4 # 冬季