11
This commit is contained in:
+39
-1
@@ -47,6 +47,44 @@
|
|||||||
"adventureName": "阴阳两界",
|
"adventureName": "阴阳两界",
|
||||||
"serverName": "眉间雪"
|
"serverName": "眉间雪"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"aijx3_SearchData":{
|
||||||
|
"url":"https://www.aijx3.cn/api2/aijx3-wblwg/basedata/getSearchData",
|
||||||
|
"method":"POST",
|
||||||
|
"description":"获取爱剑网三所有外观数据",
|
||||||
|
"params":{
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jx3box_exterior":{
|
||||||
|
"url":"https://node.jx3box.com/api/node/v2/exterior/list",
|
||||||
|
"method":"GET",
|
||||||
|
"description":"魔盒中模糊查找剑网三的外观数据",
|
||||||
|
"params":{
|
||||||
|
"keyword": "秃盒",
|
||||||
|
"tradable": "0",
|
||||||
|
"list_type": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"aijx3_GoodsDetail":{
|
||||||
|
"url":"https://www.aijx3.cn/api2/aijx3-wj/goods/getGoodsDetail",
|
||||||
|
"method":"POST",
|
||||||
|
"description":"在爱剑网三中获取外观详细数据",
|
||||||
|
"params":{
|
||||||
|
"goodsName": "秃盒"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"aijx3_wblwg":{
|
||||||
|
"url":"https://www.aijx3.cn/api2/aijx3-wblwg/record/queryByCondition",
|
||||||
|
"method":"POST",
|
||||||
|
"description":"在爱剑网三中获取外观详细数据",
|
||||||
|
"params":{
|
||||||
|
"accoSeq": "",
|
||||||
|
"orderMode": 1,
|
||||||
|
"orderBy": "price_num",
|
||||||
|
"searchId": [48356],
|
||||||
|
"current": 1,
|
||||||
|
"size": 10,
|
||||||
|
"tradeStatus": ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def load_template(template_name):
|
||||||
|
"""
|
||||||
|
从模板文件加载模板内容
|
||||||
|
|
||||||
|
Args:
|
||||||
|
template_name: 模板文件名(不带路径)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 模板内容
|
||||||
|
"""
|
||||||
|
# 获取模板文件路径
|
||||||
|
plugin_dir = Path(__file__).parent.parent
|
||||||
|
template_path = plugin_dir / "templates" / template_name
|
||||||
|
|
||||||
|
# 检查文件是否存在
|
||||||
|
if not template_path.exists():
|
||||||
|
raise FileNotFoundError(f"模板文件不存在: {template_path}")
|
||||||
|
|
||||||
|
# 读取模板内容
|
||||||
|
with open(template_path, 'r', encoding='utf-8') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def extract_field(data_list, field_name):
|
||||||
|
"""
|
||||||
|
从列表中的字典提取指定字段的所有值
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data_list (list): 数据列表,每个元素是 dict
|
||||||
|
field_name (str): 要提取的字段名
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: 提取出来的字段值列表
|
||||||
|
"""
|
||||||
|
extracted_data = []
|
||||||
|
for item in data_list:
|
||||||
|
if field_name in item and item[field_name]:
|
||||||
|
# 如果是 list,展开
|
||||||
|
if isinstance(item[field_name], list):
|
||||||
|
extracted_data.extend(item[field_name])
|
||||||
|
else:
|
||||||
|
extracted_data.append(item[field_name])
|
||||||
|
return extracted_data
|
||||||
+184
-2
@@ -3,11 +3,13 @@ from datetime import datetime
|
|||||||
from astrbot.api import logger
|
from astrbot.api import logger
|
||||||
|
|
||||||
from .api_data import APIClient
|
from .api_data import APIClient
|
||||||
from .load_template import load_template
|
from .sql_data import AsyncMySQL
|
||||||
|
from .basic_Function import load_template, extract_field
|
||||||
|
|
||||||
class JX3Function:
|
class JX3Function:
|
||||||
def __init__(self, api_config):
|
def __init__(self, api_config,db: AsyncMySQL ):
|
||||||
self.__api = APIClient()
|
self.__api = APIClient()
|
||||||
|
self.__db = db
|
||||||
self.__api_config = api_config
|
self.__api_config = api_config
|
||||||
|
|
||||||
async def richang(self,server: str = "眉间雪",num: int = 0):
|
async def richang(self,server: str = "眉间雪",num: int = 0):
|
||||||
@@ -200,3 +202,183 @@ class JX3Function:
|
|||||||
return return_data
|
return return_data
|
||||||
return_data["code"] = 200
|
return_data["code"] = 200
|
||||||
return return_data
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
async def SearchData(self):
|
||||||
|
return_data = {
|
||||||
|
"code": 0,
|
||||||
|
"msg": "功能函数未执行",
|
||||||
|
"data": {}
|
||||||
|
}
|
||||||
|
#在配置文件中获取接口配置
|
||||||
|
api_config = self.__api_config["aijx3_SearchData"]
|
||||||
|
# 获取数据
|
||||||
|
data = await self.__api.post(api_config["url"],api_config["params"],"data")
|
||||||
|
if not data:
|
||||||
|
return_data["msg"] = "获取接口信息失败"
|
||||||
|
return return_data
|
||||||
|
# 提取数据
|
||||||
|
try:
|
||||||
|
extracted_data = extract_field(data, "dataModels")
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"提取数据失败: {e}")
|
||||||
|
return_data["msg"] = "提取指定数据字段失败"
|
||||||
|
return return_data
|
||||||
|
# 准备SQL和批量数据
|
||||||
|
sql = """
|
||||||
|
INSERT INTO searchdata
|
||||||
|
(typeName, name, showName, picUrl, searchId, searchDescType)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s)
|
||||||
|
"""
|
||||||
|
values_list = [
|
||||||
|
(
|
||||||
|
item['typeName'],
|
||||||
|
item['name'],
|
||||||
|
item['showName'],
|
||||||
|
item['picUrl'],
|
||||||
|
item['searchId'],
|
||||||
|
item['searchDescType']
|
||||||
|
)
|
||||||
|
for item in extracted_data
|
||||||
|
]
|
||||||
|
# 插入数据
|
||||||
|
try:
|
||||||
|
await self.__db.truncate_table("searchdata")
|
||||||
|
await self.__db.executemany(sql, values_list)
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"数据插入失败: {e}")
|
||||||
|
return_data["msg"] = "数据插入数据库失败"
|
||||||
|
return return_data
|
||||||
|
return_data["msg"] = f"成功批量插入 {len(extracted_data)} 条数据!"
|
||||||
|
return_data["code"] = 200
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
async def wujia(self,Name: str = "秃盒"):
|
||||||
|
return_data = {
|
||||||
|
"code": 0,
|
||||||
|
"msg": "功能函数未执行",
|
||||||
|
"data": {}
|
||||||
|
}
|
||||||
|
#在配置文件中获取接口配置
|
||||||
|
api_config = self.__api_config["jx3box_exterior"]
|
||||||
|
#更新参数
|
||||||
|
api_config["params"]["keyword"] = Name
|
||||||
|
# 获取查找信息
|
||||||
|
data = await self.__api.get(api_config["url"],api_config["params"],"data")
|
||||||
|
if data["total"] == 0:
|
||||||
|
try:
|
||||||
|
sql = "SELECT showName, searchId FROM searchdata WHERE name = %s OR showName = %s"
|
||||||
|
sqldata = await self.__db.fetch_one(sql, (Name,Name))
|
||||||
|
searchId=sqldata["searchId"]
|
||||||
|
showName=sqldata["showName"]
|
||||||
|
return_data["data"]["searchId"]=searchId
|
||||||
|
return_data["data"]["showName"]=showName
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取外观信息错误: {e}")
|
||||||
|
return_data["msg"] = "未找到该外观信息"
|
||||||
|
return return_data
|
||||||
|
else:
|
||||||
|
# 查询魔盒获取外观名称
|
||||||
|
try:
|
||||||
|
showName=data["list"][0]["name"]
|
||||||
|
return_data["data"]["showName"]=showName
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取外观名称错误: {e}")
|
||||||
|
return_data["msg"] = "未找到该外观信息"
|
||||||
|
return return_data
|
||||||
|
# 查询爱剑三获取外观ID
|
||||||
|
try:
|
||||||
|
sql = "SELECT searchId FROM searchdata WHERE showName=%s"
|
||||||
|
sqldata = await self.__db.fetch_one(sql, (return_data["data"]["showName"],))
|
||||||
|
searchId=sqldata["searchId"]
|
||||||
|
return_data["data"]["searchId"]=searchId
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取外观ID错误: {e}\n{return_data['data']['showName']}")
|
||||||
|
return_data["msg"] = "未找到该外观信息"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
#在配置文件中获取接口配置
|
||||||
|
api_config = self.__api_config["aijx3_GoodsDetail"]
|
||||||
|
#更新参数
|
||||||
|
api_config["params"]["goodsName"] = showName
|
||||||
|
# 获取数据外观详细数据
|
||||||
|
data = await self.__api.post(api_config["url"],api_config["params"],"data")
|
||||||
|
if not data:
|
||||||
|
return_data["msg"] = "获取外观详细数据失败"
|
||||||
|
return return_data
|
||||||
|
# 提取外观详细数据
|
||||||
|
try:
|
||||||
|
imgs = data.get("imgs", [])
|
||||||
|
return_data["data"]["goodsDesc"]=data.get("goodsDesc", "无描述")
|
||||||
|
return_data["data"]["publishTime"]=data.get("publishTime", "无价格")
|
||||||
|
return_data["data"]["priceNum"]=data.get("priceNum", 0)
|
||||||
|
return_data["data"]["goodsId"]=data.get("goodsId", "无数据")
|
||||||
|
return_data["data"]["imgs"]=imgs[0] if imgs else ""
|
||||||
|
return_data["data"]["goodsAlias"]=data.get("goodsAlias", "无别名")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"提取外观详细数据失败: {e}")
|
||||||
|
return_data["msg"] = "提取外观详细数据失败"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
# 查询万宝楼数据(公示和在售)
|
||||||
|
wbl_data = await self.__get_wbl_data(searchId)
|
||||||
|
if not wbl_data:
|
||||||
|
return_data["msg"] = "获取万宝楼数据失败"
|
||||||
|
return return_data
|
||||||
|
if wbl_data:
|
||||||
|
return_data["data"]["wblgs"]=wbl_data["wblgs"]
|
||||||
|
return_data["data"]["wblzs"]=wbl_data["wblzs"]
|
||||||
|
return_data["msg"] = "获取万宝楼数据完成"
|
||||||
|
# 加载模板
|
||||||
|
try:
|
||||||
|
return_data["temp"] = load_template("wujia.html")
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"加载模板失败: {e}")
|
||||||
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
|
return return_data
|
||||||
|
return_data["code"] = 200
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
async def __get_wbl_data(self,search_id):
|
||||||
|
"""获取万宝楼数据(公示和在售)"""
|
||||||
|
try:
|
||||||
|
#在配置文件中获取接口配置
|
||||||
|
api_config = self.__api_config["aijx3_wblwg"]
|
||||||
|
#更新参数
|
||||||
|
api_config["params"]["searchId"] = [search_id]
|
||||||
|
# 获取公示数据
|
||||||
|
api_config["params"]["tradeStatus"] = "3"
|
||||||
|
datawblgs = await self.__api.post(api_config["url"], api_config["params"], "data")
|
||||||
|
# 获取在售数据
|
||||||
|
api_config["params"]["tradeStatus"] = "5"
|
||||||
|
datawblzs = await self.__api.post(api_config["url"], api_config["params"], "data")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"wblgs": await self.__process_wbl_records(datawblgs.get("records", [])),
|
||||||
|
"wblzs": await self.__process_wbl_records(datawblzs.get("records", []))
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取万宝楼数据出错: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def __process_wbl_records(self,records):
|
||||||
|
"""处理万宝楼记录数据"""
|
||||||
|
processed = []
|
||||||
|
for record in records:
|
||||||
|
try:
|
||||||
|
# 转换时间戳为可读格式
|
||||||
|
timestamp = record.get("replyTime", 0)
|
||||||
|
dt = datetime.fromtimestamp(timestamp / 1000) if timestamp else datetime.now()
|
||||||
|
|
||||||
|
processed.append({
|
||||||
|
"priceNum": record.get("priceNum", 0),
|
||||||
|
"belongQf2": record.get("belongQf2", "无数据"),
|
||||||
|
"replyTime": dt.strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
"discountRate": record.get("discountRate", 0.0),
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"处理万宝楼记录出错: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
return processed
|
||||||
+1
-2
@@ -4,7 +4,6 @@ from urllib.parse import quote
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from .api_data import api_data_get, api_data_post
|
from .api_data import api_data_get, api_data_post
|
||||||
from .sql_data import sql_data_searchdata,sql_data_select
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_all_pages(base_url, initial_params, max_pages=None):
|
def fetch_all_pages(base_url, initial_params, max_pages=None):
|
||||||
@@ -204,7 +203,7 @@ def jx3_data_wujia(inname="秃盒"):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# 1. 获取物品ID和名称
|
# 1. 获取物品ID和名称
|
||||||
idname = sql_data_select(inname)
|
idname = api_data_get(inname)
|
||||||
if not idname:
|
if not idname:
|
||||||
datas.update({"code": 201, "msg": "未找到该外观信息"})
|
datas.update({"code": 201, "msg": "未找到该外观信息"})
|
||||||
return datas
|
return datas
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def load_template(template_name):
|
|
||||||
"""
|
|
||||||
从模板文件加载模板内容
|
|
||||||
|
|
||||||
Args:
|
|
||||||
template_name: 模板文件名(不带路径)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 模板内容
|
|
||||||
"""
|
|
||||||
# 获取模板文件路径
|
|
||||||
plugin_dir = Path(__file__).parent.parent
|
|
||||||
template_path = plugin_dir / "templates" / template_name
|
|
||||||
|
|
||||||
# 检查文件是否存在
|
|
||||||
if not template_path.exists():
|
|
||||||
raise FileNotFoundError(f"模板文件不存在: {template_path}")
|
|
||||||
|
|
||||||
# 读取模板内容
|
|
||||||
with open(template_path, 'r', encoding='utf-8') as f:
|
|
||||||
return f.read()
|
|
||||||
+54
-98
@@ -1,107 +1,63 @@
|
|||||||
import pymysql
|
import aiomysql
|
||||||
import time
|
|
||||||
from astrbot.api import logger
|
|
||||||
from .api_data import api_data_get, api_data_post
|
|
||||||
|
|
||||||
|
|
||||||
#连接数据库配置
|
class AsyncMySQL:
|
||||||
db_config = {
|
def __init__(self, db_config: dict):
|
||||||
'host': '154.201.70.116',
|
self.db_config = db_config
|
||||||
'port': 3306,
|
self.pool = None
|
||||||
'user': 'asrtbot',
|
|
||||||
'password': 'qsc123456',
|
|
||||||
'database': 'asrtbot',
|
|
||||||
'charset': 'utf8mb4',
|
|
||||||
'cursorclass': pymysql.cursors.DictCursor #返回字典格式数据
|
|
||||||
}
|
|
||||||
|
|
||||||
# 提取所有 dataModels 中的数据
|
async def init_pool(self):
|
||||||
def extract_data_models(source_data):
|
"""初始化连接池"""
|
||||||
extracted_data = []
|
if self.pool is None:
|
||||||
for category in source_data:
|
self.pool = await aiomysql.create_pool(**self.db_config)
|
||||||
if "dataModels" in category and category["dataModels"]:
|
|
||||||
extracted_data.extend(category["dataModels"])
|
|
||||||
return extracted_data
|
|
||||||
|
|
||||||
# 获取并存储搜索数据
|
async def close_pool(self):
|
||||||
def sql_data_searchdata():
|
"""关闭连接池"""
|
||||||
# 接口配置
|
if self.pool:
|
||||||
custom_url = "https://www.aijx3.cn/api2/aijx3-wblwg/basedata/getSearchData"
|
self.pool.close()
|
||||||
params = {}
|
await self.pool.wait_closed()
|
||||||
|
self.pool = None
|
||||||
|
|
||||||
try:
|
async def fetch_one(self, sql: str, params=None):
|
||||||
# 获取数据
|
"""查询单条数据"""
|
||||||
source_data = api_data_post(custom_url, params, "data")
|
await self.init_pool()
|
||||||
if not source_data:
|
async with self.pool.acquire() as conn:
|
||||||
return "获取数据失败或数据为空"
|
async with conn.cursor(aiomysql.DictCursor) as cursor:
|
||||||
|
await cursor.execute(sql, params or ())
|
||||||
|
return await cursor.fetchone()
|
||||||
|
|
||||||
# 处理数据
|
async def fetch_all(self, sql: str, params=None):
|
||||||
extracted_data = extract_data_models(source_data)
|
"""查询多条数据"""
|
||||||
if not extracted_data: # 修正:应该是extracted_data而不是source_data
|
await self.init_pool()
|
||||||
return "未提取到数据"
|
async with self.pool.acquire() as conn:
|
||||||
|
async with conn.cursor(aiomysql.DictCursor) as cursor:
|
||||||
|
await cursor.execute(sql, params or ())
|
||||||
|
return await cursor.fetchall()
|
||||||
|
|
||||||
# 连接数据库并插入数据
|
async def execute(self, sql: str, params=None):
|
||||||
with pymysql.connect(**db_config) as connection:
|
"""执行单条 SQL(insert/update/delete)"""
|
||||||
with connection.cursor() as cursor:
|
await self.init_pool()
|
||||||
# 清空表数据
|
async with self.pool.acquire() as conn:
|
||||||
cursor.execute("TRUNCATE TABLE searchdata")
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute(sql, params or ())
|
||||||
|
await conn.commit()
|
||||||
|
return cursor.rowcount
|
||||||
|
|
||||||
# 准备SQL和批量数据
|
async def executemany(self, sql: str, params_list):
|
||||||
sql = """
|
"""批量执行 SQL"""
|
||||||
INSERT INTO searchdata
|
await self.init_pool()
|
||||||
(typeName, name, showName, picUrl, searchId, searchDescType)
|
async with self.pool.acquire() as conn:
|
||||||
VALUES (%s, %s, %s, %s, %s, %s)
|
async with conn.cursor() as cursor:
|
||||||
"""
|
await cursor.executemany(sql, params_list)
|
||||||
|
await conn.commit()
|
||||||
|
return cursor.rowcount
|
||||||
|
|
||||||
# 使用列表推导式简化数据准备
|
async def truncate_table(self, table_name: str):
|
||||||
values_list = [
|
"""清空指定表的内容"""
|
||||||
(
|
await self.init_pool()
|
||||||
item['typeName'],
|
async with self.pool.acquire() as conn:
|
||||||
item['name'],
|
async with conn.cursor() as cursor:
|
||||||
item['showName'],
|
sql = f"TRUNCATE TABLE `{table_name}`"
|
||||||
item['picUrl'],
|
await cursor.execute(sql)
|
||||||
item['searchId'],
|
await conn.commit()
|
||||||
item['searchDescType']
|
return True
|
||||||
)
|
|
||||||
for item in extracted_data
|
|
||||||
]
|
|
||||||
|
|
||||||
# 批量插入
|
|
||||||
cursor.executemany(sql, values_list)
|
|
||||||
connection.commit()
|
|
||||||
|
|
||||||
return f"成功批量插入 {len(extracted_data)} 条数据!"
|
|
||||||
|
|
||||||
except pymysql.Error as e:
|
|
||||||
return f"数据库操作失败: {e}"
|
|
||||||
except Exception as e:
|
|
||||||
return f"操作失败: {e}"
|
|
||||||
|
|
||||||
# 根据搜索字符串查询匹配的数据
|
|
||||||
def sql_data_select(search_string):
|
|
||||||
"""
|
|
||||||
根据搜索字符串查询匹配的数据
|
|
||||||
|
|
||||||
Args:
|
|
||||||
search_string: 搜索字符串
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
匹配的第一条记录,如果没有匹配则返回None
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
with pymysql.connect(**db_config) as connection:
|
|
||||||
with connection.cursor() as cursor:
|
|
||||||
sql = """
|
|
||||||
SELECT searchId, showName, name
|
|
||||||
FROM searchdata
|
|
||||||
WHERE name = %s OR showName = %s
|
|
||||||
LIMIT 1
|
|
||||||
"""
|
|
||||||
params = (search_string, search_string)
|
|
||||||
|
|
||||||
cursor.execute(sql, params)
|
|
||||||
return cursor.fetchone()
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"查询数据时出错: {e}")
|
|
||||||
return None
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ from astrbot.api.star import Context, Star, register, StarTools
|
|||||||
from astrbot.api import logger
|
from astrbot.api import logger
|
||||||
from astrbot.api import AstrBotConfig
|
from astrbot.api import AstrBotConfig
|
||||||
|
|
||||||
from .core.load_template import load_template
|
from .core.basic_Function import load_template
|
||||||
from .core.jx3_data import jx3_data_jiaoyihang,jx3_data_wujia
|
from .core.jx3_data import jx3_data_jiaoyihang,jx3_data_wujia
|
||||||
from .core.sql_data import sql_data_searchdata,sql_data_select
|
from .core.sql_data import AsyncMySQL
|
||||||
from .core.jx3_Function import JX3Function
|
from .core.jx3_Function import JX3Function
|
||||||
|
|
||||||
|
|
||||||
@@ -31,12 +31,25 @@ class Jx3ApiPlugin(Star):
|
|||||||
# 读取文件内容
|
# 读取文件内容
|
||||||
with open(self.api_file_path, 'r', encoding='utf-8') as f:
|
with open(self.api_file_path, 'r', encoding='utf-8') as f:
|
||||||
self.api_config = json.load(f)
|
self.api_config = json.load(f)
|
||||||
|
|
||||||
logger.info("jx3api插件初始化完成")
|
logger.info("jx3api插件初始化完成")
|
||||||
|
|
||||||
|
|
||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
"""可选择实现异步的插件初始化方法,当实例化该插件类之后会自动调用该方法。"""
|
"""可选择实现异步的插件初始化方法,当实例化该插件类之后会自动调用该方法。"""
|
||||||
|
# 数据库配置
|
||||||
|
db_config = {
|
||||||
|
'host': '154.201.70.116',
|
||||||
|
'port': 3306,
|
||||||
|
'user': 'asrtbot',
|
||||||
|
'password': 'qsc123456',
|
||||||
|
'db': 'asrtbot',
|
||||||
|
'charset': 'utf8mb4',
|
||||||
|
'autocommit': True
|
||||||
|
}
|
||||||
#创建类实例
|
#创建类实例
|
||||||
self.jx3fun = JX3Function(self.api_config)
|
self.db = AsyncMySQL(db_config)
|
||||||
|
self.jx3fun = JX3Function(self.api_config,self.db)
|
||||||
logger.info("jx3api插件创建实例完成")
|
logger.info("jx3api插件创建实例完成")
|
||||||
|
|
||||||
|
|
||||||
@@ -136,6 +149,38 @@ class Jx3ApiPlugin(Star):
|
|||||||
yield event.plain_result("猪脑过载,请稍后再试")
|
yield event.plain_result("猪脑过载,请稍后再试")
|
||||||
|
|
||||||
|
|
||||||
|
@jx3.command("物价")
|
||||||
|
async def jx3_wujia(self, event: AstrMessageEvent,Name: str = "秃盒"):
|
||||||
|
"""剑三 外观名称"""
|
||||||
|
try:
|
||||||
|
data=await self.jx3fun.wujia(Name)
|
||||||
|
if data["code"] == 200:
|
||||||
|
url = await self.html_render(data["temp"], data["data"], options={})
|
||||||
|
yield event.image_result(url)
|
||||||
|
else:
|
||||||
|
yield event.plain_result(data["msg"])
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"功能函数执行错误: {e}")
|
||||||
|
yield event.plain_result("猪脑过载,请稍后再试")
|
||||||
|
|
||||||
|
|
||||||
|
@filter.permission_type(filter.PermissionType.ADMIN)
|
||||||
|
@jx3.command("外观数据同步")
|
||||||
|
async def jx3_SearchData(self, event: AstrMessageEvent):
|
||||||
|
"""剑三外观数据同步"""
|
||||||
|
try:
|
||||||
|
data=await self.jx3fun.SearchData()
|
||||||
|
if data["code"] == 200:
|
||||||
|
yield event.plain_result(data["msg"])
|
||||||
|
else:
|
||||||
|
yield event.plain_result(data["msg"])
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"功能函数执行错误: {e}")
|
||||||
|
yield event.plain_result("猪脑过载,请稍后再试")
|
||||||
|
|
||||||
|
|
||||||
@filter.command("剑三交易行")
|
@filter.command("剑三交易行")
|
||||||
async def jx3_data_jiaoyihang(self, event: AstrMessageEvent):
|
async def jx3_data_jiaoyihang(self, event: AstrMessageEvent):
|
||||||
"""剑三交易行 物品名称 服务器"""
|
"""剑三交易行 物品名称 服务器"""
|
||||||
@@ -192,21 +237,11 @@ class Jx3ApiPlugin(Star):
|
|||||||
yield event.plain_result("查询交易行数据时出错,请稍后再试")
|
yield event.plain_result("查询交易行数据时出错,请稍后再试")
|
||||||
|
|
||||||
|
|
||||||
@filter.command("剑三外观数据同步")
|
|
||||||
async def jx3_SearchData(self, event: AstrMessageEvent):
|
|
||||||
"""剑三外观数据同步"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
test = sql_data_searchdata()
|
|
||||||
yield event.plain_result(f"{test}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"处理数据时出错: {e}")
|
|
||||||
yield event.plain_result("处理接口返回信息时出错")
|
|
||||||
|
|
||||||
|
|
||||||
@filter.command("剑三物价")
|
@filter.command("剑三物价")
|
||||||
async def jx3_wujia(self, event: AstrMessageEvent):
|
async def jx3_wujia11(self, event: AstrMessageEvent):
|
||||||
"""剑三物价 外观名称"""
|
"""剑三物价 外观名称"""
|
||||||
|
|
||||||
inname = "秃盒"
|
inname = "秃盒"
|
||||||
@@ -245,3 +280,5 @@ class Jx3ApiPlugin(Star):
|
|||||||
|
|
||||||
async def terminate(self):
|
async def terminate(self):
|
||||||
"""可选择实现异步的插件销毁方法,当插件被卸载/停用时会调用。"""
|
"""可选择实现异步的插件销毁方法,当插件被卸载/停用时会调用。"""
|
||||||
|
await self.db.close_pool()
|
||||||
|
logger.info("jx3api插件已卸载/停用")
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
pymysql
|
aiomysql
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{ data.showName }} - 剑网3万宝楼</title>
|
<title>{{ showName }} - 剑网3万宝楼</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
<style>
|
<style>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
.product-img {
|
.product-img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 850px;
|
max-width: 1000px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
@@ -117,19 +117,19 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- 图片在上方 -->
|
<!-- 图片在上方 -->
|
||||||
<div class="col-12 text-center mb-3">
|
<div class="col-12 text-center mb-3">
|
||||||
<img src="{{ data.imgs }}" alt="{{ data.showName }}" class="product-img">
|
<img src="{{ imgs }}" alt="{{ showName }}" class="product-img">
|
||||||
</div>
|
</div>
|
||||||
<!-- 文字信息在下方 -->
|
<!-- 文字信息在下方 -->
|
||||||
<div class="col-12 text-center">
|
<div class="col-12 text-center">
|
||||||
<h2 class="mb-2">{{ data.showName }}({{data.Name}})</h2>
|
<h2 class="mb-2">{{ showName }}({{goodsAlias}})</h2>
|
||||||
<p class="mb-3">{{ data.goodsDesc }}</p>
|
<p class="mb-3">{{ goodsDesc }}</p>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<strong>发布时间:</strong> {{ data.publishTime.split('T')[0] if data.publishTime else '未知' }}
|
<strong>发布时间:</strong> {{ publishTime.split('T')[0] if publishTime else '未知' }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<strong>参考价格:</strong> <span class="price-tag">{{ data.priceNum }} 元</span>
|
<strong>参考价格:</strong> <span class="price-tag">{{ priceNum }} 元</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -156,7 +156,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in data.wblgs %}
|
{% for item in wblgs %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ item.replyTime }}</td>
|
<td>{{ item.replyTime }}</td>
|
||||||
<td><span class="server-badge">{{ item.belongQf2 }}</span></td>
|
<td><span class="server-badge">{{ item.belongQf2 }}</span></td>
|
||||||
@@ -191,7 +191,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in data.wblzs %}
|
{% for item in wblzs %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ item.replyTime }}</td>
|
<td>{{ item.replyTime }}</td>
|
||||||
<td><span class="server-badge">{{ item.belongQf2 }}</span></td>
|
<td><span class="server-badge">{{ item.belongQf2 }}</span></td>
|
||||||
|
|||||||
+120
-122
@@ -7,32 +7,107 @@
|
|||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
<style>
|
<style>
|
||||||
/* 上面的CSS样式 */
|
:root {
|
||||||
|
--jx3-primary: #ffd700;
|
||||||
|
--jx3-secondary: #8b4513;
|
||||||
|
--jx3-dark: #1a1a1a;
|
||||||
|
--jx3-light: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
font-family: 'Microsoft YaHei', sans-serif;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-img {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 850px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-tag {
|
||||||
|
color: #e74c3c;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-badge {
|
||||||
|
background-color: #e9ecef;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.discount-badge {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
background-color: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.discount-positive {
|
||||||
|
background-color: #d4edda;
|
||||||
|
color: #155724;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background-color: var(--jx3-secondary);
|
||||||
|
color: white;
|
||||||
|
border-radius: 10px 10px 0 0 !important;
|
||||||
|
padding: 12px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th {
|
||||||
|
background-color: var(--jx3-light);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
background-color: var(--jx3-dark);
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-cell {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #e74c3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 双列布局 */
|
||||||
|
.dual-columns {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dual-columns > div {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.dual-columns {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="header">
|
<div class="container mt-4">
|
||||||
<div class="container">
|
|
||||||
<div class="row align-items-center">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="logo">
|
|
||||||
<i class="fas fa-dragon"></i> 剑网3万宝楼
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 text-end">
|
|
||||||
<span class="status-badge {% if code == 4 %}code-success{% else %}code-error{% endif %}">
|
|
||||||
<i class="fas fa-{% if code == 4 %}check-circle{% else %}exclamation-circle{% endif %}"></i>
|
|
||||||
{{ msg }}
|
|
||||||
</span>
|
|
||||||
<span class="time-badge ms-2">
|
|
||||||
<i class="far fa-clock"></i> 数据更新时间: {{ now.strftime('%Y-%m-%d %H:%M:%S') if now else '未知' }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<!-- 商品基本信息 -->
|
<!-- 商品基本信息 -->
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
@@ -40,12 +115,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<!-- 图片在上方 -->
|
||||||
|
<div class="col-12 text-center mb-3">
|
||||||
<img src="{{ data.imgs }}" alt="{{ data.showName }}" class="product-img">
|
<img src="{{ data.imgs }}" alt="{{ data.showName }}" class="product-img">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-8">
|
<!-- 文字信息在下方 -->
|
||||||
<h2 class="mb-3">{{ data.showName }}</h2>
|
<div class="col-12 text-center">
|
||||||
<p class="text-muted">商品ID: {{ data.goodsId }} | 搜索ID: {{ data.searchId }}</p>
|
<h2 class="mb-2">{{ data.showName }}({{data.goodsAlias}})</h2>
|
||||||
<p class="mb-3">{{ data.goodsDesc }}</p>
|
<p class="mb-3">{{ data.goodsDesc }}</p>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
@@ -56,109 +132,40 @@
|
|||||||
<strong>参考价格:</strong> <span class="price-tag">{{ data.priceNum }} 元</span>
|
<strong>参考价格:</strong> <span class="price-tag">{{ data.priceNum }} 元</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex">
|
|
||||||
<button class="btn me-2" style="background-color: var(--jx3-primary); color: var(--jx3-dark);">
|
|
||||||
<i class="fas fa-shopping-cart"></i> 加入关注
|
|
||||||
</button>
|
|
||||||
<button class="btn" style="background-color: var(--jx3-secondary); color: white;">
|
|
||||||
<i class="fas fa-bell"></i> 价格提醒
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 价格摘要 -->
|
<!-- 双列数据布局 -->
|
||||||
<div class="card summary-card mb-4">
|
<div class="dual-columns">
|
||||||
<div class="card-body">
|
<!-- 万宝楼公示 -->
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h5><i class="fas fa-store"></i> 当前在售摘要</h5>
|
|
||||||
{% set min_price = data.wblgs|min(attribute='priceNum') %}
|
|
||||||
{% set max_price = data.wblgs|max(attribute='priceNum') %}
|
|
||||||
{% set avg_price = (data.wblgs|sum(attribute='priceNum') / data.wblgs|length)|round(1) %}
|
|
||||||
|
|
||||||
<div class="summary-item">
|
|
||||||
<span>在售商品数量:</span>
|
|
||||||
<span class="price-cell">{{ data.wblgs|length }}件</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-item">
|
|
||||||
<span>最低价格:</span>
|
|
||||||
<span class="price-cell">{{ min_price.priceNum if min_price else 0 }}元 ({{ min_price.belongQf2 if min_price else '未知' }})</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-item">
|
|
||||||
<span>最高价格:</span>
|
|
||||||
<span class="price-cell">{{ max_price.priceNum if max_price else 0 }}元 ({{ max_price.belongQf2 if max_price else '未知' }})</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-item">
|
|
||||||
<span>平均价格:</span>
|
|
||||||
<span class="price-cell">{{ avg_price }}元</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h5><i class="fas fa-history"></i> 历史在售摘要</h5>
|
|
||||||
{% set min_price_hist = data.wblzs|min(attribute='priceNum') %}
|
|
||||||
{% set max_price_hist = data.wblzs|max(attribute='priceNum') %}
|
|
||||||
{% set avg_price_hist = (data.wblzs|sum(attribute='priceNum') / data.wblzs|length)|round(1) %}
|
|
||||||
|
|
||||||
<div class="summary-item">
|
|
||||||
<span>历史记录数量:</span>
|
|
||||||
<span class="price-cell">{{ data.wblzs|length }}条</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-item">
|
|
||||||
<span>最低价格:</span>
|
|
||||||
<span class="price-cell">{{ min_price_hist.priceNum if min_price_hist else 0 }}元</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-item">
|
|
||||||
<span>最高价格:</span>
|
|
||||||
<span class="price-cell">{{ max_price_hist.priceNum if max_price_hist else 0 }}元</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-item">
|
|
||||||
<span>平均价格:</span>
|
|
||||||
<span class="price-cell">{{ avg_price_hist }}元</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 在售数据 -->
|
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header">
|
||||||
<h4 class="mb-0"><i class="fas fa-store"></i> 当前在售商品 ({{ data.wblgs|length }}件)</h4>
|
<h4 class="mb-0"><i class="fas fa-store"></i> 万宝楼公示 </h4>
|
||||||
<div>
|
|
||||||
{% set min_price = data.wblgs|min(attribute='priceNum') %}
|
|
||||||
{% set max_price = data.wblgs|max(attribute='priceNum') %}
|
|
||||||
<span class="badge bg-success">最低: {{ min_price.priceNum if min_price else 0 }}元</span>
|
|
||||||
<span class="badge bg-danger ms-2">最高: {{ max_price.priceNum if max_price else 0 }}元</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>价格</th>
|
<th>上架时间</th>
|
||||||
<th>服务器</th>
|
<th>服务器</th>
|
||||||
|
<th>价格</th>
|
||||||
<th>折扣率</th>
|
<th>折扣率</th>
|
||||||
<th>更新时间</th>
|
|
||||||
<th>操作</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in data.wblgs %}
|
{% for item in data.wblgs %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="price-cell">{{ item.priceNum }}元</td>
|
<td>{{ item.replyTime }}</td>
|
||||||
<td><span class="server-badge">{{ item.belongQf2 }}</span></td>
|
<td><span class="server-badge">{{ item.belongQf2 }}</span></td>
|
||||||
|
<td class="price-cell">{{ item.priceNum }}元</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="discount-badge {% if item.discountRate > 0 %}discount-positive{% endif %}">
|
<span class="discount-badge {% if item.discountRate > 0 %}discount-positive{% endif %}">
|
||||||
{{ item.discountRate }}%
|
{{ item.discountRate }}%
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ item.replyTime }}</td>
|
|
||||||
<td><button class="btn btn-sm" style="background-color: var(--jx3-primary); color: var(--jx3-dark);">购买</button></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -167,39 +174,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 历史在售数据 -->
|
<!-- 万宝楼在售 -->
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header">
|
||||||
<h4 class="mb-0"><i class="fas fa-history"></i> 历史在售记录 ({{ data.wblzs|length }}条)</h4>
|
<h4 class="mb-0"><i class="fas fa-history"></i> 万宝楼在售 </h4>
|
||||||
<div>
|
|
||||||
{% set min_price_hist = data.wblzs|min(attribute='priceNum') %}
|
|
||||||
{% set max_price_hist = data.wblzs|max(attribute='priceNum') %}
|
|
||||||
<span class="badge bg-success">最低: {{ min_price_hist.priceNum if min_price_hist else 0 }}元</span>
|
|
||||||
<span class="badge bg-danger ms-2">最高: {{ max_price_hist.priceNum if max_price_hist else 0 }}元</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>价格</th>
|
<th>上架时间</th>
|
||||||
<th>服务器</th>
|
<th>服务器</th>
|
||||||
|
<th>价格</th>
|
||||||
<th>折扣率</th>
|
<th>折扣率</th>
|
||||||
<th>更新时间</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in data.wblzs %}
|
{% for item in data.wblzs %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="price-cell">{{ item.priceNum }}元</td>
|
<td>{{ item.replyTime }}</td>
|
||||||
<td><span class="server-badge">{{ item.belongQf2 }}</span></td>
|
<td><span class="server-badge">{{ item.belongQf2 }}</span></td>
|
||||||
|
<td class="price-cell">{{ item.priceNum }}元</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="discount-badge {% if item.discountRate > 0 %}discount-positive{% endif %}">
|
<span class="discount-badge {% if item.discountRate > 0 %}discount-positive{% endif %}">
|
||||||
{{ item.discountRate }}%
|
{{ item.discountRate }}%
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ item.replyTime }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -208,15 +209,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p>剑网3万宝楼商品数据展示 © 2025 | 数据仅供参考,实际价格以游戏内为准</p>
|
<p>剑网3万宝楼商品数据展示 © 2025 | 数据仅供参考,实际价格以游戏内为准</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
|
||||||
// 上面的JavaScript代码
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user