📝 继续对数据库进行优化操作

This commit is contained in:
Art_Sakura 2025-04-28 19:27:16 +08:00
parent b5334dfc01
commit 6b99ec427c
12 changed files with 1813 additions and 1122 deletions

View File

@ -1,909 +0,0 @@
import math
import os
import re
from contextlib import asynccontextmanager
from datetime import date, datetime, timedelta
from io import StringIO
from math import e
from typing import Any, Dict, List, Optional
import aiosqlite
from zhenxun.services.log import logger
from .config import g_pJsonManager, g_sDBFilePath, g_sDBPath
class CSqlManager:
def __init__(self):
g_sDBPath.mkdir(parents=True, exist_ok=True)
@classmethod
async def cleanup(cls):
if cls.m_pDB:
await cls.m_pDB.close()
@classmethod
async def init(cls) -> bool:
bIsExist = os.path.exists(g_sDBFilePath)
cls.m_pDB = await aiosqlite.connect(g_sDBFilePath)
cls.m_pDB.row_factory = aiosqlite.Row
await cls.checkDB()
return True
@classmethod
@asynccontextmanager
async def _transaction(cls):
await cls.m_pDB.execute("BEGIN;")
try:
yield
except:
await cls.m_pDB.execute("ROLLBACK;")
raise
else:
await cls.m_pDB.execute("COMMIT;")
@classmethod
async def getTableInfo(cls, tableName: str) -> list:
if not re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', tableName):
raise ValueError(f"Illegal table name: {tableName}")
try:
cursor = await cls.m_pDB.execute(f'PRAGMA table_info("{tableName}")')
rows = await cursor.fetchall()
return [{"name": row[1], "type": row[2]} for row in rows]
except aiosqlite.Error:
return []
@classmethod
async def ensureTableSchema(cls, tableName: str, columns: dict) -> bool:
"""由AI生成
创建表或为已存在表添加缺失字段
返回 True 表示有变更创建或新增列False 则无操作
Args:
tableName (_type_): 表名
columns (_type_): 字典
Returns:
_type_: _description_
"""
info = await cls.getTableInfo(tableName)
existing = {col['name']: col['type'].upper() for col in info}
desired = {k: v.upper() for k, v in columns.items() if k != "PRIMARY KEY"}
primaryKey = columns.get("PRIMARY KEY", "")
if not existing:
colsDef = ", ".join(f'"{k}" {v}' for k, v in desired.items())
if primaryKey:
colsDef += f", PRIMARY KEY {primaryKey}"
await cls.m_pDB.execute(f'CREATE TABLE "{tableName}" ({colsDef});')
return True
toAdd = [k for k in desired if k not in existing]
toRemove = [k for k in existing if k not in desired]
typeMismatch = [k for k in desired if k in existing and existing[k] != desired[k]]
if toAdd and not toRemove and not typeMismatch:
for col in toAdd:
await cls.m_pDB.execute(
f'ALTER TABLE "{tableName}" ADD COLUMN "{col}" {columns[col]}'
)
return True
async with cls._transaction():
tmpTable = f"{tableName}_new"
colsDef = ", ".join(f'"{k}" {v}' for k, v in desired.items())
if primaryKey:
colsDef += f", PRIMARY KEY {primaryKey}"
await cls.m_pDB.execute(f'CREATE TABLE "{tmpTable}" ({colsDef});')
commonCols = [k for k in desired if k in existing]
if commonCols:
colsStr = ", ".join(f'"{c}"' for c in commonCols)
await cls.m_pDB.execute(
f'INSERT INTO "{tmpTable}" ({colsStr}) SELECT {colsStr} FROM "{tableName}";'
)
await cls.m_pDB.execute(f'DROP TABLE "{tableName}";')
await cls.m_pDB.execute(f'ALTER TABLE "{tmpTable}" RENAME TO "{tableName}";')
return True
@classmethod
async def checkDB(cls) -> bool:
#1. 用户表
userInfo = {
"uid": "INTEGER PRIMARY KEY AUTOINCREMENT",
"name": "TEXT NOT NULL",
"exp": "INTEGER DEFAULT 0",
"point": "INTEGER DEFAULT 0",
"soil": "INTEGER DEFAULT 3",
"stealing": "TEXT DEFAULT NULL"
}
#2. 土地表
userSoilInfo = {
"uid": "INTEGER PRIMARY KEY AUTOINCREMENT",
**{f"soil{i}": "TEXT DEFAULT ''" for i in range(1, 31)}
}
#3. 用户作物明细表
userPlant = {
"uid": "INTEGER NOT NULL",
"plant": "TEXT NOT NULL",
"count": "INTEGER NOT NULL DEFAULT 0",
#建联合主键保证每个品种一行
"PRIMARY KEY": "(uid, plant)"
}
#4. 用户种子明细表
userSeed = {
"uid": "INTEGER NOT NULL",
"seed": "TEXT NOT NULL",
"count": "INTEGER NOT NULL DEFAULT 0",
"PRIMARY KEY": "(uid, seed)"
}
# 5. 用户道具明细表
userItem = {
"uid": "INTEGER NOT NULL",
"item": "TEXT NOT NULL",
"count": "INTEGER NOT NULL DEFAULT 0",
"PRIMARY KEY": "(uid, item)"
}
#建表(或增列)
await cls.ensureTableSchema("user", userInfo)
await cls.ensureTableSchema("soil", userSoilInfo)
await cls.ensureTableSchema("userPlant", userPlant)
await cls.ensureTableSchema("userSeed", userSeed)
await cls.ensureTableSchema("userItem", userItem)
return True
@classmethod
async def executeDB(cls, command: str) -> bool:
"""执行自定义SQL
Args:
command (str): SQL语句
Returns:
bool: 是否执行成功
"""
if len(command) <= 0:
logger.warning("数据库语句长度为空!")
return False
try:
async with cls._transaction():
await cls.m_pDB.execute(command)
return True
except Exception as e:
logger.warning("数据库语句执行出错:" + command)
return False
@classmethod
async def initUserInfoByUid(cls, uid: str, name: str = "", exp: int = 0, point: int = 500):
"""初始化用户信息
Args:
uid (str): 用户Uid
name (str): 农场名称
exp (int): 农场经验
point (int): 农场币
"""
#用户信息
userInfo = f"""
INSERT INTO user (uid, name, exp, point, soil, stealing) VALUES ({uid}, '{name}', {exp}, {point}, 3, '{date.today()}|5')
"""
#用户土地
userSoilInfo = f"""
INSERT INTO soil (uid) VALUES ({uid});
"""
if not await cls.executeDB(userInfo):
return False
if not await cls.executeDB(userSoilInfo):
return False
return "开通农场成功"
@classmethod
async def getUserInfoByUid(cls, uid: str) -> dict:
"""根据用户Uid获取用户信息
Args:
uid (str): 用户Uid
Returns:
list[dict]: 用户信息
"""
if len(uid) <= 0:
return {}
try:
async with cls.m_pDB.execute(
"SELECT * FROM user WHERE uid = ?", (uid,)
) as cursor:
async for row in cursor:
userDict = {
"uid": row[0],
"name": row[1],
"exp": row[2],
"point": row[3],
"soil": row[4],
"stealing": row[5]
}
return userDict
return {}
except Exception as e:
logger.warning(f"getUserInfoByUid查询失败: {e}")
return {}
@classmethod
async def getUserNameByUid(cls, uid: str) -> str:
"""根据用户uid查询用户名
Args:
uid (str): 用户uid
Returns:
str: 用户名如果失败返回空字符串
"""
if not uid:
return ""
try:
async with cls.m_pDB.execute(
"SELECT name FROM user WHERE uid = ?",
(uid,)
) as cursor:
row = await cursor.fetchone()
return row["name"] if row else ""
except Exception as e:
logger.warning(f"真寻农场getUserNameByUid查询失败: {e}")
return ""
@classmethod
async def updateUserNameByUid(cls, uid: str, name: str) -> bool:
"""根据用户uid修改用户名
Args:
uid (str): 用户uid
name (str): 新用户名
Returns:
bool: 是否更新成功
"""
if not uid or not name:
return False
try:
async with cls._transaction():
await cls.m_pDB.execute(
"UPDATE user SET name = ? WHERE uid = ?",
(name, uid)
)
return True
except Exception as e:
logger.warning(f"真寻农场updateUserNameByUid失败: {e}")
return False
@classmethod
async def getUserPointByUid(cls, uid: str) -> int:
"""根据用户Uid获取用户农场币
Args:
uid (str): 用户Uid
Returns:
int: 用户农场币
"""
if len(uid) <= 0:
return -1
try:
async with cls.m_pDB.execute(f"SELECT point FROM user WHERE uid = {uid}") as cursor:
async for row in cursor:
return int(row[0])
return -1
except Exception as e:
logger.warning(f"getUserPointByUid查询失败: {e}")
return -1
@classmethod
async def updateUserPointByUid(cls, uid: str, point: int) -> int:
"""根据用户Uid修改用户农场币
Args:
uid (str): 用户Uid
point (int): 要更新的新农场币数量 0
Returns:
int: 更新后的农场币数量成功时-1失败时
"""
if len(uid) <= 0:
logger.warning("参数校验失败: uid为空或农场币值无效")
return -1
try:
return await cls.executeDB(f"UPDATE user SET point = {point} WHERE uid = {uid}")
except Exception as e:
logger.error(f"金币更新失败: {e}")
return -1
@classmethod
async def getUserExpByUid(cls, uid: str) -> int:
"""根据用户Uid获取用户经验
Args:
uid (str): 用户Uid
Returns:
int: 用户经验值
"""
if len(uid) <= 0:
return -1
try:
async with cls.m_pDB.execute(f"SELECT exp FROM user WHERE uid = {uid}") as cursor:
async for row in cursor:
return int(row[0])
return -1
except Exception as e:
logger.warning(f"getUserLevelByUid查询失败: {e}")
return -1
@classmethod
async def UpdateUserExpByUid(cls, uid: str, exp: int) -> bool:
"""根据用户Uid刷新用户经验
Args:
uid (str): 用户Uid
Returns:
bool: 是否成功
"""
if len(uid) <= 0:
return False
sql = f"UPDATE user SET exp = '{exp}' WHERE uid = {uid}"
return await cls.executeDB(sql)
@classmethod
async def getUserLevelByUid(cls, uid: str) -> tuple[int, int, int]:
"""根据用户Uid获取用户等级
Args:
uid (str): 用户Uid
Returns:
tuple[int, int, int]: (当前等级, 下级所需经验, 当前等级剩余经验)
"""
if len(uid) <= 0:
return -1, -1, -1
try:
async with cls.m_pDB.execute(f"SELECT exp FROM user WHERE uid = {uid}") as cursor:
async for row in cursor:
exp = int(row[0])
level = exp // 200
nextLevelExp = 200 * (level + 1)
currentLevelExp = level * 200
remainingExp = exp - currentLevelExp
return level, nextLevelExp, remainingExp
return -1, -1, -1
except Exception as e:
logger.warning(f"getUserLevelByUid查询失败: {e}")
return -1, -1, -1
@classmethod
async def getUserSoilByUid(cls, uid: str) -> int:
"""根据用户Uid获取解锁地块
Args:
uid (str): 用户Uid
Returns:
int: 解锁几块地
"""
if len(uid) <= 0:
return 0
async with cls.m_pDB.execute(f"SELECT soil FROM user WHERE uid = {uid}") as cursor:
async for row in cursor:
if not row[0]:
return 0
else:
return int(row[0])
return 0
@classmethod
async def getUserSoilStatusBySoilID(cls, uid: str, soil: str) -> tuple[bool, str]:
"""根据土地块获取用户土地状态
Args:
uid (str): 用户Uid
soil (str): 土地id
Returns:
tuple[bool, str]: [是否可以播种土地信息]
"""
if len(uid) <= 0:
return False, ""
async with cls.m_pDB.execute(f"SELECT {soil} FROM soil WHERE uid = {uid}") as cursor:
async for row in cursor:
if row[0] == None or len(row[0]) <= 0:
return True, ""
else:
return False, row[0]
return False, ""
@classmethod
async def updateUserSoilStatusByPlantName(cls, uid: str, soil: str,
plant: str = "",
status: int = 0) -> bool:
"""根据种子名称使用户播种
Args:
uid (str): 用户Uid
soil (str): 土地id
plant (str): 种子名称
Returns:
bool: 是否更新成功
"""
if len(uid) <= 0:
return False
if len(plant) <= 0 and status == 4:
s = f",,,{status},"
elif len(plant) <= 0 and status != 4:
s = ""
else:
#获取种子信息 这里能崩我吃
plantInfo = g_pJsonManager.m_pPlant['plant'][plant]
currentTime = datetime.now()
newTime = currentTime + timedelta(hours=int(plantInfo['time']))
#0: 种子名称
#1: 种下时间
#2: 预计成熟时间
#3: 地状态0无 1长草 2生虫 3缺水 4枯萎
#4: 是否被偷 示例QQ号-偷取数量|QQ号-偷取数量
#5: 土地等级 0普通 1红土地 2黑土地 3金土地 4紫晶土地 5蓝晶土地 6黑晶土地
s = f"{plant},{int(currentTime.timestamp())},{int(newTime.timestamp())},{status},,"
sql = f"UPDATE soil SET {soil} = '{s}' WHERE uid = {uid}"
return await cls.executeDB(sql)
@classmethod
async def addUserSeedByUid(cls, uid: str, seed: str, count: int = 1) -> bool:
"""根据用户uid添加种子信息
Args:
uid (str): 用户uid
seed (str): 种子名称
count (int): 数量
Returns:
bool: 是否添加成功
"""
try:
async with cls._transaction():
#检查是否已存在该种子
async with cls.m_pDB.execute(
"SELECT count FROM userSeed WHERE uid = ? AND seed = ?",
(uid, seed)
) as cursor:
row = await cursor.fetchone()
if row:
#如果种子已存在,则更新数量
newCount = row[0] + count
await cls.m_pDB.execute(
"UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?",
(newCount, uid, seed)
)
else:
#如果种子不存在,则插入新记录
newCount = count
await cls.m_pDB.execute(
"INSERT INTO userSeed (uid, seed, count) VALUES (?, ?, ?)",
(uid, seed, count)
)
#如果种子数量为 0删除记录
if newCount <= 0:
await cls.m_pDB.execute(
"DELETE FROM userSeed WHERE uid = ? AND seed = ?",
(uid, seed)
)
return True
except Exception as e:
logger.warning(f"真寻农场addUserSeedByUid 失败: {e}")
return False
@classmethod
async def getUserSeedByName(cls, uid: str, seed: str) -> Optional[int]:
"""根据种子名称获取种子数量
Args:
uid (str): 用户uid
seed (str): 种子名称
Returns:
Optional[int]: 种子数量
"""
try:
async with cls.m_pDB.execute(
"SELECT count FROM userSeed WHERE uid = ? AND seed = ?",
(uid, seed)
) as cursor:
row = await cursor.fetchone()
return row[0] if row else None
except Exception as e:
logger.warning(f"真寻农场getUserSeedByName 查询失败: {e}")
return None
@classmethod
async def getUserSeedByUid(cls, uid: str) -> dict:
"""根据用户Uid获取仓库全部种子信息
Args:
uid (str): 用户uid
Returns:
dict: 种子信息
"""
cursor = await cls.m_pDB.execute(
"SELECT seed, count FROM userSeed WHERE uid=?",
(uid,)
)
rows = await cursor.fetchall()
return {row["seed"]: row["count"] for row in rows}
@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}")
return False
@classmethod
async def deleteUserSeedByName(cls, uid: str, seed: str) -> bool:
"""根据种子名称从种子仓库中删除种子
Args:
uid (str): 用户uid
seed (str): 种子名称
Returns:
bool: 是否成功
"""
try:
async with cls._transaction():
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}")
return False
@classmethod
async def addUserPlantByUid(cls, uid: str, plant: str, count: int = 1) -> bool:
"""根据用户uid添加作物信息
Args:
uid (str): 用户uid
plant (str): 作物名称
count (int): 数量
Returns:
bool: 是否添加成功
"""
try:
async with cls._transaction():
#检查是否已存在该作物
async with cls.m_pDB.execute(
"SELECT count FROM userPlant WHERE uid = ? AND plant = ?",
(uid, plant)
) as cursor:
row = await cursor.fetchone()
if row:
#如果作物已存在,则更新数量
new_count = row[0] + count
await cls.m_pDB.execute(
"UPDATE userPlant SET count = ? WHERE uid = ? AND plant = ?",
(new_count, uid, plant)
)
else:
#如果作物不存在,则插入新记录
await cls.m_pDB.execute(
"INSERT INTO userPlant (uid, plant, count) VALUES (?, ?, ?)",
(uid, plant, count)
)
return True
except Exception as e:
logger.warning(f"真寻农场addUserPlantByUid 失败: {e}")
return False
@classmethod
async def getUserPlantByUid(cls, uid: str) -> Dict[str, int]:
"""根据用户uid获取全部作物信息
Args:
uid (str): 用户uid
Returns:
Dict[str, int]: 作物名称和数量
"""
cursor = await cls.m_pDB.execute(
"SELECT plant, count FROM userPlant WHERE uid=?",
(uid,)
)
rows = await cursor.fetchall()
return {row["plant"]: row["count"] for row in rows}
@classmethod
async def getUserPlantByName(cls, uid: str, plant: str) -> Optional[int]:
"""根据作物名称获取用户的作物数量
Args:
uid (str): 用户uid
plant (str): 作物名称
Returns:
Optional[int]: 作物数量
"""
try:
async with cls.m_pDB.execute(
"SELECT count FROM userPlant WHERE uid = ? AND plant = ?",
(uid, plant)
) as cursor:
row = await cursor.fetchone()
return row[0] if row else None
except Exception as e:
logger.warning(f"真寻农场getUserPlantByName 查询失败: {e}")
return None
@classmethod
async def updateUserPlantByName(cls, uid: str, plant: str, count: int) -> bool:
"""更新 userPlant 表中某个作物的数量
Args:
uid (str): 用户uid
plant (str): 作物名称
count (int): 新的作物数量
Returns:
bool: 是否更新成功
"""
try:
if count <= 0:
return await cls.deleteUserPlantByName(uid, plant)
async with cls._transaction():
await cls.m_pDB.execute(
"UPDATE userPlant SET count = ? WHERE uid = ? AND plant = ?",
(count, uid, plant)
)
return True
except Exception as e:
logger.warning(f"真寻农场updateUserPlantByName失败:{e}")
return False
@classmethod
async def deleteUserPlantByName(cls, uid: str, plant: str) -> bool:
"""从 userPlant 表中删除某个作物记录
Args:
uid (str): 用户uid
plant (str): 作物名称
Returns:
bool: 是否删除成功
"""
try:
async with cls._transaction():
await cls.m_pDB.execute(
"DELETE FROM userPlant WHERE uid = ? AND plant = ?",
(uid, plant)
)
return True
except Exception as e:
logger.warning(f"真寻农场deleteUserPlantByName 失败: {e}")
return False
@classmethod
async def getUserItemByName(cls, uid: str, item: str) -> Optional[int]:
"""根据道具名称查询某一项数量
Args:
uid (str): 用户uid
item (str): 道具名称
Returns:
Optional[int]: 数量不存在返回None
"""
if not uid or not item:
return None
try:
async with cls.m_pDB.execute(
"SELECT count FROM userItem WHERE uid = ? AND item = ?",
(uid, item)
) as cursor:
row = await cursor.fetchone()
return row[0] if row else None
except Exception as e:
logger.warning(f"真寻农场getUserItemByName查询失败: {e}")
return None
@classmethod
async def getUserItemByUid(cls, uid: str) -> dict:
"""根据用户Uid获取全部道具信息
Args:
uid (str): 用户uid
Returns:
dict: {itemName: count, ...}
"""
if not uid:
return {}
try:
cursor = await cls.m_pDB.execute(
"SELECT item, count FROM userItem WHERE uid = ?",
(uid,)
)
rows = await cursor.fetchall()
return {row["item"]: row["count"] for row in rows}
except Exception as e:
logger.warning(f"真寻农场getUserItemByUid查询失败: {e}")
return {}
@classmethod
async def deleteUserItemByName(cls, uid: str, item: str) -> bool:
"""根据道具名删除道具
Args:
uid (str): 用户uid
item (str): 道具名称
Returns:
bool: 是否删除成功
"""
if not uid or not item:
return False
try:
async with cls._transaction():
await cls.m_pDB.execute(
"DELETE FROM userItem WHERE uid = ? AND item = ?",
(uid, item)
)
return True
except Exception as e:
logger.warning(f"真寻农场deleteUserItemByName失败: {e}")
return False
@classmethod
async def updateUserItemByName(cls, uid: str, item: str, count: int) -> bool:
"""根据道具名直接更新道具数量
Args:
uid (str): 用户uid
item (str): 道具名称
count (int): 要更新的新数量
Returns:
bool: 是否更新成功
"""
if not uid or not item:
return False
try:
async with cls._transaction():
if count <= 0:
await cls.m_pDB.execute(
"DELETE FROM userItem WHERE uid = ? AND item = ?",
(uid, item)
)
else:
await cls.m_pDB.execute(
"UPDATE userItem SET count = ? WHERE uid = ? AND item = ?",
(count, uid, item)
)
return True
except Exception as e:
logger.warning(f"真寻农场updateUserItemByName失败: {e}")
return False
@classmethod
async def addUserItemByUid(cls, uid: str, item: str, count: int = 1) -> bool:
"""根据用户uid添加道具信息
Args:
uid (str): 用户uid
item (str): 道具名称
count (int, optional): 数量.Defaults to 1.
Returns:
bool: 是否添加成功
"""
if not uid or not item:
return False
try:
async with cls._transaction():
async with cls.m_pDB.execute(
"SELECT count FROM userItem WHERE uid = ? AND item = ?",
(uid, item)
) as cursor:
row = await cursor.fetchone()
if row:
newCount = row[0] + count
if newCount <= 0:
await cls.m_pDB.execute(
"DELETE FROM userItem WHERE uid = ? AND item = ?",
(uid, item)
)
else:
await cls.m_pDB.execute(
"UPDATE userItem SET count = ? WHERE uid = ? AND item = ?",
(newCount, uid, item)
)
else:
if count > 0:
await cls.m_pDB.execute(
"INSERT INTO userItem (uid, item, count) VALUES (?, ?, ?)",
(uid, item, count)
)
return True
except Exception as e:
logger.warning(f"真寻农场addUserItemByUid失败: {e}")
return False
g_pSqlManager = CSqlManager()

133
database/database.py Normal file
View File

@ -0,0 +1,133 @@
import math
import os
import re
from contextlib import asynccontextmanager
import aiosqlite
from zhenxun.services.log import logger
from ..config import g_sDBFilePath, g_sDBPath
from ..dbService import g_pDBService
class CSqlManager:
def __init__(self):
g_sDBPath.mkdir(parents=True, exist_ok=True)
@classmethod
async def cleanup(cls):
if cls.m_pDB:
await cls.m_pDB.close()
@classmethod
async def init(cls) -> bool:
bIsExist = os.path.exists(g_sDBFilePath)
cls.m_pDB = await aiosqlite.connect(g_sDBFilePath)
cls.m_pDB.row_factory = aiosqlite.Row
await g_pDBService.init()
return True
@classmethod
@asynccontextmanager
async def _transaction(cls):
await cls.m_pDB.execute("BEGIN;")
try:
yield
except:
await cls.m_pDB.execute("ROLLBACK;")
raise
else:
await cls.m_pDB.execute("COMMIT;")
@classmethod
async def getTableInfo(cls, tableName: str) -> list:
if not re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', tableName):
raise ValueError(f"Illegal table name: {tableName}")
try:
cursor = await cls.m_pDB.execute(f'PRAGMA table_info("{tableName}")')
rows = await cursor.fetchall()
return [{"name": row[1], "type": row[2]} for row in rows]
except aiosqlite.Error:
return []
@classmethod
async def ensureTableSchema(cls, tableName: str, columns: dict) -> bool:
"""由AI生成
创建表或为已存在表添加缺失字段
返回 True 表示有变更创建或新增列False 则无操作
Args:
tableName (_type_): 表名
columns (_type_): 字典
Returns:
_type_: _description_
"""
info = await cls.getTableInfo(tableName)
existing = {col['name']: col['type'].upper() for col in info}
desired = {k: v.upper() for k, v in columns.items() if k != "PRIMARY KEY"}
primaryKey = columns.get("PRIMARY KEY", "")
if not existing:
colsDef = ", ".join(f'"{k}" {v}' for k, v in desired.items())
if primaryKey:
colsDef += f", PRIMARY KEY {primaryKey}"
await cls.m_pDB.execute(f'CREATE TABLE "{tableName}" ({colsDef});')
return True
toAdd = [k for k in desired if k not in existing]
toRemove = [k for k in existing if k not in desired]
typeMismatch = [k for k in desired if k in existing and existing[k] != desired[k]]
if toAdd and not toRemove and not typeMismatch:
for col in toAdd:
await cls.m_pDB.execute(
f'ALTER TABLE "{tableName}" ADD COLUMN "{col}" {columns[col]}'
)
return True
async with cls._transaction():
tmpTable = f"{tableName}_new"
colsDef = ", ".join(f'"{k}" {v}' for k, v in desired.items())
if primaryKey:
colsDef += f", PRIMARY KEY {primaryKey}"
await cls.m_pDB.execute(f'CREATE TABLE "{tmpTable}" ({colsDef});')
commonCols = [k for k in desired if k in existing]
if commonCols:
colsStr = ", ".join(f'"{c}"' for c in commonCols)
await cls.m_pDB.execute(
f'INSERT INTO "{tmpTable}" ({colsStr}) SELECT {colsStr} FROM "{tableName}";'
)
await cls.m_pDB.execute(f'DROP TABLE "{tableName}";')
await cls.m_pDB.execute(f'ALTER TABLE "{tmpTable}" RENAME TO "{tableName}";')
return True
@classmethod
async def executeDB(cls, command: str) -> bool:
"""执行自定义SQL
Args:
command (str): SQL语句
Returns:
bool: 是否执行成功
"""
if len(command) <= 0:
logger.warning("数据库语句长度为空!")
return False
try:
async with cls._transaction():
await cls.m_pDB.execute(command)
return True
except Exception as e:
logger.warning("数据库语句执行出错:" + command, e=e)
return False
g_pSqlManager = CSqlManager()

400
database/user.py Normal file
View File

@ -0,0 +1,400 @@
from datetime import date, datetime, timedelta
from typing import List, Union
from database import CSqlManager
from zhenxun.services.log import logger
class CUserDB(CSqlManager):
@classmethod
async def initDB(cls):
"""初始化用户表结构确保user表存在且字段完整"""
# 用户Uid
# 农场名称
# 经验值
# 金币
# 解锁土地数量
# 偷菜时间字符串
# 剩余偷菜次数
userInfo = {
"uid": "INTEGER PRIMARY KEY AUTOINCREMENT",
"name": "TEXT NOT NULL",
"exp": "INTEGER DEFAULT 0",
"point": "INTEGER DEFAULT 0",
"soil": "INTEGER DEFAULT 3",
"stealTime": "TEXT DEFAULT NULL",
"stealCount": "INTEGER DEFAULT 0"
}
await cls.ensureTableSchema("user", userInfo)
@classmethod
async def initUserInfoByUid(cls, uid: str, name: str = "", exp: int = 0, point: int = 500) -> Union[bool, str]:
"""初始化用户信息,包含初始偷菜时间字符串与次数
Args:
uid (str): 用户Uid
name (str): 农场名称
exp (int): 农场经验
point (int): 农场币
Returns:
Union[bool, str]: False 表示失败字符串表示成功信息
"""
nowStr = date.today().strftime('%Y-%m-%d')
sql = (
f"INSERT INTO user (uid, name, exp, point, soil, stealTime, stealCount) "
f"VALUES ({uid}, '{name}', {exp}, {point}, 3, '{nowStr}', 5)"
)
try:
async with cls._transaction():
await cls.m_pDB.execute(sql)
return "开通农场成功"
except Exception as e:
logger.warning("initUserInfoByUid 事务执行失败!", e=e)
return False
@classmethod
async def getAllUsers(cls) -> List[str]:
"""获取所有用户UID列表
Returns:
List[str]: 用户UID列表
"""
cursor = await cls.m_pDB.execute("SELECT uid FROM user")
rows = await cursor.fetchall()
return [row[0] for row in rows]
@classmethod
async def getUserInfoByUid(cls, uid: str) -> dict:
"""获取指定用户完整信息
Args:
uid (str): 用户Uid
Returns:
dict: 包含所有用户字段的字典
"""
if not uid:
return {}
try:
async with cls.m_pDB.execute(
"SELECT * FROM user WHERE uid = ?", (uid,)
) as cursor:
async for row in cursor:
return {
"uid": row[0],
"name": row[1],
"exp": row[2],
"point": row[3],
"soil": row[4],
"stealTime": row[5] or "",
"stealCount": int(row[6])
}
return {}
except Exception as e:
logger.warning("getUserInfoByUid 查询失败!", e=e)
return {}
@classmethod
async def getUserNameByUid(cls, uid: str) -> str:
"""根据用户Uid获取用户名
Args:
uid (str): 用户Uid
Returns:
str: 用户名失败返回空字符串
"""
if not uid:
return ""
try:
async with cls.m_pDB.execute(
"SELECT name FROM user WHERE uid = ?", (uid,)
) as cursor:
row = await cursor.fetchone()
return row["name"] if row else ""
except Exception as e:
logger.warning("getUserNameByUid 查询失败!", e=e)
return ""
@classmethod
async def updateUserNameByUid(cls, uid: str, name: str) -> bool:
"""根据用户Uid更新用户名
Args:
uid (str): 用户Uid
name (str): 新用户名
Returns:
bool: 是否更新成功
"""
if not uid or not name:
return False
try:
async with cls._transaction():
await cls.m_pDB.execute(
"UPDATE user SET name = ? WHERE uid = ?", (name, uid)
)
return True
except Exception as e:
logger.warning("updateUserNameByUid 事务执行失败!", e=e)
return False
@classmethod
async def getUserPointByUid(cls, uid: str) -> int:
"""获取指定用户农场币
Args:
uid (str): 用户Uid
Returns:
int: 农场币数量失败返回 -1
"""
if not uid:
return -1
try:
async with cls.m_pDB.execute(
"SELECT point FROM user WHERE uid = ?", (uid,)
) as cursor:
row = await cursor.fetchone()
return int(row[0]) if row and row[0] is not None else -1
except Exception as e:
logger.warning("getUserPointByUid 查询失败!", e=e)
return -1
@classmethod
async def updateUserPointByUid(cls, uid: str, point: int) -> bool:
"""根据用户Uid更新农场币数量
Args:
uid (str): 用户Uid
point (int): 新农场币数量
Returns:
bool: 是否更新成功
"""
if not uid or point < 0:
logger.warning("updateUserPointByUid 参数校验失败!")
return False
try:
async with cls._transaction():
await cls.m_pDB.execute(
"UPDATE user SET point = ? WHERE uid = ?", (point, uid)
)
return True
except Exception as e:
logger.error("updateUserPointByUid 事务执行失败!", e=e)
return False
@classmethod
async def getUserExpByUid(cls, uid: str) -> int:
"""获取指定用户经验值
Args:
uid (str): 用户Uid
Returns:
int: 经验值失败返回 -1
"""
if not uid:
return -1
try:
async with cls.m_pDB.execute(
"SELECT exp FROM user WHERE uid = ?", (uid,)
) as cursor:
row = await cursor.fetchone()
return int(row[0]) if row and row[0] is not None else -1
except Exception as e:
logger.warning("getUserExpByUid 查询失败!", e=e)
return -1
@classmethod
async def updateUserExpByUid(cls, uid: str, exp: int) -> bool:
"""根据用户Uid更新经验值
Args:
uid (str): 用户Uid
exp (int): 新经验值
Returns:
bool: 是否更新成功
"""
if not uid:
return False
try:
async with cls._transaction():
await cls.m_pDB.execute(
"UPDATE user SET exp = ? WHERE uid = ?", (exp, uid)
)
return True
except Exception as e:
logger.warning("updateUserExpByUid 事务执行失败!", e=e)
return False
@classmethod
async def getUserLevelByUid(cls, uid: str) -> tuple[int, int, int]:
"""获取用户等级信息
Args:
uid (str): 用户Uid
Returns:
tuple[int, int, int]: (当前等级, 下级所需经验, 当前等级剩余经验)失败返回(-1, -1, -1)
"""
if not uid:
return -1, -1, -1
try:
async with cls.m_pDB.execute(
"SELECT exp FROM user WHERE uid = ?", (uid,)
) as cursor:
row = await cursor.fetchone()
if row and row[0] is not None:
expVal = int(row[0])
level = expVal // 200
nextLevelExp = 200 * (level + 1)
currentLevelExp = level * 200
remainingExp = expVal - currentLevelExp
return level, nextLevelExp, remainingExp
return -1, -1, -1
except Exception as e:
logger.warning("getUserLevelByUid 查询失败!", e=e)
return -1, -1, -1
@classmethod
async def getUserSoilByUid(cls, uid: str) -> int:
"""获取解锁土地数量
Args:
uid (str): 用户Uid
Returns:
int: 解锁土地块数失败返回0
"""
if not uid:
return 0
try:
async with cls.m_pDB.execute(
"SELECT soil FROM user WHERE uid = ?", (uid,)
) as cursor:
row = await cursor.fetchone()
return int(row[0]) if row and row[0] is not None else 0
except Exception as e:
logger.warning("getUserSoilByUid 查询失败!", e=e)
return 0
@classmethod
async def updateUserSoilByUid(cls, uid: str, soil: int) -> bool:
"""更新指定用户解锁土地数量
Args:
uid (str): 用户Uid
soil (int): 新土地数量
Returns:
bool: 更新成功返回True否则False
"""
if not uid or soil < 0:
return False
try:
async with cls._transaction():
await cls.m_pDB.execute(
"UPDATE user SET soil = ? WHERE uid = ?", (soil, uid)
)
return True
except Exception as e:
logger.warning("updateUserSoilByUid 事务执行失败!", e=e)
return False
@classmethod
async def getStealTimeByUid(cls, uid: str) -> str:
"""根据用户Uid获取偷菜时间字符串
Args:
uid (str): 用户Uid
Returns:
str: 偷菜时间字符串失败返回空字符串
"""
if not uid:
return ""
try:
async with cls.m_pDB.execute(
"SELECT stealTime FROM user WHERE uid = ?", (uid,
)
) as cursor:
row = await cursor.fetchone()
return row[0] if row and row[0] else ""
except Exception as e:
logger.warning("getStealTimeByUid 查询失败!", e=e)
return ""
@classmethod
async def updateStealTimeByUid(cls, uid: str, stealTime: str) -> bool:
"""根据用户Uid更新偷菜时间字符串
Args:
uid (str): 用户Uid
stealTime (str): 新偷菜时间字符串
Returns:
bool: 是否更新成功
"""
if not uid or not stealTime:
logger.warning("updateStealTimeByUid 参数校验失败!")
return False
try:
async with cls._transaction():
await cls.m_pDB.execute(
"UPDATE user SET stealTime = ? WHERE uid = ?", (stealTime, uid)
)
return True
except Exception as e:
logger.warning("updateStealTimeByUid 事务执行失败!", e=e)
return False
@classmethod
async def getStealCountByUid(cls, uid: str) -> int:
"""根据用户Uid获取剩余偷菜次数
Args:
uid (str): 用户Uid
Returns:
int: 剩余偷菜次数失败返回 -1
"""
if not uid:
return -1
try:
async with cls.m_pDB.execute(
"SELECT stealCount FROM user WHERE uid = ?", (uid,)
) as cursor:
row = await cursor.fetchone()
return int(row[0]) if row and row[0] is not None else 0
except Exception as e:
logger.warning("getStealCountByUid 查询失败!", e=e)
return -1
@classmethod
async def updateStealCountByUid(cls, uid: str, stealCount: int) -> bool:
"""根据用户Uid更新剩余偷菜次数
Args:
uid (str): 用户Uid
stealCount (int): 新剩余偷菜次数
Returns:
bool: 是否更新成功
"""
if not uid or stealCount < 0:
logger.warning("updateStealCountByUid 参数校验失败!")
return False
try:
async with cls._transaction():
await cls.m_pDB.execute(
"UPDATE user SET stealCount = ? WHERE uid = ?", (stealCount, uid)
)
return True
except Exception as e:
logger.warning("updateStealCountByUid 事务执行失败!", e=e)
return False

168
database/userItem.py Normal file
View File

@ -0,0 +1,168 @@
from typing import Optional
from database import CSqlManager
from zhenxun.services.log import logger
class CUserItemDB(CSqlManager):
@classmethod
async def initDB(cls):
#用户Uid
#物品名称
#数量
userItem = {
"uid": "INTEGER NOT NULL",
"item": "TEXT NOT NULL",
"count": "INTEGER NOT NULL DEFAULT 0",
"PRIMARY KEY": "(uid, item)"
}
await cls.ensureTableSchema("userItem", userItem)
@classmethod
async def getUserItemByName(cls, uid: str, item: str) -> Optional[int]:
"""根据道具名称查询某一项数量
Args:
uid (str): 用户uid
item (str): 道具名称
Returns:
Optional[int]: 数量不存在返回None
"""
if not uid or not item:
return None
try:
async with cls.m_pDB.execute(
"SELECT count FROM userItem WHERE uid = ? AND item = ?",
(uid, item)
) as cursor:
row = await cursor.fetchone()
return row[0] if row else None
except Exception as e:
logger.warning(f"真寻农场getUserItemByName查询失败", e=e)
return None
@classmethod
async def getUserItemByUid(cls, uid: str) -> dict:
"""根据用户Uid获取全部道具信息
Args:
uid (str): 用户uid
Returns:
dict: {itemName: count, ...}
"""
if not uid:
return {}
try:
cursor = await cls.m_pDB.execute(
"SELECT item, count FROM userItem WHERE uid = ?",
(uid,)
)
rows = await cursor.fetchall()
return {row["item"]: row["count"] for row in rows}
except Exception as e:
logger.warning(f"真寻农场getUserItemByUid查询失败", e=e)
return {}
@classmethod
async def deleteUserItemByName(cls, uid: str, item: str) -> bool:
"""根据道具名删除道具
Args:
uid (str): 用户uid
item (str): 道具名称
Returns:
bool: 是否删除成功
"""
if not uid or not item:
return False
try:
async with cls._transaction():
await cls.m_pDB.execute(
"DELETE FROM userItem WHERE uid = ? AND item = ?",
(uid, item)
)
return True
except Exception as e:
logger.warning(f"真寻农场deleteUserItemByName失败", e=e)
return False
@classmethod
async def updateUserItemByName(cls, uid: str, item: str, count: int) -> bool:
"""根据道具名直接更新道具数量
Args:
uid (str): 用户uid
item (str): 道具名称
count (int): 要更新的新数量
Returns:
bool: 是否更新成功
"""
if not uid or not item:
return False
try:
async with cls._transaction():
if count <= 0:
await cls.m_pDB.execute(
"DELETE FROM userItem WHERE uid = ? AND item = ?",
(uid, item)
)
else:
await cls.m_pDB.execute(
"UPDATE userItem SET count = ? WHERE uid = ? AND item = ?",
(count, uid, item)
)
return True
except Exception as e:
logger.warning(f"真寻农场updateUserItemByName失败", e=e)
return False
@classmethod
async def addUserItemByUid(cls, uid: str, item: str, count: int = 1) -> bool:
"""根据用户uid添加道具信息
Args:
uid (str): 用户uid
item (str): 道具名称
count (int, optional): 数量.Defaults to 1.
Returns:
bool: 是否添加成功
"""
if not uid or not item:
return False
try:
async with cls._transaction():
async with cls.m_pDB.execute(
"SELECT count FROM userItem WHERE uid = ? AND item = ?",
(uid, item)
) as cursor:
row = await cursor.fetchone()
if row:
newCount = row[0] + count
if newCount <= 0:
await cls.m_pDB.execute(
"DELETE FROM userItem WHERE uid = ? AND item = ?",
(uid, item)
)
else:
await cls.m_pDB.execute(
"UPDATE userItem SET count = ? WHERE uid = ? AND item = ?",
(newCount, uid, item)
)
else:
if count > 0:
await cls.m_pDB.execute(
"INSERT INTO userItem (uid, item, count) VALUES (?, ?, ?)",
(uid, item, count)
)
return True
except Exception as e:
logger.warning(f"真寻农场addUserItemByUid失败", e=e)
return False

147
database/userPlant.py Normal file
View File

@ -0,0 +1,147 @@
from typing import Dict, Optional
from database import CSqlManager
from zhenxun.services.log import logger
class CUserPlantDB(CSqlManager):
@classmethod
async def initDB(cls):
#用户UiD
#作物名称
#数量
userPlant = {
"uid": "INTEGER NOT NULL",
"plant": "TEXT NOT NULL",
"count": "INTEGER NOT NULL DEFAULT 0",
"PRIMARY KEY": "(uid, plant)"
}
await cls.ensureTableSchema("userPlant", userPlant)
@classmethod
async def addUserPlantByUid(cls, uid: str, plant: str, count: int = 1) -> bool:
"""根据用户uid添加作物信息
Args:
uid (str): 用户uid
plant (str): 作物名称
count (int): 数量
Returns:
bool: 是否添加成功
"""
try:
async with cls._transaction():
#检查是否已存在该作物
async with cls.m_pDB.execute(
"SELECT count FROM userPlant WHERE uid = ? AND plant = ?",
(uid, plant)
) as cursor:
row = await cursor.fetchone()
if row:
#如果作物已存在,则更新数量
new_count = row[0] + count
await cls.m_pDB.execute(
"UPDATE userPlant SET count = ? WHERE uid = ? AND plant = ?",
(new_count, uid, plant)
)
else:
#如果作物不存在,则插入新记录
await cls.m_pDB.execute(
"INSERT INTO userPlant (uid, plant, count) VALUES (?, ?, ?)",
(uid, plant, count)
)
return True
except Exception as e:
logger.warning(f"真寻农场addUserPlantByUid 失败!", e=e)
return False
@classmethod
async def getUserPlantByUid(cls, uid: str) -> Dict[str, int]:
"""根据用户uid获取全部作物信息
Args:
uid (str): 用户uid
Returns:
Dict[str, int]: 作物名称和数量
"""
cursor = await cls.m_pDB.execute(
"SELECT plant, count FROM userPlant WHERE uid=?",
(uid,)
)
rows = await cursor.fetchall()
return {row["plant"]: row["count"] for row in rows}
@classmethod
async def getUserPlantByName(cls, uid: str, plant: str) -> Optional[int]:
"""根据作物名称获取用户的作物数量
Args:
uid (str): 用户uid
plant (str): 作物名称
Returns:
Optional[int]: 作物数量
"""
try:
async with cls.m_pDB.execute(
"SELECT count FROM userPlant WHERE uid = ? AND plant = ?",
(uid, plant)
) as cursor:
row = await cursor.fetchone()
return row[0] if row else None
except Exception as e:
logger.warning(f"真寻农场getUserPlantByName 查询失败!", e=e)
return None
@classmethod
async def updateUserPlantByName(cls, uid: str, plant: str, count: int) -> bool:
"""更新 userPlant 表中某个作物的数量
Args:
uid (str): 用户uid
plant (str): 作物名称
count (int): 新的作物数量
Returns:
bool: 是否更新成功
"""
try:
if count <= 0:
return await cls.deleteUserPlantByName(uid, plant)
async with cls._transaction():
await cls.m_pDB.execute(
"UPDATE userPlant SET count = ? WHERE uid = ? AND plant = ?",
(count, uid, plant)
)
return True
except Exception as e:
logger.warning(f"真寻农场updateUserPlantByName失败", e=e)
return False
@classmethod
async def deleteUserPlantByName(cls, uid: str, plant: str) -> bool:
"""从 userPlant 表中删除某个作物记录
Args:
uid (str): 用户uid
plant (str): 作物名称
Returns:
bool: 是否删除成功
"""
try:
async with cls._transaction():
await cls.m_pDB.execute(
"DELETE FROM userPlant WHERE uid = ? AND plant = ?",
(uid, plant)
)
return True
except Exception as e:
logger.warning(f"真寻农场deleteUserPlantByName 失败!", e=e)
return False

157
database/userSeed.py Normal file
View File

@ -0,0 +1,157 @@
from typing import Optional
from database import CSqlManager
from zhenxun.services.log import logger
class CUserSeedDB(CSqlManager):
@classmethod
async def initDB(cls):
#用户Uid
#种子名称
#数量
userSeed = {
"uid": "INTEGER NOT NULL",
"seed": "TEXT NOT NULL",
"count": "INTEGER NOT NULL DEFAULT 0",
"PRIMARY KEY": "(uid, seed)"
}
await cls.ensureTableSchema("userSeed", userSeed)
@classmethod
async def addUserSeedByUid(cls, uid: str, seed: str, count: int = 1) -> bool:
"""根据用户uid添加种子信息
Args:
uid (str): 用户uid
seed (str): 种子名称
count (int): 数量
Returns:
bool: 是否添加成功
"""
try:
async with cls._transaction():
#检查是否已存在该种子
async with cls.m_pDB.execute(
"SELECT count FROM userSeed WHERE uid = ? AND seed = ?",
(uid, seed)
) as cursor:
row = await cursor.fetchone()
if row:
#如果种子已存在,则更新数量
newCount = row[0] + count
await cls.m_pDB.execute(
"UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?",
(newCount, uid, seed)
)
else:
#如果种子不存在,则插入新记录
newCount = count
await cls.m_pDB.execute(
"INSERT INTO userSeed (uid, seed, count) VALUES (?, ?, ?)",
(uid, seed, count)
)
#如果种子数量为 0删除记录
if newCount <= 0:
await cls.m_pDB.execute(
"DELETE FROM userSeed WHERE uid = ? AND seed = ?",
(uid, seed)
)
return True
except Exception as e:
logger.warning(f"真寻农场addUserSeedByUid 失败!", e=e)
return False
@classmethod
async def getUserSeedByName(cls, uid: str, seed: str) -> Optional[int]:
"""根据种子名称获取种子数量
Args:
uid (str): 用户uid
seed (str): 种子名称
Returns:
Optional[int]: 种子数量
"""
try:
async with cls.m_pDB.execute(
"SELECT count FROM userSeed WHERE uid = ? AND seed = ?",
(uid, seed)
) as cursor:
row = await cursor.fetchone()
return row[0] if row else None
except Exception as e:
logger.warning(f"真寻农场getUserSeedByName 查询失败!", e=e)
return None
@classmethod
async def getUserSeedByUid(cls, uid: str) -> dict:
"""根据用户Uid获取仓库全部种子信息
Args:
uid (str): 用户uid
Returns:
dict: 种子信息
"""
cursor = await cls.m_pDB.execute(
"SELECT seed, count FROM userSeed WHERE uid=?",
(uid,)
)
rows = await cursor.fetchall()
return {row["seed"]: row["count"] for row in rows}
@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
@classmethod
async def deleteUserSeedByName(cls, uid: str, seed: str) -> bool:
"""根据种子名称从种子仓库中删除种子
Args:
uid (str): 用户uid
seed (str): 种子名称
Returns:
bool: 是否成功
"""
try:
async with cls._transaction():
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

261
database/userSoil.py Normal file
View File

@ -0,0 +1,261 @@
from datetime import date, datetime, timedelta
from typing import Optional
from database import CSqlManager
from zhenxun.services.log import logger
from ..config import g_pJsonManager
from ..dbService import g_pDBService
class CUserSoilDB(CSqlManager):
@classmethod
async def initDB(cls):
#用户Uid
#地块索引从1开始
#作物名称
#播种时间
#成熟时间
#土地等级 0=普通地1=红土地2=黑土地3=金土地
#枯萎状态 0=未枯萎1=枯萎
#施肥状态 0=未施肥1=施肥
#虫害状态 0=无虫害1=有虫害
#杂草状态 0=无杂草1=有杂草
#缺水状态 0=不缺水1=缺水
userSoil = {
"uid": "TEXT NOT NULL",
"soilIndex": "INTEGER NOT NULL",
"plantName": "TEXT DEFAULT ''",
"plantTime": "INTEGER DEFAULT 0",
"matureTime": "INTEGER DEFAULT 0",
"soilLevel": "INTEGER DEFAULT 0",
"wiltStatus": "INTEGER DEFAULT 0",
"fertilizerStatus": "INTEGER DEFAULT 0",
"bugStatus": "INTEGER DEFAULT 0",
"weedStatus": "INTEGER DEFAULT 0",
"waterStatus": "INTEGER DEFAULT 0",
"PRIMARY KEY": "(uid, soilIndex)"
}
await cls.ensureTableSchema("userSoil", userSoil)
@classmethod
async def getUserFarmByUid(cls, uid: str) -> dict:
"""获取指定用户的旧农场数据
Args:
uid (str): 用户ID
Returns:
dict: 包含字段名-值的字典; 若无数据则返回空字典
"""
cursor = await cls.m_pDB.execute("SELECT * FROM soil WHERE uid = ?", (uid,))
row = await cursor.fetchone()
if not row:
return {}
columns = [description[0] for description in cursor.description]
return dict(zip(columns, row))
@classmethod
async def migrateOldFarmData(cls):
"""迁移旧土地数据到新表 userSoil 并删除旧表
Raises:
aiosqlite.Error: 数据库操作失败时抛出
Returns:
None
"""
async with cls._transaction():
users = await g_pDBService.user.getAllUsers()
for uid in users:
farmInfo = await cls.getUserFarmByUid(uid)
for i in range(1, 31):
soilName = f"soil{i}"
if soilName not in farmInfo:
continue
soilData = farmInfo[soilName]
if not soilData:
continue
fields = soilData.split(',')
if len(fields) < 6:
continue
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")
@classmethod
async def insertUserSoil(cls, soilInfo: dict):
"""插入一条新的 userSoil 记录
Args:
soilInfo (dict): 新土地数据
Returns:
None
"""
async with cls._transaction():
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
async def getUserSoil(cls, uid: str, soilIndex: int) -> Optional[dict]:
"""获取指定用户某块土地的详细信息
Args:
uid (str): 用户ID
soilIndex (int): 土地索引
Returns:
Optional[dict]: 记录存在返回字段-值字典否则返回 None
"""
async with cls._transaction():
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
async def updateUserSoil(cls, uid: str, soilIndex: int, field: str, value):
"""更新指定用户土地的单个字段
Args:
uid (str): 用户ID
soilIndex (int): 土地索引
field (str): 需更新的字段名
value: 新值
Returns:
None
"""
async with cls._transaction():
await cls.m_pDB.execute(
f"UPDATE userSoil SET {field} = ? WHERE uid = ? AND soilIndex = ?",
(value, uid, soilIndex)
)
@classmethod
async def deleteUserSoil(cls, uid: str, soilIndex: int):
"""删除指定用户的土地记录
Args:
uid (str): 用户ID
soilIndex (int): 土地索引
Returns:
None
"""
async with cls._transaction():
await cls.m_pDB.execute(
"DELETE FROM userSoil WHERE uid = ? AND soilIndex = ?",
(uid, soilIndex)
)
@classmethod
async def isSoilPlanted(cls, uid: str, soilIndex: int) -> bool:
"""判断指定用户的指定土地是否已种植
Args:
uid (str): 用户ID
soilIndex (int): 土地索引
Returns:
bool: 如果 plantName 不为空且 plantTime 大于 0则视为已种植返回 True否则 False
"""
soilInfo = await cls.getUserSoil(uid, soilIndex)
if not soilInfo:
return False
return bool(soilInfo.get("plantName")) and soilInfo.get("plantTime", 0) > 0
@classmethod
async def sowingByPlantName(cls, uid: str, soilIndex: int, plantName: str) -> bool:
"""播种指定作物到用户土地区
Args:
uid (str): 用户ID
soilIndex (int): 土地区索引
plantName (str): 植物名
Returns:
bool: 播种成功返回 True否则返回 False
"""
# 校验土地区是否已种植
soilRecord = await cls.getUserSoil(uid, soilIndex)
if soilRecord and soilRecord.get("plantName"):
return False
# 获取植物配置
plantCfg = g_pJsonManager.m_pPlant.get("plant", {}).get(plantName)
if not plantCfg:
logger.error(f"未知植物: {plantName}")
return False
nowTs = int(datetime.now().timestamp())
matureTs = nowTs + int(plantCfg.get("time", 0)) * 3600
try:
async with cls._transaction():
# 复用原有记录字段,保留土壤状态等信息
prev = soilRecord or {}
await cls.deleteUserSoil(uid, soilIndex)
await cls.insertUserSoil({
"uid": uid,
"soilIndex": soilIndex,
"plantName": plantName,
"plantTime": nowTs,
"matureTime": matureTs,
# 保留之前的土壤等级和状态字段,避免数据丢失
"soilLevel": prev.get("soilLevel", 0),
"wiltStatus": prev.get("wiltStatus", 0),
"fertilizerStatus": prev.get("fertilizerStatus", 0),
"bugStatus": prev.get("bugStatus", 0),
"weedStatus": prev.get("weedStatus", 0),
"waterStatus": prev.get("waterStatus", 0)
})
return True
except Exception as e:
logger.error(f"真寻农场播种失败!", e=e)
return False

228
database/userSteal.py Normal file
View File

@ -0,0 +1,228 @@
from database import CSqlManager
from zhenxun.services.log import logger
class CUserStealDB(CSqlManager):
@classmethod
async def initDB(cls):
# 被偷用户Uid
# 被偷的地块索引 从1开始
# 偷菜用户Uid
# 被偷数量
# 被偷时间
userSteal = {
"uid": "TEXT NOT NULL",
"soilIndex": "INTEGER NOT NULL",
"stealerUid": "TEXT NOT NULL",
"stealCount": "INTEGER NOT NULL",
"stealTime": "INTEGER NOT NULL",
"PRIMARY KEY": "(uid, soilIndex, stealerUid)"
}
await cls.ensureTableSchema("userSteal", userSteal)
@classmethod
async def addStealRecord(cls, uid: str, soilIndex: int, stealerUid: str, stealCount: int, stealTime: int) -> bool:
"""添加偷菜记录
Args:
uid (str): 被偷用户Uid
soilIndex (int): 被偷地块索引
stealerUid (str): 偷菜用户Uid
stealCount (int): 被偷数量
stealTime (int): 被偷时间时间戳
Returns:
bool: 操作是否成功
"""
try:
async with cls._transaction():
await cls.m_pDB.execute(
'INSERT INTO "userSteal"(uid, soilIndex, stealerUid, stealCount, stealTime) VALUES(?, ?, ?, ?, ?);',
(uid, soilIndex, stealerUid, stealCount, stealTime)
)
return True
except Exception as e:
logger.warning("添加偷菜记录失败", e=e)
return False
@classmethod
async def getStealRecordsByUid(cls, uid: str) -> list:
"""根据用户Uid获取所有偷菜记录
Args:
uid (str): 被偷用户Uid
Returns:
list: 偷菜记录字典列表每条包含 soilIndex, stealerUid, stealCount, stealTime
"""
try:
async with cls._transaction():
cursor = await cls.m_pDB.execute(
'SELECT soilIndex, stealerUid, stealCount, stealTime FROM "userSteal" WHERE uid=?;',
(uid,)
)
rows = await cursor.fetchall()
return [
{
"uid": uid,
"soilIndex": row[0],
"stealerUid": row[1],
"stealCount": row[2],
"stealTime": row[3]
}
for row in rows
]
except Exception as e:
logger.warning("获取偷菜记录失败", e=e)
return []
@classmethod
async def getStealRecord(cls, uid: str, soilIndex: int) -> list:
"""获取指定地块的所有偷菜记录
Args:
uid (str): 被偷用户Uid
soilIndex (int): 被偷地块索引
Returns:
list: 偷菜记录字典列表每条包含 stealerUid, stealCount, stealTime
"""
try:
async with cls._transaction():
cursor = await cls.m_pDB.execute(
'SELECT stealerUid, stealCount, stealTime FROM "userSteal" WHERE uid=? AND soilIndex=?;',
(uid, soilIndex)
)
rows = await cursor.fetchall()
return [
{
"uid": uid,
"soilIndex": soilIndex,
"stealerUid": row[0],
"stealCount": row[1],
"stealTime": row[2]
}
for row in rows
]
except Exception as e:
logger.warning("获取单地块偷菜记录失败", e=e)
return []
@classmethod
async def getTotalStolenCount(cls, uid: str, soilIndex: int) -> int:
"""计算指定地块被偷的总数量(所有用户偷取数量之和)
Args:
uid (str): 被偷用户Uid
soilIndex (int): 被偷地块索引
Returns:
int: 被偷的总数量如果无记录则返回 0
"""
try:
async with cls._transaction():
cursor = await cls.m_pDB.execute(
'SELECT SUM(stealCount) FROM "userSteal" WHERE uid=? AND soilIndex=?;',
(uid, soilIndex)
)
row = await cursor.fetchone()
return row[0] or 0 # type: ignore
except Exception as e:
logger.warning("计算总偷菜数量失败", e=e)
return 0
@classmethod
async def getStealerCount(cls, uid: str, soilIndex: int) -> int:
"""计算指定地块被多少人偷过(不同偷菜用户数量)
Args:
uid (str): 被偷用户Uid
soilIndex (int): 被偷地块索引
Returns:
int: 偷菜者总数如果无记录则返回 0
"""
try:
async with cls._transaction():
cursor = await cls.m_pDB.execute(
'SELECT COUNT(DISTINCT stealerUid) FROM "userSteal" WHERE uid=? AND soilIndex=?;',
(uid, soilIndex)
)
row = await cursor.fetchone()
return row[0] or 0 # type: ignore
except Exception as e:
logger.warning("计算偷菜者数量失败", e=e)
return 0
@classmethod
async def hasStealed(cls, uid: str, soilIndex: int, stealerUid: str) -> bool:
"""判断指定用户是否曾偷取过该地块
Args:
uid (str): 被偷用户Uid
soilIndex (int): 被偷地块索引
stealerUid (str): 偷菜用户Uid
Returns:
bool: 若存在记录返回 True否则返回 False
"""
try:
async with cls._transaction():
cursor = await cls.m_pDB.execute(
'SELECT 1 FROM "userSteal" WHERE uid=? AND soilIndex=? AND stealerUid=? LIMIT 1;',
(uid, soilIndex, stealerUid)
)
row = await cursor.fetchone()
return bool(row)
except Exception as e:
logger.warning("检查偷菜记录失败", e=e)
return False
@classmethod
async def updateStealRecord(cls, uid: str, soilIndex: int, stealerUid: str, stealCount: int, stealTime: int) -> bool:
"""更新偷菜记录的数量和时间
Args:
uid (str): 被偷用户Uid
soilIndex (int): 被偷地块索引
stealerUid (str): 偷菜用户Uid
stealCount (int): 新的偷菜数量
stealTime (int): 新的偷菜时间时间戳
Returns:
bool: 操作是否成功
"""
try:
async with cls._transaction():
await cls.m_pDB.execute(
'UPDATE "userSteal" SET stealCount=?, stealTime=? WHERE uid=? AND soilIndex=? AND stealerUid=?;',
(stealCount, stealTime, uid, soilIndex, stealerUid)
)
return True
except Exception as e:
logger.warning("更新偷菜记录失败", e=e)
return False
@classmethod
async def deleteStealRecord(cls, uid: str, soilIndex: int, stealerUid: str) -> bool:
"""删除指定偷菜记录
Args:
uid (str): 被偷用户Uid
soilIndex (int): 被偷地块索引
stealerUid (str): 偷菜用户Uid
Returns:
bool: 删除是否成功
"""
try:
async with cls._transaction():
await cls.m_pDB.execute(
'DELETE FROM "userSteal" WHERE uid=? AND soilIndex=? AND stealerUid=?;',
(uid, soilIndex, stealerUid)
)
return True
except Exception as e:
logger.warning("删除偷菜记录失败", e=e)
return False

29
dbService.py Normal file
View File

@ -0,0 +1,29 @@
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:
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
async def init(cls):
cls.user = CUserDB()
cls.userSoil = CUserSoilDB()
cls.userPlant = CUserPlantDB()
cls.userSeed = CUserSeedDB()
cls.userItem = CUserItemDB()
cls.userSteal = CUserStealDB()
g_pDBService = CDBService()

103
event/event.py Normal file
View File

@ -0,0 +1,103 @@
import asyncio
import time
from zhenxun.services.log import logger
class Signal:
def __init__(self):
self._slots = [] #绑定的槽函数列表
self._onceSlots = [] #只触发一次的槽函数列表
def connect(self, slot, priority=0):
if callable(slot) and not any(s[0] == slot for s in self._slots):
self._slots.append((slot, priority))
self._slots.sort(key=lambda x: -x[1])
def connectOnce(self, slot, priority=0):
if callable(slot) and not any(s[0] == slot for s in self._onceSlots):
self._onceSlots.append((slot, priority))
self._onceSlots.sort(key=lambda x: -x[1])
def disconnect(self, slot):
self._slots = [s for s in self._slots if s[0] != slot]
self._onceSlots = [s for s in self._onceSlots if s[0] != slot]
async def emit(self, *args, **kwargs):
slots = list(self._slots)
onceSlots = list(self._onceSlots)
self._onceSlots.clear()
for slot, _ in slots + onceSlots:
startTime = time.time()
try:
if asyncio.iscoroutinefunction(slot):
await slot(*args, **kwargs)
else:
slot(*args, **kwargs)
duration = (time.time() - startTime) * 1000
logger.debug(f"事件槽 {slot.__name__} 执行完成,耗时 {duration:.2f} ms")
except Exception as e:
logger.warning(f"事件槽 {slot.__name__} 触发异常: {e}")
class FarmEventManager:
def __init__(self):
self.m_beforePlant = Signal()
"""播种前信号
Args:
uid (str): 用户Uid
name (str): 播种种子名称
num (int): 播种数量
"""
self.m_afterPlant = Signal()
"""播种后信号 每块地播种都会触发该信号
Args:
uid (str): 用户Uid
name (str): 播种种子名称
soilIndex (int): 播种地块索引 从1开始
"""
self.m_beforeHarvest = Signal()
"""收获前信号
Args:
uid (str): 用户Uid
"""
self.m_afterHarvest = Signal()
"""收获后信号 每块地收获都会触发该信号
Args:
uid (str): 用户Uid
name (str): 收获作物名称
num (int): 收获数量
soilIndex (int): 收获地块索引 从1开始
"""
self.m_beforeEradicate = Signal()
"""铲除前信号
Args:
uid (str): 用户Uid
"""
self.m_afterEradicate = Signal()
"""铲除后信号 每块地铲除都会触发该信号
Args:
uid (str): 用户Uid
soilIndex (index): 铲除地块索引 从1开始
"""
self.m_beforeExpand = Signal()
self.m_afterExpand = Signal()
self.m_beforeSteal = Signal()
self.m_afterSteal = Signal()
g_pEventManager = FarmEventManager()

View File

@ -5,8 +5,6 @@ from datetime import date, datetime
from io import StringIO from io import StringIO
from typing import Dict, List, Tuple from typing import Dict, List, Tuple
from numpy import number
from zhenxun.configs.config import Config from zhenxun.configs.config import Config
from zhenxun.models.user_console import UserConsole from zhenxun.models.user_console import UserConsole
from zhenxun.services.log import logger from zhenxun.services.log import logger
@ -16,7 +14,8 @@ from zhenxun.utils.image_utils import ImageTemplate
from zhenxun.utils.platform import PlatformUtils from zhenxun.utils.platform import PlatformUtils
from ..config import g_pJsonManager, g_sResourcePath from ..config import g_pJsonManager, g_sResourcePath
from ..database import g_pSqlManager from ..dbService import g_pDBService
from ..event.event import g_pEventManager
class CFarmManager: class CFarmManager:
@ -38,15 +37,15 @@ class CFarmManager:
if user.gold < deduction: if user.gold < deduction:
return f"你的金币不足或不足承担手续费。当前手续费为{fee}" return f"你的金币不足或不足承担手续费。当前手续费为{fee}"
await UserConsole.reduce_gold(uid, num, GoldHandle.PLUGIN , 'zhenxun_plugin_farm') await UserConsole.reduce_gold(uid, num, GoldHandle.PLUGIN , 'zhenxun_plugin_farm') # type: ignore
await UserConsole.reduce_gold(uid, fee, GoldHandle.PLUGIN , 'zhenxun_plugin_farm') await UserConsole.reduce_gold(uid, fee, GoldHandle.PLUGIN , 'zhenxun_plugin_farm') # type: ignore
point = num * pro point = num * pro
p = await g_pSqlManager.getUserPointByUid(uid) p = await g_pDBService.user.getUserPointByUid(uid)
number = point + p number = point + p
await g_pSqlManager.updateUserPointByUid(uid, int(number)) await g_pDBService.user.updateUserPointByUid(uid, int(number))
return f"充值{point}农场币成功,手续费{tax}金币,当前农场币:{number}" return f"充值{point}农场币成功,手续费{tax}金币,当前农场币:{number}"
@ -73,7 +72,7 @@ class CFarmManager:
soilPos = g_pJsonManager.m_pSoil['soil'] soilPos = g_pJsonManager.m_pSoil['soil']
userInfo = await g_pSqlManager.getUserInfoByUid(uid) userInfo = await g_pDBService.user.getUserInfoByUid(uid)
soilUnlock = int(userInfo['soil']) soilUnlock = int(userInfo['soil'])
x = 0 x = 0
@ -89,7 +88,7 @@ class CFarmManager:
if index < soilUnlock: if index < soilUnlock:
await img.paste(soil, (x, y)) await img.paste(soil, (x, y))
isPlant, plant, isRipe= await cls.drawSoilPlant(uid, f"soil{str(index + 1)}") isPlant, plant, isRipe= await cls.drawSoilPlant(uid, index + 1)
if isPlant: if isPlant:
await img.paste(plant, (x + soilSize[0] // 2 - plant.width // 2, await img.paste(plant, (x + soilSize[0] // 2 - plant.width // 2,
@ -134,7 +133,7 @@ class CFarmManager:
await img.paste(nameImg, (300, 92)) await img.paste(nameImg, (300, 92))
#经验值 #经验值
level = await g_pSqlManager.getUserLevelByUid(uid) level = await g_pDBService.user.getUserLevelByUid(uid)
beginX = 309 beginX = 309
endX = 627 endX = 627
@ -171,57 +170,59 @@ class CFarmManager:
return img.pic2bytes() return img.pic2bytes()
@classmethod @classmethod
async def drawSoilPlant(cls, uid: str, soilid: str) -> tuple[bool, BuildImage, bool]: async def drawSoilPlant(cls, uid: str, soilIndex: int) -> tuple[bool, BuildImage, bool]:
"""绘制植物资源 """绘制植物资源
Args: Args:
uid (str): 用户Uid uid (str): 用户Uid
soilid (str): 土地id soilIndex (int): 土地索引 从1开始
Returns: Returns:
tuple[bool, BuildImage]: [绘制是否成功资源图片, 是否成熟] tuple[bool, BuildImage]: [绘制是否成功资源图片, 是否成熟]
""" """
plant = None plant = None
soilStatus, soilInfo = await g_pSqlManager.getUserSoilStatusBySoilID(uid, soilid) soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex)
if soilStatus == True: if not soilInfo or not soilInfo.get("plantName"):
return False, None, False return False, None, False #type: ignore
#是否枯萎
if int(soilInfo.get("wiltStatus", 0)) == 1:
plant = BuildImage(background = g_sResourcePath / f"plant/basic/9.png")
await plant.resize(0, 150, 212)
return True, plant, False
#获取作物详细信息
plantInfo = g_pJsonManager.m_pPlant['plant'][soilInfo['plantName']]
currentTime = datetime.now()
matureTime = datetime.fromtimestamp(int(soilInfo['matureTime']))
#如果当前时间大于成熟时间 说明作物成熟
if currentTime >= matureTime:
phase = int(plantInfo['phase'])
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{phase - 1}.png")
return True, plant, True
else: else:
soilInfo = soilInfo.split(',') #如果没有成熟 则根据当前阶段进行绘制
plantedTime = datetime.fromtimestamp(int(soilInfo['plantTime']))
if int(soilInfo[3]) == 4: elapsedTime = currentTime - plantedTime
plant = BuildImage(background = g_sResourcePath / f"plant/basic/9.png") elapsedHour = elapsedTime.total_seconds() / 3600
await plant.resize(0, 150, 212)
return True, plant, False
plantInfo = g_pJsonManager.m_pPlant['plant'][soilInfo[0]] currentStage = int(elapsedHour / (plantInfo['time'] / (plantInfo['phase'] - 1)))
currentTime = datetime.now() if currentStage <= 0:
matureTime = datetime.fromtimestamp(int(soilInfo[2])) if plantInfo['general'] == False:
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/0.png")
if currentTime >= matureTime:
phase = int(plantInfo['phase'])
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo[0]}/{phase - 1}.png")
return True, plant, True
else:
plantedTime = datetime.fromtimestamp(int(soilInfo[1]))
elapsedTime = currentTime - plantedTime
elapsedHour = elapsedTime.total_seconds() / 60 / 60
currentStage = int(elapsedHour / (plantInfo['time'] / (plantInfo['phase'] - 1)))
if currentStage <= 0:
if plantInfo['general'] == False:
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo[0]}/0.png")
else:
plant = BuildImage(background = g_sResourcePath / f"plant/basic/0.png")
await plant.resize(0, 35, 58)
else: else:
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo[0]}/{currentStage}.png") plant = BuildImage(background = g_sResourcePath / f"plant/basic/0.png")
await plant.resize(0, 35, 58)
else:
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{currentStage}.png")
return True, plant, False return True, plant, False
@ -241,8 +242,8 @@ class CFarmManager:
"是否可以上架交易行" "是否可以上架交易行"
] ]
# 从数据库获取结构化数据 #从数据库获取结构化数据
seedRecords = await g_pSqlManager.getUserSeedByUid(uid) or {} seedRecords = await g_pDBService.userSeed.getUserSeedByUid(uid) or {}
if not seedRecords: if not seedRecords:
result = await ImageTemplate.table_page( result = await ImageTemplate.table_page(
@ -295,54 +296,57 @@ class CFarmManager:
str: 返回结果 str: 返回结果
""" """
try: try:
# 获取用户的种子数量 #获取用户的种子数量
count = await g_pSqlManager.getUserSeedByName(uid, name) count = await g_pDBService.userSeed.getUserSeedByName(uid, name)
if count is None: if count is None:
count = 0 # 如果返回 None则视为没有种子 count = 0 #如果返回 None则视为没有种子
if count <= 0: if count <= 0:
return f"没有在你的仓库发现{name}种子,快去买点吧!" return f"没有在你的仓库发现{name}种子,快去买点吧!"
# 如果播种数量超过仓库种子数量 #如果播种数量超过仓库种子数量
if count < num and num != -1: if count < num and num != -1:
return f"仓库中的{name}种子数量不足,当前剩余{count}个种子" return f"仓库中的{name}种子数量不足,当前剩余{count}个种子"
# 获取用户土地数量 #获取用户土地数量
soilNumber = await g_pSqlManager.getUserSoilByUid(uid) soilNumber = await g_pDBService.user.getUserSoilByUid(uid)
# 如果播种数量为 -1表示播种所有可播种的土地 #如果播种数量为 -1表示播种所有可播种的土地
if num == -1: if num == -1:
num = count num = count
# 记录是否成功播种 #发送播种前信号
await g_pEventManager.m_beforePlant.emit(uid=uid, name=name, num=num)
#记录是否成功播种
successCount = 0 successCount = 0
for i in range(1, soilNumber + 1): for i in range(1, soilNumber + 1):
if count > 0 and num > 0: if count > 0 and num > 0:
soilName = f"soil{i}" success = await g_pDBService.userSoil.sowingByPlantName(uid, i, name)
success, message = await g_pSqlManager.getUserSoilStatusBySoilID(uid, soilName)
if success: if success:
# 更新种子数量 #更新种子数量
num -= 1 num -= 1
count -= 1 count -= 1
# 记录种子消耗数量 #记录种子消耗数量
successCount += 1 successCount += 1
# 更新数据库 #发送播种后信号
await g_pSqlManager.updateUserSoilStatusByPlantName(uid, soilName, name) await g_pEventManager.m_afterPlant.emit(uid=uid, name=name, soilIndex=i)
# 确保用户仓库数量更新
#确保用户仓库数量更新
if successCount > 0: if successCount > 0:
await g_pSqlManager.updateUserSeedByName(uid, name, count) await g_pDBService.userSeed.updateUserSeedByName(uid, name, count)
# 根据播种结果给出反馈 #根据播种结果给出反馈
if num == 0: if num == 0:
return f"播种{name}成功!仓库剩余{count}个种子" return f"播种{name}成功!仓库剩余{count}个种子"
else: else:
return f"播种数量超出开垦土地数量,已将可播种土地成功播种{name}!仓库剩余{count}个种子" return f"播种数量超出开垦土地数量,已将可播种土地成功播种{name}!仓库剩余{count}个种子"
except Exception as e: except Exception as e:
logger.warning(f"播种操作失败: {e}") logger.warning(f"播种操作失败", e=e)
return "播种失败,请稍后重试!" return "播种失败,请稍后重试!"
@classmethod @classmethod
@ -355,61 +359,66 @@ class CFarmManager:
Returns: Returns:
str: 返回 str: 返回
""" """
try:
await g_pEventManager.m_beforeHarvest.emit(uid=uid)
isStealingPlant = 0 soilNumber = await g_pDBService.user.getUserSoilByUid(uid)
soilUnlock = await g_pSqlManager.getUserSoilByUid(uid)
soilNames = [f"soil{i + 1}" for i in range(soilUnlock)] harvestRecords = [] #收获日志记录
soilStatuses = await asyncio.gather(*[ experience = 0 #总经验值
g_pSqlManager.getUserSoilStatusBySoilID(uid, name) harvestCount = 0 #成功收获数量
for name in soilNames
])
harvestRecords: List[str] = [] for i in range(1, soilNumber + 1):
experience = 0 #如果没有种植
if not await g_pDBService.userSoil.isSoilPlanted(uid, i):
continue
for (soil_name, (status, info)) in zip(soilNames, soilStatuses): soilInfo = await g_pDBService.userSoil.getUserSoil(uid, i)
if len(info) <= 0: if not soilInfo:
continue continue
soilInfo = info.split(',') #如果是枯萎状态
if int(soilInfo[3]) == 4: if soilInfo.get("wiltStatus", 1) == 1:
continue continue
plantId = soilInfo[0] plantInfo = g_pJsonManager.m_pPlant.get("plant", {}).get(soilInfo['plantName'])
plantInfo = g_pJsonManager.m_pPlant['plant'][plantId] currentTime = datetime.now()
matureTime = datetime.fromtimestamp(int(soilInfo['matureTime']))
currentTime = datetime.now() if currentTime >= matureTime:
matureTime = datetime.fromtimestamp(int(soilInfo[2])) number = plantInfo['harvest']
if currentTime >= matureTime: #处理偷菜扣除数量
number = plantInfo['harvest'] stealNum = g_pDBService.userSteal.getTotalStolenCount(uid, i)
#判断该土地作物是否被透过 number -= stealNum
if len(soilInfo[4]) > 0:
stealingStatus = soilInfo[4].split('|')
for isUser in stealingStatus:
user = isUser.split('-')
number -= int(user[1])
isStealingPlant += 1 if number <= 0:
continue
experience += plantInfo['experience'] harvestCount += 1
harvestRecords.append(f"收获作物:{plantId},数量为:{number},经验为:{plantInfo['experience']}") experience += plantInfo['experience']
#更新数据库操作 harvestRecords.append(f"收获作物:{soilInfo['plantName']},数量为:{number},经验为:{plantInfo['experience']}")
await g_pSqlManager.addUserPlantByUid(uid, plantId, number)
await g_pSqlManager.updateUserSoilStatusByPlantName(uid, soil_name, "", 4)
if experience > 0: await g_pDBService.userPlant.addUserPlantByUid(uid, soilInfo['plantName'], number)
harvestRecords.append(f"\t累计获得经验:{experience}") await g_pDBService.userSoil.updateUserSoil(uid, i, "wiltStatus", 1)
exp = await g_pSqlManager.getUserExpByUid(uid)
await g_pSqlManager.UpdateUserExpByUid(uid, exp + experience)
if isStealingPlant <= 0: await g_pEventManager.m_afterHarvest.emit(uid=uid, name=soilInfo['plantName'], num=number, soilIndex=i)
return "没有可收获的作物哦~ 不要试图拔苗助长"
else: if experience > 0:
return "\n".join(harvestRecords) exp = await g_pDBService.user.getUserExpByUid(uid)
await g_pDBService.user.updateUserExpByUid(uid, exp + experience)
harvestRecords.append(f"\t累计获得经验:{experience}")
if harvestCount <= 0:
return "没有可收获的作物哦~ 不要试图拔苗助长"
else:
return "\n".join(harvestRecords)
except Exception as e:
logger.warning(f"收获操作失败!", e=e)
return "收获失败,请稍后重试!"
@classmethod @classmethod
async def eradicate(cls, uid: str) -> str: async def eradicate(cls, uid: str) -> str:
@ -421,28 +430,34 @@ class CFarmManager:
Returns: Returns:
str: 返回 str: 返回
""" """
soilNumber = await g_pDBService.user.getUserSoilByUid(uid)
soilUnlock = await g_pSqlManager.getUserSoilByUid(uid) await g_pEventManager.m_beforeEradicate.emit(uid=uid)
soilNames = [f"soil{i + 1}" for i in range(soilUnlock)]
soilStatuses = await asyncio.gather(*[
g_pSqlManager.getUserSoilStatusBySoilID(uid, name)
for name in soilNames
])
experience = 0 experience = 0
for (soil_name, (status, info)) in zip(soilNames, soilStatuses): for i in range(1, soilNumber + 1):
if info: #如果没有种植
soilInfo = info.split(',') if not await g_pDBService.userSoil.isSoilPlanted(uid, i):
if int(soilInfo[3]) == 4: continue
experience += 3
#批量更新数据库操作 soilInfo = await g_pDBService.userSoil.getUserSoil(uid, i)
await g_pSqlManager.updateUserSoilStatusByPlantName(uid, soil_name, "", 0) if not soilInfo:
continue
#如果不是枯萎状态
if soilInfo.get("wiltStatus", 0) == 0:
continue
experience += 3
#批量更新数据库操作
await g_pDBService.userSoil.deleteUserSoil(uid, i)
await g_pEventManager.m_afterEradicate.emit(uid=uid, soilIndex=i)
if experience > 0: if experience > 0:
exp = await g_pSqlManager.getUserExpByUid(uid) exp = await g_pDBService.user.getUserExpByUid(uid)
await g_pSqlManager.UpdateUserExpByUid(uid, exp + experience) await g_pDBService.user.updateUserExpByUid(uid, exp + experience)
return f"成功铲除荒废作物,累计获得经验:{experience}" return f"成功铲除荒废作物,累计获得经验:{experience}"
else: else:
@ -468,7 +483,7 @@ class CFarmManager:
"是否可以上架交易行" "是否可以上架交易行"
] ]
plant = await g_pSqlManager.getUserPlantByUid(uid) plant = await g_pDBService.userPlant.getUserPlantByUid(uid)
if plant is None: if plant is None:
result = await ImageTemplate.table_page( result = await ImageTemplate.table_page(
@ -525,121 +540,83 @@ class CFarmManager:
Returns: Returns:
str: 返回 str: 返回
""" """
#用户信息 #用户信息
userInfo = await g_pSqlManager.getUserInfoByUid(uid) userInfo = await g_pDBService.user.getUserInfoByUid(uid)
#用户可偷次数
userStealing = userInfo["stealing"].split('|')
if userStealing[0] == '': stealTime = userInfo['stealTime']
userStealing[0] = date.today().strftime('%Y-%m-%d') stealCount = int(userInfo['stealCount'])
userStealing.append(5)
elif date.fromisoformat(userStealing[0]) != date.today():
userStealing[0] = date.today().strftime('%Y-%m-%d')
userStealing[1] = 5
if int(userStealing[1]) <= 0: if stealTime == '':
stealTime = date.today().strftime('%Y-%m-%d')
stealCount = 5
elif date.fromisoformat(stealTime) != date.today():
stealTime = date.today().strftime('%Y-%m-%d')
stealCount = 5
if stealCount <= 0:
return "你今天可偷次数到达上限啦,手下留情吧" return "你今天可偷次数到达上限啦,手下留情吧"
#获取用户解锁地块数量 #获取用户解锁地块数量
soilUnlockNum = int(userInfo["soil"]) soilNumber = await g_pDBService.user.getUserSoilByUid(uid)
plant = {}
#根据解锁土地,获取每块土地状态信息
soilNames = [f"soil{i + 1}" for i in range(soilUnlockNum)]
soilStatuses = await asyncio.gather(*[
g_pSqlManager.getUserSoilStatusBySoilID(target, name)
for name in soilNames
])
isStealing = False
harvestRecords: List[str] = [] harvestRecords: List[str] = []
isStealingNumber = 0 isStealingNumber = 0
isStealingPlant = 0 isStealingPlant = 0
for (soilName, (status, info)) in zip(soilNames, soilStatuses): for i in range(1, soilNumber + 1):
isStealing = False #如果没有种植
if not await g_pDBService.userSoil.isSoilPlanted(uid, i):
if not info:
continue continue
soilInfo = info.split(',') soilInfo = await g_pDBService.userSoil.getUserSoil(uid, i)
if int(soilInfo[3]) == 4: if not soilInfo:
continue
#如果是枯萎状态
if soilInfo.get("wiltStatus", 1) == 1:
continue continue
#作物ID
plantId = soilInfo[0]
#作物信息 #作物信息
plantInfo = g_pJsonManager.m_pPlant['plant'][plantId] plantInfo = g_pJsonManager.m_pPlant.get("plant", {}).get(soilInfo['plantName'])
currentTime = datetime.now() currentTime = datetime.now()
matureTime = datetime.fromtimestamp(int(soilInfo[2])) matureTime = datetime.fromtimestamp(int(soilInfo['matureTime']))
#偷窃状态
stealingStatus: list[str] = []
#偷窃数量
stealingNumber = 0
if currentTime >= matureTime: if currentTime >= matureTime:
if soilInfo[4]:
#先获取用户是否偷过该土地
stealingStatus = soilInfo[4].split('|')
for isUser in stealingStatus:
user = isUser.split('-')
if user[0] == uid:
isStealing = True
isStealingNumber += 1
break
stealingNumber -= int(user[1])
#如果偷过,则跳过该土地 #如果偷过,则跳过该土地
if isStealing: if await g_pDBService.userSteal.hasStealed(target, i, uid):
continue continue
stealingNumber += plantInfo['harvest'] stealingNumber = plantInfo['harvest'] - await g_pDBService.userSteal.getTotalStolenCount(target, i)
randomNumber = random.choice([1, 2]) randomNumber = random.choice([1, 2])
randomNumber = min(randomNumber, stealingNumber) randomNumber = min(randomNumber, stealingNumber)
logger.info(f"{randomNumber}")
if randomNumber > 0: if randomNumber > 0:
await g_pSqlManager.addUserPlantByUid(uid, plantId, randomNumber) await g_pDBService.userPlant.addUserPlantByUid(uid, soilInfo['plantName'], randomNumber)
harvestRecords.append(f"成功偷到作物:{plantId},数量为:{randomNumber}") harvestRecords.append(f"成功偷到作物:{soilInfo['plantName']},数量为:{randomNumber}")
stealingStatus.append(f"{uid}-{randomNumber}")
isStealingPlant += 1 isStealingPlant += 1
#如果将作物偷完,就直接更新状态 并记录用户偷取过 #如果将作物偷完,就直接更新状态 并记录用户偷取过
if plantInfo['harvest'] - randomNumber + stealingNumber == 0: if plantInfo['harvest'] - randomNumber + stealingNumber == 0:
sql = f"UPDATE soil SET {soilName} = ',,,4,{'|'.join(stealingStatus)}' WHERE uid = '{target}'" await g_pDBService.userSoil.updateUserSoil(target, i, "wiltStatus", 1)
else: else:
sql = f"UPDATE soil SET {soilName} = '{soilInfo[0]},{soilInfo[1]},{soilInfo[2]},{soilInfo[3]},{'|'.join(stealingStatus)}' WHERE uid = '{target}'" await g_pDBService.userSteal.addStealRecord(target, i, uid, randomNumber, int(datetime.now().timestamp()))
await g_pSqlManager.executeDB(sql)
if isStealingPlant <= 0 and isStealingNumber <= 0: if isStealingPlant <= 0 and isStealingNumber <= 0:
return "目标没有作物可以被偷" return "目标没有作物可以被偷"
elif isStealingPlant <= 0 and isStealingNumber > 0: elif isStealingPlant <= 0 and isStealingNumber > 0:
return "你已经偷过目标啦,请手下留情" return "你已经偷过目标啦,请手下留情"
else: else:
userStealing[1] = int(userStealing[1]) - 1 stealCount -= 1
# 转换所有元素为字符串
userStealing_str = [str(item) for item in userStealing]
sql = f"UPDATE user SET stealing = '{userStealing[0]}|{userStealing[1]}' WHERE uid = {uid}" await g_pDBService.user.updateStealCountByUid(uid, stealCount)
#更新用户每日偷取次数
await g_pSqlManager.executeDB(sql)
return "\n".join(harvestRecords) return "\n".join(harvestRecords)
@classmethod @classmethod
async def reclamationCondition(cls, uid: str) -> str: async def reclamationCondition(cls, uid: str) -> str:
userInfo = await g_pSqlManager.getUserInfoByUid(uid) userInfo = await g_pDBService.user.getUserInfoByUid(uid)
rec = g_pJsonManager.m_pLevel["reclamation"] rec = g_pJsonManager.m_pLevel['reclamation']
try: try:
if userInfo['soil'] >= 30: if userInfo['soil'] >= 30:
@ -663,10 +640,10 @@ class CFarmManager:
@classmethod @classmethod
async def reclamation(cls, uid: str) -> str: async def reclamation(cls, uid: str) -> str:
userInfo = await g_pSqlManager.getUserInfoByUid(uid) userInfo = await g_pDBService.user.getUserInfoByUid(uid)
level = await g_pSqlManager.getUserLevelByUid(uid) level = await g_pDBService.user.getUserLevelByUid(uid)
rec = g_pJsonManager.m_pLevel["reclamation"] rec = g_pJsonManager.m_pLevel['reclamation']
try: try:
if userInfo['soil'] >= 30: if userInfo['soil'] >= 30:
@ -678,18 +655,15 @@ class CFarmManager:
point = rec['point'] point = rec['point']
item = rec['item'] item = rec['item']
logger.info(f"{level[0]}")
if level[0] < levelFileter: if level[0] < levelFileter:
return f"当前用户等级{level[0]},升级所需等级为{levelFileter}" return f"当前用户等级{level[0]},升级所需等级为{levelFileter}"
if userInfo['point'] < point: if userInfo['point'] < point:
return f"当前用户农场币不足,升级所需农场币为{point}" return f"当前用户农场币不足,升级所需农场币为{point}"
#TODO 缺少判断需要的Item #TODO 缺少判断消耗的item
sql = f"UPDATE user SET point = '{userInfo['point'] - point}', soil = '{userInfo['soil'] + 1}' WHERE uid = {uid}" await g_pDBService.user.updateUserPointByUid(uid, userInfo['point'] - point)
await g_pDBService.user.updateUserSoilByUid(uid, userInfo['soil'] + 1)
await g_pSqlManager.executeDB(sql)
return "开垦土地成功!" return "开垦土地成功!"
except Exception as e: except Exception as e:

View File

@ -6,7 +6,7 @@ from zhenxun.utils._build_image import BuildImage
from zhenxun.utils.image_utils import ImageTemplate from zhenxun.utils.image_utils import ImageTemplate
from ..config import g_pJsonManager, g_sResourcePath from ..config import g_pJsonManager, g_sResourcePath
from ..database import g_pSqlManager from ..dbService import g_pDBService
class CShopManager: class CShopManager:
@ -102,12 +102,12 @@ class CShopManager:
except Exception as e: except Exception as e:
return "购买出错!请检查需购买的种子名称!" return "购买出错!请检查需购买的种子名称!"
level = await g_pSqlManager.getUserLevelByUid(uid) level = await g_pDBService.user.getUserLevelByUid(uid)
if level[0] < int(plantInfo['level']): if level[0] < int(plantInfo['level']):
return "你的等级不够哦,努努力吧" return "你的等级不够哦,努努力吧"
point = await g_pSqlManager.getUserPointByUid(uid) point = await g_pDBService.user.getUserPointByUid(uid)
total = int(plantInfo['buy']) * num total = int(plantInfo['buy']) * num
logger.debug(f"用户:{uid}购买{name},数量为{num}。用户农场币为{point},购买需要{total}") logger.debug(f"用户:{uid}购买{name},数量为{num}。用户农场币为{point},购买需要{total}")
@ -115,9 +115,9 @@ class CShopManager:
if point < total: if point < total:
return "你的农场币不够哦~ 快速速氪金吧!" return "你的农场币不够哦~ 快速速氪金吧!"
else: else:
await g_pSqlManager.updateUserPointByUid(uid, point - total) await g_pDBService.user.updateUserPointByUid(uid, point - total)
if await g_pSqlManager.addUserSeedByUid(uid, name, num) == False: if await g_pDBService.userSeed.addUserSeedByUid(uid, name, num) == False:
return "购买失败,执行数据库错误!" return "购买失败,执行数据库错误!"
return f"成功购买{name},花费{total}农场币, 剩余{point - total}农场币" return f"成功购买{name},花费{total}农场币, 剩余{point - total}农场币"
@ -135,7 +135,7 @@ class CShopManager:
if not isinstance(name, str) or name.strip() == "": if not isinstance(name, str) or name.strip() == "":
name = "" name = ""
plant = await g_pSqlManager.getUserPlantByUid(uid) plant = await g_pDBService.userPlant.getUserPlantByUid(uid)
if not plant: if not plant:
return "你仓库没有可以出售的作物" return "你仓库没有可以出售的作物"
@ -147,7 +147,7 @@ class CShopManager:
for plantName, count in plant.items(): for plantName, count in plant.items():
plantInfo = g_pJsonManager.m_pPlant['plant'][plantName] plantInfo = g_pJsonManager.m_pPlant['plant'][plantName]
point += plantInfo['price'] * count point += plantInfo['price'] * count
await g_pSqlManager.updateUserPlantByName(uid, plantName, 0) await g_pDBService.userPlant.updateUserPlantByName(uid, plantName, 0)
else: else:
if name not in plant: if name not in plant:
return f"出售作物{name}出错:仓库中不存在该作物" return f"出售作物{name}出错:仓库中不存在该作物"
@ -155,12 +155,12 @@ class CShopManager:
sellAmount = available if isAll else min(available, num) sellAmount = available if isAll else min(available, num)
if sellAmount <= 0: if sellAmount <= 0:
return f"出售作物{name}出错:数量不足" return f"出售作物{name}出错:数量不足"
await g_pSqlManager.updateUserPlantByName(uid, name, available - sellAmount) await g_pDBService.userPlant.updateUserPlantByName(uid, name, available - sellAmount)
totalSold = sellAmount totalSold = sellAmount
totalPoint = point if name == "" else totalSold * g_pJsonManager.m_pPlant['plant'][name]['price'] totalPoint = point if name == "" else totalSold * g_pJsonManager.m_pPlant['plant'][name]['price']
currentPoint = await g_pSqlManager.getUserPointByUid(uid) currentPoint = await g_pDBService.user.getUserPointByUid(uid)
await g_pSqlManager.updateUserPointByUid(uid, currentPoint + totalPoint) await g_pDBService.user.updateUserPointByUid(uid, currentPoint + totalPoint)
if name == "": if name == "":
return f"成功出售所有作物,获得农场币:{totalPoint},当前农场币:{currentPoint + totalPoint}" return f"成功出售所有作物,获得农场币:{totalPoint},当前农场币:{currentPoint + totalPoint}"