📝 继续完善数据库

This commit is contained in:
Art_Sakura 2025-04-29 18:11:09 +08:00
parent 6b99ec427c
commit 8243a1e1c8
12 changed files with 358 additions and 153 deletions

View File

@ -7,7 +7,8 @@ from zhenxun.utils.message import MessageUtils
from .command import diuse_farm, diuse_register, reclamation from .command import diuse_farm, diuse_register, reclamation
from .config import g_pJsonManager from .config import g_pJsonManager
from .database import g_pSqlManager from .database.database import g_pSqlManager
from .dbService import g_pDBService
from .farm.farm import g_pFarmManager from .farm.farm import g_pFarmManager
from .farm.shop import g_pShopManager from .farm.shop import g_pShopManager
from .request import g_pRequestManager from .request import g_pRequestManager
@ -36,7 +37,7 @@ __plugin_meta__ = PluginMetadata(
""".strip(), """.strip(),
extra=PluginExtraData( extra=PluginExtraData(
author="Art_Sakura", author="Art_Sakura",
version="1.1", version="1.2",
commands=[Command(command="我的农场")], commands=[Command(command="我的农场")],
menu_type="群内小游戏", menu_type="群内小游戏",
configs=[ configs=[
@ -79,6 +80,8 @@ async def start():
# 初始化读取Json # 初始化读取Json
await g_pJsonManager.init() await g_pJsonManager.init()
await g_pDBService.init()
# 析构函数 # 析构函数
@driver.on_shutdown @driver.on_shutdown
async def shutdown(): async def shutdown():

View File

@ -1,6 +1,5 @@
from nonebot.adapters import Event, MessageTemplate from nonebot.adapters import Event, MessageTemplate
from nonebot.rule import to_me from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot_plugin_alconna import (Alconna, AlconnaMatch, AlconnaQuery, Args, from nonebot_plugin_alconna import (Alconna, AlconnaMatch, AlconnaQuery, Args,
Arparma, At, Match, MultiVar, Option, Arparma, At, Match, MultiVar, Option,
Query, Subcommand, on_alconna, store_true) Query, Subcommand, on_alconna, store_true)
@ -11,15 +10,15 @@ from zhenxun.services.log import logger
from zhenxun.utils.depends import UserName from zhenxun.utils.depends import UserName
from zhenxun.utils.message import MessageUtils from zhenxun.utils.message import MessageUtils
from .database import g_pSqlManager from .dbService import g_pDBService
from .farm.farm import g_pFarmManager from .farm.farm import g_pFarmManager
from .farm.shop import g_pShopManager from .farm.shop import g_pShopManager
async def isRegisteredByUid(uid: str) -> bool: async def isRegisteredByUid(uid: str) -> bool:
point = await g_pSqlManager.getUserPointByUid(uid) result = await g_pDBService.user.isUserExist(uid)
if point < 0: if not result:
await MessageUtils.build_message("尚未开通农场快at我发送 开通农场 开通吧").send() await MessageUtils.build_message("尚未开通农场快at我发送 开通农场 开通吧").send()
return False return False
@ -36,7 +35,7 @@ diuse_register = on_alconna(
@diuse_register.handle() @diuse_register.handle()
async def handle_register(session: Uninfo): async def handle_register(session: Uninfo):
uid = str(session.user.id) uid = str(session.user.id)
user = await g_pSqlManager.getUserInfoByUid(uid) user = await g_pDBService.user.getUserInfoByUid(uid)
if user: if user:
await MessageUtils.build_message("🎉 您已经开通农场啦~").send(reply_to=True) await MessageUtils.build_message("🎉 您已经开通农场啦~").send(reply_to=True)
@ -48,7 +47,7 @@ async def handle_register(session: Uninfo):
safe_name = sanitize_username(raw_name) safe_name = sanitize_username(raw_name)
# 初始化用户信息 # 初始化用户信息
success = await g_pSqlManager.initUserInfoByUid( success = await g_pDBService.user.initUserInfoByUid(
uid=uid, uid=uid,
name=safe_name, name=safe_name,
exp=0, exp=0,
@ -160,7 +159,7 @@ diuse_farm.shortcut(
@diuse_farm.assign("my-point") @diuse_farm.assign("my-point")
async def _(session: Uninfo): async def _(session: Uninfo):
uid = str(session.user.id) uid = str(session.user.id)
point = await g_pSqlManager.getUserPointByUid(uid) point = await g_pDBService.user.getUserPointByUid(uid)
if point < 0: if point < 0:
await MessageUtils.build_message("尚未开通农场快at我发送 开通农场 开通吧").send() await MessageUtils.build_message("尚未开通农场快at我发送 开通农场 开通吧").send()
@ -366,9 +365,9 @@ async def _(session: Uninfo, target: Match[At]):
await MessageUtils.build_message("请在指令后跟需要at的人").finish(reply_to=True) await MessageUtils.build_message("请在指令后跟需要at的人").finish(reply_to=True)
tar = target.result tar = target.result
point = await g_pSqlManager.getUserPointByUid(tar.target) result = await g_pDBService.user.isUserExist(tar.target)
if point < 0: if not result:
await MessageUtils.build_message("目标尚未开通农场快邀请ta开通吧").send() await MessageUtils.build_message("目标尚未开通农场快邀请ta开通吧").send()
return None return None
@ -419,7 +418,7 @@ async def _(session: Uninfo, name: Match[str]):
safeName = sanitize_username(name.result) safeName = sanitize_username(name.result)
result = await g_pSqlManager.updateUserNameByUid(uid, safeName) result = await g_pDBService.user.updateUserNameByUid(uid, safeName)
if result == True: if result == True:
await MessageUtils.build_message("更新用户名成功!").send(reply_to=True) await MessageUtils.build_message("更新用户名成功!").send(reply_to=True)

View File

@ -8,7 +8,6 @@ import aiosqlite
from zhenxun.services.log import logger from zhenxun.services.log import logger
from ..config import g_sDBFilePath, g_sDBPath from ..config import g_sDBFilePath, g_sDBPath
from ..dbService import g_pDBService
class CSqlManager: class CSqlManager:
@ -27,8 +26,6 @@ class CSqlManager:
cls.m_pDB = await aiosqlite.connect(g_sDBFilePath) cls.m_pDB = await aiosqlite.connect(g_sDBFilePath)
cls.m_pDB.row_factory = aiosqlite.Row cls.m_pDB.row_factory = aiosqlite.Row
await g_pDBService.init()
return True return True
@classmethod @classmethod

View File

@ -1,10 +1,11 @@
import math
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
from typing import List, Union from typing import List, Union
from database import CSqlManager
from zhenxun.services.log import logger from zhenxun.services.log import logger
from .database import CSqlManager
class CUserDB(CSqlManager): class CUserDB(CSqlManager):
@classmethod @classmethod
@ -65,6 +66,28 @@ class CUserDB(CSqlManager):
rows = await cursor.fetchall() rows = await cursor.fetchall()
return [row[0] for row in rows] return [row[0] for row in rows]
@classmethod
async def isUserExist(cls, uid: str) -> bool:
"""判断用户是否存在
Args:
uid (str): 用户Uid
Returns:
bool: 如果用户存在返回True否则返回False
"""
if not uid:
return False
try:
async with cls.m_pDB.execute(
"SELECT 1 FROM user WHERE uid = ?", (uid,)
) as cursor:
row = await cursor.fetchone()
return row is not None
except Exception as e:
logger.warning("isUserExist 查询失败!", e=e)
return False
@classmethod @classmethod
async def getUserInfoByUid(cls, uid: str) -> dict: async def getUserInfoByUid(cls, uid: str) -> dict:
"""获取指定用户完整信息 """获取指定用户完整信息
@ -240,22 +263,37 @@ class CUserDB(CSqlManager):
uid (str): 用户Uid uid (str): 用户Uid
Returns: Returns:
tuple[int, int, int]: (当前等级, 下级所需经验, 当前等级剩余经验)失败返回(-1, -1, -1) tuple[int, int, int]: (当前等级, 升至下级还需经验, 当前等级已获经验)失败返回(-1, -1, -1)
""" """
if not uid: if not uid:
return -1, -1, -1 return -1, -1, -1
try: try:
async with cls.m_pDB.execute( async with cls.m_pDB.execute(
"SELECT exp FROM user WHERE uid = ?", (uid,) "SELECT exp FROM user WHERE uid = ?", (uid,)
) as cursor: ) as cursor:
row = await cursor.fetchone() row = await cursor.fetchone()
if row and row[0] is not None: if not row or row[0] is None:
expVal = int(row[0]) return -1, -1, -1
level = expVal // 200
nextLevelExp = 200 * (level + 1) expVal = int(row[0])
currentLevelExp = level * 200 levelStep = 200 # 每级经验增量
remainingExp = expVal - currentLevelExp
return level, nextLevelExp, remainingExp discriminant = 1 + 8 * expVal / levelStep
level = int((-1 + math.sqrt(discriminant)) // 2)
if level < 0:
level = 0
def cumExp(k: int) -> int:
return levelStep * k * (k + 1) // 2
totalExpCurrentLevel = cumExp(level)
totalExpNextLevel = cumExp(level + 1)
currentExp = expVal - totalExpCurrentLevel
return level, totalExpNextLevel, currentExp
return -1, -1, -1 return -1, -1, -1
except Exception as e: except Exception as e:
logger.warning("getUserLevelByUid 查询失败!", e=e) logger.warning("getUserLevelByUid 查询失败!", e=e)

View File

@ -1,9 +1,9 @@
from typing import Optional from typing import Optional
from database import CSqlManager
from zhenxun.services.log import logger from zhenxun.services.log import logger
from .database import CSqlManager
class CUserItemDB(CSqlManager): class CUserItemDB(CSqlManager):
@classmethod @classmethod
@ -41,7 +41,7 @@ class CUserItemDB(CSqlManager):
row = await cursor.fetchone() row = await cursor.fetchone()
return row[0] if row else None return row[0] if row else None
except Exception as e: except Exception as e:
logger.warning(f"真寻农场getUserItemByName查询失败", e=e) logger.warning(f"getUserItemByName查询失败", e=e)
return None return None
@classmethod @classmethod
@ -64,7 +64,7 @@ class CUserItemDB(CSqlManager):
rows = await cursor.fetchall() rows = await cursor.fetchall()
return {row["item"]: row["count"] for row in rows} return {row["item"]: row["count"] for row in rows}
except Exception as e: except Exception as e:
logger.warning(f"真寻农场getUserItemByUid查询失败", e=e) logger.warning(f"getUserItemByUid查询失败", e=e)
return {} return {}
@classmethod @classmethod
@ -88,7 +88,7 @@ class CUserItemDB(CSqlManager):
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"真寻农场deleteUserItemByName失败", e=e) logger.warning(f"deleteUserItemByName失败", e=e)
return False return False
@classmethod @classmethod
@ -119,7 +119,7 @@ class CUserItemDB(CSqlManager):
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"真寻农场updateUserItemByName失败", e=e) logger.warning(f"updateUserItemByName失败", e=e)
return False return False
@classmethod @classmethod
@ -164,5 +164,5 @@ class CUserItemDB(CSqlManager):
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"真寻农场addUserItemByUid失败", e=e) logger.warning(f"addUserItemByUid失败", e=e)
return False return False

View File

@ -1,9 +1,9 @@
from typing import Dict, Optional from typing import Dict, Optional
from database import CSqlManager
from zhenxun.services.log import logger from zhenxun.services.log import logger
from .database import CSqlManager
class CUserPlantDB(CSqlManager): class CUserPlantDB(CSqlManager):
@classmethod @classmethod
@ -56,7 +56,7 @@ class CUserPlantDB(CSqlManager):
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"真寻农场addUserPlantByUid 失败!", e=e) logger.warning(f"addUserPlantByUid 失败!", e=e)
return False return False
@classmethod @classmethod
@ -95,7 +95,7 @@ class CUserPlantDB(CSqlManager):
row = await cursor.fetchone() row = await cursor.fetchone()
return row[0] if row else None return row[0] if row else None
except Exception as e: except Exception as e:
logger.warning(f"真寻农场getUserPlantByName 查询失败!", e=e) logger.warning(f"getUserPlantByName 查询失败!", e=e)
return None return None
@classmethod @classmethod
@ -121,7 +121,7 @@ class CUserPlantDB(CSqlManager):
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"真寻农场updateUserPlantByName失败", e=e) logger.warning(f"updateUserPlantByName失败", e=e)
return False return False
@classmethod @classmethod
@ -143,5 +143,5 @@ class CUserPlantDB(CSqlManager):
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"真寻农场deleteUserPlantByName 失败!", e=e) logger.warning(f"deleteUserPlantByName 失败!", e=e)
return False return False

View File

@ -1,9 +1,9 @@
from typing import Optional from typing import Optional
from database import CSqlManager
from zhenxun.services.log import logger from zhenxun.services.log import logger
from .database import CSqlManager
class CUserSeedDB(CSqlManager): class CUserSeedDB(CSqlManager):
@classmethod @classmethod
@ -22,7 +22,7 @@ class CUserSeedDB(CSqlManager):
@classmethod @classmethod
async def addUserSeedByUid(cls, uid: str, seed: str, count: int = 1) -> bool: async def addUserSeedByUid(cls, uid: str, seed: str, count: int = 1) -> bool:
"""根据用户uid添加种子信息 """根据用户uid添加种子信息(事务版本)
Args: Args:
uid (str): 用户uid uid (str): 用户uid
@ -34,7 +34,6 @@ class CUserSeedDB(CSqlManager):
""" """
try: try:
async with cls._transaction(): async with cls._transaction():
#检查是否已存在该种子
async with cls.m_pDB.execute( async with cls.m_pDB.execute(
"SELECT count FROM userSeed WHERE uid = ? AND seed = ?", "SELECT count FROM userSeed WHERE uid = ? AND seed = ?",
(uid, seed) (uid, seed)
@ -42,21 +41,18 @@ class CUserSeedDB(CSqlManager):
row = await cursor.fetchone() row = await cursor.fetchone()
if row: if row:
#如果种子已存在,则更新数量
newCount = row[0] + count newCount = row[0] + count
await cls.m_pDB.execute( await cls.m_pDB.execute(
"UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?", "UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?",
(newCount, uid, seed) (newCount, uid, seed)
) )
else: else:
#如果种子不存在,则插入新记录
newCount = count newCount = count
await cls.m_pDB.execute( await cls.m_pDB.execute(
"INSERT INTO userSeed (uid, seed, count) VALUES (?, ?, ?)", "INSERT INTO userSeed (uid, seed, count) VALUES (?, ?, ?)",
(uid, seed, count) (uid, seed, count)
) )
#如果种子数量为 0删除记录
if newCount <= 0: if newCount <= 0:
await cls.m_pDB.execute( await cls.m_pDB.execute(
"DELETE FROM userSeed WHERE uid = ? AND seed = ?", "DELETE FROM userSeed WHERE uid = ? AND seed = ?",
@ -64,9 +60,33 @@ class CUserSeedDB(CSqlManager):
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"真寻农场addUserSeedByUid 失败!", e=e) logger.warning(f"addUserSeedByUid 失败!", e=e)
return False return False
@classmethod
async def _addUserSeedByUid(cls, uid: str, seed: str, count: int = 1) -> bool:
"""根据用户uid添加种子信息非事务版复用其他非事务接口"""
try:
existing = await cls.getUserSeedByName(uid, seed)
newCount = (existing or 0) + count
if existing is not None:
await cls._updateUserSeedByName(uid, seed, newCount)
else:
await cls.m_pDB.execute(
"INSERT INTO userSeed (uid, seed, count) VALUES (?, ?, ?)",
(uid, seed, newCount)
)
if newCount <= 0:
await cls._deleteUserSeedByName(uid, seed)
return True
except Exception as e:
logger.warning(f"_addUserSeedByUid 失败!", e=e)
return False
@classmethod @classmethod
async def getUserSeedByName(cls, uid: str, seed: str) -> Optional[int]: async def getUserSeedByName(cls, uid: str, seed: str) -> Optional[int]:
"""根据种子名称获取种子数量 """根据种子名称获取种子数量
@ -87,7 +107,7 @@ class CUserSeedDB(CSqlManager):
row = await cursor.fetchone() row = await cursor.fetchone()
return row[0] if row else None return row[0] if row else None
except Exception as e: except Exception as e:
logger.warning(f"真寻农场getUserSeedByName 查询失败!", e=e) logger.warning(f"getUserSeedByName 查询失败!", e=e)
return None return None
@classmethod @classmethod
@ -131,7 +151,33 @@ class CUserSeedDB(CSqlManager):
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"真寻农场updateUserSeedByName失败", e=e) logger.warning(f"updateUserSeedByName失败", e=e)
return False
@classmethod
async def _updateUserSeedByName(cls, uid: str, seed: str, count: int) -> bool:
"""根据种子名称更新种子数量
Args:
uid (str): 用户uid
seed (str): 种子名称
count (int): 种子数量
Returns:
bool: 是否成功
"""
try:
if count <= 0:
return await cls.deleteUserSeedByName(uid, seed)
async with cls._transaction():
await cls.m_pDB.execute(
"UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?",
(count, uid, seed)
)
return True
except Exception as e:
logger.warning(f"updateUserSeedByName失败", e=e)
return False return False
@classmethod @classmethod
@ -153,5 +199,26 @@ class CUserSeedDB(CSqlManager):
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"真寻农场deleteUserSeedByName 删除失败!", e=e) logger.warning(f"deleteUserSeedByName 删除失败!", e=e)
return False
@classmethod
async def _deleteUserSeedByName(cls, uid: str, seed: str) -> bool:
"""根据种子名称从种子仓库中删除种子
Args:
uid (str): 用户uid
seed (str): 种子名称
Returns:
bool: 是否成功
"""
try:
await cls.m_pDB.execute(
"DELETE FROM userSeed WHERE uid = ? AND seed = ?",
(uid, seed)
)
return True
except Exception as e:
logger.warning(f"deleteUserSeedByName 删除失败!", e=e)
return False return False

View File

@ -1,28 +1,27 @@
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
from typing import Optional from typing import Optional
from database import CSqlManager
from zhenxun.services.log import logger from zhenxun.services.log import logger
from ..config import g_pJsonManager from ..config import g_pJsonManager
from ..dbService import g_pDBService from ..dbService import g_pDBService
from .database import CSqlManager
class CUserSoilDB(CSqlManager): class CUserSoilDB(CSqlManager):
@classmethod @classmethod
async def initDB(cls): async def initDB(cls):
#用户Uid # 用户Uid
#地块索引从1开始 # 地块索引从1开始
#作物名称 # 作物名称
#播种时间 # 播种时间
#成熟时间 # 成熟时间
#土地等级 0=普通地1=红土地2=黑土地3=金土地 # 土地等级 0=普通地1=红土地2=黑土地3=金土地
#枯萎状态 0=未枯萎1=枯萎 # 枯萎状态 0=未枯萎1=枯萎
#施肥状态 0=未施肥1=施肥 # 施肥状态 0=未施肥1=施肥
#虫害状态 0=无虫害1=有虫害 # 虫害状态 0=无虫害1=有虫害
#杂草状态 0=无杂草1=有杂草 # 杂草状态 0=无杂草1=有杂草
#缺水状态 0=不缺水1=缺水 # 缺水状态 0=不缺水1=缺水
userSoil = { userSoil = {
"uid": "TEXT NOT NULL", "uid": "TEXT NOT NULL",
"soilIndex": "INTEGER NOT NULL", "soilIndex": "INTEGER NOT NULL",
@ -35,7 +34,7 @@ class CUserSoilDB(CSqlManager):
"bugStatus": "INTEGER DEFAULT 0", "bugStatus": "INTEGER DEFAULT 0",
"weedStatus": "INTEGER DEFAULT 0", "weedStatus": "INTEGER DEFAULT 0",
"waterStatus": "INTEGER DEFAULT 0", "waterStatus": "INTEGER DEFAULT 0",
"PRIMARY KEY": "(uid, soilIndex)" "PRIMARY KEY": "(uid, soilIndex)",
} }
await cls.ensureTableSchema("userSoil", userSoil) await cls.ensureTableSchema("userSoil", userSoil)
@ -52,61 +51,50 @@ class CUserSoilDB(CSqlManager):
""" """
cursor = await cls.m_pDB.execute("SELECT * FROM soil WHERE uid = ?", (uid,)) cursor = await cls.m_pDB.execute("SELECT * FROM soil WHERE uid = ?", (uid,))
row = await cursor.fetchone() row = await cursor.fetchone()
if not row: if not row:
return {} return {}
columns = [description[0] for description in cursor.description] columns = [description[0] for description in cursor.description]
return dict(zip(columns, row)) return dict(zip(columns, row))
@classmethod @classmethod
async def migrateOldFarmData(cls): async def migrateOldFarmData(cls) -> bool:
"""迁移旧土地数据到新表 userSoil 并删除旧表 """迁移旧土地数据到新表 userSoil 并删除旧表
Raises:
aiosqlite.Error: 数据库操作失败时抛出
Returns: Returns:
None bool: 如果旧表不存在则返回 False否则迁移并删除后返回 True
""" """
# 检查旧表是否存在
cursor = await cls.m_pDB.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name='soil'"
)
if not await cursor.fetchone():
return False
async with cls._transaction(): async with cls._transaction():
users = await g_pDBService.user.getAllUsers() users = await g_pDBService.user.getAllUsers()
for uid in users: for uid in users:
farmInfo = await cls.getUserFarmByUid(uid) farmInfo = await cls.getUserFarmByUid(uid)
for i in range(1, 31): for i in range(1, 31):
soilName = f"soil{i}" key = f"soil{i}"
if soilName not in farmInfo: data = farmInfo.get(key)
if not data:
continue continue
soilData = farmInfo[soilName] parts = data.split(",")
if not soilData: if len(parts) < 3:
continue continue
name = parts[0]
pt = int(parts[1])
mt = int(parts[2])
fields = soilData.split(',') await cls.m_pDB.execute(
if len(fields) < 6: "INSERT INTO userSoil (uid,soilIndex,plantName,plantTime,matureTime) VALUES (?,?,?,?,?)",
continue (uid, i, name, pt, mt),
)
plantName = fields[0]
plantTime = int(fields[1])
matureTime = int(fields[2])
soilLevel = int(fields[5])
# 构建新的土地数据
soilInfo = {
"uid": uid,
"soilIndex": i,
"plantName": plantName,
"plantTime": plantTime,
"matureTime": matureTime,
"soilLevel": soilLevel,
"wiltStatus":0,
"fertilizerStatus": 0,
"bugStatus": 0,
"weedStatus": 0,
"waterStatus": 0
}
await cls.insertUserSoil(soilInfo)
# 彻底清除旧表
await cls.m_pDB.execute("DROP TABLE soil") await cls.m_pDB.execute("DROP TABLE soil")
return True
@classmethod @classmethod
async def insertUserSoil(cls, soilInfo: dict): async def insertUserSoil(cls, soilInfo: dict):
@ -131,10 +119,36 @@ class CUserSoilDB(CSqlManager):
soilInfo.get("fertilizerStatus", 0), soilInfo.get("fertilizerStatus", 0),
soilInfo.get("bugStatus", 0), soilInfo.get("bugStatus", 0),
soilInfo.get("weedStatus", 0), soilInfo.get("weedStatus", 0),
soilInfo.get("waterStatus", 0) soilInfo.get("waterStatus", 0),
) ),
) )
@classmethod
async def _insertUserSoil(cls, soilInfo: dict):
"""插入一条新的 userSoil 记录
Args:
soilInfo (dict): 新土地数据
Returns:
None
"""
await cls.m_pDB.execute(
"INSERT INTO userSoil (uid, soilIndex, plantName, plantTime, matureTime, soilLevel, fertilizerStatus, bugStatus, weedStatus, waterStatus) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(
soilInfo["uid"],
soilInfo["soilIndex"],
soilInfo.get("plantName", ""),
soilInfo.get("plantTime", 0),
soilInfo.get("matureTime", 0),
soilInfo.get("soilLevel", 0),
soilInfo.get("fertilizerStatus", 0),
soilInfo.get("bugStatus", 0),
soilInfo.get("weedStatus", 0),
soilInfo.get("waterStatus", 0),
),
)
@classmethod @classmethod
async def getUserSoil(cls, uid: str, soilIndex: int) -> Optional[dict]: async def getUserSoil(cls, uid: str, soilIndex: int) -> Optional[dict]:
"""获取指定用户某块土地的详细信息 """获取指定用户某块土地的详细信息
@ -149,7 +163,7 @@ class CUserSoilDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
cursor = await cls.m_pDB.execute( cursor = await cls.m_pDB.execute(
"SELECT * FROM userSoil WHERE uid = ? AND soilIndex = ?", "SELECT * FROM userSoil WHERE uid = ? AND soilIndex = ?",
(uid, soilIndex) (uid, soilIndex),
) )
row = await cursor.fetchone() row = await cursor.fetchone()
if not row: if not row:
@ -157,6 +171,27 @@ class CUserSoilDB(CSqlManager):
columns = [description[0] for description in cursor.description] columns = [description[0] for description in cursor.description]
return dict(zip(columns, row)) return dict(zip(columns, row))
@classmethod
async def _getUserSoil(cls, uid: str, soilIndex: int) -> Optional[dict]:
"""获取指定用户某块土地的详细信息
Args:
uid (str): 用户ID
soilIndex (int): 土地索引
Returns:
Optional[dict]: 记录存在返回字段-值字典否则返回 None
"""
cursor = await cls.m_pDB.execute(
"SELECT * FROM userSoil WHERE uid = ? AND soilIndex = ?",
(uid, soilIndex),
)
row = await cursor.fetchone()
if not row:
return None
columns = [description[0] for description in cursor.description]
return dict(zip(columns, row))
@classmethod @classmethod
async def updateUserSoil(cls, uid: str, soilIndex: int, field: str, value): async def updateUserSoil(cls, uid: str, soilIndex: int, field: str, value):
"""更新指定用户土地的单个字段 """更新指定用户土地的单个字段
@ -173,9 +208,27 @@ class CUserSoilDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
f"UPDATE userSoil SET {field} = ? WHERE uid = ? AND soilIndex = ?", f"UPDATE userSoil SET {field} = ? WHERE uid = ? AND soilIndex = ?",
(value, uid, soilIndex) (value, uid, soilIndex),
) )
@classmethod
async def _updateUserSoil(cls, uid: str, soilIndex: int, field: str, value):
"""更新指定用户土地的单个字段
Args:
uid (str): 用户ID
soilIndex (int): 土地索引
field (str): 需更新的字段名
value: 新值
Returns:
None
"""
await cls.m_pDB.execute(
f"UPDATE userSoil SET {field} = ? WHERE uid = ? AND soilIndex = ?",
(value, uid, soilIndex),
)
@classmethod @classmethod
async def deleteUserSoil(cls, uid: str, soilIndex: int): async def deleteUserSoil(cls, uid: str, soilIndex: int):
"""删除指定用户的土地记录 """删除指定用户的土地记录
@ -189,10 +242,24 @@ class CUserSoilDB(CSqlManager):
""" """
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
"DELETE FROM userSoil WHERE uid = ? AND soilIndex = ?", "DELETE FROM userSoil WHERE uid = ? AND soilIndex = ?", (uid, soilIndex)
(uid, soilIndex)
) )
@classmethod
async def _deleteUserSoil(cls, uid: str, soilIndex: int):
"""删除指定用户的土地记录
Args:
uid (str): 用户ID
soilIndex (int): 土地索引
Returns:
None
"""
await cls.m_pDB.execute(
"DELETE FROM userSoil WHERE uid = ? AND soilIndex = ?", (uid, soilIndex)
)
@classmethod @classmethod
async def isSoilPlanted(cls, uid: str, soilIndex: int) -> bool: async def isSoilPlanted(cls, uid: str, soilIndex: int) -> bool:
"""判断指定用户的指定土地是否已种植 """判断指定用户的指定土地是否已种植
@ -240,22 +307,24 @@ class CUserSoilDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
# 复用原有记录字段,保留土壤状态等信息 # 复用原有记录字段,保留土壤状态等信息
prev = soilRecord or {} prev = soilRecord or {}
await cls.deleteUserSoil(uid, soilIndex) await cls._deleteUserSoil(uid, soilIndex)
await cls.insertUserSoil({ await cls._insertUserSoil(
"uid": uid, {
"soilIndex": soilIndex, "uid": uid,
"plantName": plantName, "soilIndex": soilIndex,
"plantTime": nowTs, "plantName": plantName,
"matureTime": matureTs, "plantTime": nowTs,
# 保留之前的土壤等级和状态字段,避免数据丢失 "matureTime": matureTs,
"soilLevel": prev.get("soilLevel", 0), # 保留之前的土壤等级和状态字段,避免数据丢失
"wiltStatus": prev.get("wiltStatus", 0), "soilLevel": prev.get("soilLevel", 0),
"fertilizerStatus": prev.get("fertilizerStatus", 0), "wiltStatus": prev.get("wiltStatus", 0),
"bugStatus": prev.get("bugStatus", 0), "fertilizerStatus": prev.get("fertilizerStatus", 0),
"weedStatus": prev.get("weedStatus", 0), "bugStatus": prev.get("bugStatus", 0),
"waterStatus": prev.get("waterStatus", 0) "weedStatus": prev.get("weedStatus", 0),
}) "waterStatus": prev.get("waterStatus", 0),
}
)
return True return True
except Exception as e: except Exception as e:
logger.error(f"真寻农场播种失败!", e=e) logger.error(f"播种失败!", e=e)
return False return False

View File

@ -1,7 +1,7 @@
from database import CSqlManager
from zhenxun.services.log import logger from zhenxun.services.log import logger
from .database import CSqlManager
class CUserStealDB(CSqlManager): class CUserStealDB(CSqlManager):
@classmethod @classmethod

View File

@ -1,29 +1,35 @@
from typing import Optional from typing import Optional
from .database.user import CUserDB
from .database.userItem import CUserItemDB
from .database.userPlant import CUserPlantDB
from .database.userSeed import CUserSeedDB
from .database.userSoil import CUserSoilDB
from .database.userSteal import CUserStealDB
class CDBService: class CDBService:
def __init__(self):
user: Optional["CUserDB"] = None
userSoil: Optional["CUserSoilDB"] = None
userPlant: Optional["CUserPlantDB"] = None
userSeed: Optional["CUserSeedDB"] = None
userItem: Optional["CUserItemDB"] = None
userSteal: Optional["CUserStealDB"] = None
@classmethod @classmethod
async def init(cls): async def init(cls):
from .database.user import CUserDB
from .database.userItem import CUserItemDB
from .database.userPlant import CUserPlantDB
from .database.userSeed import CUserSeedDB
from .database.userSoil import CUserSoilDB
from .database.userSteal import CUserStealDB
cls.user = CUserDB() cls.user = CUserDB()
await cls.user.initDB()
cls.userSoil = CUserSoilDB() cls.userSoil = CUserSoilDB()
await cls.userSoil.initDB()
cls.userPlant = CUserPlantDB() cls.userPlant = CUserPlantDB()
await cls.userPlant.initDB()
cls.userSeed = CUserSeedDB() cls.userSeed = CUserSeedDB()
await cls.userSeed.initDB()
cls.userItem = CUserItemDB() cls.userItem = CUserItemDB()
await cls.userItem.initDB()
cls.userSteal = CUserStealDB() cls.userSteal = CUserStealDB()
await cls.userSteal.initDB()
#迁移旧数据库
await cls.userSoil.migrateOldFarmData()
g_pDBService = CDBService() g_pDBService = CDBService()

View File

@ -389,7 +389,7 @@ class CFarmManager:
number = plantInfo['harvest'] number = plantInfo['harvest']
#处理偷菜扣除数量 #处理偷菜扣除数量
stealNum = g_pDBService.userSteal.getTotalStolenCount(uid, i) stealNum = await g_pDBService.userSteal.getTotalStolenCount(uid, i)
number -= stealNum number -= stealNum

View File

@ -37,47 +37,73 @@ class CRequestManager:
return False return False
@classmethod @classmethod
async def post(cls, endpoint: str, name: str = "", jsonData: dict = None, formData: dict = None) -> dict: async def post(cls, endpoint: str, name: str = "", jsonData: dict = None) -> dict:
"""发送POST请求到指定接口供其他方法统一调用 """发送POST请求到指定接口统一调用仅支持JSON格式数据
Args: Args:
endpoint (str): 请求的接口路径 endpoint (str): 请求的接口路径
name (str, optional): 操作名称用于日志记录 name (str, optional): 操作名称用于日志记录
jsonData (dict, optional): 以JSON格式发送的数据 jsonData (dict): 以JSON格式发送的数据
formData (dict, optional): 以表单格式发送的数据
Raises: Raises:
ValueError: 当jsonData和formData都未提供时抛出 ValueError: 当jsonData未提供时抛出
Returns: Returns:
dict: 返回请求结果的JSON数据 dict: 返回请求结果的JSON数据
""" """
if jsonData is None and formData is None: if jsonData is None:
raise ValueError("post请求必须提供jsonData或formData其中之一") raise ValueError("post请求必须提供jsonData")
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址") baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
url = f"{baseUrl.rstrip('/')}/{endpoint.lstrip('/')}" url = f"{baseUrl.rstrip('/')}/{endpoint.lstrip('/')}"
headers = {"token": "xZ%?z5LtWV7H:0-Xnwp+bNRNQ-jbfrxG"}
try: try:
async with httpx.AsyncClient(timeout=5.0) as client: async with httpx.AsyncClient(timeout=5.0) as client:
if jsonData is not None: response = await client.post(url, json=jsonData, headers=headers)
response = await client.post(url, json=jsonData)
else:
response = await client.post(url, data=formData)
if response.status_code == 200: if response.status_code == 200:
return response.json() return response.json()
else: else:
logger.warning(f"真寻农场{name}请求失败: HTTP {response.status_code} {response.text}") logger.warning(f"{name}请求失败: HTTP {response.status_code} {response.text}")
return {} return {}
except httpx.RequestError as e: except httpx.RequestError as e:
logger.warning(f"真寻农场{name}请求异常: {e}") logger.warning(f"{name}请求异常", e=e)
return {} return {}
except Exception as e: except Exception as e:
logger.warning(f"真寻农场{name}处理异常: {e}") logger.warning(f"{name}处理异常", e=e)
return {} return {}
@classmethod
async def get(cls, endpoint: str, name: str = "") -> dict:
"""发送GET请求到指定接口统一调用仅支持无体的查询
Args:
endpoint (str): 请求的接口路径
name (str, optional): 操作名称用于日志记录
Returns:
dict: 返回请求结果的JSON数据
"""
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
url = f"{baseUrl.rstrip('/')}/{endpoint.lstrip('/')}"
headers = {"token": "xZ%?z5LtWV7H:0-Xnwp+bNRNQ-jbfrxG"}
try:
async with httpx.AsyncClient(timeout=5.0) as client:
response = await client.get(url, headers=headers)
if response.status_code == 200:
return response.json()
else:
logger.warning(f"{name}请求失败: HTTP {response.status_code} {response.text}")
return {}
except httpx.RequestError as e:
logger.warning(f"{name}请求异常", e=e)
return {}
except Exception as e:
logger.warning(f"{name}处理异常", e=e)
return {}
@classmethod @classmethod
async def sign(cls, uid: str) -> str: async def sign(cls, uid: str) -> str: