Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fce307cc94 | ||
|
|
db294e6998 | ||
|
|
1ef506d2fd | ||
|
|
a475d0188b | ||
|
|
7dc55bccf1 | ||
|
|
afa2259ad8 | ||
|
|
c131692b19 | ||
|
|
cca3dc5d4d | ||
|
|
2fbaf5d54f | ||
|
|
6d3eb5d48f | ||
|
|
ff5b446b52 | ||
|
|
55394e3590 | ||
|
|
0cd9b9d8db | ||
|
|
8243a1e1c8 | ||
|
|
6b99ec427c | ||
|
|
b5334dfc01 | ||
|
|
72baa5d095 | ||
|
|
719494e4e5 | ||
|
|
5d3d8a4ef1 | ||
|
|
b72190cc45 | ||
|
|
4cd0302426 |
@@ -1 +1,3 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
|
|
||||||
|
./config/sign_in.json
|
||||||
|
|||||||
@@ -1,43 +1,105 @@
|
|||||||
|
<div align="center">
|
||||||
|
|
||||||
# 真寻农场(zhenxun_plugin_farm)
|
# 真寻农场(zhenxun_plugin_farm)
|
||||||
|
<p align="center">
|
||||||
|
<a href="./LICENSE">
|
||||||
|
<img src="https://img.shields.io/badge/license-GPL3.0-FE7D37" alt="license">
|
||||||
|
</a>
|
||||||
|
<a href="https://www.python.org">
|
||||||
|
<img src="https://img.shields.io/badge/Python-3.10%2B-blue" alt="Python">
|
||||||
|
</a>
|
||||||
|
<a href="https://nonebot.dev/">
|
||||||
|
<img src="https://img.shields.io/badge/Nonebot-2.0.0%2B-black" alt="Nonebot">
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/zhenxun-org/zhenxun_bot">
|
||||||
|
<img src="https://img.shields.io/badge/zhenxun-0.2.4%2B-%23ECD9D3" alt="Python">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
|
||||||
|
[](https://qm.qq.com/q/7hsOD4rOw2)
|
||||||
|
|
||||||
|
</p>
|
||||||
|
|
||||||
你是说可以种地对吧🤔?
|
你是说可以种地对吧🤔?
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
## 目录
|
||||||
|
- [真寻农场(zhenxun\_plugin\_farm)](#真寻农场zhenxun_plugin_farm)
|
||||||
|
- [目录](#目录)
|
||||||
|
- [如何安装](#如何安装)
|
||||||
|
- [使用指令](#使用指令)
|
||||||
|
- [更新日志(详细):](#更新日志详细)
|
||||||
|
- [用户方面](#用户方面)
|
||||||
|
- [代码方面](#代码方面)
|
||||||
|
- [待办事宜 `Todo` 列表](#待办事宜-todo-列表)
|
||||||
|
- [关于](#关于)
|
||||||
|
- [致谢](#致谢)
|
||||||
|
- [许可证](#许可证)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 如何安装
|
## 如何安装
|
||||||
|
|
||||||
方法一(推荐):在小真寻后台的插件商店下载即可。
|
方法一(推荐):在小真寻后台的插件商店下载即可<br>
|
||||||
方法二:下载源码放在小真寻`plugin`目录下
|
方法二:下载源码放在小真寻`plugin`目录下
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 使用指令
|
## 使用指令
|
||||||
|
|
||||||
| 指令 | 描述 | Tip |
|
| 指令 | 描述 | Tip |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| @小真寻 开通农场 | 首次开通农场 | |
|
| @小真寻 开通农场 | 首次开通农场 | |
|
||||||
|
| 我的农场 | 你的农场 | |
|
||||||
|
| 农场详述 | 农场详细信息 | |
|
||||||
| 我的农场币 | 查询农场币 | |
|
| 我的农场币 | 查询农场币 | |
|
||||||
| 种子商店 [页数] | 查看种子商店 | 数量不填默认为1 |
|
| 种子商店 [筛选关键字] [页数] or [页数] | 查看种子商店 | 当第一个参数为非整数时,会默认进入筛选状态。页数不填默认为1 |
|
||||||
| 购买种子 [种子名称] [数量] | 购买种子 | 数量不填默认为1 |
|
| 购买种子 [种子名称] [数量] | 购买种子 | 数量不填默认为1 |
|
||||||
| 我的种子 | 查询仓库种子 | |
|
| 我的种子 | 查询仓库种子 | |
|
||||||
| 播种 [种子名称] [数量] | 播种种子 | 数量不填默认将最大可能播种 |
|
| 播种 [种子名称] [数量] | 播种种子 | 数量不填默认将最大可能播种 |
|
||||||
| 收获 | 收获成熟作物 | |
|
| 收获 | 收获成熟作物 | |
|
||||||
| 铲除 | 铲除荒废作物 | |
|
| 铲除 | 铲除荒废作物 | |
|
||||||
| 我的作物 | | |
|
| 我的作物 | 你的作物 | |
|
||||||
| 出售作物 [作物名称] [数量] | 从仓库里向系统售卖作物 | 作物不填默认清空仓库 数量不填默认全部 |
|
| 出售作物 [作物名称] [数量] | 从仓库里向系统售卖作物 | 不填写作物名将售卖仓库种全部作物 填作物名不填数量将指定作物全部出售 |
|
||||||
| @美波理 偷菜 | 偷别人的菜 | 每人每天只能偷5次 |
|
| @美波理 偷菜 | 偷别人的菜 | 每人每天只能偷5次 |
|
||||||
| 购买农场币 | 将真寻金币兑换成农场币 | 兑换比例默认为1:2 手续费默认20% |
|
| 购买农场币 | 将真寻金币兑换成农场币 | 兑换比例默认为1:2 手续费默认20% |
|
||||||
|
| 更改农场名 [新的农场名] | 改名 | 农场名称无法存储特殊字符 |
|
||||||
|
| 农场签到 | 签到 | 需要注意,该项会从服务器拉取签到数据 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 更新日志[(详细)](./log/log.md):
|
||||||
|
用户方面
|
||||||
|
---
|
||||||
|
- 新增种子商店筛选功能(如果没有BUG的话后续我的种子、我的作物等也会加入筛选功能
|
||||||
|
- 新增签到功能(测试
|
||||||
|
- 新增农场详述功能,能通过该功能更加详细的观看农场数据
|
||||||
|
- 修复了迁移旧数据库无法正常迁移的BUG
|
||||||
|
- 修复了偷菜会导致偷自己的BUG
|
||||||
|
- 修正了作物阶段绘制不正确的BUG
|
||||||
|
|
||||||
|
代码方面
|
||||||
|
---
|
||||||
|
- 修正了多阶段作物成长素材计算逻辑
|
||||||
|
- 修正了作物数据库字段错乱的问题
|
||||||
|
- 作物新增offset字段,用于以后偏移坐标和大小(尚未启用,该模式有商议
|
||||||
|
---
|
||||||
|
|
||||||
## 待办事宜 `Todo` 列表
|
## 待办事宜 `Todo` 列表
|
||||||
|
|
||||||
- [x] 完善我的农场图片,例如左上角显示用户数据
|
- [x] 完善我的农场图片,例如左上角显示用户数据
|
||||||
- [x] 完善升级数据、作物数据、作物图片
|
- [ ] 完善升级数据、作物数据、作物图片
|
||||||
- [x] 签到功能
|
- [x] 签到功能
|
||||||
- [x] 添加渔场功能
|
- [ ] 添加渔场功能
|
||||||
- [x] 增加活动、交易行功能
|
- [ ] 增加活动、交易行功能
|
||||||
- [x] 增加交易行总行功能
|
- [ ] 增加交易行总行功能
|
||||||
- [x] 添加其他游戏种子素材
|
- [ ] 添加其他游戏种子素材
|
||||||
- [x] 想不到了,想到再说
|
- [ ] 想不到了,想到再说
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
from nonebot_plugin_apscheduler import scheduler
|
||||||
|
|
||||||
from zhenxun.configs.utils import Command, PluginExtraData, RegisterConfig
|
from zhenxun.configs.utils import Command, PluginExtraData, RegisterConfig
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
from zhenxun.utils.message import MessageUtils
|
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 .database.database import g_pSqlManager
|
||||||
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
|
||||||
|
from .json import g_pJsonManager
|
||||||
from .request import g_pRequestManager
|
from .request import g_pRequestManager
|
||||||
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
@@ -21,21 +23,23 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
at 开通农场
|
at 开通农场
|
||||||
我的农场
|
我的农场
|
||||||
我的农场币
|
我的农场币
|
||||||
种子商店 [页数]
|
种子商店 [筛选关键字] [页数] or [页数]
|
||||||
购买种子 [作物/种子名称] [数量]
|
购买种子 [作物/种子名称] [数量]
|
||||||
我的种子
|
我的种子
|
||||||
播种 [作物/种子名称] [数量]
|
播种 [作物/种子名称] [数量] (数量不填默认将最大可能播种
|
||||||
收获
|
收获
|
||||||
铲除
|
铲除
|
||||||
我的作物
|
我的作物
|
||||||
出售作物 [作物/种子名称] [数量]
|
出售作物 [作物/种子名称] [数量] (不填写作物名将售卖仓库种全部作物 填作物名不填数量将指定作物全部出售
|
||||||
偷菜 at
|
偷菜 at (每人每天只能偷5次
|
||||||
开垦
|
开垦
|
||||||
购买农场币 [数量] 数量为消耗金币的数量
|
购买农场币 [数量] 数量为消耗金币的数量
|
||||||
|
更改农场名 [新农场名]
|
||||||
|
农场签到
|
||||||
""".strip(),
|
""".strip(),
|
||||||
extra=PluginExtraData(
|
extra=PluginExtraData(
|
||||||
author="Art_Sakura",
|
author="Art_Sakura",
|
||||||
version="1.0",
|
version="1.4",
|
||||||
commands=[Command(command="我的农场")],
|
commands=[Command(command="我的农场")],
|
||||||
menu_type="群内小游戏",
|
menu_type="群内小游戏",
|
||||||
configs=[
|
configs=[
|
||||||
@@ -78,7 +82,24 @@ 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():
|
||||||
await g_pSqlManager.cleanup()
|
await g_pSqlManager.cleanup()
|
||||||
|
|
||||||
|
await g_pDBService.cleanup()
|
||||||
|
|
||||||
|
|
||||||
|
@scheduler.scheduled_job(
|
||||||
|
trigger="cron",
|
||||||
|
hour=0,
|
||||||
|
minute=30,
|
||||||
|
id="signInFile"
|
||||||
|
)
|
||||||
|
async def signInFile():
|
||||||
|
try:
|
||||||
|
await g_pJsonManager.initSignInFile()
|
||||||
|
except:
|
||||||
|
logger.info("农场签到文件下载失败!")
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
|
import inspect
|
||||||
|
|
||||||
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, AlconnaQuery, Args, Arparma, At,
|
Arparma, At, Match, MultiVar, Option,
|
||||||
Match, MultiVar, Option, Query, Subcommand,
|
Query, Subcommand, on_alconna, store_true)
|
||||||
on_alconna, store_true)
|
|
||||||
from nonebot_plugin_uninfo import Uninfo
|
from nonebot_plugin_uninfo import Uninfo
|
||||||
from nonebot_plugin_waiter import waiter
|
from nonebot_plugin_waiter import waiter
|
||||||
|
|
||||||
|
from zhenxun.configs.config import BotConfig
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
from zhenxun.utils.depends import UserName
|
|
||||||
from zhenxun.utils.message import MessageUtils
|
from zhenxun.utils.message import MessageUtils
|
||||||
|
|
||||||
from .database import g_pSqlManager
|
from .config import g_bSignStatus
|
||||||
|
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 .json import g_pJsonManager
|
||||||
|
from .tool import g_pToolManager
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@@ -34,50 +38,142 @@ diuse_register = on_alconna(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@diuse_register.handle()
|
@diuse_register.handle()
|
||||||
async def _(session: Uninfo):
|
async def handle_register(session: Uninfo):
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
user = await g_pDBService.user.getUserInfoByUid(uid)
|
||||||
user = await g_pSqlManager.getUserInfoByUid(uid)
|
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
await MessageUtils.build_message("你已经有啦").send(reply_to=True)
|
await MessageUtils.build_message("🎉 您已经开通农场啦~").send(reply_to=True)
|
||||||
else:
|
return
|
||||||
aaa = await g_pSqlManager.initUserInfoByUid(uid, str(session.user.name), 0, 100)
|
|
||||||
|
|
||||||
await MessageUtils.build_message(str(aaa)).send(reply_to=True)
|
try:
|
||||||
|
raw_name = str(session.user.name)
|
||||||
|
safe_name = sanitize_username(raw_name)
|
||||||
|
|
||||||
|
# 初始化用户信息
|
||||||
|
success = await g_pDBService.user.initUserInfoByUid(
|
||||||
|
uid=uid,
|
||||||
|
name=safe_name,
|
||||||
|
exp=0,
|
||||||
|
point=500
|
||||||
|
)
|
||||||
|
|
||||||
|
msg = (
|
||||||
|
"✅ 农场开通成功!\n💼 初始资金:500农场币"
|
||||||
|
if success
|
||||||
|
else "⚠️ 开通失败,请稍后再试"
|
||||||
|
)
|
||||||
|
logger.info(f"用户注册 {'成功' if success else '失败'}:{uid}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
msg = "⚠️ 系统繁忙,请稍后再试"
|
||||||
|
logger.error(f"注册异常 | UID:{uid} | 错误:{str(e)}")
|
||||||
|
|
||||||
|
await MessageUtils.build_message(msg).send(reply_to=True)
|
||||||
|
|
||||||
|
def sanitize_username(username: str, max_length: int = 15) -> str:
|
||||||
|
"""
|
||||||
|
安全处理用户名
|
||||||
|
功能:
|
||||||
|
1. 移除首尾空白
|
||||||
|
2. 过滤危险字符
|
||||||
|
3. 转义单引号
|
||||||
|
4. 处理空值
|
||||||
|
5. 限制长度
|
||||||
|
"""
|
||||||
|
# 处理空值
|
||||||
|
if not username:
|
||||||
|
return "神秘农夫"
|
||||||
|
|
||||||
|
# 基础清洗
|
||||||
|
cleaned = username.strip()
|
||||||
|
|
||||||
|
# 允许的字符白名单(可自定义扩展)
|
||||||
|
safe_chars = {
|
||||||
|
'_', '-', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
|
||||||
|
'+', '=', '.', ',', '~', '·', ' ',
|
||||||
|
'a','b','c','d','e','f','g','h','i','j','k','l','m',
|
||||||
|
'n','o','p','q','r','s','t','u','v','w','x','y','z',
|
||||||
|
'A','B','C','D','E','F','G','H','I','J','K','L','M',
|
||||||
|
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
|
||||||
|
'0','1','2','3','4','5','6','7','8','9',
|
||||||
|
}
|
||||||
|
# 添加常用中文字符(Unicode范围)
|
||||||
|
safe_chars.update(chr(c) for c in range(0x4E00, 0x9FFF+1))
|
||||||
|
|
||||||
|
# 过滤危险字符
|
||||||
|
filtered = [
|
||||||
|
c if c in safe_chars or 0x4E00 <= ord(c) <= 0x9FFF
|
||||||
|
else ''
|
||||||
|
for c in cleaned
|
||||||
|
]
|
||||||
|
|
||||||
|
# 合并处理结果
|
||||||
|
safe_str = ''.join(filtered)
|
||||||
|
|
||||||
|
# 转义单引号(双重保障)
|
||||||
|
escaped = safe_str.replace("'", "''")
|
||||||
|
|
||||||
|
# 处理空结果
|
||||||
|
if not escaped:
|
||||||
|
return "神秘农夫"
|
||||||
|
|
||||||
|
# 长度限制
|
||||||
|
return escaped[:max_length]
|
||||||
|
|
||||||
diuse_farm = on_alconna(
|
diuse_farm = on_alconna(
|
||||||
Alconna(
|
Alconna(
|
||||||
"我的农场",
|
"我的农场",
|
||||||
Option("--all", action=store_true),
|
Option("--all", action=store_true),
|
||||||
|
Subcommand("detail", help_text="农场详述"),
|
||||||
Subcommand("my-point", help_text="我的农场币"),
|
Subcommand("my-point", help_text="我的农场币"),
|
||||||
Subcommand("seed-shop", Args["num?", int], help_text="种子商店"),
|
Subcommand("seed-shop", Args["res?", MultiVar(str)], help_text="种子商店"),
|
||||||
Subcommand("buy-seed", Args["name?", str]["num?", int], help_text="购买种子"),
|
Subcommand("buy-seed", Args["name?", str]["num?", int], help_text="购买种子"),
|
||||||
Subcommand("my-seed", help_text="我的种子"),
|
Subcommand("my-seed", help_text="我的种子"),
|
||||||
Subcommand("sowing", Args["name?", str]["num?", int], help_text="播种"),
|
Subcommand("sowing", Args["name?", str]["num?", int], help_text="播种"),
|
||||||
Subcommand("harvest", help_text="收获"),
|
Subcommand("harvest", help_text="收获"),
|
||||||
Subcommand("eradicate", help_text="铲除"),
|
Subcommand("eradicate", help_text="铲除"),
|
||||||
Subcommand("my-plant", help_text="我的作物"),
|
Subcommand("my-plant", help_text="我的作物"),
|
||||||
# Subcommand("reclamation", Args["isBool?", str], help_text="开垦"),
|
|
||||||
Subcommand("sell-plant", Args["name?", str]["num?", int], help_text="出售作物"),
|
Subcommand("sell-plant", Args["name?", str]["num?", int], help_text="出售作物"),
|
||||||
Subcommand("stealing", Args["target?", At], help_text="偷菜"),
|
Subcommand("stealing", Args["target?", At], help_text="偷菜"),
|
||||||
Subcommand("buy-point", Args["num?", int], help_text="购买农场币"),
|
Subcommand("buy-point", Args["num?", int], help_text="购买农场币"),
|
||||||
#Subcommand("sell-point", Args["num?", int], help_text="转换金币")
|
#Subcommand("sell-point", Args["num?", int], help_text="转换金币")
|
||||||
|
Subcommand("change-name", Args["name?", str], help_text="更改农场名"),
|
||||||
|
Subcommand("sign-in", help_text="农场签到"),
|
||||||
|
Subcommand("admin-up", Args["num?", int], help_text="农场下阶段"),
|
||||||
),
|
),
|
||||||
priority=5,
|
priority=5,
|
||||||
block=True,
|
block=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@diuse_farm.assign("$main")
|
@diuse_farm.assign("$main")
|
||||||
async def _(session: Uninfo, nickname: str = UserName()):
|
async def _(session: Uninfo):
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
|
||||||
if await isRegisteredByUid(uid) == False:
|
if not await isRegisteredByUid(uid):
|
||||||
return
|
return
|
||||||
|
|
||||||
image = await g_pFarmManager.drawFarmByUid(uid, nickname)
|
image = await g_pFarmManager.drawFarmByUid(uid)
|
||||||
await MessageUtils.build_message(image).send(reply_to=True)
|
await MessageUtils.build_message(image).send(reply_to=True)
|
||||||
|
|
||||||
|
diuse_farm.shortcut(
|
||||||
|
"农场详述",
|
||||||
|
command="我的农场",
|
||||||
|
arguments=["detail"],
|
||||||
|
prefix=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@diuse_farm.assign("detail")
|
||||||
|
async def _(session: Uninfo):
|
||||||
|
uid = str(session.user.id)
|
||||||
|
|
||||||
|
if not await isRegisteredByUid(uid):
|
||||||
|
return
|
||||||
|
|
||||||
|
info = await g_pFarmManager.drawDetailFarmByUid(uid)
|
||||||
|
|
||||||
|
await MessageUtils.alc_forward_msg([info], session.self_id, BotConfig.self_nickname).send()
|
||||||
|
|
||||||
diuse_farm.shortcut(
|
diuse_farm.shortcut(
|
||||||
"我的农场币",
|
"我的农场币",
|
||||||
command="我的农场",
|
command="我的农场",
|
||||||
@@ -88,7 +184,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()
|
||||||
@@ -104,13 +200,35 @@ diuse_farm.shortcut(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@diuse_farm.assign("seed-shop")
|
@diuse_farm.assign("seed-shop")
|
||||||
async def _(session: Uninfo, num: Query[int] = AlconnaQuery("num", 1)):
|
async def _(session: Uninfo, res: Match[tuple[str, ...]]):
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
|
||||||
if await isRegisteredByUid(uid) == False:
|
if not await isRegisteredByUid(uid):
|
||||||
return
|
return
|
||||||
|
|
||||||
image = await g_pShopManager.getSeedShopImage(num.result)
|
if res.result is inspect._empty:
|
||||||
|
raw = []
|
||||||
|
else:
|
||||||
|
raw = res.result
|
||||||
|
|
||||||
|
filterKey: str | int | None = None
|
||||||
|
page: int = 1
|
||||||
|
|
||||||
|
if len(raw) >= 1 and raw[0] is not None:
|
||||||
|
first = raw[0]
|
||||||
|
if isinstance(first, str) and first.isdigit():
|
||||||
|
page = int(first)
|
||||||
|
else:
|
||||||
|
filterKey = first
|
||||||
|
|
||||||
|
if len(raw) >= 2 and raw[1] is not None and isinstance(raw[1], str) and raw[1].isdigit():
|
||||||
|
page = int(raw[1])
|
||||||
|
|
||||||
|
if filterKey is None:
|
||||||
|
image = await g_pShopManager.getSeedShopImage(page)
|
||||||
|
else:
|
||||||
|
image = await g_pShopManager.getSeedShopImage(filterKey, page)
|
||||||
|
|
||||||
await MessageUtils.build_message(image).send()
|
await MessageUtils.build_message(image).send()
|
||||||
|
|
||||||
diuse_farm.shortcut(
|
diuse_farm.shortcut(
|
||||||
@@ -121,7 +239,7 @@ diuse_farm.shortcut(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@diuse_farm.assign("buy-seed")
|
@diuse_farm.assign("buy-seed")
|
||||||
async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", 1),):
|
async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", 1)):
|
||||||
if not name.available:
|
if not name.available:
|
||||||
await MessageUtils.build_message(
|
await MessageUtils.build_message(
|
||||||
"请在指令后跟需要购买的种子名称"
|
"请在指令后跟需要购买的种子名称"
|
||||||
@@ -129,7 +247,7 @@ async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("n
|
|||||||
|
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
|
||||||
if await isRegisteredByUid(uid) == False:
|
if not await isRegisteredByUid(uid):
|
||||||
return
|
return
|
||||||
|
|
||||||
result = await g_pShopManager.buySeed(uid, name.result, num.result)
|
result = await g_pShopManager.buySeed(uid, name.result, num.result)
|
||||||
@@ -146,7 +264,7 @@ diuse_farm.shortcut(
|
|||||||
async def _(session: Uninfo):
|
async def _(session: Uninfo):
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
|
||||||
if await isRegisteredByUid(uid) == False:
|
if not await isRegisteredByUid(uid):
|
||||||
return
|
return
|
||||||
|
|
||||||
result = await g_pFarmManager.getUserSeedByUid(uid)
|
result = await g_pFarmManager.getUserSeedByUid(uid)
|
||||||
@@ -160,7 +278,7 @@ diuse_farm.shortcut(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@diuse_farm.assign("sowing")
|
@diuse_farm.assign("sowing")
|
||||||
async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", -1),):
|
async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", -1)):
|
||||||
if not name.available:
|
if not name.available:
|
||||||
await MessageUtils.build_message(
|
await MessageUtils.build_message(
|
||||||
"请在指令后跟需要播种的种子名称"
|
"请在指令后跟需要播种的种子名称"
|
||||||
@@ -168,7 +286,7 @@ async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("n
|
|||||||
|
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
|
||||||
if await isRegisteredByUid(uid) == False:
|
if not await isRegisteredByUid(uid):
|
||||||
return
|
return
|
||||||
|
|
||||||
result = await g_pFarmManager.sowing(uid, name.result, num.result)
|
result = await g_pFarmManager.sowing(uid, name.result, num.result)
|
||||||
@@ -186,7 +304,7 @@ diuse_farm.shortcut(
|
|||||||
async def _(session: Uninfo):
|
async def _(session: Uninfo):
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
|
||||||
if await isRegisteredByUid(uid) == False:
|
if not await isRegisteredByUid(uid):
|
||||||
return
|
return
|
||||||
|
|
||||||
result = await g_pFarmManager.harvest(uid)
|
result = await g_pFarmManager.harvest(uid)
|
||||||
@@ -203,7 +321,7 @@ diuse_farm.shortcut(
|
|||||||
async def _(session: Uninfo):
|
async def _(session: Uninfo):
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
|
||||||
if await isRegisteredByUid(uid) == False:
|
if not await isRegisteredByUid(uid):
|
||||||
return
|
return
|
||||||
|
|
||||||
result = await g_pFarmManager.eradicate(uid)
|
result = await g_pFarmManager.eradicate(uid)
|
||||||
@@ -221,7 +339,7 @@ diuse_farm.shortcut(
|
|||||||
async def _(session: Uninfo):
|
async def _(session: Uninfo):
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
|
||||||
if await isRegisteredByUid(uid) == False:
|
if not await isRegisteredByUid(uid):
|
||||||
return
|
return
|
||||||
|
|
||||||
result = await g_pFarmManager.getUserPlantByUid(uid)
|
result = await g_pFarmManager.getUserPlantByUid(uid)
|
||||||
@@ -238,7 +356,7 @@ reclamation = on_alconna(
|
|||||||
async def _(session: Uninfo):
|
async def _(session: Uninfo):
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
|
||||||
if await isRegisteredByUid(uid) == False:
|
if not await isRegisteredByUid(uid):
|
||||||
return
|
return
|
||||||
|
|
||||||
condition = await g_pFarmManager.reclamationCondition(uid)
|
condition = await g_pFarmManager.reclamationCondition(uid)
|
||||||
@@ -267,15 +385,10 @@ diuse_farm.shortcut(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@diuse_farm.assign("sell-plant")
|
@diuse_farm.assign("sell-plant")
|
||||||
async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", 1),):
|
async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", -1)):
|
||||||
if not name.available:
|
|
||||||
await MessageUtils.build_message(
|
|
||||||
"请在指令后跟需要出售的作物名称"
|
|
||||||
).finish(reply_to=True)
|
|
||||||
|
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
|
||||||
if await isRegisteredByUid(uid) == False:
|
if not await isRegisteredByUid(uid):
|
||||||
return
|
return
|
||||||
|
|
||||||
result = await g_pShopManager.sellPlantByUid(uid, name.result, num.result)
|
result = await g_pShopManager.sellPlantByUid(uid, name.result, num.result)
|
||||||
@@ -292,16 +405,16 @@ diuse_farm.shortcut(
|
|||||||
async def _(session: Uninfo, target: Match[At]):
|
async def _(session: Uninfo, target: Match[At]):
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
|
||||||
if await isRegisteredByUid(uid) == False:
|
if not await isRegisteredByUid(uid):
|
||||||
return
|
return
|
||||||
|
|
||||||
if not target.available:
|
if not target.available:
|
||||||
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
|
||||||
|
|
||||||
@@ -324,8 +437,114 @@ async def _(session: Uninfo, num: Query[int] = AlconnaQuery("num", 0)):
|
|||||||
|
|
||||||
uid = str(session.user.id)
|
uid = str(session.user.id)
|
||||||
|
|
||||||
if await isRegisteredByUid(uid) == False:
|
if not await isRegisteredByUid(uid):
|
||||||
return
|
return
|
||||||
|
|
||||||
result = await g_pFarmManager.buyPointByUid(uid, num.result)
|
result = await g_pFarmManager.buyPointByUid(uid, num.result)
|
||||||
await MessageUtils.build_message(result).send(reply_to=True)
|
await MessageUtils.build_message(result).send(reply_to=True)
|
||||||
|
|
||||||
|
|
||||||
|
diuse_farm.shortcut(
|
||||||
|
"更改农场名(?P<name>)",
|
||||||
|
command="我的农场",
|
||||||
|
arguments=["change-name", "{name}"],
|
||||||
|
prefix=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@diuse_farm.assign("change-name")
|
||||||
|
async def _(session: Uninfo, name: Match[str]):
|
||||||
|
if not name.available:
|
||||||
|
await MessageUtils.build_message(
|
||||||
|
"请在指令后跟需要更改的农场名"
|
||||||
|
).finish(reply_to=True)
|
||||||
|
|
||||||
|
uid = str(session.user.id)
|
||||||
|
|
||||||
|
if not await isRegisteredByUid(uid):
|
||||||
|
return
|
||||||
|
|
||||||
|
safeName = sanitize_username(name.result)
|
||||||
|
|
||||||
|
if safeName == "神秘农夫":
|
||||||
|
await MessageUtils.build_message("农场名不支持特殊符号!").send(reply_to=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
result = await g_pDBService.user.updateUserNameByUid(uid, safeName)
|
||||||
|
|
||||||
|
if result == True:
|
||||||
|
await MessageUtils.build_message("更新农场名成功!").send(reply_to=True)
|
||||||
|
else:
|
||||||
|
await MessageUtils.build_message("更新农场名失败!").send(reply_to=True)
|
||||||
|
|
||||||
|
diuse_farm.shortcut(
|
||||||
|
"农场签到",
|
||||||
|
command="我的农场",
|
||||||
|
arguments=["sign-in"],
|
||||||
|
prefix=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@diuse_farm.assign("sign-in")
|
||||||
|
async def _(session: Uninfo):
|
||||||
|
uid = str(session.user.id)
|
||||||
|
|
||||||
|
if not await isRegisteredByUid(uid):
|
||||||
|
return
|
||||||
|
|
||||||
|
#判断签到是否正常加载
|
||||||
|
if not g_bSignStatus:
|
||||||
|
await MessageUtils.build_message("签到功能异常!").send()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
toDay = g_pToolManager.dateTime().date().today()
|
||||||
|
message = ""
|
||||||
|
status = await g_pDBService.userSign.sign(uid, toDay.strftime("%Y-%m-%d"))
|
||||||
|
|
||||||
|
#如果完成签到
|
||||||
|
if status == 1 or status == 2:
|
||||||
|
#获取签到总天数
|
||||||
|
signDay = await g_pDBService.userSign.getUserSignCountByDate(uid, toDay.strftime("%Y-%m"))
|
||||||
|
exp, point = await g_pDBService.userSign.getUserSignRewardByDate(uid, toDay.strftime("%Y-%m-%d"))
|
||||||
|
|
||||||
|
message += f"签到成功!累计签到天数:{signDay}\n获得经验{exp},获得金币{point}"
|
||||||
|
|
||||||
|
reward = g_pJsonManager.m_pSign['continuou'].get(f"{signDay}", None)
|
||||||
|
|
||||||
|
if reward:
|
||||||
|
extraPoint = reward.get('point', 0)
|
||||||
|
extraExp = reward.get('exp', 0)
|
||||||
|
|
||||||
|
plant = reward.get('plant', {})
|
||||||
|
|
||||||
|
message += f"\n\n成功领取累计签到奖励:\n额外获得经验{extraExp},额外获得金币{extraPoint}"
|
||||||
|
|
||||||
|
vipPoint = reward.get('vipPoint', 0)
|
||||||
|
|
||||||
|
if vipPoint > 0:
|
||||||
|
message += f",额外获得点券{vipPoint}"
|
||||||
|
|
||||||
|
if plant:
|
||||||
|
for key, value in plant.items():
|
||||||
|
message += f"\n获得{key}种子 * {value}"
|
||||||
|
else:
|
||||||
|
message = "签到失败!未知错误"
|
||||||
|
|
||||||
|
await MessageUtils.build_message(message).send()
|
||||||
|
|
||||||
|
# await MessageUtils.alc_forward_msg([info], session.self_id, BotConfig.self_nickname).send(reply_to=True)
|
||||||
|
|
||||||
|
diuse_farm.shortcut(
|
||||||
|
"农场下阶段(.*?)",
|
||||||
|
command="我的农场",
|
||||||
|
arguments=["admin-up"],
|
||||||
|
prefix=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@diuse_farm.assign("admin-up")
|
||||||
|
async def _(session: Uninfo, num: Query[int] = AlconnaQuery("num", 0)):
|
||||||
|
uid = str(session.user.id)
|
||||||
|
|
||||||
|
if not await isRegisteredByUid(uid):
|
||||||
|
return
|
||||||
|
|
||||||
|
await g_pDBService.userSoil.nextPhase(uid, num.result)
|
||||||
|
|||||||
@@ -1,106 +1,16 @@
|
|||||||
import json
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from zhenxun.configs.path_config import DATA_PATH
|
from zhenxun.configs.path_config import DATA_PATH
|
||||||
from zhenxun.services.log import logger
|
|
||||||
|
g_bIsDebug = False
|
||||||
|
|
||||||
g_sDBPath = DATA_PATH / "farm_db"
|
g_sDBPath = DATA_PATH / "farm_db"
|
||||||
g_sDBFilePath = DATA_PATH / "farm_db/farm.db"
|
g_sDBFilePath = DATA_PATH / "farm_db/farm.db"
|
||||||
|
|
||||||
g_sResourcePath = Path(__file__).resolve().parent / "resource"
|
g_sResourcePath = Path(__file__).resolve().parent / "resource"
|
||||||
|
g_sPlantPath = g_sResourcePath / "db/plant.db"
|
||||||
|
|
||||||
class CJsonManager:
|
g_sConfigPath = Path(__file__).resolve().parent / "config"
|
||||||
def __init__(self):
|
g_sSignInPath = g_sConfigPath / "sign_in.json"
|
||||||
self.m_pItem = {}
|
|
||||||
self.m_pPlant = {}
|
|
||||||
self.m_pLevel = {}
|
|
||||||
self.m_pSoil = {}
|
|
||||||
|
|
||||||
async def init(self) -> bool:
|
g_bSignStatus = True
|
||||||
if not await self.initItem():
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not await self.initPlant():
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not await self.initLevel():
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not await self.initSoil():
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
async def initItem(self) -> bool:
|
|
||||||
current_file_path = Path(__file__)
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(
|
|
||||||
current_file_path.resolve().parent / "config/item.json",
|
|
||||||
encoding="utf-8",
|
|
||||||
) as file:
|
|
||||||
self.m_pItem = json.load(file)
|
|
||||||
|
|
||||||
return True
|
|
||||||
except FileNotFoundError:
|
|
||||||
logger.warning("item.json 打开失败")
|
|
||||||
return False
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
logger.warning(f"item.json JSON格式错误: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def initPlant(self) -> bool:
|
|
||||||
current_file_path = Path(__file__)
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(
|
|
||||||
current_file_path.resolve().parent / "config/plant.json",
|
|
||||||
encoding="utf-8",
|
|
||||||
) as file:
|
|
||||||
self.m_pPlant = json.load(file)
|
|
||||||
|
|
||||||
return True
|
|
||||||
except FileNotFoundError:
|
|
||||||
logger.warning("plant.json 打开失败")
|
|
||||||
return False
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
logger.warning(f"plant.json JSON格式错误: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def initLevel(self) -> bool:
|
|
||||||
current_file_path = Path(__file__)
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(
|
|
||||||
current_file_path.resolve().parent / "config/level.json",
|
|
||||||
encoding="utf-8",
|
|
||||||
) as file:
|
|
||||||
self.m_pLevel = json.load(file)
|
|
||||||
|
|
||||||
return True
|
|
||||||
except FileNotFoundError:
|
|
||||||
logger.warning("level.json 打开失败")
|
|
||||||
return False
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
logger.warning(f"level.json JSON格式错误: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def initSoil(self) -> bool:
|
|
||||||
current_file_path = Path(__file__)
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(
|
|
||||||
current_file_path.resolve().parent / "config/soil.json",
|
|
||||||
encoding="utf-8",
|
|
||||||
) as file:
|
|
||||||
self.m_pSoil = json.load(file)
|
|
||||||
|
|
||||||
return True
|
|
||||||
except FileNotFoundError:
|
|
||||||
logger.warning("soil.json 打开失败")
|
|
||||||
return False
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
logger.warning(f"soil.json JSON格式错误: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
g_pJsonManager = CJsonManager()
|
|
||||||
|
|||||||
@@ -8,80 +8,164 @@
|
|||||||
{
|
{
|
||||||
"4":
|
"4":
|
||||||
{
|
{
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"point": 800,
|
"point": 800,
|
||||||
"item": []
|
"item": []
|
||||||
},
|
},
|
||||||
"5":
|
"5":
|
||||||
{
|
{
|
||||||
"level": 3,
|
"level": 3,
|
||||||
"point": 1300,
|
"point": 1300,
|
||||||
"item": []
|
"item": []
|
||||||
},
|
},
|
||||||
"6":
|
"6":
|
||||||
{
|
{
|
||||||
"level": 5,
|
"level": 5,
|
||||||
"point": 3200,
|
"point": 3200,
|
||||||
"item": []
|
"item": []
|
||||||
},
|
},
|
||||||
"7":
|
"7":
|
||||||
{
|
{
|
||||||
"level": 7,
|
"level": 7,
|
||||||
"point": 5500,
|
"point": 5500,
|
||||||
"item": []
|
"item": []
|
||||||
},
|
},
|
||||||
"8":
|
"8":
|
||||||
{
|
{
|
||||||
"level": 9,
|
"level": 9,
|
||||||
"point": 12000,
|
"point": 12000,
|
||||||
"item": []
|
"item": []
|
||||||
},
|
},
|
||||||
"9":
|
"9":
|
||||||
{
|
{
|
||||||
"level": 15,
|
"level": 11,
|
||||||
"point": 19800,
|
"point": 19800,
|
||||||
"item": []
|
"item": []
|
||||||
},
|
},
|
||||||
"10":
|
"10":
|
||||||
{
|
{
|
||||||
"level":3,
|
"level": 13,
|
||||||
"point": 3000,
|
"point": 30000,
|
||||||
"item": []
|
"item": []
|
||||||
},
|
},
|
||||||
"11":
|
"11":
|
||||||
{
|
{
|
||||||
"level":3,
|
"level": 15,
|
||||||
"point": 3000,
|
"point": 50000,
|
||||||
"item": []
|
"item": []
|
||||||
},
|
},
|
||||||
"12":
|
"12":
|
||||||
{
|
{
|
||||||
"level":3,
|
"level": 17,
|
||||||
"point": 3000,
|
"point": 70000,
|
||||||
"item": []
|
"item": []
|
||||||
},
|
},
|
||||||
"13":
|
"13":
|
||||||
{
|
{
|
||||||
"level":3,
|
"level": 19,
|
||||||
"point": 3000,
|
"point": 90000,
|
||||||
"item": []
|
"item": []
|
||||||
},
|
},
|
||||||
"14":
|
"14":
|
||||||
{
|
{
|
||||||
"level":3,
|
"level": 21,
|
||||||
"point": 3000,
|
"point": 130000,
|
||||||
"item": []
|
"item": []
|
||||||
},
|
},
|
||||||
"15":
|
"15":
|
||||||
{
|
{
|
||||||
"level":3,
|
"level": 23,
|
||||||
"point": 3000,
|
"point": 150000,
|
||||||
"item": []
|
"item": []
|
||||||
},
|
},
|
||||||
"16":
|
"16":
|
||||||
{
|
{
|
||||||
"level":3,
|
"level": 25,
|
||||||
"point": 3000,
|
"point": 180000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"17":
|
||||||
|
{
|
||||||
|
"level": 27,
|
||||||
|
"point": 230000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"18":
|
||||||
|
{
|
||||||
|
"level": 29,
|
||||||
|
"point": 300000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"19":
|
||||||
|
{
|
||||||
|
"level": 31,
|
||||||
|
"point": 500000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"20":
|
||||||
|
{
|
||||||
|
"level": 33,
|
||||||
|
"point": 850000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"21":
|
||||||
|
{
|
||||||
|
"level": 36,
|
||||||
|
"point": 1100000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"22":
|
||||||
|
{
|
||||||
|
"level": 39,
|
||||||
|
"point": 1300000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"23":
|
||||||
|
{
|
||||||
|
"level": 42,
|
||||||
|
"point": 1500000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"24":
|
||||||
|
{
|
||||||
|
"level": 45,
|
||||||
|
"point": 1700000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"25":
|
||||||
|
{
|
||||||
|
"level": 48,
|
||||||
|
"point": 2000000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"26":
|
||||||
|
{
|
||||||
|
"level": 51,
|
||||||
|
"point": 2200000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"27":
|
||||||
|
{
|
||||||
|
"level": 54,
|
||||||
|
"point": 2800000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"28":
|
||||||
|
{
|
||||||
|
"level": 57,
|
||||||
|
"point": 3500000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"29":
|
||||||
|
{
|
||||||
|
"level": 60,
|
||||||
|
"point": 4300000,
|
||||||
|
"item": []
|
||||||
|
},
|
||||||
|
"30":
|
||||||
|
{
|
||||||
|
"level": 70,
|
||||||
|
"point": 5200000,
|
||||||
"item": []
|
"item": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 0.01,
|
|
||||||
"zhuShi":
|
|
||||||
[
|
|
||||||
"level: 解锁等级",
|
|
||||||
"limit: 限制等级 0:普通土地 1:红土地 2:黄土地 3:黑土地",
|
|
||||||
"experience: 收获经验",
|
|
||||||
"harvest: 收获数量",
|
|
||||||
"price: 单价",
|
|
||||||
"time: 成熟时间 小时",
|
|
||||||
"crop: 作物可以收几次",
|
|
||||||
"again: 再次成熟时间 单位:小时",
|
|
||||||
"phase: 阶段 目前为 成熟时间 / 阶段 来显示每阶段图片",
|
|
||||||
"general: 第一阶段是否为通用阶段素材",
|
|
||||||
"sell: 是否可以上架交易行"
|
|
||||||
],
|
|
||||||
"plant":
|
|
||||||
{
|
|
||||||
"胡萝卜":
|
|
||||||
{
|
|
||||||
"level": 0,
|
|
||||||
"limit": 0,
|
|
||||||
"experience": 18,
|
|
||||||
"harvest": 17,
|
|
||||||
"price": 21,
|
|
||||||
"time": 13,
|
|
||||||
"crop": 1,
|
|
||||||
"again": 0,
|
|
||||||
"phase": 5,
|
|
||||||
"general": true,
|
|
||||||
"sell": false
|
|
||||||
},
|
|
||||||
"白萝卜":
|
|
||||||
{
|
|
||||||
"level": 0,
|
|
||||||
"limit": 0,
|
|
||||||
"experience": 15,
|
|
||||||
"harvest": 16,
|
|
||||||
"price": 17,
|
|
||||||
"time": 10,
|
|
||||||
"crop": 1,
|
|
||||||
"again": 0,
|
|
||||||
"phase": 5,
|
|
||||||
"general": true,
|
|
||||||
"sell": false
|
|
||||||
},
|
|
||||||
"牧草":
|
|
||||||
{
|
|
||||||
"level": 0,
|
|
||||||
"limit": 0,
|
|
||||||
"experience": 10,
|
|
||||||
"harvest": 25,
|
|
||||||
"price": 6,
|
|
||||||
"time": 8,
|
|
||||||
"crop": 1,
|
|
||||||
"again": 0,
|
|
||||||
"phase": 5,
|
|
||||||
"general": true,
|
|
||||||
"sell": false
|
|
||||||
},
|
|
||||||
"大白菜":
|
|
||||||
{
|
|
||||||
"level": 1,
|
|
||||||
"limit": 0,
|
|
||||||
"experience": 19,
|
|
||||||
"harvest": 17,
|
|
||||||
"price": 22,
|
|
||||||
"time": 14,
|
|
||||||
"crop": 1,
|
|
||||||
"again": 0,
|
|
||||||
"phase": 5,
|
|
||||||
"general": true,
|
|
||||||
"sell": false
|
|
||||||
},
|
|
||||||
"大蒜":
|
|
||||||
{
|
|
||||||
"level": 1,
|
|
||||||
"limit": 0,
|
|
||||||
"experience": 19,
|
|
||||||
"harvest": 17,
|
|
||||||
"price": 22,
|
|
||||||
"time": 14,
|
|
||||||
"crop": 1,
|
|
||||||
"again": 0,
|
|
||||||
"phase": 6,
|
|
||||||
"general": true,
|
|
||||||
"sell": false
|
|
||||||
},
|
|
||||||
"水稻":
|
|
||||||
{
|
|
||||||
"level": 2,
|
|
||||||
"limit": 0,
|
|
||||||
"experience": 19,
|
|
||||||
"harvest": 18,
|
|
||||||
"price": 21,
|
|
||||||
"time": 14,
|
|
||||||
"crop": 1,
|
|
||||||
"again": 0,
|
|
||||||
"phase": 6,
|
|
||||||
"general": true,
|
|
||||||
"sell": false
|
|
||||||
},
|
|
||||||
"小麦":
|
|
||||||
{
|
|
||||||
"level": 2,
|
|
||||||
"limit": 0,
|
|
||||||
"experience": 19,
|
|
||||||
"harvest": 18,
|
|
||||||
"price": 21,
|
|
||||||
"time": 14,
|
|
||||||
"crop": 1,
|
|
||||||
"again": 0,
|
|
||||||
"phase": 6,
|
|
||||||
"general": true,
|
|
||||||
"sell": false
|
|
||||||
},
|
|
||||||
"玉米":
|
|
||||||
{
|
|
||||||
"level": 3,
|
|
||||||
"limit": 0,
|
|
||||||
"experience": 19,
|
|
||||||
"harvest": 17,
|
|
||||||
"price": 23,
|
|
||||||
"time": 14,
|
|
||||||
"crop": 1,
|
|
||||||
"again": 0,
|
|
||||||
"phase": 6,
|
|
||||||
"general": true,
|
|
||||||
"sell": false
|
|
||||||
},
|
|
||||||
"油菜":
|
|
||||||
{
|
|
||||||
"level": 4,
|
|
||||||
"limit": 0,
|
|
||||||
"experience": 29,
|
|
||||||
"harvest": 23,
|
|
||||||
"price": 24,
|
|
||||||
"time": 17,
|
|
||||||
"crop": 1,
|
|
||||||
"again": 0,
|
|
||||||
"phase": 5,
|
|
||||||
"general": true,
|
|
||||||
"sell": false
|
|
||||||
},
|
|
||||||
"生菜":
|
|
||||||
{
|
|
||||||
"level": 4,
|
|
||||||
"limit": 0,
|
|
||||||
"experience": 25,
|
|
||||||
"harvest": 21,
|
|
||||||
"price": 24,
|
|
||||||
"time": 19,
|
|
||||||
"crop": 1,
|
|
||||||
"again": 0,
|
|
||||||
"phase": 6,
|
|
||||||
"general": true,
|
|
||||||
"sell": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"date": "202505",
|
||||||
|
"exp_max": 50,
|
||||||
|
"exp_min": 5,
|
||||||
|
"point_max": 2000,
|
||||||
|
"point_min": 200,
|
||||||
|
"continuou":
|
||||||
|
{
|
||||||
|
"1":
|
||||||
|
{
|
||||||
|
"point": 3000,
|
||||||
|
"exp": 20
|
||||||
|
},
|
||||||
|
"3":
|
||||||
|
{
|
||||||
|
"point": 5000,
|
||||||
|
"exp": 25,
|
||||||
|
"plant":
|
||||||
|
{
|
||||||
|
"胡萝卜": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"5":
|
||||||
|
{
|
||||||
|
"point": 7000,
|
||||||
|
"exp": 50,
|
||||||
|
"plant":
|
||||||
|
{
|
||||||
|
"胡萝卜": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"7":
|
||||||
|
{
|
||||||
|
"point": 7000,
|
||||||
|
"exp": 50,
|
||||||
|
"plant":
|
||||||
|
{
|
||||||
|
"胡萝卜": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"10":
|
||||||
|
{
|
||||||
|
"point": 7000,
|
||||||
|
"exp": 50,
|
||||||
|
"plant":
|
||||||
|
{
|
||||||
|
"胡萝卜": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"15":
|
||||||
|
{
|
||||||
|
"point": 7000,
|
||||||
|
"exp": 50,
|
||||||
|
"plant":
|
||||||
|
{
|
||||||
|
"胡萝卜": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"20":
|
||||||
|
{
|
||||||
|
"point": 7000,
|
||||||
|
"exp": 50,
|
||||||
|
"plant":
|
||||||
|
{
|
||||||
|
"胡萝卜": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"25":
|
||||||
|
{
|
||||||
|
"point": 7000,
|
||||||
|
"exp": 50,
|
||||||
|
"plant":
|
||||||
|
{
|
||||||
|
"胡萝卜": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,649 +0,0 @@
|
|||||||
import math
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
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)
|
|
||||||
|
|
||||||
#if bIsExist == False:
|
|
||||||
#TODO 缺少判断创建失败事件
|
|
||||||
#await cls.createDB()
|
|
||||||
|
|
||||||
await cls.checkDB()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def getColumns(cls, tableName):
|
|
||||||
""" 由AI生成
|
|
||||||
获取表的列信息
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
cursor = await cls.m_pDB.execute(f'PRAGMA table_info("{tableName}")')
|
|
||||||
columns = [row[1] for row in await cursor.fetchall()]
|
|
||||||
return columns
|
|
||||||
except aiosqlite.Error as e:
|
|
||||||
logger.error(f"获取表结构失败: {str(e)}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def ensure_table_exists(cls, tableName, columns) -> bool:
|
|
||||||
"""智能创建并分析数据库表、字段是否存在 由AI生成
|
|
||||||
|
|
||||||
Args:
|
|
||||||
tableName (_type_): 表名
|
|
||||||
columns (_type_): 字典
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
_type_: _description_
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
current_columns = await cls.getColumns(tableName)
|
|
||||||
|
|
||||||
#检查表是否存在
|
|
||||||
table_exists = bool(current_columns)
|
|
||||||
|
|
||||||
#如果表不存在,直接创建
|
|
||||||
if not table_exists:
|
|
||||||
create_sql = f'''
|
|
||||||
CREATE TABLE "{tableName}" (
|
|
||||||
{", ".join(f'"{k}" {v}' for k, v in columns.items())}
|
|
||||||
);
|
|
||||||
'''
|
|
||||||
await cls.m_pDB.execute(create_sql)
|
|
||||||
await cls.m_pDB.commit() #显式提交新建表操作
|
|
||||||
return True
|
|
||||||
|
|
||||||
#表存在时的处理
|
|
||||||
columns_to_add = []
|
|
||||||
columns_to_remove = []
|
|
||||||
|
|
||||||
#检查需要添加的列
|
|
||||||
for k, v in columns.items():
|
|
||||||
if k not in current_columns:
|
|
||||||
columns_to_add.append(f'"{k}" {v}')
|
|
||||||
|
|
||||||
#检查需要移除的列
|
|
||||||
for col in current_columns:
|
|
||||||
if col not in columns.keys():
|
|
||||||
columns_to_remove.append(col)
|
|
||||||
|
|
||||||
#执行修改
|
|
||||||
if columns_to_add or columns_to_remove:
|
|
||||||
try:
|
|
||||||
#开启事务(使用connection级别的事务控制)
|
|
||||||
await cls.m_pDB.execute('BEGIN TRANSACTION')
|
|
||||||
|
|
||||||
#添加新列
|
|
||||||
for col_def in columns_to_add:
|
|
||||||
await cls.m_pDB.execute(f'ALTER TABLE "{tableName}" ADD COLUMN {col_def}')
|
|
||||||
|
|
||||||
#删除旧列
|
|
||||||
for col in columns_to_remove:
|
|
||||||
await cls.m_pDB.execute(f'ALTER TABLE "{tableName}" DROP COLUMN "{col}"')
|
|
||||||
|
|
||||||
#显式提交事务
|
|
||||||
await cls.m_pDB.commit()
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
#回滚事务
|
|
||||||
await cls.m_pDB.rollback()
|
|
||||||
logger.error(f"表结构迁移失败: {str(e)}")
|
|
||||||
|
|
||||||
return False
|
|
||||||
except aiosqlite.Error as e:
|
|
||||||
logger.error(f"表结构迁移失败: {str(e)}")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def checkDB(cls) -> bool:
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
userStorehouse = {
|
|
||||||
"uid": "INTEGER PRIMARY KEY AUTOINCREMENT",
|
|
||||||
"item": "TEXT DEFAULT ''",
|
|
||||||
"plant": "TEXT DEFAULT ''",
|
|
||||||
"seed": "TEXT DEFAULT ''"
|
|
||||||
}
|
|
||||||
|
|
||||||
userSoilInfo = {
|
|
||||||
"uid": "INTEGER PRIMARY KEY AUTOINCREMENT",
|
|
||||||
**{f"soil{i}": "TEXT DEFAULT ''" for i in range(1, 31)}
|
|
||||||
}
|
|
||||||
|
|
||||||
await cls.ensure_table_exists("user", userInfo)
|
|
||||||
|
|
||||||
await cls.ensure_table_exists("storehouse", userStorehouse)
|
|
||||||
|
|
||||||
await cls.ensure_table_exists("soil", userSoilInfo)
|
|
||||||
|
|
||||||
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:
|
|
||||||
await cls.m_pDB.execute(command)
|
|
||||||
await cls.m_pDB.commit()
|
|
||||||
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 = 100):
|
|
||||||
"""初始化用户信息
|
|
||||||
|
|
||||||
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')
|
|
||||||
"""
|
|
||||||
|
|
||||||
#用户仓库
|
|
||||||
userStorehouse = f"""
|
|
||||||
INSERT INTO storehouse (uid) VALUES ({uid});
|
|
||||||
"""
|
|
||||||
|
|
||||||
#用户土地
|
|
||||||
userSoilInfo = f"""
|
|
||||||
INSERT INTO soil (uid) VALUES ({uid});
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not await cls.executeDB(userInfo):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not await cls.executeDB(userStorehouse):
|
|
||||||
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 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 getUserSeedByUid(cls, uid: str) -> str:
|
|
||||||
"""获取用户仓库种子信息
|
|
||||||
|
|
||||||
Args:
|
|
||||||
info (list[dict]): 用户信息
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 仓库种子信息
|
|
||||||
"""
|
|
||||||
|
|
||||||
if len(uid) <= 0:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
try:
|
|
||||||
async with cls.m_pDB.execute(f"SELECT seed FROM storehouse WHERE uid = {uid}") as cursor:
|
|
||||||
async for row in cursor:
|
|
||||||
return row[0]
|
|
||||||
|
|
||||||
return ""
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"getUserSeedByUid查询失败: {e}")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def getUserSeedByName(cls, uid: str, name: str) -> int:
|
|
||||||
"""获取用户仓库种子信息
|
|
||||||
|
|
||||||
Args:
|
|
||||||
uid (str): 用户信息
|
|
||||||
name (str): 种子名称
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 仓库种子信息
|
|
||||||
"""
|
|
||||||
|
|
||||||
if len(uid) <= 0:
|
|
||||||
return -1
|
|
||||||
|
|
||||||
try:
|
|
||||||
async with cls.m_pDB.execute(f"SELECT seed FROM storehouse WHERE uid = {uid}") as cursor:
|
|
||||||
async for row in cursor:
|
|
||||||
if row[0] == None or len(row[0]) == 0:
|
|
||||||
return -1
|
|
||||||
|
|
||||||
plantDict: Dict[str, int] = {}
|
|
||||||
for item in row[0].split(','):
|
|
||||||
if '|' in item:
|
|
||||||
seedName, count = item.split('|', 1)
|
|
||||||
plantDict[seedName] = int(count)
|
|
||||||
|
|
||||||
if name in plantDict:
|
|
||||||
return plantDict[name]
|
|
||||||
else:
|
|
||||||
return -1
|
|
||||||
|
|
||||||
return -1
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"getUserSeedByUid查询失败: {e}")
|
|
||||||
return -1
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def updateUserSeedByUid(cls, uid: str, seed: str) -> bool:
|
|
||||||
"""更新用户种子仓库
|
|
||||||
|
|
||||||
Args:
|
|
||||||
uid (str): 用户Uid
|
|
||||||
seed (str): 种子名称
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool:
|
|
||||||
"""
|
|
||||||
|
|
||||||
if len(uid) <= 0:
|
|
||||||
return False
|
|
||||||
|
|
||||||
sql = f"UPDATE storehouse SET seed = '{seed}' WHERE uid = {uid}"
|
|
||||||
|
|
||||||
return await cls.executeDB(sql)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def addUserSeedByPlant(cls, uid: str, seed: str, num: int) -> bool:
|
|
||||||
"""添加作物信息至仓库
|
|
||||||
|
|
||||||
Args:
|
|
||||||
uid (str): 用户Uid
|
|
||||||
seed (str): 种子名称
|
|
||||||
num(str): 种子数量
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool:
|
|
||||||
"""
|
|
||||||
|
|
||||||
if len(uid) <= 0:
|
|
||||||
return False
|
|
||||||
|
|
||||||
seedsDict = {}
|
|
||||||
currentSeeds = await cls.getUserSeedByUid(uid)
|
|
||||||
|
|
||||||
if currentSeeds:
|
|
||||||
for item in currentSeeds.split(','):
|
|
||||||
if item.strip():
|
|
||||||
name, count = item.split('|')
|
|
||||||
seedsDict[name.strip()] = int(count.strip())
|
|
||||||
|
|
||||||
if seed in seedsDict:
|
|
||||||
seedsDict[seed] += num
|
|
||||||
if seedsDict[seed] <= 0:
|
|
||||||
del seedsDict[seed]
|
|
||||||
else:
|
|
||||||
if num > 0:
|
|
||||||
seedsDict[seed] = num
|
|
||||||
|
|
||||||
updatedSeeds = ','.join([f"{name}|{count}" for name, count in seedsDict.items()])
|
|
||||||
|
|
||||||
sql = f"UPDATE storehouse SET seed = '{updatedSeeds}' WHERE uid = {uid}"
|
|
||||||
|
|
||||||
return await cls.executeDB(sql)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def getUserPlantByUid(cls, uid: str) -> str:
|
|
||||||
"""获取用户仓库作物信息
|
|
||||||
|
|
||||||
Args:
|
|
||||||
info (list[dict]): 用户信息
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 仓库作物信息
|
|
||||||
"""
|
|
||||||
|
|
||||||
if len(uid) <= 0:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
try:
|
|
||||||
async with cls.m_pDB.execute(f"SELECT plant FROM storehouse WHERE uid = {uid}") as cursor:
|
|
||||||
async for row in cursor:
|
|
||||||
return row[0]
|
|
||||||
|
|
||||||
return ""
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"getUserPlantByUid查询失败: {e}")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def updateUserPlantByUid(cls, uid: str, plant: str) -> bool:
|
|
||||||
"""更新用户作物仓库
|
|
||||||
|
|
||||||
Args:
|
|
||||||
uid (str): 用户Uid
|
|
||||||
plant (str): 作物名称
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool:
|
|
||||||
"""
|
|
||||||
|
|
||||||
if len(uid) <= 0:
|
|
||||||
return False
|
|
||||||
|
|
||||||
sql = f"UPDATE storehouse SET plant = '{plant}' WHERE uid = {uid}"
|
|
||||||
|
|
||||||
return await cls.executeDB(sql)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def addUserPlantByPlant(cls, uid: str, plant: str, num: int) -> bool:
|
|
||||||
"""添加作物信息至仓库
|
|
||||||
|
|
||||||
Args:
|
|
||||||
uid (str): 用户Uid
|
|
||||||
plant (str): 作物名称
|
|
||||||
num(str): 作物数量
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool:
|
|
||||||
"""
|
|
||||||
|
|
||||||
if len(uid) <= 0:
|
|
||||||
return False
|
|
||||||
|
|
||||||
plantsDict = {}
|
|
||||||
currentPlants = await cls.getUserPlantByUid(uid)
|
|
||||||
|
|
||||||
if currentPlants:
|
|
||||||
for item in currentPlants.split(','):
|
|
||||||
if item.strip():
|
|
||||||
name, count = item.split('|')
|
|
||||||
plantsDict[name.strip()] = int(count.strip())
|
|
||||||
|
|
||||||
if plant in plantsDict:
|
|
||||||
plantsDict[plant] += num
|
|
||||||
if plantsDict[plant] <= 0:
|
|
||||||
del plantsDict[plant]
|
|
||||||
else:
|
|
||||||
if num > 0:
|
|
||||||
plantsDict[plant] = num
|
|
||||||
|
|
||||||
updatedPlants = ','.join([f"{name}|{count}" for name, count in plantsDict.items()])
|
|
||||||
|
|
||||||
sql = f"UPDATE storehouse SET plant = '{updatedPlants}' WHERE uid = {uid}"
|
|
||||||
|
|
||||||
return await cls.executeDB(sql)
|
|
||||||
|
|
||||||
g_pSqlManager = CSqlManager()
|
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import math
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import aiosqlite
|
||||||
|
|
||||||
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
|
from ..config import g_sDBFilePath, g_sDBPath
|
||||||
|
|
||||||
|
|
||||||
|
class CSqlManager:
|
||||||
|
def __init__(self):
|
||||||
|
dbPath = Path(g_sDBPath)
|
||||||
|
if dbPath and not dbPath.exists():
|
||||||
|
os.makedirs(dbPath, exist_ok=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def cleanup(cls):
|
||||||
|
if hasattr(cls, "m_pDB") and cls.m_pDB:
|
||||||
|
await cls.m_pDB.close()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def init(cls) -> bool:
|
||||||
|
try:
|
||||||
|
cls.m_pDB = await aiosqlite.connect(g_sDBFilePath)
|
||||||
|
cls.m_pDB.row_factory = aiosqlite.Row
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("初始化总数据库失败", e=e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@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 not command:
|
||||||
|
logger.warning("数据库语句长度为空!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with cls._transaction():
|
||||||
|
await cls.m_pDB.execute(command)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"数据库语句执行出错: {command}", e=e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
g_pSqlManager = CSqlManager()
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
import ast
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from unittest import result
|
||||||
|
|
||||||
|
import aiosqlite
|
||||||
|
|
||||||
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
|
from ..config import g_bIsDebug, g_sPlantPath
|
||||||
|
|
||||||
|
|
||||||
|
class CPlantManager:
|
||||||
|
def __init__(self):
|
||||||
|
try:
|
||||||
|
os.mkdir(g_sPlantPath)
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def cleanup(cls):
|
||||||
|
if hasattr(cls, "m_pDB") and cls.m_pDB:
|
||||||
|
await cls.m_pDB.close()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def init(cls) -> bool:
|
||||||
|
try:
|
||||||
|
_ = os.path.exists(g_sPlantPath)
|
||||||
|
|
||||||
|
if g_bIsDebug:
|
||||||
|
cls.m_pDB = await aiosqlite.connect(str(g_sPlantPath.parent / "plant-test.db"))
|
||||||
|
else:
|
||||||
|
cls.m_pDB = await aiosqlite.connect(str(g_sPlantPath))
|
||||||
|
|
||||||
|
cls.m_pDB.row_factory = aiosqlite.Row
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("初始化植物数据库失败", e=e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@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 executeDB(cls, command: str) -> bool:
|
||||||
|
"""执行自定义SQL
|
||||||
|
|
||||||
|
Args:
|
||||||
|
command (str): SQL语句
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否执行成功
|
||||||
|
"""
|
||||||
|
if not command:
|
||||||
|
logger.warning("数据库语句长度为空!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with cls._transaction():
|
||||||
|
await cls.m_pDB.execute(command)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"数据库语句执行出错: {command}", e=e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def getPlantByName(cls, name: str) -> dict | None:
|
||||||
|
"""根据作物名称查询记录
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): 作物名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict | None: 返回记录字典,未找到返回None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
async with cls.m_pDB.execute(
|
||||||
|
"SELECT * FROM plant WHERE name = ?", (name,)
|
||||||
|
) as cursor:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
return dict(row) if row else None
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"查询作物失败: {name}", e=e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def getPlantPhaseByName(cls, name: str) -> list[int]:
|
||||||
|
"""根据作物名称获取作物各个阶段
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): 作物名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: 阶段数组
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
async with cls.m_pDB.execute(
|
||||||
|
"SELECT phase FROM plant WHERE name = ?", (name,)
|
||||||
|
) as cursor:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
|
||||||
|
if not row:
|
||||||
|
return []
|
||||||
|
|
||||||
|
phase = row[0].split(',')
|
||||||
|
|
||||||
|
seen = set()
|
||||||
|
result = []
|
||||||
|
for x in phase:
|
||||||
|
if x not in seen:
|
||||||
|
seen.add(int(x))
|
||||||
|
result.append(int(x))
|
||||||
|
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"查询作物阶段失败: {name}", e=e)
|
||||||
|
return []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def getPlantPhaseNumberByName(cls, name: str) -> int:
|
||||||
|
"""根据作物名称获取作物总阶段数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): 作物名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: 总阶段数
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
async with cls.m_pDB.execute(
|
||||||
|
"SELECT phase FROM plant WHERE name = ?", (name,)
|
||||||
|
) as cursor:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
|
||||||
|
if not row:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
phase = row[0].split(',')
|
||||||
|
|
||||||
|
#去重
|
||||||
|
seen = set()
|
||||||
|
result = []
|
||||||
|
for x in phase:
|
||||||
|
if x not in seen:
|
||||||
|
seen.add(x)
|
||||||
|
result.append(x)
|
||||||
|
|
||||||
|
return len(result)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"查询作物阶段失败: {name}", e=e)
|
||||||
|
return -1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def getPlantAgainByName(cls, name: str) -> int:
|
||||||
|
"""根据作物名称获取作物再次成熟时间
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): 作物名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: 再次成熟时间 单位:h
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with cls.m_pDB.execute(
|
||||||
|
"SELECT phase FROM plant WHERE name = ?", (name,)
|
||||||
|
) as cursor:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
|
||||||
|
if not row:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
phase = row[0].split(',')
|
||||||
|
again = phase[-1] - phase[3] / 60 / 60
|
||||||
|
|
||||||
|
return again
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"查询作物阶段失败: {name}", e=e)
|
||||||
|
return -1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def existsPlant(cls, name: str) -> bool:
|
||||||
|
"""判断作物是否存在
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): 作物名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 存在返回True,否则False
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
async with cls.m_pDB.execute(
|
||||||
|
"SELECT 1 FROM plant WHERE name = ? LIMIT 1", (name,)
|
||||||
|
) as cursor:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
return True if row else False
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"检查作物存在性失败: {name}", e=e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def countPlants(cls, onlyBuy: bool = False) -> int:
|
||||||
|
"""获取作物总数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
onlyBuy (bool): 若为True,仅统计isBuy=1的记录,默认False
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: 符合条件的记录数
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if onlyBuy:
|
||||||
|
sql = "SELECT COUNT(*) FROM plant WHERE isBuy = 1"
|
||||||
|
params: tuple = ()
|
||||||
|
else:
|
||||||
|
sql = "SELECT COUNT(*) FROM plant"
|
||||||
|
params: tuple = ()
|
||||||
|
async with cls.m_pDB.execute(sql, params) as cursor:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
return row[0] if row else 0
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"统计作物数量失败, onlyBuy={onlyBuy}", e=e)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def listPlants(cls) -> list[dict]:
|
||||||
|
"""查询所有作物记录"""
|
||||||
|
try:
|
||||||
|
async with cls.m_pDB.execute("SELECT * FROM plant ORDER BY level") as cursor:
|
||||||
|
rows = await cursor.fetchall()
|
||||||
|
return [dict(r) for r in rows]
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("查询所有作物失败", e=e)
|
||||||
|
return []
|
||||||
@@ -0,0 +1,475 @@
|
|||||||
|
import math
|
||||||
|
from typing import List, Union
|
||||||
|
from unittest import result
|
||||||
|
|
||||||
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
|
from ..tool import g_pToolManager
|
||||||
|
from .database import CSqlManager
|
||||||
|
|
||||||
|
|
||||||
|
class CUserDB(CSqlManager):
|
||||||
|
@classmethod
|
||||||
|
async def initDB(cls):
|
||||||
|
"""初始化用户表结构,确保user表存在且字段完整"""
|
||||||
|
userInfo = {
|
||||||
|
"uid": "TEXT PRIMARY KEY", #用户Uid
|
||||||
|
"name": "TEXT NOT NULL", #农场名称
|
||||||
|
"exp": "INTEGER DEFAULT 0", #经验值
|
||||||
|
"point": "INTEGER DEFAULT 0", #金币
|
||||||
|
"vipPoint": "INTEGER DEFAULT 0", #点券
|
||||||
|
"soil": "INTEGER DEFAULT 3", #解锁土地数量
|
||||||
|
"stealTime": "TEXT DEFAULT ''", #偷菜时间字符串
|
||||||
|
"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 = g_pToolManager.dateTime().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 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
|
||||||
|
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:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
if not row:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
result = dict(row)
|
||||||
|
|
||||||
|
return result
|
||||||
|
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 getUserVipPointByUid(cls, uid: str) -> int:
|
||||||
|
"""获取指定用户点券
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (str): 用户Uid
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: 点券数量,失败返回 -1
|
||||||
|
"""
|
||||||
|
if not uid:
|
||||||
|
return -1
|
||||||
|
try:
|
||||||
|
async with cls.m_pDB.execute(
|
||||||
|
"SELECT vipPoint 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("getUservipPointByUid 查询失败!", e=e)
|
||||||
|
return -1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def updateUserVipPointByUid(cls, uid: str, vipPoint: int) -> bool:
|
||||||
|
"""根据用户Uid更新农场币数量
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (str): 用户Uid
|
||||||
|
vipPoint (int): 新农场币数量
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否更新成功
|
||||||
|
"""
|
||||||
|
if not uid or vipPoint < 0:
|
||||||
|
logger.warning("updateUservipPointByUid 参数校验失败!")
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
async with cls._transaction():
|
||||||
|
await cls.m_pDB.execute(
|
||||||
|
"UPDATE user SET vipPoint = ? WHERE uid = ?", (vipPoint, uid)
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("updateUservipPointByUid 事务执行失败!", 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 not row or row[0] is None:
|
||||||
|
return -1, -1, -1
|
||||||
|
|
||||||
|
expVal = int(row[0])
|
||||||
|
levelStep = 200 # 每级经验增量
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
|
from .database import CSqlManager
|
||||||
|
|
||||||
|
|
||||||
|
class CUserItemDB(CSqlManager):
|
||||||
|
@classmethod
|
||||||
|
async def initDB(cls):
|
||||||
|
userItem = {
|
||||||
|
"uid": "TEXT NOT NULL", #用户Uid
|
||||||
|
"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
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
|
from .database import CSqlManager
|
||||||
|
|
||||||
|
|
||||||
|
class CUserPlantDB(CSqlManager):
|
||||||
|
@classmethod
|
||||||
|
async def initDB(cls):
|
||||||
|
userPlant = {
|
||||||
|
"uid": "TEXT NOT NULL", #用户Uid
|
||||||
|
"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
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
|
from .database import CSqlManager
|
||||||
|
|
||||||
|
|
||||||
|
class CUserSeedDB(CSqlManager):
|
||||||
|
@classmethod
|
||||||
|
async def initDB(cls):
|
||||||
|
userSeed = {
|
||||||
|
"uid": "TEXT NOT NULL", #用户Uid
|
||||||
|
"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)
|
||||||
|
)
|
||||||
|
|
||||||
|
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 _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
|
||||||
|
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 _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
|
||||||
|
|
||||||
|
@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
|
||||||
@@ -0,0 +1,262 @@
|
|||||||
|
import calendar
|
||||||
|
import random
|
||||||
|
from datetime import date, datetime, timedelta
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from zhenxun.services.log import logger
|
||||||
|
from zhenxun.utils._build_image import BuildImage
|
||||||
|
|
||||||
|
from ..config import g_bIsDebug
|
||||||
|
from ..dbService import g_pDBService
|
||||||
|
from ..json import g_pJsonManager
|
||||||
|
from ..tool import g_pToolManager
|
||||||
|
from .database import CSqlManager
|
||||||
|
|
||||||
|
|
||||||
|
class CUserSignDB(CSqlManager):
|
||||||
|
@classmethod
|
||||||
|
async def initDB(cls):
|
||||||
|
#userSignLog 表结构,每条为一次签到事件
|
||||||
|
userSignLog = {
|
||||||
|
"uid": "TEXT NOT NULL", #用户ID
|
||||||
|
"signDate": "DATE NOT NULL", #签到日期
|
||||||
|
"isSupplement": "TINYINT NOT NULL DEFAULT 0", #是否补签
|
||||||
|
"exp": "INT NOT NULL DEFAULT 0", #当天签到经验
|
||||||
|
"point": "INT NOT NULL DEFAULT 0", #当天签到金币
|
||||||
|
"createdAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime'))",#创建时间
|
||||||
|
"PRIMARY KEY": "(uid, signDate)"
|
||||||
|
}
|
||||||
|
|
||||||
|
#userSignSummary 表结构,每用户一行用于缓存签到状态
|
||||||
|
userSignSummary = {
|
||||||
|
"uid": "TEXT PRIMARY KEY NOT NULL", #用户ID
|
||||||
|
"totalSignDays": "INT NOT NULL DEFAULT 0", #累计签到天数
|
||||||
|
"currentMonth": "CHAR(7) NOT NULL DEFAULT ''", #当前月份(如2025-05)
|
||||||
|
"monthSignDays": "INT NOT NULL DEFAULT 0", #本月签到次数
|
||||||
|
"lastSignDate": "DATE DEFAULT NULL", #上次签到日期
|
||||||
|
"continuousDays": "INT NOT NULL DEFAULT 0", #连续签到天数
|
||||||
|
"supplementCount": "INT NOT NULL DEFAULT 0", #补签次数
|
||||||
|
"updatedAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime'))"#更新时间
|
||||||
|
}
|
||||||
|
|
||||||
|
await cls.ensureTableSchema("userSignLog", userSignLog)
|
||||||
|
await cls.ensureTableSchema("userSignSummary", userSignSummary)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def getUserSignRewardByDate(cls, uid: str, date: str) -> tuple[int, int]:
|
||||||
|
"""根据指定日期获取用户签到随机奖励
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (str): 用户Uid
|
||||||
|
date (str): 用户签到日期 示例:2025-05-27
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple[int, int]: 经验、金币
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
async with cls._transaction():
|
||||||
|
async with cls.m_pDB.execute(
|
||||||
|
"SELECT exp, point FROM userSignLog WHERE uid=? AND signDate=?",
|
||||||
|
(uid, date)
|
||||||
|
) as cursor:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
|
||||||
|
if row is None:
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
exp = row['exp']
|
||||||
|
point = row['point']
|
||||||
|
|
||||||
|
return exp, point
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("获取用户签到数据失败", e=e)
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def getUserSignCountByDate(cls, uid: str, monthStr: str) -> int:
|
||||||
|
"""根据日期查询用户签到总天数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (str): 用户Uid
|
||||||
|
monthStr (str): 需要查询的日期 示例: 2025-05
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: 查询月总签到天数
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
sql = "SELECT COUNT(*) FROM userSignLog WHERE uid=? AND signDate LIKE ?"
|
||||||
|
param = f"{monthStr}-%"
|
||||||
|
async with cls.m_pDB.execute(sql, (uid, param)) as cursor:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
return row[0] if row else 0
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("统计用户月签到次数失败", e=e)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def hasSigned(cls, uid: str, signDate: str) -> bool:
|
||||||
|
"""判断指定日期是否已签到
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (int): 用户ID
|
||||||
|
signDate (str): 日期字符串 'YYYY-MM-DD'
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True=已签到,False=未签到
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
sql = "SELECT 1 FROM userSignLog WHERE uid=? AND signDate=? LIMIT 1"
|
||||||
|
async with cls.m_pDB.execute(sql, (uid, signDate)) as cursor:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
return row is not None
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("查询是否已签到失败", e=e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def sign(cls, uid: str, signDate: str = '') -> int:
|
||||||
|
"""签到
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (int): 用户ID
|
||||||
|
signDate (str): 日期字符串 'YYYY-MM-DD' 不传默认当前系统日期
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 0: 签到失败 1: 签到成功 2: 重复签到
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not signDate:
|
||||||
|
signDate = g_pToolManager.dateTime().date().today().strftime("%Y-%m-%d")
|
||||||
|
|
||||||
|
if await cls.hasSigned(uid, signDate):
|
||||||
|
return 2
|
||||||
|
|
||||||
|
todayStr = g_pToolManager.dateTime().date().today().strftime("%Y-%m-%d")
|
||||||
|
isSupplement = 0 if signDate == todayStr else 1
|
||||||
|
|
||||||
|
expMax, expMin, pointMax, pointMin = [
|
||||||
|
g_pJsonManager.m_pSign.get(key, default)
|
||||||
|
for key, default in (
|
||||||
|
("exp_max", 50),
|
||||||
|
("exp_min", 5),
|
||||||
|
("point_max", 2000),
|
||||||
|
("point_min", 200),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
exp = random.randint(expMin, expMax)
|
||||||
|
point = random.randint(pointMin, pointMax)
|
||||||
|
vipPoint = 0
|
||||||
|
|
||||||
|
async with cls._transaction():
|
||||||
|
await cls.m_pDB.execute(
|
||||||
|
"INSERT INTO userSignLog (uid, signDate, isSupplement, exp, point) VALUES (?, ?, ?, ?, ?)",
|
||||||
|
(uid, signDate, isSupplement, exp, point)
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor = await cls.m_pDB.execute("SELECT * FROM userSignSummary WHERE uid=?", (uid,))
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
|
||||||
|
currentMonth = signDate[:7]
|
||||||
|
if row:
|
||||||
|
monthSignDays = row['monthSignDays'] + 1 if row['currentMonth'] == currentMonth else 1
|
||||||
|
totalSignDays = row['totalSignDays']
|
||||||
|
lastDate = row['lastSignDate']
|
||||||
|
prevDate = (g_pToolManager.dateTime().strptime(signDate, "%Y-%m-%d") - timedelta(days=1)).strftime("%Y-%m-%d")
|
||||||
|
continuousDays = row['continuousDays'] + 1 if lastDate == prevDate else 1
|
||||||
|
supplementCount = row['supplementCount'] + 1 if isSupplement else row['supplementCount']
|
||||||
|
await cls.m_pDB.execute(
|
||||||
|
"""
|
||||||
|
UPDATE userSignSummary
|
||||||
|
SET totalSignDays=totalSignDays+1,
|
||||||
|
currentMonth=?,
|
||||||
|
monthSignDays=?,
|
||||||
|
lastSignDate=?,
|
||||||
|
continuousDays=?,
|
||||||
|
supplementCount=?
|
||||||
|
WHERE uid=?
|
||||||
|
""",
|
||||||
|
(currentMonth, monthSignDays, signDate, continuousDays, supplementCount, uid)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
totalSignDays = 1
|
||||||
|
await cls.m_pDB.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO userSignSummary
|
||||||
|
(uid, totalSignDays, currentMonth, monthSignDays, lastSignDate, continuousDays, supplementCount)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""",
|
||||||
|
(uid, 1, currentMonth, 1, signDate, 1, 1 if isSupplement else 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
#计算累签奖励
|
||||||
|
reward = g_pJsonManager.m_pSign['continuou'].get(f"{totalSignDays}", None)
|
||||||
|
|
||||||
|
if reward:
|
||||||
|
point += reward.get('point', 0)
|
||||||
|
exp += reward.get('exp', 0)
|
||||||
|
vipPoint = reward.get('vipPoint', 0)
|
||||||
|
|
||||||
|
plant = reward.get('plant', {})
|
||||||
|
|
||||||
|
if plant:
|
||||||
|
for key, value in plant.items():
|
||||||
|
await g_pDBService.userSeed.addUserSeedByUid(uid, key, value)
|
||||||
|
|
||||||
|
if g_bIsDebug:
|
||||||
|
exp += 9999
|
||||||
|
|
||||||
|
#向数据库更新
|
||||||
|
currentExp = await g_pDBService.user.getUserExpByUid(uid)
|
||||||
|
await g_pDBService.user.updateUserExpByUid(uid, currentExp + exp)
|
||||||
|
|
||||||
|
currentPoint = await g_pDBService.user.getUserPointByUid(uid)
|
||||||
|
await g_pDBService.user.updateUserPointByUid(uid, currentPoint + point)
|
||||||
|
|
||||||
|
if vipPoint > 0:
|
||||||
|
currentVipPoint = await g_pDBService.user.getUserVipPointByUid(uid)
|
||||||
|
await g_pDBService.user.updateUserVipPointByUid(uid, currentVipPoint + vipPoint)
|
||||||
|
|
||||||
|
return 1
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("执行签到失败", e=e)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def drawSignCalendarImage(cls, uid: str, year: int, month: int):
|
||||||
|
#绘制签到图,自动提取数据库中该用户该月的签到天数
|
||||||
|
cellSize = 80
|
||||||
|
padding = 40
|
||||||
|
titleHeight = 80
|
||||||
|
cols = 7
|
||||||
|
rows = 6
|
||||||
|
width = cellSize * cols + padding * 2
|
||||||
|
height = cellSize * rows + padding * 2 + titleHeight
|
||||||
|
|
||||||
|
img = BuildImage(width, height, color=(255, 255, 255))
|
||||||
|
await img.text((padding, 20), f"{year}年{month}月签到表", font_size=36)
|
||||||
|
|
||||||
|
firstWeekday, totalDays = calendar.monthrange(year, month)
|
||||||
|
monthStr = f"{year:04d}-{month:02d}"
|
||||||
|
try:
|
||||||
|
sql = "SELECT signDate FROM userSignLog WHERE uid=? AND signDate LIKE ?"
|
||||||
|
async with cls.m_pDB.execute(sql, (uid, f"{monthStr}-%")) as cursor:
|
||||||
|
rows = await cursor.fetchall()
|
||||||
|
signedDays = set(int(r[0][-2:]) for r in rows if r[0][-2:].isdigit())
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("绘制签到图时数据库查询失败", e=e)
|
||||||
|
signedDays = set()
|
||||||
|
|
||||||
|
for day in range(1, totalDays + 1):
|
||||||
|
index = day + firstWeekday - 1
|
||||||
|
row = index // cols
|
||||||
|
col = index % cols
|
||||||
|
x1 = padding + col * cellSize
|
||||||
|
y1 = padding + titleHeight + row * cellSize
|
||||||
|
x2 = x1 + cellSize - 10
|
||||||
|
y2 = y1 + cellSize - 10
|
||||||
|
color = (112, 196, 112) if day in signedDays else (220, 220, 220)
|
||||||
|
await img.rectangle((x1, y1, x2, y2), fill=color, outline="black", width=2)
|
||||||
|
await img.text((x1 + 10, y1 + 10), str(day), font_size=24)
|
||||||
|
|
||||||
|
return img
|
||||||
@@ -0,0 +1,453 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
|
from ..config import g_bIsDebug
|
||||||
|
from ..dbService import g_pDBService
|
||||||
|
from ..json import g_pJsonManager
|
||||||
|
from ..tool import g_pToolManager
|
||||||
|
from .database import CSqlManager
|
||||||
|
|
||||||
|
|
||||||
|
class CUserSoilDB(CSqlManager):
|
||||||
|
@classmethod
|
||||||
|
async def initDB(cls):
|
||||||
|
userSoil = {
|
||||||
|
"uid": "TEXT NOT NULL",
|
||||||
|
"soilIndex": "INTEGER NOT NULL", #地块索引从1开始
|
||||||
|
"plantName": "TEXT DEFAULT ''", #作物名称
|
||||||
|
"plantTime": "INTEGER DEFAULT 0", #播种时间
|
||||||
|
"matureTime": "INTEGER DEFAULT 0", #成熟时间
|
||||||
|
"soilLevel": "INTEGER DEFAULT 0", #土地等级 0=普通地,1=红土地,2=黑土地,3=金土地
|
||||||
|
"wiltStatus": "INTEGER DEFAULT 0", #枯萎状态 0=未枯萎,1=枯萎
|
||||||
|
"fertilizerStatus": "INTEGER DEFAULT 0",#施肥状态 0=未施肥,1=施肥 2=增肥
|
||||||
|
"bugStatus": "INTEGER DEFAULT 0", #虫害状态 0=无虫害,1=有虫害
|
||||||
|
"weedStatus": "INTEGER DEFAULT 0", #杂草状态 0=无杂草,1=有杂草
|
||||||
|
"waterStatus": "INTEGER DEFAULT 0", #缺水状态 0=不缺水,1=缺水
|
||||||
|
"harvestCount": "INTEGER DEFAULT 0", #收获次数
|
||||||
|
"PRIMARY KEY": "(uid, soilIndex)",
|
||||||
|
}
|
||||||
|
|
||||||
|
await cls.ensureTableSchema("userSoil", userSoil)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def nextPhase(cls, uid: str, soilIndex: int):
|
||||||
|
"""将指定地块的作物进入下个阶段
|
||||||
|
|
||||||
|
Args:
|
||||||
|
soilIndex (int): 地块索引 从1开始
|
||||||
|
"""
|
||||||
|
if not g_bIsDebug:
|
||||||
|
return
|
||||||
|
|
||||||
|
soilInfo = await cls.getUserSoil(uid, soilIndex)
|
||||||
|
|
||||||
|
if not soilInfo:
|
||||||
|
return
|
||||||
|
|
||||||
|
plantInfo = await g_pDBService.plant.getPlantByName(soilInfo['plantName'])
|
||||||
|
|
||||||
|
if not plantInfo:
|
||||||
|
return
|
||||||
|
|
||||||
|
currentTime = g_pToolManager.dateTime().now().timestamp()
|
||||||
|
phaseList = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName'])
|
||||||
|
|
||||||
|
if currentTime >= soilInfo['matureTime']:
|
||||||
|
return
|
||||||
|
|
||||||
|
currentStage = 0
|
||||||
|
elapsedTime = currentTime - soilInfo['plantTime']
|
||||||
|
|
||||||
|
for idx, thr in enumerate(phaseList, start=1):
|
||||||
|
if elapsedTime < thr:
|
||||||
|
currentStage = idx
|
||||||
|
break
|
||||||
|
|
||||||
|
t = int(soilInfo['plantTime']) - phaseList[currentStage]
|
||||||
|
s = int(soilInfo['matureTime']) - phaseList[currentStage]
|
||||||
|
|
||||||
|
await cls.updateUserSoilFields(uid, soilIndex,
|
||||||
|
{
|
||||||
|
"plantTime": t,
|
||||||
|
"matureTime": s
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.debug(f"当前阶段{currentStage}, 阶段时间{phaseList[currentStage]}, 播种时间{t}, 收获时间{s}")
|
||||||
|
|
||||||
|
@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) -> bool:
|
||||||
|
"""迁移旧土地数据到新表 userSoil 并删除旧表
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
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():
|
||||||
|
users = await g_pDBService.user.getAllUsers()
|
||||||
|
|
||||||
|
for uid in users:
|
||||||
|
farmInfo = await cls.getUserFarmByUid(uid)
|
||||||
|
for i in range(1, 31):
|
||||||
|
key = f"soil{i}"
|
||||||
|
data = farmInfo.get(key)
|
||||||
|
if not data:
|
||||||
|
continue
|
||||||
|
parts = data.split(",")
|
||||||
|
if len(parts) < 3:
|
||||||
|
continue
|
||||||
|
name = parts[0]
|
||||||
|
pt = int(parts[1])
|
||||||
|
mt = int(parts[2])
|
||||||
|
|
||||||
|
await cls.m_pDB.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO userSoil
|
||||||
|
(uid,soilIndex,plantName,plantTime,matureTime,harvestCount)
|
||||||
|
VALUES (?,?,?,?,?,?)
|
||||||
|
""",
|
||||||
|
(uid, i, name, pt, mt, 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
await cls.m_pDB.execute("DROP TABLE soil")
|
||||||
|
|
||||||
|
logger.info("数据库迁移完毕!")
|
||||||
|
return True
|
||||||
|
|
||||||
|
@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, wiltStatus, fertilizerStatus, bugStatus,
|
||||||
|
weedStatus, waterStatus, harvestCount)
|
||||||
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
soilInfo["uid"],
|
||||||
|
soilInfo["soilIndex"],
|
||||||
|
soilInfo.get("plantName", ""),
|
||||||
|
soilInfo.get("plantTime", 0),
|
||||||
|
soilInfo.get("matureTime", 0),
|
||||||
|
soilInfo.get("soilLevel", 0),
|
||||||
|
soilInfo.get("wiltStatus", 0),
|
||||||
|
soilInfo.get("fertilizerStatus", 0),
|
||||||
|
soilInfo.get("bugStatus", 0),
|
||||||
|
soilInfo.get("weedStatus", 0),
|
||||||
|
soilInfo.get("waterStatus", 0),
|
||||||
|
soilInfo.get("harvestCount", 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, wiltStatus, fertilizerStatus, bugStatus,
|
||||||
|
weedStatus, waterStatus, harvestCount)
|
||||||
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
soilInfo["uid"],
|
||||||
|
soilInfo["soilIndex"],
|
||||||
|
soilInfo.get("plantName", ""),
|
||||||
|
soilInfo.get("plantTime", 0),
|
||||||
|
soilInfo.get("matureTime", 0),
|
||||||
|
soilInfo.get("soilLevel", 0),
|
||||||
|
soilInfo.get("wiltStatus", 0),
|
||||||
|
soilInfo.get("fertilizerStatus", 0),
|
||||||
|
soilInfo.get("bugStatus", 0),
|
||||||
|
soilInfo.get("weedStatus", 0),
|
||||||
|
soilInfo.get("waterStatus", 0),
|
||||||
|
soilInfo.get("harvestCount", 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 _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
|
||||||
|
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 _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
|
||||||
|
async def updateUserSoilFields(cls, uid: str, soilIndex: int, updates: dict) -> bool:
|
||||||
|
"""批量更新指定用户土地的多个字段
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (str): 用户ID
|
||||||
|
soilIndex (int): 土地索引
|
||||||
|
updates (dict): 字段-新值的字典
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 如果无可更新字段则返回 False,否则更新成功返回 True
|
||||||
|
"""
|
||||||
|
#允许更新的列白名单
|
||||||
|
allowedFields = {
|
||||||
|
"plantName", "plantTime", "matureTime", "soilLevel",
|
||||||
|
"wiltStatus", "fertilizerStatus", "bugStatus",
|
||||||
|
"weedStatus", "waterStatus", "harvestCount"
|
||||||
|
}
|
||||||
|
setClauses = []
|
||||||
|
values = []
|
||||||
|
for field, value in updates.items():
|
||||||
|
if field not in allowedFields:
|
||||||
|
continue
|
||||||
|
setClauses.append(f'"{field}" = ?')
|
||||||
|
values.append(value)
|
||||||
|
if not setClauses:
|
||||||
|
return False
|
||||||
|
|
||||||
|
values.extend([uid, soilIndex])
|
||||||
|
sql = f'UPDATE userSoil SET {", ".join(setClauses)} WHERE uid = ? AND soilIndex = ?'
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with cls._transaction():
|
||||||
|
await cls.m_pDB.execute(sql, tuple(values))
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"批量更新土地字段失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
@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 _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
|
||||||
|
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 = await g_pDBService.plant.getPlantByName(plantName)
|
||||||
|
if not plantCfg:
|
||||||
|
logger.error(f"未知植物: {plantName}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
nowTs = int(g_pToolManager.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),
|
||||||
|
"harvestCount": 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"播种失败!", e=e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def getUserSoilStatus(cls, uid: str, soilIndex: int) -> str:
|
||||||
|
status = []
|
||||||
|
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex)
|
||||||
|
|
||||||
|
if not soilInfo:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
if soilInfo.get("wiltStatus", 0) == 1:
|
||||||
|
return "枯萎"
|
||||||
|
|
||||||
|
if soilInfo.get("fertilizerStatus", 0) == 1:
|
||||||
|
status.append("施肥")
|
||||||
|
elif soilInfo.get("fertilizerStatus", 0) == 2:
|
||||||
|
status.append("增肥")
|
||||||
|
|
||||||
|
if soilInfo.get("bugStatus", 0) == 1:
|
||||||
|
status.append("虫害")
|
||||||
|
|
||||||
|
if soilInfo.get("weedStatus", 0) == 1:
|
||||||
|
status.append("杂草")
|
||||||
|
|
||||||
|
if soilInfo.get("waterStatus", 0) == 1:
|
||||||
|
status.append("缺水")
|
||||||
|
|
||||||
|
return ",".join(status)
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
|
from .database import CSqlManager
|
||||||
|
|
||||||
|
|
||||||
|
class CUserStealDB(CSqlManager):
|
||||||
|
@classmethod
|
||||||
|
async def initDB(cls):
|
||||||
|
userSteal = {
|
||||||
|
"uid": "TEXT NOT NULL", #被偷用户Uid
|
||||||
|
"soilIndex": "INTEGER NOT NULL", #被偷的地块索引 从1开始
|
||||||
|
"stealerUid": "TEXT NOT NULL", #偷菜用户Uid
|
||||||
|
"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) -> bool:
|
||||||
|
"""删除指定偷菜记录(只需被偷用户Uid和地块索引)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (str): 被偷用户Uid
|
||||||
|
soilIndex (int): 被偷地块索引
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 删除是否成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
async with cls._transaction():
|
||||||
|
await cls.m_pDB.execute(
|
||||||
|
'DELETE FROM "userSteal" WHERE uid=? AND soilIndex=?;',
|
||||||
|
(uid, soilIndex)
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("删除偷菜记录失败", e=e)
|
||||||
|
return False
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
class CDBService:
|
||||||
|
@classmethod
|
||||||
|
async def init(cls):
|
||||||
|
from .database.plant import CPlantManager
|
||||||
|
from .database.user import CUserDB
|
||||||
|
from .database.userItem import CUserItemDB
|
||||||
|
from .database.userPlant import CUserPlantDB
|
||||||
|
from .database.userSeed import CUserSeedDB
|
||||||
|
from .database.userSign import CUserSignDB
|
||||||
|
from .database.userSoil import CUserSoilDB
|
||||||
|
from .database.userSteal import CUserStealDB
|
||||||
|
|
||||||
|
cls.plant = CPlantManager()
|
||||||
|
await cls.plant.init()
|
||||||
|
|
||||||
|
cls.user = CUserDB()
|
||||||
|
await cls.user.initDB()
|
||||||
|
|
||||||
|
cls.userSoil = CUserSoilDB()
|
||||||
|
await cls.userSoil.initDB()
|
||||||
|
|
||||||
|
cls.userPlant = CUserPlantDB()
|
||||||
|
await cls.userPlant.initDB()
|
||||||
|
|
||||||
|
cls.userSeed = CUserSeedDB()
|
||||||
|
await cls.userSeed.initDB()
|
||||||
|
|
||||||
|
cls.userItem = CUserItemDB()
|
||||||
|
await cls.userItem.initDB()
|
||||||
|
|
||||||
|
cls.userSteal = CUserStealDB()
|
||||||
|
await cls.userSteal.initDB()
|
||||||
|
|
||||||
|
cls.userSign = CUserSignDB()
|
||||||
|
await cls.userSign.initDB()
|
||||||
|
|
||||||
|
#迁移旧数据库
|
||||||
|
await cls.userSoil.migrateOldFarmData()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def cleanup(cls):
|
||||||
|
await cls.plant.cleanup()
|
||||||
|
|
||||||
|
g_pDBService = CDBService()
|
||||||
@@ -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()
|
||||||
@@ -1,12 +1,8 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import math
|
import math
|
||||||
import random
|
import random
|
||||||
from datetime import date, datetime
|
|
||||||
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
|
||||||
@@ -15,8 +11,11 @@ from zhenxun.utils.enum import GoldHandle
|
|||||||
from zhenxun.utils.image_utils import ImageTemplate
|
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_bIsDebug, g_sResourcePath
|
||||||
from ..database import g_pSqlManager
|
from ..dbService import g_pDBService
|
||||||
|
from ..event.event import g_pEventManager
|
||||||
|
from ..json import g_pJsonManager
|
||||||
|
from ..tool import g_pToolManager
|
||||||
|
|
||||||
|
|
||||||
class CFarmManager:
|
class CFarmManager:
|
||||||
@@ -38,20 +37,20 @@ 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}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def drawFarmByUid(cls, uid: str, name: str) -> bytes:
|
async def drawFarmByUid(cls, uid: str) -> bytes:
|
||||||
"""绘制用户农场
|
"""绘制用户农场
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -72,7 +71,9 @@ class CFarmManager:
|
|||||||
await grass.resize(0, soilSize[0], soilSize[1])
|
await grass.resize(0, soilSize[0], soilSize[1])
|
||||||
|
|
||||||
soilPos = g_pJsonManager.m_pSoil['soil']
|
soilPos = g_pJsonManager.m_pSoil['soil']
|
||||||
soilUnlock = await g_pSqlManager.getUserSoilByUid(uid)
|
|
||||||
|
userInfo = await g_pDBService.user.getUserInfoByUid(uid)
|
||||||
|
soilUnlock = int(userInfo['soil'])
|
||||||
|
|
||||||
x = 0
|
x = 0
|
||||||
y = 0
|
y = 0
|
||||||
@@ -87,11 +88,11 @@ 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, offsetX, offsetY = 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 + offsetX,
|
||||||
y + soilSize[1] // 2 - plant.height // 2))
|
y + soilSize[1] // 2 - plant.height // 2 + offsetY))
|
||||||
|
|
||||||
#1700 275
|
#1700 275
|
||||||
#首次添加可收获图片
|
#首次添加可收获图片
|
||||||
@@ -128,11 +129,11 @@ class CFarmManager:
|
|||||||
await img.paste(frame, (75, 44))
|
await img.paste(frame, (75, 44))
|
||||||
|
|
||||||
#用户名
|
#用户名
|
||||||
nameImg = await BuildImage.build_text_image(name, size = 24, font_color = (77, 35, 4))
|
nameImg = await BuildImage.build_text_image(userInfo['name'], size = 24, font_color = (77, 35, 4))
|
||||||
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
|
||||||
@@ -148,12 +149,10 @@ class CFarmManager:
|
|||||||
await img.paste(levelImg, (660, 187))
|
await img.paste(levelImg, (660, 187))
|
||||||
|
|
||||||
#金币
|
#金币
|
||||||
point = await g_pSqlManager.getUserPointByUid(uid)
|
pointImg = await BuildImage.build_text_image(str(userInfo['point']), size = 24, font_color = (253, 253, 253))
|
||||||
pointImg = await BuildImage.build_text_image(str(point), size = 24, font_color = (253, 253, 253))
|
|
||||||
await img.paste(pointImg, (330, 255))
|
await img.paste(pointImg, (330, 255))
|
||||||
|
|
||||||
#点券
|
#点券 TODO
|
||||||
bonds = await g_pSqlManager.getUserPointByUid(uid)
|
|
||||||
bondsImg = await BuildImage.build_text_image("0", size = 24, font_color = (253, 253, 253))
|
bondsImg = await BuildImage.build_text_image("0", size = 24, font_color = (253, 253, 253))
|
||||||
await img.paste(bondsImg, (570, 255))
|
await img.paste(bondsImg, (570, 255))
|
||||||
|
|
||||||
@@ -171,70 +170,169 @@ class CFarmManager:
|
|||||||
return img.pic2bytes()
|
return img.pic2bytes()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def drawSoilPlant(cls, uid: str, soilid: str) -> tuple[bool, BuildImage, bool]:
|
async def drawDetailFarmByUid(cls, uid: str) -> list:
|
||||||
|
info = []
|
||||||
|
|
||||||
|
farm = await cls.drawFarmByUid(uid)
|
||||||
|
|
||||||
|
info.append(BuildImage.open(farm))
|
||||||
|
|
||||||
|
dataList = []
|
||||||
|
columnName = [
|
||||||
|
"-",
|
||||||
|
"土地ID",
|
||||||
|
"作物名称",
|
||||||
|
"成熟时间",
|
||||||
|
"土地状态",
|
||||||
|
"被偷数量",
|
||||||
|
"剩余产出",
|
||||||
|
]
|
||||||
|
|
||||||
|
icon = ""
|
||||||
|
soilNumber = await g_pDBService.user.getUserSoilByUid(uid)
|
||||||
|
|
||||||
|
for i in range(1, soilNumber + 1):
|
||||||
|
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, i)
|
||||||
|
|
||||||
|
if soilInfo:
|
||||||
|
if soilInfo["soilLevel"] == 1:
|
||||||
|
iconPath = g_sResourcePath / f"soil/TODO.png"
|
||||||
|
else:
|
||||||
|
iconPath = g_sResourcePath / f"soil/普通土地.png"
|
||||||
|
|
||||||
|
if iconPath.exists():
|
||||||
|
icon = (iconPath, 33, 33)
|
||||||
|
|
||||||
|
plantName = soilInfo.get("plantName", "-")
|
||||||
|
|
||||||
|
if plantName == "-":
|
||||||
|
matureTime = "-"
|
||||||
|
soilStatus = "-"
|
||||||
|
totalNumber = "-"
|
||||||
|
plantNumber = "-"
|
||||||
|
else:
|
||||||
|
matureTime = g_pToolManager.dateTime().fromtimestamp(int(soilInfo.get("matureTime", 0))).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
soilStatus = await g_pDBService.userSoil.getUserSoilStatus(uid, i)
|
||||||
|
|
||||||
|
totalNumber = await g_pDBService.userSteal.getTotalStolenCount(uid, i)
|
||||||
|
planInfo = await g_pDBService.plant.getPlantByName(plantName)
|
||||||
|
|
||||||
|
if not planInfo:
|
||||||
|
plantNumber = f"None"
|
||||||
|
else:
|
||||||
|
plantNumber = f"{planInfo['harvest'] - totalNumber}"
|
||||||
|
|
||||||
|
dataList.append(
|
||||||
|
[
|
||||||
|
icon,
|
||||||
|
i,
|
||||||
|
plantName,
|
||||||
|
matureTime,
|
||||||
|
soilStatus,
|
||||||
|
totalNumber,
|
||||||
|
plantNumber,
|
||||||
|
])
|
||||||
|
|
||||||
|
if len(dataList) >= 15:
|
||||||
|
result = await ImageTemplate.table_page(
|
||||||
|
"土地详细信息",
|
||||||
|
"",
|
||||||
|
columnName,
|
||||||
|
dataList,
|
||||||
|
)
|
||||||
|
|
||||||
|
info.append(result.copy())
|
||||||
|
dataList.clear()
|
||||||
|
|
||||||
|
if i >= soilNumber:
|
||||||
|
result = await ImageTemplate.table_page(
|
||||||
|
"土地详细信息",
|
||||||
|
"",
|
||||||
|
columnName,
|
||||||
|
dataList,
|
||||||
|
)
|
||||||
|
|
||||||
|
info.append(result.copy())
|
||||||
|
dataList.clear()
|
||||||
|
|
||||||
|
return info
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def drawSoilPlant(cls, uid: str, soilIndex: int) -> tuple[bool, BuildImage, bool, int, int]:
|
||||||
"""绘制植物资源
|
"""绘制植物资源
|
||||||
|
|
||||||
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:
|
||||||
return False, None, False
|
return False, None, False, 0, 0 #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, 0, 0
|
||||||
|
|
||||||
|
#获取作物详细信息
|
||||||
|
plantInfo = await g_pDBService.plant.getPlantByName(soilInfo['plantName'])
|
||||||
|
if not plantInfo:
|
||||||
|
logger.error(f"绘制植物资源失败: {soilInfo['plantName']}")
|
||||||
|
return False, None, False, 0, 0 #type: ignore
|
||||||
|
|
||||||
|
offsetX = plantInfo.get('officX', 0)
|
||||||
|
offsetY = plantInfo.get('officY', 0)
|
||||||
|
offsetW = plantInfo.get('officW', 0)
|
||||||
|
offsetH = plantInfo.get('officH', 0)
|
||||||
|
|
||||||
|
currentTime = g_pToolManager.dateTime().now().timestamp()
|
||||||
|
phaseList = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName'])
|
||||||
|
|
||||||
|
#如果当前时间大于成熟时间 说明作物成熟
|
||||||
|
if currentTime >= soilInfo['matureTime']:
|
||||||
|
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{len(phaseList)}.png")
|
||||||
|
|
||||||
|
return True, plant, True, offsetX, offsetY
|
||||||
else:
|
else:
|
||||||
soilInfo = soilInfo.split(',')
|
#如果是多阶段作物 且没有成熟 #早期思路 多阶段作物 直接是倒数第二阶段图片
|
||||||
|
# if soilInfo['harvestCount'] >= 1:
|
||||||
|
# plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{plantInfo['phase'] - 1s}.png")
|
||||||
|
|
||||||
if int(soilInfo[3]) == 4:
|
# return True, plant, False, offsetX, offsetY
|
||||||
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[0]]
|
#如果没有成熟 则根据当前阶段进行绘制
|
||||||
|
currentStage = 0
|
||||||
|
elapsedTime = currentTime - soilInfo['plantTime']
|
||||||
|
|
||||||
currentTime = datetime.now()
|
for idx, thr in enumerate(phaseList, start=1):
|
||||||
matureTime = datetime.fromtimestamp(int(soilInfo[2]))
|
if elapsedTime < thr:
|
||||||
|
currentStage = idx
|
||||||
|
break
|
||||||
|
|
||||||
if currentTime >= matureTime:
|
if currentStage <= 0:
|
||||||
phase = int(plantInfo['phase'])
|
if plantInfo['general'] == False:
|
||||||
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo[0]}/{phase - 1}.png")
|
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/0.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)))
|
|
||||||
|
|
||||||
#TODO 缺少判断部分种子是否是通用0阶段图片
|
|
||||||
if currentStage <= 0:
|
|
||||||
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")
|
||||||
|
|
||||||
return True, plant, False
|
await plant.resize(0, 35 + offsetW, 58 + offsetH)
|
||||||
|
else:
|
||||||
|
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{currentStage}.png")
|
||||||
|
|
||||||
|
return True, plant, False, offsetX, offsetY
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def getUserSeedByUid(cls, uid: str) -> bytes:
|
async def getUserSeedByUid(cls, uid: str) -> bytes:
|
||||||
"""获取用户种子仓库
|
"""获取用户种子仓库"""
|
||||||
|
dataList = []
|
||||||
Args:
|
columnNames = [
|
||||||
uid (str): 用户Uid
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bytes: 返回图片
|
|
||||||
"""
|
|
||||||
|
|
||||||
data_list = []
|
|
||||||
column_name = [
|
|
||||||
"-",
|
"-",
|
||||||
"种子名称",
|
"种子名称",
|
||||||
"数量",
|
"数量",
|
||||||
@@ -242,63 +340,50 @@ class CFarmManager:
|
|||||||
"收获数量",
|
"收获数量",
|
||||||
"成熟时间(小时)",
|
"成熟时间(小时)",
|
||||||
"收获次数",
|
"收获次数",
|
||||||
"再次成熟时间(小时)",
|
|
||||||
"是否可以上架交易行"
|
"是否可以上架交易行"
|
||||||
]
|
]
|
||||||
|
|
||||||
seed = await g_pSqlManager.getUserSeedByUid(uid)
|
#从数据库获取结构化数据
|
||||||
|
seedRecords = await g_pDBService.userSeed.getUserSeedByUid(uid) or {}
|
||||||
|
|
||||||
if seed == None:
|
if not seedRecords:
|
||||||
result = await ImageTemplate.table_page(
|
result = await ImageTemplate.table_page(
|
||||||
"种子仓库",
|
"种子仓库",
|
||||||
"播种示例:@小真寻 播种 大白菜 [数量]",
|
"播种示例:@小真寻 播种 大白菜 [数量]",
|
||||||
column_name,
|
columnNames,
|
||||||
data_list,
|
dataList,
|
||||||
)
|
)
|
||||||
|
|
||||||
return result.pic2bytes()
|
return result.pic2bytes()
|
||||||
|
|
||||||
sell = ""
|
for seedName, count in seedRecords.items():
|
||||||
for item in seed.split(','):
|
try:
|
||||||
if '|' in item:
|
plantInfo = await g_pDBService.plant.getPlantByName(seedName)
|
||||||
seedName, count = item.split('|', 1) #分割一次,避免多竖线问题
|
if not plantInfo:
|
||||||
try:
|
|
||||||
plantInfo = g_pJsonManager.m_pPlant['plant'][seedName]
|
|
||||||
|
|
||||||
icon = ""
|
|
||||||
icon_path = g_sResourcePath / f"plant/{seedName}/icon.png"
|
|
||||||
if icon_path.exists():
|
|
||||||
icon = (icon_path, 33, 33)
|
|
||||||
|
|
||||||
if plantInfo['again'] == True:
|
|
||||||
sell = "可以"
|
|
||||||
else:
|
|
||||||
sell = "不可以"
|
|
||||||
|
|
||||||
data_list.append(
|
|
||||||
[
|
|
||||||
icon,
|
|
||||||
seedName,
|
|
||||||
count,
|
|
||||||
plantInfo['experience'],
|
|
||||||
plantInfo['harvest'],
|
|
||||||
plantInfo['time'],
|
|
||||||
plantInfo['crop'],
|
|
||||||
plantInfo['again'],
|
|
||||||
sell
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
iconPath = g_sResourcePath / f"plant/{seedName}/icon.png"
|
||||||
|
icon = (iconPath, 33, 33) if iconPath.exists() else ""
|
||||||
|
sellable = "可以" if plantInfo['sell'] else "不可以"
|
||||||
|
|
||||||
|
dataList.append([
|
||||||
|
icon,
|
||||||
|
seedName,
|
||||||
|
count,
|
||||||
|
plantInfo['experience'],
|
||||||
|
plantInfo['harvest'],
|
||||||
|
plantInfo['time'],
|
||||||
|
plantInfo['crop'],
|
||||||
|
sellable
|
||||||
|
])
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
|
||||||
result = await ImageTemplate.table_page(
|
result = await ImageTemplate.table_page(
|
||||||
"种子仓库",
|
"种子仓库",
|
||||||
"播种示例:@小真寻 播种 大白菜 [数量]",
|
"播种示例:@小真寻 播种 大白菜 [数量]",
|
||||||
column_name,
|
columnNames,
|
||||||
data_list,
|
dataList,
|
||||||
)
|
)
|
||||||
|
|
||||||
return result.pic2bytes()
|
return result.pic2bytes()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -311,55 +396,61 @@ class CFarmManager:
|
|||||||
num (int, optional): 播种数量
|
num (int, optional): 播种数量
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str:
|
str: 返回结果
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
|
#获取用户的种子数量
|
||||||
|
count = await g_pDBService.userSeed.getUserSeedByName(uid, name)
|
||||||
|
if count is None:
|
||||||
|
count = 0 #如果返回 None,则视为没有种子
|
||||||
|
|
||||||
number = 0
|
if count <= 0:
|
||||||
count = await g_pSqlManager.getUserSeedByName(uid, name)
|
return f"没有在你的仓库发现{name}种子,快去买点吧!"
|
||||||
|
|
||||||
if count <= 0:
|
#如果播种数量超过仓库种子数量
|
||||||
return f"没有在你的仓库发现{name}种子,快去买点吧!"
|
if count < num and num != -1:
|
||||||
|
return f"仓库中的{name}种子数量不足,当前剩余{count}个种子"
|
||||||
|
|
||||||
#如果播种超过仓库种子
|
#获取用户土地数量
|
||||||
isMax = False
|
soilNumber = await g_pDBService.user.getUserSoilByUid(uid)
|
||||||
if count < num:
|
|
||||||
isMax = True
|
|
||||||
|
|
||||||
#如果播种全部
|
#如果播种数量为 -1,表示播种所有可播种的土地
|
||||||
isAll = False
|
if num == -1:
|
||||||
if num == -1:
|
num = count
|
||||||
isAll = True
|
|
||||||
|
|
||||||
soilNumber = await g_pSqlManager.getUserSoilByUid(uid)
|
#发送播种前信号
|
||||||
|
await g_pEventManager.m_beforePlant.emit(uid=uid, name=name, num=num)
|
||||||
|
|
||||||
for i in range(1, soilNumber + 1):
|
#记录是否成功播种
|
||||||
if count > 0:
|
successCount = 0
|
||||||
if isAll or num > 0:
|
for i in range(1, soilNumber + 1):
|
||||||
soilName = f"soil{i}"
|
if count > 0 and num > 0:
|
||||||
success, message = await g_pSqlManager.getUserSoilStatusBySoilID(uid, soilName)
|
success = await g_pDBService.userSoil.sowingByPlantName(uid, i, name)
|
||||||
if success:
|
if success:
|
||||||
#更新种子数量
|
#更新种子数量
|
||||||
num -= 1
|
num -= 1
|
||||||
count -= 1
|
count -= 1
|
||||||
|
|
||||||
#记录种子消耗数量
|
#记录种子消耗数量
|
||||||
number -= 1
|
successCount += 1
|
||||||
|
|
||||||
#更新数据库
|
#发送播种后信号
|
||||||
await g_pSqlManager.updateUserSoilStatusByPlantName(uid, soilName, name)
|
await g_pEventManager.m_afterPlant.emit(uid=uid, name=name, soilIndex=i)
|
||||||
|
|
||||||
str = ""
|
|
||||||
|
|
||||||
if isMax:
|
#确保用户仓库数量更新
|
||||||
str = f"仓库数量不够那么多,已将剩余数量全部播种!"
|
if successCount > 0:
|
||||||
elif num > 0:
|
await g_pDBService.userSeed.updateUserSeedByName(uid, name, count)
|
||||||
str = f"播种数量超出开垦土地数量,已将可播种土地成功播种{name}!仓库还剩下{count}个种子"
|
|
||||||
else:
|
|
||||||
str = f"播种{name}成功!仓库还剩下{count}个种子"
|
|
||||||
|
|
||||||
await g_pSqlManager.addUserSeedByPlant(uid, name, number)
|
#根据播种结果给出反馈
|
||||||
|
if num == 0:
|
||||||
|
return f"播种{name}成功!仓库剩余{count}个种子"
|
||||||
|
else:
|
||||||
|
return f"播种数量超出开垦土地数量,已将可播种土地成功播种{name}!仓库剩余{count}个种子"
|
||||||
|
|
||||||
return str
|
except Exception as e:
|
||||||
|
logger.warning(f"播种操作失败!", e=e)
|
||||||
|
return "播种失败,请稍后重试!"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def harvest(cls, uid: str) -> str:
|
async def harvest(cls, uid: str) -> str:
|
||||||
@@ -371,61 +462,85 @@ 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 = await g_pDBService.plant.getPlantByName(soilInfo['plantName'])
|
||||||
plantInfo = g_pJsonManager.m_pPlant['plant'][plantId]
|
if not plantInfo:
|
||||||
|
continue
|
||||||
|
|
||||||
currentTime = datetime.now()
|
currentTime = g_pToolManager.dateTime().now()
|
||||||
matureTime = datetime.fromtimestamp(int(soilInfo[2]))
|
matureTime = g_pToolManager.dateTime().fromtimestamp(int(soilInfo['matureTime']))
|
||||||
|
|
||||||
if currentTime >= matureTime:
|
if currentTime >= matureTime:
|
||||||
number = plantInfo['harvest']
|
number = plantInfo['harvest']
|
||||||
|
|
||||||
#判断该土地作物是否被透过
|
#处理偷菜扣除数量
|
||||||
if len(soilInfo[4]) > 0:
|
stealNum = await g_pDBService.userSteal.getTotalStolenCount(uid, i)
|
||||||
stealingStatus = soilInfo[4].split('|')
|
|
||||||
for isUser in stealingStatus:
|
|
||||||
user = isUser.split('-')
|
|
||||||
number -= int(user[1])
|
|
||||||
|
|
||||||
isStealingPlant += 1
|
number -= stealNum
|
||||||
|
|
||||||
experience += plantInfo['experience']
|
if number <= 0:
|
||||||
harvestRecords.append(f"收获作物:{plantId},数量为:{number},经验为:{plantInfo['experience']}")
|
continue
|
||||||
|
|
||||||
#更新数据库操作
|
harvestCount += 1
|
||||||
await g_pSqlManager.addUserPlantByPlant(uid, plantId, number)
|
experience += plantInfo['experience']
|
||||||
await g_pSqlManager.updateUserSoilStatusByPlantName(uid, soil_name, "", 4)
|
|
||||||
|
|
||||||
if experience > 0:
|
harvestRecords.append(f"收获作物:{soilInfo['plantName']},数量为:{number},经验为:{plantInfo['experience']}")
|
||||||
harvestRecords.append(f"\t累计获得经验:{experience}")
|
|
||||||
exp = await g_pSqlManager.getUserExpByUid(uid)
|
|
||||||
await g_pSqlManager.UpdateUserExpByUid(uid, exp + experience)
|
|
||||||
|
|
||||||
if isStealingPlant <= 0:
|
await g_pDBService.userPlant.addUserPlantByUid(uid, soilInfo['plantName'], number)
|
||||||
return "没有可收获的作物哦~ 不要试图拔苗助长"
|
|
||||||
else:
|
#如果到达收获次数上限
|
||||||
return "\n".join(harvestRecords)
|
if soilInfo['harvestCount'] + 1 >= plantInfo['crop']:
|
||||||
|
await g_pDBService.userSoil.updateUserSoil(uid, i, "wiltStatus", 1)
|
||||||
|
else:
|
||||||
|
phase = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName'])
|
||||||
|
|
||||||
|
ts, hc = int(currentTime.timestamp()), soilInfo["harvestCount"] + 1
|
||||||
|
p1, p2, *rest = phase
|
||||||
|
|
||||||
|
await g_pDBService.userSoil.updateUserSoilFields(uid, i,
|
||||||
|
{
|
||||||
|
"harvestCount": hc,
|
||||||
|
"plantTime": ts - p1 - p2,
|
||||||
|
"matureTime": ts + p2 + sum(rest),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await g_pEventManager.m_afterHarvest.emit(uid=uid, name=soilInfo['plantName'], num=number, soilIndex=i)
|
||||||
|
|
||||||
|
if experience > 0:
|
||||||
|
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:
|
||||||
@@ -437,28 +552,40 @@ 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
|
||||||
|
|
||||||
|
if g_bIsDebug:
|
||||||
|
experience += 999
|
||||||
|
|
||||||
|
#批量更新数据库操作
|
||||||
|
await g_pDBService.userSoil.deleteUserSoil(uid, i)
|
||||||
|
|
||||||
|
#铲除作物会将偷菜记录清空
|
||||||
|
await g_pDBService.userSteal.deleteStealRecord(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:
|
||||||
@@ -474,7 +601,6 @@ class CFarmManager:
|
|||||||
Returns:
|
Returns:
|
||||||
bytes: 返回图片
|
bytes: 返回图片
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data_list = []
|
data_list = []
|
||||||
column_name = [
|
column_name = [
|
||||||
"-",
|
"-",
|
||||||
@@ -485,54 +611,49 @@ class CFarmManager:
|
|||||||
"是否可以上架交易行"
|
"是否可以上架交易行"
|
||||||
]
|
]
|
||||||
|
|
||||||
plant = await g_pSqlManager.getUserPlantByUid(uid)
|
plant = await g_pDBService.userPlant.getUserPlantByUid(uid)
|
||||||
|
|
||||||
if plant == None:
|
if plant is None:
|
||||||
result = await ImageTemplate.table_page(
|
result = await ImageTemplate.table_page(
|
||||||
"作物仓库",
|
"作物仓库",
|
||||||
"播种示例:@小真寻 出售作物 大白菜 [数量]",
|
"出售示例:@小真寻 出售作物 大白菜 [数量]",
|
||||||
column_name,
|
column_name,
|
||||||
data_list,
|
data_list,
|
||||||
)
|
)
|
||||||
|
|
||||||
return result.pic2bytes()
|
return result.pic2bytes()
|
||||||
|
|
||||||
sell = ""
|
sell = ""
|
||||||
for item in plant.split(','):
|
for name, count in plant.items():
|
||||||
if '|' in item:
|
plantInfo = await g_pDBService.plant.getPlantByName(name)
|
||||||
plantName, count = item.split('|', 1) #分割一次,避免多竖线问题
|
if not plantInfo:
|
||||||
try:
|
continue
|
||||||
plantInfo = g_pJsonManager.m_pPlant['plant'][plantName]
|
|
||||||
|
|
||||||
icon = ""
|
icon = ""
|
||||||
icon_path = g_sResourcePath / f"plant/{plantName}/icon.png"
|
icon_path = g_sResourcePath / f"plant/{name}/icon.png"
|
||||||
if icon_path.exists():
|
if icon_path.exists():
|
||||||
icon = (icon_path, 33, 33)
|
icon = (icon_path, 33, 33)
|
||||||
|
|
||||||
if plantInfo['again'] == True:
|
if plantInfo['sell'] == True:
|
||||||
sell = "可以"
|
sell = "可以"
|
||||||
else:
|
else:
|
||||||
sell = "不可以"
|
sell = "不可以"
|
||||||
|
|
||||||
number = int(count) * plantInfo['price']
|
number = int(count) * plantInfo['price']
|
||||||
|
|
||||||
data_list.append(
|
data_list.append(
|
||||||
[
|
[
|
||||||
icon,
|
icon,
|
||||||
plantName,
|
name,
|
||||||
count,
|
count,
|
||||||
plantInfo['price'],
|
plantInfo['price'],
|
||||||
number,
|
number,
|
||||||
sell
|
sell
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
continue
|
|
||||||
|
|
||||||
result = await ImageTemplate.table_page(
|
result = await ImageTemplate.table_page(
|
||||||
"作物仓库",
|
"作物仓库",
|
||||||
"播种示例:@小真寻 出售作物 大白菜 [数量]",
|
"出售示例:@小真寻 出售作物 大白菜 [数量]",
|
||||||
column_name,
|
column_name,
|
||||||
data_list,
|
data_list,
|
||||||
)
|
)
|
||||||
@@ -550,121 +671,104 @@ 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.get('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 == "" or not stealTime:
|
||||||
|
stealTime = g_pToolManager.dateTime().date().today().strftime('%Y-%m-%d')
|
||||||
|
stealCount = 5
|
||||||
|
elif g_pToolManager.dateTime().date().fromisoformat(stealTime) != g_pToolManager.dateTime().date().today():
|
||||||
|
stealTime = g_pToolManager.dateTime().date().today().strftime('%Y-%m-%d')
|
||||||
|
stealCount = 5
|
||||||
|
|
||||||
|
if stealCount <= 0:
|
||||||
return "你今天可偷次数到达上限啦,手下留情吧"
|
return "你今天可偷次数到达上限啦,手下留情吧"
|
||||||
|
|
||||||
#获取用户解锁地块数量
|
#获取用户解锁地块数量
|
||||||
soilUnlockNum = int(userInfo["soil"])
|
soilNumber = await g_pDBService.user.getUserSoilByUid(target)
|
||||||
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(target, i):
|
||||||
if not info:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
soilInfo = info.split(',')
|
soilInfo = await g_pDBService.userSoil.getUserSoil(target, 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 = await g_pDBService.plant.getPlantByName(soilInfo['plantName'])
|
||||||
|
if not plantInfo:
|
||||||
|
continue
|
||||||
|
|
||||||
currentTime = datetime.now()
|
currentTime = g_pToolManager.dateTime().now()
|
||||||
matureTime = datetime.fromtimestamp(int(soilInfo[2]))
|
matureTime = g_pToolManager.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.addUserPlantByPlant(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}'"
|
#如果作物 是最后一阶段作物且偷完 则直接枯萎
|
||||||
else:
|
if soilInfo['harvestCount'] + 1 >= plantInfo['crop']:
|
||||||
sql = f"UPDATE soil SET {soilName} = '{soilInfo[0]},{soilInfo[1]},{soilInfo[2]},{soilInfo[3]},{'|'.join(stealingStatus)}' WHERE uid = '{target}'"
|
await g_pDBService.userSoil.updateUserSoil(target, i, "wiltStatus", 1)
|
||||||
|
else:
|
||||||
|
phase = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName'])
|
||||||
|
|
||||||
await g_pSqlManager.executeDB(sql)
|
ts, hc = int(currentTime.timestamp()), soilInfo["harvestCount"] + 1
|
||||||
|
p1, p2, *rest = phase
|
||||||
|
|
||||||
|
await g_pDBService.userSoil.updateUserSoilFields(uid, i,
|
||||||
|
{
|
||||||
|
"harvestCount": hc,
|
||||||
|
"plantTime": ts - p1 - p2,
|
||||||
|
"matureTime": ts + p2 + sum(rest),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await g_pDBService.userSteal.addStealRecord(target, i, uid, randomNumber, int(g_pToolManager.dateTime().now().timestamp()))
|
||||||
|
|
||||||
|
else:
|
||||||
|
await g_pDBService.userSteal.addStealRecord(target, i, uid, randomNumber, int(g_pToolManager.dateTime().now().timestamp()))
|
||||||
|
|
||||||
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:
|
||||||
@@ -678,20 +782,20 @@ class CFarmManager:
|
|||||||
|
|
||||||
str = ""
|
str = ""
|
||||||
if len(item) == 0:
|
if len(item) == 0:
|
||||||
str = f"下次升级所需条件:等级:{level},农场币:{point}"
|
str = f"下次开垦所需条件:等级:{level},农场币:{point}"
|
||||||
else:
|
else:
|
||||||
str = f"下次升级所需条件:等级:{level},农场币:{point},物品:{item}"
|
str = f"下次开垦所需条件:等级:{level},农场币:{point},物品:{item}"
|
||||||
|
|
||||||
return str
|
return str
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return "获取升级土地条件失败!"
|
return "获取开垦土地条件失败!"
|
||||||
|
|
||||||
@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:
|
||||||
@@ -703,18 +807,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:
|
||||||
|
|||||||
@@ -1,81 +1,108 @@
|
|||||||
import math
|
import math
|
||||||
|
import plistlib
|
||||||
|
from itertools import islice
|
||||||
|
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
from zhenxun.utils._build_image import BuildImage
|
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_sResourcePath
|
||||||
from ..database import g_pSqlManager
|
from ..dbService import g_pDBService
|
||||||
|
from ..json import g_pJsonManager
|
||||||
|
|
||||||
|
|
||||||
class CShopManager:
|
class CShopManager:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def getSeedShopImage(cls, num: int = 1) -> bytes:
|
async def getSeedShopImage(cls, filterKey: str|int = 1, num: int = 1) -> bytes:
|
||||||
"""获取商店页面
|
"""获取商店页面
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filterKey (str|int):
|
||||||
|
- 字符串: 根据关键字筛选种子名称
|
||||||
|
- 整数: 翻至对应页(无筛选)
|
||||||
|
num (int, optional): 当 filterKey 为字符串时,用于指定页码。Defaults to 1.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bytes: 返回商店图片bytes
|
bytes: 返回商店图片bytes
|
||||||
"""
|
"""
|
||||||
|
# 解析参数:区分筛选关键字和页码
|
||||||
|
filterStr = None
|
||||||
|
if isinstance(filterKey, int):
|
||||||
|
page = filterKey
|
||||||
|
else:
|
||||||
|
filterStr = filterKey
|
||||||
|
page = num
|
||||||
|
|
||||||
data_list = []
|
# 表头定义
|
||||||
column_name = [
|
columnName = [
|
||||||
"-",
|
"-",
|
||||||
"种子名称",
|
"种子名称",
|
||||||
"解锁等级",
|
|
||||||
"种子单价",
|
"种子单价",
|
||||||
|
"解锁等级",
|
||||||
|
"果实单价",
|
||||||
"收获经验",
|
"收获经验",
|
||||||
"收获数量",
|
"收获数量",
|
||||||
"成熟时间(小时)",
|
"成熟时间(小时)",
|
||||||
"收获次数",
|
"收获次数",
|
||||||
"再次成熟时间(小时)",
|
|
||||||
"是否可以上架交易行"
|
"是否可以上架交易行"
|
||||||
]
|
]
|
||||||
|
|
||||||
sell = ""
|
# 查询所有可购买作物,并根据筛选关键字过滤
|
||||||
plants = list(g_pJsonManager.m_pPlant['plant'].items())
|
plants = await g_pDBService.plant.listPlants()
|
||||||
start = (num - 1) * 15
|
filteredPlants = []
|
||||||
maxItems = min(len(plants) - start, 15)
|
for plant in plants:
|
||||||
items = plants[start:start + maxItems]
|
# 跳过未解锁购买的种子
|
||||||
|
if plant['isBuy'] == 0:
|
||||||
|
continue
|
||||||
|
# 字符串筛选
|
||||||
|
if filterStr and filterStr not in plant['name']:
|
||||||
|
continue
|
||||||
|
filteredPlants.append(plant)
|
||||||
|
|
||||||
for key, plant in items:
|
# 计算分页
|
||||||
|
totalCount = len(filteredPlants)
|
||||||
|
pageCount = math.ceil(totalCount / 15) if totalCount else 1
|
||||||
|
startIndex = (page - 1) * 15
|
||||||
|
pageItems = filteredPlants[startIndex: startIndex + 15]
|
||||||
|
|
||||||
|
# 构建数据行
|
||||||
|
dataList = []
|
||||||
|
for plant in pageItems:
|
||||||
|
# 图标处理
|
||||||
icon = ""
|
icon = ""
|
||||||
icon_path = g_sResourcePath / f"plant/{key}/icon.png"
|
iconPath = g_sResourcePath / f"plant/{plant['name']}/icon.png"
|
||||||
if icon_path.exists():
|
if iconPath.exists():
|
||||||
icon = (icon_path, 33, 33)
|
icon = (iconPath, 33, 33)
|
||||||
|
|
||||||
if plant['again'] == True:
|
# 交易行标记
|
||||||
sell = "可以"
|
sell = "可以" if plant['sell'] else "不可以"
|
||||||
else:
|
|
||||||
sell = "不可以"
|
|
||||||
|
|
||||||
data_list.append(
|
dataList.append([
|
||||||
[
|
icon,
|
||||||
icon,
|
plant['name'], # 种子名称
|
||||||
key,
|
plant['buy'], # 种子单价
|
||||||
plant['level'],
|
plant['level'], # 解锁等级
|
||||||
plant['price'],
|
plant['price'], # 果实单价
|
||||||
plant['experience'],
|
plant['experience'], # 收获经验
|
||||||
plant['harvest'],
|
plant['harvest'], # 收获数量
|
||||||
plant['time'],
|
plant['time'], # 成熟时间(小时)
|
||||||
plant['crop'],
|
plant['crop'], # 收获次数
|
||||||
plant['again'],
|
sell # 是否可上架交易行
|
||||||
sell
|
])
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
count = math.ceil(len(g_pJsonManager.m_pPlant['plant']) / 15)
|
# 页码标题
|
||||||
title = f"种子商店 页数: {num}/{count}"
|
title = f"种子商店 页数: {page}/{pageCount}"
|
||||||
|
|
||||||
|
# 渲染表格并返回图片bytes
|
||||||
result = await ImageTemplate.table_page(
|
result = await ImageTemplate.table_page(
|
||||||
title,
|
title,
|
||||||
"购买示例:@小真寻 购买种子 大白菜 5",
|
"购买示例:@小真寻 购买种子 大白菜 5",
|
||||||
column_name,
|
columnName,
|
||||||
data_list,
|
dataList,
|
||||||
)
|
)
|
||||||
|
|
||||||
return result.pic2bytes()
|
return result.pic2bytes()
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def buySeed(cls, uid: str, name: str, num: int = 1) -> str:
|
async def buySeed(cls, uid: str, name: str, num: int = 1) -> str:
|
||||||
"""购买种子
|
"""购买种子
|
||||||
@@ -92,29 +119,26 @@ class CShopManager:
|
|||||||
if num <= 0:
|
if num <= 0:
|
||||||
return "请输入购买数量!"
|
return "请输入购买数量!"
|
||||||
|
|
||||||
plantInfo = None
|
plantInfo = await g_pDBService.plant.getPlantByName(name)
|
||||||
|
if not plantInfo:
|
||||||
try:
|
|
||||||
plantInfo = g_pJsonManager.m_pPlant['plant'][name]
|
|
||||||
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['price']) * num
|
total = int(plantInfo['buy']) * num
|
||||||
|
|
||||||
logger.debug(f"用户:{uid}购买{name},数量为{num}。用户农场币为{point},购买需要{total}")
|
logger.debug(f"用户:{uid}购买{name},数量为{num}。用户农场币为{point},购买需要{total}")
|
||||||
|
|
||||||
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.addUserSeedByPlant(uid, name, num) == False:
|
if not await g_pDBService.userSeed.addUserSeedByUid(uid, name, num):
|
||||||
return "购买失败,执行数据库错误!"
|
return "购买失败,执行数据库错误!"
|
||||||
|
|
||||||
return f"成功购买{name},花费{total}农场币, 剩余{point - total}农场币"
|
return f"成功购买{name},花费{total}农场币, 剩余{point - total}农场币"
|
||||||
@@ -129,81 +153,52 @@ class CShopManager:
|
|||||||
Returns:
|
Returns:
|
||||||
str:
|
str:
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(name, str) or name.strip() == "":
|
||||||
|
name = ""
|
||||||
|
|
||||||
plant = await g_pSqlManager.getUserPlantByUid(uid)
|
plant = await g_pDBService.userPlant.getUserPlantByUid(uid)
|
||||||
|
if not plant:
|
||||||
if plant == None:
|
|
||||||
return "你仓库没有可以出售的作物"
|
return "你仓库没有可以出售的作物"
|
||||||
|
|
||||||
point = 0
|
point = 0
|
||||||
totalSold = 0
|
totalSold = 0
|
||||||
remainingItems = []
|
isAll = (num == -1)
|
||||||
|
|
||||||
isAll = False
|
if name == "":
|
||||||
if num == -1:
|
for plantName, count in plant.items():
|
||||||
isAll = True
|
plantInfo = await g_pDBService.plant.getPlantByName(plantName)
|
||||||
|
if not plantInfo:
|
||||||
|
continue
|
||||||
|
|
||||||
items = plant.split(',')
|
point += plantInfo['price'] * count
|
||||||
if len(name) <= 0:
|
await g_pDBService.userPlant.updateUserPlantByName(uid, plantName, 0)
|
||||||
#出售全部
|
|
||||||
for item in items:
|
|
||||||
if '|' in item:
|
|
||||||
plant_name, count_str = item.split('|', 1)
|
|
||||||
try:
|
|
||||||
count = int(count_str)
|
|
||||||
plant_info = g_pJsonManager.m_pPlant['plant'][plant_name]
|
|
||||||
point += plant_info['price'] * count
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
|
|
||||||
await g_pSqlManager.updateUserPlantByUid(uid, "") # 清空仓库
|
|
||||||
else:
|
else:
|
||||||
for item in items:
|
if name not in plant:
|
||||||
if '|' in item:
|
return f"出售作物{name}出错:仓库中不存在该作物"
|
||||||
plantName, countStr = item.split('|', 1)
|
available = plant[name]
|
||||||
try:
|
sellAmount = available if isAll else min(available, num)
|
||||||
count = int(countStr)
|
if sellAmount <= 0:
|
||||||
if plantName == name:
|
return f"出售作物{name}出错:数量不足"
|
||||||
|
await g_pDBService.userPlant.updateUserPlantByName(uid, name, available - sellAmount)
|
||||||
|
totalSold = sellAmount
|
||||||
|
|
||||||
if isAll:
|
if name == "":
|
||||||
sellAmount = count
|
totalPoint = point
|
||||||
else:
|
|
||||||
sellAmount = min(num, count)
|
|
||||||
|
|
||||||
totalSold += sellAmount
|
|
||||||
remaining = count - sellAmount
|
|
||||||
|
|
||||||
if remaining > 0:
|
|
||||||
remainingItems.append(f"{plantName}|{remaining}")
|
|
||||||
|
|
||||||
if isAll == False:
|
|
||||||
num -= sellAmount
|
|
||||||
|
|
||||||
break
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if num > 0 and isAll == False:
|
|
||||||
return f"出售作物{name}出错:数量不足"
|
|
||||||
|
|
||||||
#计算收益
|
|
||||||
try:
|
|
||||||
plantInfo = g_pJsonManager.m_pPlant['plant'][name]
|
|
||||||
totalPoint = plantInfo['price'] * totalSold
|
|
||||||
except KeyError:
|
|
||||||
return f"出售作物{name}出错:作物不存在"
|
|
||||||
|
|
||||||
#更新剩余作物
|
|
||||||
remainingPlant = ','.join(remainingItems) if remainingItems else ""
|
|
||||||
await g_pSqlManager.updateUserPlantByUid(uid, remainingPlant)
|
|
||||||
|
|
||||||
#更新农场币
|
|
||||||
p = await g_pSqlManager.getUserPointByUid(uid)
|
|
||||||
await g_pSqlManager.updateUserPointByUid(uid, p + totalPoint)
|
|
||||||
|
|
||||||
if name:
|
|
||||||
return f"成功出售{name},获得农场币:{totalPoint}"
|
|
||||||
else:
|
else:
|
||||||
return f"成功出售所有作物,获得农场币:{totalPoint}"
|
plantInfo = await g_pDBService.plant.getPlantByName(name)
|
||||||
|
if not plantInfo:
|
||||||
|
price = 0
|
||||||
|
else:
|
||||||
|
price = plantInfo['price']
|
||||||
|
|
||||||
|
totalPoint = totalSold * price
|
||||||
|
|
||||||
|
currentPoint = await g_pDBService.user.getUserPointByUid(uid)
|
||||||
|
await g_pDBService.user.updateUserPointByUid(uid, currentPoint + totalPoint)
|
||||||
|
|
||||||
|
if name == "":
|
||||||
|
return f"成功出售所有作物,获得农场币:{totalPoint},当前农场币:{currentPoint + totalPoint}"
|
||||||
|
else:
|
||||||
|
return f"成功出售{name},获得农场币:{totalPoint},当前农场币:{currentPoint + totalPoint}"
|
||||||
|
|
||||||
g_pShopManager = CShopManager()
|
g_pShopManager = CShopManager()
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
|
from . import config
|
||||||
|
from .request import g_pRequestManager
|
||||||
|
|
||||||
|
|
||||||
|
class CJsonManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.m_pItem = {}
|
||||||
|
self.m_pLevel = {}
|
||||||
|
self.m_pSoil = {}
|
||||||
|
self.m_pSign = {}
|
||||||
|
|
||||||
|
async def init(self) -> bool:
|
||||||
|
if not await self.initItem():
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not await self.initLevel():
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not await self.initSoil():
|
||||||
|
return False
|
||||||
|
|
||||||
|
return await self.initSignInFile()
|
||||||
|
|
||||||
|
async def initItem(self) -> bool:
|
||||||
|
try:
|
||||||
|
with open(
|
||||||
|
config.g_sConfigPath / "item.json",
|
||||||
|
encoding="utf-8",
|
||||||
|
) as file:
|
||||||
|
self.m_pItem = json.load(file)
|
||||||
|
|
||||||
|
return True
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.warning("item.json 打开失败")
|
||||||
|
return False
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logger.warning(f"item.json JSON格式错误: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def initLevel(self) -> bool:
|
||||||
|
try:
|
||||||
|
with open(
|
||||||
|
config.g_sConfigPath / "level.json",
|
||||||
|
encoding="utf-8",
|
||||||
|
) as file:
|
||||||
|
self.m_pLevel = json.load(file)
|
||||||
|
|
||||||
|
return True
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.warning("level.json 打开失败")
|
||||||
|
return False
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logger.warning(f"level.json JSON格式错误: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def initSoil(self) -> bool:
|
||||||
|
try:
|
||||||
|
with open(
|
||||||
|
config.g_sConfigPath / "soil.json",
|
||||||
|
encoding="utf-8",
|
||||||
|
) as file:
|
||||||
|
self.m_pSoil = json.load(file)
|
||||||
|
|
||||||
|
return True
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.warning("soil.json 打开失败")
|
||||||
|
return False
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logger.warning(f"soil.json JSON格式错误: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def initSignInFile(self) -> bool:
|
||||||
|
if not await g_pRequestManager.initSignInFile():
|
||||||
|
config.g_bSignStatus = False
|
||||||
|
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return await self.initSign()
|
||||||
|
|
||||||
|
async def initSign(self) -> bool:
|
||||||
|
try:
|
||||||
|
with open(
|
||||||
|
config.g_sSignInPath,
|
||||||
|
encoding="utf-8",
|
||||||
|
) as file:
|
||||||
|
self.m_pSign = json.load(file)
|
||||||
|
|
||||||
|
return True
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.warning("sign_in.json 打开失败")
|
||||||
|
return False
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logger.warning(f"sign_in.json JSON格式错误: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
g_pJsonManager = CJsonManager()
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# 真寻农场更新日志
|
||||||
|
|
||||||
|
## V1.4
|
||||||
|
用户方面
|
||||||
|
---
|
||||||
|
- 新增种子商店筛选功能(如果没有BUG的话后续我的种子、我的作物等也会加入筛选功能
|
||||||
|
- 新增签到功能(测试
|
||||||
|
- 新增农场详述功能,能通过该功能更加详细的观看农场数据
|
||||||
|
- 修复了迁移旧数据库无法正常迁移的BUG
|
||||||
|
- 修复了偷菜会导致偷自己的BUG
|
||||||
|
- 修正了作物阶段绘制不正确的BUG
|
||||||
|
|
||||||
|
代码方面
|
||||||
|
---
|
||||||
|
- 修正了多阶段作物成长素材计算逻辑
|
||||||
|
- 修正了作物数据库字段错乱的问题
|
||||||
|
- 作物新增offset字段,用于以后偏移坐标和大小(尚未启用,该模式有商议
|
||||||
|
|
||||||
|
## V1.3
|
||||||
|
用户方面
|
||||||
|
---
|
||||||
|
- 新增部分作物
|
||||||
|
- 修复一个严重BUG, 该BUG会导致无法重复偷别人菜的问题
|
||||||
|
|
||||||
|
代码方面
|
||||||
|
---
|
||||||
|
- 将作物数据从plant.json迁移至resource/db/plant.db中,并将操作json改为操作数据库
|
||||||
|
|
||||||
|
## V1.2
|
||||||
|
用户方面
|
||||||
|
---
|
||||||
|
- 修复重大BUG,该BUG会导致用户经验等级计算异常
|
||||||
|
- 修正种子价格,现在不会以单价的形式购买种子了,而是新增的种子价格
|
||||||
|
|
||||||
|
代码方面
|
||||||
|
---
|
||||||
|
- 彻底重构数据库,但是不会像V1.1一样导致用户数据丢失
|
||||||
|
- 迁移部分代码结构,使文件名和代码功能更加匹配
|
||||||
|
- 加入事件机制,采用类似Qt信号槽机制
|
||||||
|
|
||||||
|
## V1.1
|
||||||
|
用户方面
|
||||||
|
---
|
||||||
|
- 完善我的农场图片资源,现在会在左上角显示经验、等级、金币等详细信息了
|
||||||
|
- 完善出售作物逻辑,现在可以空置作物名称来一键出售全部作物了,也可以选择空置数量来一键出售仓库种指定作物
|
||||||
|
- 完善播种逻辑,现在可以空置数量来一键播种指定作物了
|
||||||
|
- 新增更改农场名指令
|
||||||
|
- 改进对土地开垦条件判断
|
||||||
|
|
||||||
|
代码方面
|
||||||
|
---
|
||||||
|
- 感谢[quanquan1014](https://github.com/quanquan1014)对农场名称中包含特殊字符的处理。
|
||||||
|
- 更正数据库写法,但是会导致V1.0用户的作物和种子丢失
|
||||||
|
|
||||||
|
## V1.0
|
||||||
|
|
||||||
|
世界的起源。
|
||||||
@@ -1,43 +1,168 @@
|
|||||||
from unittest import result
|
import json
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
import requests
|
import httpx
|
||||||
|
|
||||||
|
from zhenxun.configs.config import Config
|
||||||
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
|
from .config import g_sSignInPath
|
||||||
|
from .tool import g_pToolManager
|
||||||
|
|
||||||
|
|
||||||
class CRequestManager:
|
class CRequestManager:
|
||||||
|
m_sTokens = "xZ%?z5LtWV7H:0-Xnwp+bNRNQ-jbfrxG"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def post(cls, url, json_data=None, form_data=None):
|
async def download(cls,
|
||||||
"""发送 POST 请求(支持 JSON/Form-Data 格式)"""
|
url: str,
|
||||||
|
savePath: str,
|
||||||
|
fileName: str,
|
||||||
|
params: dict | None = None,
|
||||||
|
jsonData: dict | None = None) -> bool:
|
||||||
|
"""下载文件到指定路径并覆盖已存在的文件
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url (str): 文件的下载链接
|
||||||
|
savePath (str): 保存文件夹路径
|
||||||
|
fileName (str): 保存后的文件名
|
||||||
|
params (dict | None): 可选的 URL 查询参数
|
||||||
|
jsonData (dict | None): 可选的 JSON 请求体
|
||||||
|
Returns:
|
||||||
|
bool: 是否下载成功
|
||||||
|
"""
|
||||||
|
headers = {"token": cls.m_sTokens}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
headers = {"Content-Type": "application/json"} if json_data else None
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||||
response = requests.post(
|
requestArgs: dict = {"headers": headers}
|
||||||
url,
|
if params:
|
||||||
json=json_data,
|
requestArgs["params"] = params
|
||||||
data=form_data,
|
if jsonData:
|
||||||
headers=headers,
|
requestArgs["json"] = jsonData
|
||||||
timeout=5
|
|
||||||
)
|
response = await client.request("GET", url, **requestArgs)
|
||||||
response.raise_for_status()
|
|
||||||
return response.json()
|
if response.status_code == 200:
|
||||||
except requests.exceptions.RequestException as e:
|
fullPath = os.path.join(savePath, fileName)
|
||||||
print(f"请求失败: {e}")
|
os.makedirs(os.path.dirname(fullPath), exist_ok=True)
|
||||||
|
with open(fullPath, "wb") as f:
|
||||||
|
f.write(response.content)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warning(f"文件下载失败: HTTP {response.status_code} {response.text}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"下载文件异常: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def post(cls, endpoint: str, name: str = "", jsonData: dict = {}) -> dict:
|
||||||
|
"""发送POST请求到指定接口,统一调用,仅支持JSON格式数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
endpoint (str): 请求的接口路径
|
||||||
|
name (str, optional): 操作名称用于日志记录
|
||||||
|
jsonData (dict): 以JSON格式发送的数据
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: 当jsonData未提供时抛出
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 返回请求结果的JSON数据
|
||||||
|
"""
|
||||||
|
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
||||||
|
url = f"{baseUrl.rstrip('/')}/{endpoint.lstrip('/')}"
|
||||||
|
headers = {"token": cls.m_sTokens}
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient(timeout=5.0) as client:
|
||||||
|
response = await client.post(url, json=jsonData, 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 {}
|
return {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def sign(cls, uid: str) -> str:
|
async def get(cls, endpoint: str, name: str = "") -> dict:
|
||||||
a = await cls.post("http://diuse.work:9099/testPost", json_data={"level":3})
|
"""发送GET请求到指定接口,统一调用,仅支持无体的查询
|
||||||
|
|
||||||
result = ""
|
Args:
|
||||||
|
endpoint (str): 请求的接口路径
|
||||||
|
name (str, optional): 操作名称用于日志记录
|
||||||
|
|
||||||
type = int(a["type"])
|
Returns:
|
||||||
if type == 1:
|
dict: 返回请求结果的JSON数据
|
||||||
result = f"签到成功 type = 1"
|
"""
|
||||||
elif type == 2:
|
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
||||||
result = f"签到成功 type = 2"
|
url = f"{baseUrl.rstrip('/')}/{endpoint.lstrip('/')}"
|
||||||
|
headers = {"token": cls.m_sTokens}
|
||||||
|
|
||||||
|
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
|
||||||
|
async def initSignInFile(cls) -> bool:
|
||||||
|
if os.path.exists(g_sSignInPath):
|
||||||
|
try:
|
||||||
|
with open(g_sSignInPath, "r", encoding="utf-8") as f:
|
||||||
|
content = f.read()
|
||||||
|
sign = json.loads(content)
|
||||||
|
|
||||||
|
date = sign.get("date", "")
|
||||||
|
yearMonth = g_pToolManager.dateTime().now().strftime("%Y%m")
|
||||||
|
|
||||||
|
if date == yearMonth:
|
||||||
|
logger.debug("真寻农场签到文件检查完毕")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warning("真寻农场签到文件检查失败, 即将下载")
|
||||||
|
return await cls.downloadSignInFile()
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logger.warning(f"真寻农场签到文件格式错误, 即将下载")
|
||||||
|
return await cls.downloadSignInFile()
|
||||||
else:
|
else:
|
||||||
result = f"签到成功 type = {type}"
|
return await cls.downloadSignInFile()
|
||||||
|
|
||||||
return result
|
@classmethod
|
||||||
|
async def downloadSignInFile(cls) -> bool:
|
||||||
|
try:
|
||||||
|
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
||||||
|
|
||||||
|
url = f"{baseUrl.rstrip('/')}:8998/sign_in"
|
||||||
|
path = str(g_sSignInPath.parent.resolve(strict=False))
|
||||||
|
yearMonth = g_pToolManager.dateTime().now().strftime("%Y%m")
|
||||||
|
|
||||||
|
await cls.download(url, path, "signTemp.json", jsonData={'date':yearMonth})
|
||||||
|
g_pToolManager.renameFile(f"{path}/signTemp.json", "sign_in.json")
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("下载签到文件失败", e=e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
g_pRequestManager = CRequestManager()
|
g_pRequestManager = CRequestManager()
|
||||||
|
|||||||
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 744 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |