diff --git a/zhenxun/builtin_plugins/__init__.py b/zhenxun/builtin_plugins/__init__.py index 9bce3c1c..acf3764e 100644 --- a/zhenxun/builtin_plugins/__init__.py +++ b/zhenxun/builtin_plugins/__init__.py @@ -1,16 +1,21 @@ import uuid +from datetime import datetime +import nonebot +import ujson as json from nonebot import require -from nonebot.drivers import Driver from tortoise import Tortoise +from nonebot.adapters import Bot +from nonebot.drivers import Driver from tortoise.exceptions import OperationalError -from zhenxun.models.goods_info import GoodsInfo -from zhenxun.models.group_member_info import GroupInfoUser -from zhenxun.models.sign_user import SignUser -from zhenxun.models.user_console import UserConsole from zhenxun.services.log import logger +from zhenxun.models.sign_user import SignUser +from zhenxun.models.goods_info import GoodsInfo +from zhenxun.models.user_console import UserConsole from zhenxun.utils.decorator.shop import shop_register +from zhenxun.models.bot_connect_log import BotConnectLog +from zhenxun.models.group_member_info import GroupInfoUser require("nonebot_plugin_apscheduler") require("nonebot_plugin_alconna") @@ -19,16 +24,30 @@ require("nonebot_plugin_userinfo") require("nonebot_plugin_htmlrender") -import nonebot -import ujson as json - driver: Driver = nonebot.get_driver() +@driver.on_bot_connect +async def _(bot: Bot): + logger.debug(f"Bot: {bot.self_id} 建立连接...") + await BotConnectLog.create( + bot_id=bot.self_id, platform=bot.adapter, connect_time=datetime.now(), type=1 + ) + + +@driver.on_bot_disconnect +async def _(bot: Bot): + logger.debug(f"Bot: {bot.self_id} 断开连接...") + await BotConnectLog.create( + bot_id=bot.self_id, platform=bot.adapter, connect_time=datetime.now(), type=0 + ) + + SIGN_SQL = """ -select distinct on("user_id") t1.user_id, t1.checkin_count, t1.add_probability, t1.specify_probability, t1.impression +select distinct on("user_id") t1.user_id, t1.checkin_count, t1.add_probability, +t1.specify_probability, t1.impression from public.sign_group_users t1 - join ( + join ( select user_id, max(t2.impression) as max_impression from public.sign_group_users t2 group by user_id @@ -38,7 +57,7 @@ from public.sign_group_users t1 BAG_SQL = """ select t1.user_id, t1.gold, t1.property from public.bag_users t1 - join ( + join ( select user_id, max(t2.gold) as max_gold from public.bag_users t2 group by user_id @@ -74,15 +93,12 @@ async def _(): } create_list = [] sign_id_list = [] - max_uid = 0 - if user2uid: - max_uid = max(user2uid.values()) + 1 + max_uid = max(user2uid.values()) + 1 if user2uid else 0 for old_sign in old_sign_list: sign_id_list.append(old_sign["user_id"]) - old_bag = [ + if old_bag := [ b for b in old_bag_list if b["user_id"] == old_sign["user_id"] - ] - if old_bag: + ]: old_bag = old_bag[0] property = json.loads(old_bag["property"]) props = {} @@ -115,9 +131,9 @@ async def _(): create_list.clear() uc_dict = {u.user_id: u for u in await UserConsole.all()} for old_sign in old_sign_list: - user_console = uc_dict.get(old_sign["user_id"]) - if not user_console: - user_console = await UserConsole.get_user(old_sign["user_id"], "qq") + user_console = uc_dict.get( + old_sign["user_id"] + ) or await UserConsole.get_user(old_sign["user_id"], "qq") create_list.append( SignUser( user_id=old_sign["user_id"], diff --git a/zhenxun/builtin_plugins/statistics/statistics_hook.py b/zhenxun/builtin_plugins/statistics/statistics_hook.py index d1fc4635..7b352bcb 100644 --- a/zhenxun/builtin_plugins/statistics/statistics_hook.py +++ b/zhenxun/builtin_plugins/statistics/statistics_hook.py @@ -1,16 +1,17 @@ from datetime import datetime -from nonebot.adapters import Bot, Event -from nonebot.adapters.onebot.v11 import PokeNotifyEvent from nonebot.matcher import Matcher -from nonebot.message import run_postprocessor +from nonebot.adapters import Bot, Event from nonebot.plugin import PluginMetadata +from nonebot.message import run_postprocessor from nonebot_plugin_session import EventSession +from nonebot.adapters.onebot.v11 import PokeNotifyEvent +from zhenxun.services.log import logger +from zhenxun.utils.enum import PluginType +from zhenxun.models.statistics import Statistics from zhenxun.configs.utils import PluginExtraData from zhenxun.models.plugin_info import PluginInfo -from zhenxun.models.statistics import Statistics -from zhenxun.utils.enum import PluginType __plugin_meta__ = PluginMetadata( name="功能调用统计", @@ -36,13 +37,12 @@ async def _( if session.id1: plugin = await PluginInfo.get_or_none(module=matcher.module_name) plugin_type = plugin.plugin_type if plugin else None - if plugin_type == PluginType.NORMAL and matcher.plugin_name not in [ - "update_info", - "statistics_handle", - ]: + if plugin_type == PluginType.NORMAL: + logger.debug(f"提交调用记录: {matcher.plugin_name}...", session=session) await Statistics.create( user_id=session.id1, group_id=session.id3 or session.id2, plugin_name=matcher.plugin_name, create_time=datetime.now(), + bot_id=bot.self_id, ) diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/__init__.py b/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/__init__.py index 60159296..fce48089 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/__init__.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/__init__.py @@ -1,9 +1,18 @@ +from datetime import datetime, timedelta + from nonebot import require from fastapi import APIRouter +from tortoise.functions import Count +from tortoise.expressions import RawSQL + +from zhenxun.models.statistics import Statistics +from zhenxun.models.chat_history import ChatHistory +from zhenxun.models.bot_connect_log import BotConnectLog -from ....base_model import Result from .data_source import BotManage from ....utils import authentication +from ....base_model import Result, QueryModel, BaseResultModel +from .model import ChatCallMonthCount, QueryChatCallCount, AllChatAndCallCount require("plugin_store") @@ -20,3 +29,142 @@ async def _() -> Result: return Result.ok(await BotManage.get_bot_list(), "拿到信息啦!") except Exception as e: return Result.fail(f"发生了一点错误捏 {type(e)}: {e}") + + +@router.get( + "/get_chat_and_call_count", + dependencies=[authentication()], + description="获取聊天/调用记录的全部和今日数量", +) +async def _(bot_id: str | None = None) -> Result: + now = datetime.now() + query = ChatHistory + if bot_id: + query = query.filter(bot_id=bot_id) + chat_all_count = await query.annotate().count() + chat_day_count = await query.filter( + create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute) + ).count() + query = Statistics + if bot_id: + query = query.filter(bot_id=bot_id) + call_all_count = await query.annotate().count() + call_day_count = await query.filter( + create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute) + ).count() + return Result.ok( + QueryChatCallCount( + chat_num=chat_all_count, + chat_day=chat_day_count, + call_num=call_all_count, + call_day=call_day_count, + ) + ) + + +@router.get( + "/get_all_chat_and_call_count", + dependencies=[authentication()], + description="获取聊天/调用记录的全部数据次数", +) +async def _(bot_id: str | None = None) -> Result: + now = datetime.now() + query = ChatHistory + if bot_id: + query = query.filter(bot_id=bot_id) + chat_week_count = await query.filter( + create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute) + ).count() + chat_month_count = await query.filter( + create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute) + ).count() + chat_year_count = await query.filter( + create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute) + ).count() + query = Statistics + if bot_id: + query = query.filter(bot_id=bot_id) + call_week_count = await query.filter( + create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute) + ).count() + call_month_count = await query.filter( + create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute) + ).count() + call_year_count = await query.filter( + create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute) + ).count() + return Result.ok( + AllChatAndCallCount( + chat_week=chat_week_count, + chat_month=chat_month_count, + chat_year=chat_year_count, + call_week=call_week_count, + call_month=call_month_count, + call_year=call_year_count, + ) + ) + + +@router.get( + "/get_chat_and_call_month", + dependencies=[authentication()], + deprecated="获取聊天/调用记录的一个月数量", # type: ignore +) +async def _() -> Result: + now = datetime.now() + filter_date = now - timedelta(days=30, hours=now.hour, minutes=now.minute) + chat_date_list = ( + await ChatHistory.filter(create_time__gte=filter_date) + .annotate(date=RawSQL("DATE(create_time)"), count=Count("id")) + .group_by("date") + .values("date", "count") + ) + call_date_list = ( + await Statistics.filter(create_time__gte=filter_date) + .annotate(date=RawSQL("DATE(create_time)"), count=Count("id")) + .group_by("date") + .values("date", "count") + ) + date_list = [] + chat_count_list = [] + call_count_list = [] + chat_date2cnt = {str(date["date"]): date["count"] for date in chat_date_list} + call_date2cnt = {str(date["date"]): date["count"] for date in call_date_list} + date = now.date() + for _ in range(30): + if str(date) in chat_date2cnt: + chat_count_list.append(chat_date2cnt[str(date)]) + else: + chat_count_list.append(0) + if str(date) in call_date2cnt: + call_count_list.append(call_date2cnt[str(date)]) + else: + call_count_list.append(0) + date_list.append(str(date)[5:]) + date -= timedelta(days=1) + chat_count_list.reverse() + call_count_list.reverse() + date_list.reverse() + return Result.ok( + ChatCallMonthCount(chat=chat_count_list, call=call_count_list, date=date_list) + ) + + +@router.post( + "/get_connect_log", + dependencies=[authentication()], + deprecated="获取Bot连接记录", # type: ignore +) +async def _(query: QueryModel) -> Result: + total = await BotConnectLog.all().count() + if total % query.size: + total += 1 + data = ( + await BotConnectLog.all() + .order_by("-id") + .offset((query.index - 1) * query.size) + .limit(query.size) + ) + for v in data: + v.connect_time = v.connect_time.replace(tzinfo=None).replace(microsecond=0) + return Result.ok(BaseResultModel(total=total, data=data)) diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/data_source.py b/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/data_source.py index b09d4ba9..4c39fb64 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/data_source.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/data_source.py @@ -62,10 +62,7 @@ class BotManage: bot_info.connect_time = bot_live.get(bot.self_id) or 0 if bot_info.connect_time: connect_date = datetime.fromtimestamp(CONNECT_TIME) - connect_date_str = connect_date.strftime("%Y-%m-%d %H:%M:%S") - bot_info.connect_date = datetime.strptime( - connect_date_str, "%Y-%m-%d %H:%M:%S" - ) + bot_info.connect_date = connect_date.strftime("%Y-%m-%d %H:%M:%S") return bot_info @classmethod diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/model.py b/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/model.py index d2891251..1293c7d7 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/model.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/model.py @@ -1,5 +1,3 @@ -from datetime import datetime - from pydantic import BaseModel @@ -22,5 +20,52 @@ class BotInfo(BaseModel): """今日调用插件次数""" connect_time: int = 0 """连接时间""" - connect_date: datetime | None = None + connect_date: str | None = None """连接日期""" + + +class QueryChatCallCount(BaseModel): + """ + 查询聊天/调用记录次数 + """ + + chat_num: int + """聊天记录总数""" + chat_day: int + """今日消息""" + call_num: int + """调用记录总数""" + call_day: int + """今日调用""" + + +class ChatCallMonthCount(BaseModel): + """ + 查询聊天/调用一个月记录次数 + """ + + chat: list[int] + """一个月内聊天总数""" + call: list[int] + """一个月内调用数据""" + date: list[str] + """日期""" + + +class AllChatAndCallCount(BaseModel): + """ + 查询聊天/调用记录次数 + """ + + chat_week: int + """一周内聊天次数""" + chat_month: int + """一月内聊天次数""" + chat_year: int + """一年内聊天次数""" + call_week: int + """一周内调用次数""" + call_month: int + """一月内调用次数""" + call_year: int + """一年内调用次数""" diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/main/__init__.py b/zhenxun/builtin_plugins/web_ui/api/tabs/main/__init__.py index 88f5ee9b..8a454c61 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/main/__init__.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/main/__init__.py @@ -21,8 +21,9 @@ from ....base_model import Result from .data_source import bot_live from ....utils import authentication, get_system_status from ....config import AVA_URL, GROUP_AVA_URL, QueryDateType -from .model import BaseInfo, HotPlugin, ActiveGroup, ChatHistoryCount +from .model import BaseInfo, HotPlugin, QueryCount, ActiveGroup, NonebotData +driver = nonebot.get_driver() run_time = time.time() ws_router = APIRouter() @@ -39,6 +40,7 @@ async def _(bot_id: str | None = None) -> Result: 返回: Result: 获取指定bot信息与bot列表 """ + global run_time bot_list: list[BaseInfo] = [] if bots := nonebot.get_bots(): select_bot: BaseInfo @@ -82,10 +84,7 @@ async def _(bot_id: str | None = None) -> Result: select_bot.connect_time = bot_live.get(select_bot.self_id) or 0 if select_bot.connect_time: connect_date = datetime.fromtimestamp(select_bot.connect_time) - connect_date_str = connect_date.strftime("%Y-%m-%d %H:%M:%S") - select_bot.connect_date = datetime.strptime( - connect_date_str, "%Y-%m-%d %H:%M:%S" - ) + select_bot.connect_date = connect_date.strftime("%Y-%m-%d %H:%M:%S") version_file = Path() / "__version__" if version_file.exists(): if text := version_file.open().read(): @@ -102,23 +101,58 @@ async def _(bot_id: str | None = None) -> Result: @router.get( "/get_all_ch_count", dependencies=[authentication()], description="获取接收消息数量" ) -async def _(bot_id: str) -> Result: +async def _(bot_id: str | None = None) -> Result: now = datetime.now() - all_count = await ChatHistory.filter(bot_id=bot_id).count() - day_count = await ChatHistory.filter( - bot_id=bot_id, create_time__gte=now - timedelta(hours=now.hour) + query = ChatHistory + if bot_id: + query = query.filter(bot_id=bot_id) + all_count = await query.annotate().count() + day_count = await query.filter( + create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute) ).count() - week_count = await ChatHistory.filter( - bot_id=bot_id, create_time__gte=now - timedelta(days=7) + week_count = await query.filter( + create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute) ).count() - month_count = await ChatHistory.filter( - bot_id=bot_id, create_time__gte=now - timedelta(days=30) + month_count = await query.filter( + create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute) ).count() - year_count = await ChatHistory.filter( - bot_id=bot_id, create_time__gte=now - timedelta(days=365) + year_count = await query.filter( + create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute) ).count() return Result.ok( - ChatHistoryCount( + QueryCount( + num=all_count, + day=day_count, + week=week_count, + month=month_count, + year=year_count, + ) + ) + + +@router.get( + "/get_all_call_count", dependencies=[authentication()], description="获取调用次数" +) +async def _(bot_id: str | None = None) -> Result: + now = datetime.now() + query = Statistics + # if bot_id: + # query = query.filter(bot_id=bot_id) + all_count = await query.annotate().count() + day_count = await query.filter( + create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute) + ).count() + week_count = await query.filter( + create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute) + ).count() + month_count = await query.filter( + create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute) + ).count() + year_count = await query.filter( + create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute) + ).count() + return Result.ok( + QueryCount( num=all_count, day=day_count, week=week_count, @@ -182,11 +216,21 @@ async def _(bot_id: str) -> Result: return Result.warning_("无Bot连接...") +@router.get("/get_nb_data", dependencies=[authentication()], description="获取nb数据") +async def _() -> Result: + return Result.ok(NonebotData(config=driver.config, run_time=int(run_time))) + + +@router.get("/get_nb_config", dependencies=[authentication()], description="获取nb配置") +async def _() -> Result: + return Result.ok(driver.config) + + @router.get( "/get_run_time", dependencies=[authentication()], description="获取nb运行时间" ) async def _() -> Result: - return Result.ok(int(time.time() - run_time)) + return Result.ok(int(run_time)) @router.get( diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py b/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py index ca445016..281ac701 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py @@ -1,8 +1,8 @@ import time import nonebot -from nonebot.adapters.onebot.v11 import Bot from nonebot.drivers import Driver +from nonebot.adapters.onebot.v11 import Bot driver: Driver = nonebot.get_driver() diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/main/model.py b/zhenxun/builtin_plugins/web_ui/api/tabs/main/model.py index c9d76706..d8ec0c0f 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/main/model.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/main/model.py @@ -1,8 +1,6 @@ -from datetime import datetime - +from pydantic import BaseModel from nonebot.adapters import Bot from nonebot.config import Config -from pydantic import BaseModel class SystemStatus(BaseModel): @@ -36,7 +34,7 @@ class BaseInfo(BaseModel): """今日 累计接收消息""" connect_time: int = 0 """连接时间""" - connect_date: datetime | None = None + connect_date: str | None = None """连接日期""" plugin_count: int = 0 @@ -60,7 +58,7 @@ class BaseInfo(BaseModel): arbitrary_types_allowed = True -class ChatHistoryCount(BaseModel): +class QueryCount(BaseModel): """ 聊天记录数量 """ @@ -103,3 +101,10 @@ class HotPlugin(BaseModel): """插件名称""" count: int """调用次数""" + + +class NonebotData(BaseModel): + config: Config + """nb配置""" + run_time: int + """运行时间""" diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/store.py b/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/store.py index f2b54859..aa6584ab 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/store.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/store.py @@ -1,6 +1,8 @@ from nonebot import require from fastapi import APIRouter +from zhenxun.models.plugin_info import PluginInfo + from .model import PluginIr from ....base_model import Result from ....utils import authentication @@ -19,7 +21,14 @@ router = APIRouter(prefix="/store") async def _() -> Result: try: data = await ShopManage.get_data() - return Result.ok(data) + plugin_list = [ + {**data[name].dict(), "name": name, "id": idx} + for idx, name in enumerate(data) + ] + modules = await PluginInfo.filter(load_status=True).values_list( + "module", flat=True + ) + return Result.ok({"install_module": modules, "plugin_list": plugin_list}) except Exception as e: return Result.fail(f"获取插件商店插件信息失败: {type(e)}: {e}") @@ -32,11 +41,24 @@ async def _() -> Result: async def _(param: PluginIr) -> Result: try: result = await ShopManage.add_plugin(param.id) # type: ignore - return Result.ok(result) + return Result.ok(info=result) except Exception as e: return Result.fail(f"安装插件失败: {type(e)}: {e}") +@router.post( + "/update_plugin", + dependencies=[authentication()], + deprecated="更新插件", # type: ignore +) +async def _(param: PluginIr) -> Result: + try: + result = await ShopManage.update_plugin(param.id) # type: ignore + return Result.ok(info=result) + except Exception as e: + return Result.fail(f"更新插件失败: {type(e)}: {e}") + + @router.post( "/remove_plugin", dependencies=[authentication()], @@ -45,6 +67,6 @@ async def _(param: PluginIr) -> Result: async def _(param: PluginIr) -> Result: try: result = await ShopManage.remove_plugin(param.id) # type: ignore - return Result.ok(result) + return Result.ok(info=result) except Exception as e: return Result.fail(f"移除插件失败: {type(e)}: {e}") diff --git a/zhenxun/models/bot_connect_log.py b/zhenxun/models/bot_connect_log.py new file mode 100644 index 00000000..768f049c --- /dev/null +++ b/zhenxun/models/bot_connect_log.py @@ -0,0 +1,22 @@ +from tortoise import fields + +from zhenxun.services.db_context import Model + + +class BotConnectLog(Model): + id = fields.IntField(pk=True, generated=True, auto_increment=True) + """自增id""" + bot_id = fields.CharField(255, description="Bot id") + """Bot id""" + platform = fields.CharField(255, null=True, description="平台") + """平台""" + connect_time = fields.DatetimeField(description="连接时间") + """日期""" + type = fields.IntField(null=True, description="1: 连接, 0: 断开") + """1: 连接, 0: 断开""" + create_time = fields.DatetimeField(auto_now_add=True) + """创建时间""" + + class Meta: # type: ignore + table = "bot_connect_log" + table_description = "bot连接表" diff --git a/zhenxun/models/statistics.py b/zhenxun/models/statistics.py index 43576fcb..100e01e9 100644 --- a/zhenxun/models/statistics.py +++ b/zhenxun/models/statistics.py @@ -4,7 +4,6 @@ from zhenxun.services.db_context import Model class Statistics(Model): - id = fields.IntField(pk=True, generated=True, auto_increment=True) """自增id""" user_id = fields.CharField(255) @@ -15,15 +14,19 @@ class Statistics(Model): """插件名称""" create_time = fields.DatetimeField(auto_now=True) """添加日期""" + bot_id = fields.CharField(255, null=True) + """Bot Id""" - class Meta: + class Meta: # type: ignore table = "statistics" table_description = "插件调用统计数据库" @classmethod async def _run_script(cls): return [ - "ALTER TABLE statistics RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id + "ALTER TABLE statistics RENAME COLUMN user_qq TO user_id;", + # 将user_qq改为user_id "ALTER TABLE statistics ALTER COLUMN user_id TYPE character varying(255);", "ALTER TABLE statistics ALTER COLUMN group_id TYPE character varying(255);", + "ALTER TABLE statistics ADD boe_id Text DEFAULT '';", ] diff --git a/zhenxun/utils/plugin_models/base.py b/zhenxun/utils/plugin_models/base.py index a50f6b4a..74692004 100644 --- a/zhenxun/utils/plugin_models/base.py +++ b/zhenxun/utils/plugin_models/base.py @@ -2,7 +2,6 @@ from pydantic import BaseModel class CommonSql(BaseModel): - sql: str """sql语句""" remark: str