From ed1e1474d19a1fa339fc74f52aaf4a857c108844 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Mon, 28 Jul 2025 15:10:27 +0800 Subject: [PATCH 01/38] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dlogout=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=9C=AA=E6=8C=89=E7=85=A7app=5Fsame=5Ftime=5Flogin?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E9=A1=B9=E5=8A=A8=E6=80=81=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module_admin/controller/login_controller.py | 7 +++++-- .../module_admin/service/login_service.py | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ruoyi-fastapi-backend/module_admin/controller/login_controller.py b/ruoyi-fastapi-backend/module_admin/controller/login_controller.py index 6d13fcc..1a3c198 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/login_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/login_controller.py @@ -141,8 +141,11 @@ async def logout(request: Request, token: Optional[str] = Depends(oauth2_scheme) payload = jwt.decode( token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm], options={'verify_exp': False} ) - session_id: str = payload.get('session_id') - await LoginService.logout_services(request, session_id) + if AppConfig.app_same_time_login: + token_id: str = payload.get('session_id') + else: + token_id: str = payload.get('user_id') + await LoginService.logout_services(request, token_id) logger.info('退出成功') return ResponseUtil.success(msg='退出成功') diff --git a/ruoyi-fastapi-backend/module_admin/service/login_service.py b/ruoyi-fastapi-backend/module_admin/service/login_service.py index 5579f0d..9c61f22 100644 --- a/ruoyi-fastapi-backend/module_admin/service/login_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/login_service.py @@ -466,15 +466,15 @@ class LoginService: return CrudResponseModel(**result) @classmethod - async def logout_services(cls, request: Request, session_id: str): + async def logout_services(cls, request: Request, token_id: str): """ 退出登录services :param request: Request对象 - :param session_id: 会话编号 + :param token_id: 令牌编号 :return: 退出登录结果 """ - await request.app.state.redis.delete(f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{session_id}') + await request.app.state.redis.delete(f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{token_id}') # await request.app.state.redis.delete(f'{current_user.user.user_id}_access_token') # await request.app.state.redis.delete(f'{current_user.user.user_id}_session_id') -- Gitee From 80a0414be4dac97c11831f67f85d59eb81cb1e75 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Mon, 28 Jul 2025 15:10:58 +0800 Subject: [PATCH 02/38] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E8=80=97=E6=97=B6=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module_admin/annotation/log_annotation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruoyi-fastapi-backend/module_admin/annotation/log_annotation.py b/ruoyi-fastapi-backend/module_admin/annotation/log_annotation.py index 1d01c1c..4485247 100644 --- a/ruoyi-fastapi-backend/module_admin/annotation/log_annotation.py +++ b/ruoyi-fastapi-backend/module_admin/annotation/log_annotation.py @@ -46,7 +46,7 @@ class Log: def __call__(self, func): @wraps(func) async def wrapper(*args, **kwargs): - start_time = time.time() + start_time = time.perf_counter() # 获取被装饰函数的文件路径 file_path = inspect.getfile(func) # 获取项目根路径 @@ -129,7 +129,7 @@ class Log: logger.exception(e) result = ResponseUtil.error(msg=str(e)) # 获取请求耗时 - cost_time = float(time.time() - start_time) * 100 + cost_time = float(time.perf_counter() - start_time) * 1000 # 判断请求是否来自api文档 request_from_swagger = ( request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False -- Gitee From 33ea3fbff90fe9b8df5b2db5ec9c9d3569669830 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Mon, 4 Aug 2025 11:00:39 +0800 Subject: [PATCH 03/38] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84IP=E5=BD=92?= =?UTF-8?q?=E5=B1=9E=E5=8C=BA=E5=9F=9F=E6=9F=A5=E8=AF=A2=E4=B8=BA=E5=BC=82?= =?UTF-8?q?=E6=AD=A5=E8=B0=83=E7=94=A8=20#7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module_admin/annotation/log_annotation.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/ruoyi-fastapi-backend/module_admin/annotation/log_annotation.py b/ruoyi-fastapi-backend/module_admin/annotation/log_annotation.py index 4485247..f7e938c 100644 --- a/ruoyi-fastapi-backend/module_admin/annotation/log_annotation.py +++ b/ruoyi-fastapi-backend/module_admin/annotation/log_annotation.py @@ -1,12 +1,13 @@ +import httpx import inspect import json import os -import requests import time +from async_lru import alru_cache from datetime import datetime from fastapi import Request from fastapi.responses import JSONResponse, ORJSONResponse, UJSONResponse -from functools import lru_cache, wraps +from functools import wraps from sqlalchemy.ext.asyncio import AsyncSession from typing import Any, Callable, Literal, Optional from user_agents import parse @@ -74,7 +75,7 @@ class Log: oper_ip = request.headers.get('X-Forwarded-For') oper_location = '内网IP' if AppConfig.app_ip_location_query: - oper_location = get_ip_location(oper_ip) + oper_location = await get_ip_location(oper_ip) # 根据不同的请求类型使用不同的方法获取请求参数 content_type = request.headers.get('Content-Type') if content_type and ( @@ -203,8 +204,8 @@ class Log: return wrapper -@lru_cache() -def get_ip_location(oper_ip: str): +@alru_cache() +async def get_ip_location(oper_ip: str): """ 查询ip归属区域 @@ -215,12 +216,13 @@ def get_ip_location(oper_ip: str): try: if oper_ip != '127.0.0.1' and oper_ip != 'localhost': oper_location = '未知' - ip_result = requests.get(f'https://qifu-api.baidubce.com/ip/geo/v1/district?ip={oper_ip}') - if ip_result.status_code == 200: - prov = ip_result.json().get('data').get('prov') - city = ip_result.json().get('data').get('city') - if prov or city: - oper_location = f'{prov}-{city}' + async with httpx.AsyncClient() as client: + ip_result = await client.get(f'https://qifu-api.baidubce.com/ip/geo/v1/district?ip={oper_ip}') + if ip_result.status_code == 200: + prov = ip_result.json().get('data', {}).get('prov') + city = ip_result.json().get('data', {}).get('city') + if prov or city: + oper_location = f'{prov}-{city}' except Exception as e: oper_location = '未知' print(e) -- Gitee From 39b4b6910164930afa3051e45504101c59a0f4ea Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Mon, 4 Aug 2025 11:00:53 +0800 Subject: [PATCH 04/38] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-fastapi-backend/requirements-pg.txt | 2 +- ruoyi-fastapi-backend/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ruoyi-fastapi-backend/requirements-pg.txt b/ruoyi-fastapi-backend/requirements-pg.txt index 095197f..3c9ce65 100644 --- a/ruoyi-fastapi-backend/requirements-pg.txt +++ b/ruoyi-fastapi-backend/requirements-pg.txt @@ -1,4 +1,5 @@ APScheduler==3.11.0 +async-lru==2.0.5 asyncpg==0.30.0 DateTime==5.5 fastapi[all]==0.115.8 @@ -12,7 +13,6 @@ pydantic-validation-decorator==0.1.4 PyJWT[crypto]==2.10.1 psycopg2==2.9.10 redis==5.2.1 -requests==2.32.3 SQLAlchemy[asyncio]==2.0.38 sqlglot[rs]==26.6.0 user-agents==2.2.0 diff --git a/ruoyi-fastapi-backend/requirements.txt b/ruoyi-fastapi-backend/requirements.txt index 45ff472..4ddd3af 100644 --- a/ruoyi-fastapi-backend/requirements.txt +++ b/ruoyi-fastapi-backend/requirements.txt @@ -1,4 +1,5 @@ APScheduler==3.11.0 +async-lru==2.0.5 asyncmy==0.2.10 DateTime==5.5 fastapi[all]==0.115.8 @@ -12,7 +13,6 @@ pydantic-validation-decorator==0.1.4 PyJWT[crypto]==2.10.1 PyMySQL==1.1.1 redis==5.2.1 -requests==2.32.3 SQLAlchemy[asyncio]==2.0.38 sqlglot[rs]==26.6.0 user-agents==2.2.0 -- Gitee From f124d77e3e19db0ca3e7782b4fa2b6a1e55fa42b Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Thu, 21 Aug 2025 17:24:40 +0800 Subject: [PATCH 05/38] =?UTF-8?q?feat:=20=E9=9B=86=E6=88=90alembic?= =?UTF-8?q?=E4=BB=A5=E6=94=AF=E6=8C=81=E6=95=B0=E6=8D=AE=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-fastapi-backend/alembic.ini | 153 +++++++++++++++++++ ruoyi-fastapi-backend/alembic/README | 1 + ruoyi-fastapi-backend/alembic/env.py | 116 ++++++++++++++ ruoyi-fastapi-backend/alembic/script.py.mako | 28 ++++ ruoyi-fastapi-backend/config/env.py | 11 +- ruoyi-fastapi-backend/utils/import_util.py | 121 +++++++++++++++ 6 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 ruoyi-fastapi-backend/alembic.ini create mode 100644 ruoyi-fastapi-backend/alembic/README create mode 100644 ruoyi-fastapi-backend/alembic/env.py create mode 100644 ruoyi-fastapi-backend/alembic/script.py.mako create mode 100644 ruoyi-fastapi-backend/utils/import_util.py diff --git a/ruoyi-fastapi-backend/alembic.ini b/ruoyi-fastapi-backend/alembic.ini new file mode 100644 index 0000000..d9a7ee9 --- /dev/null +++ b/ruoyi-fastapi-backend/alembic.ini @@ -0,0 +1,153 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# this is typically a path given in POSIX (e.g. forward slashes) +# format, relative to the token %(here)s which refers to the location of this +# ini file +script_location = %(here)s/alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. for multiple paths, the path separator +# is defined by "path_separator" below. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to /versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "path_separator" +# below. +# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions + +# path_separator; This indicates what character is used to split lists of file +# paths, including version_locations and prepend_sys_path within configparser +# files such as alembic.ini. +# The default rendered in new alembic.ini files is "os", which uses os.pathsep +# to provide os-dependent path splitting. +# +# Note that in order to support legacy alembic.ini files, this default does NOT +# take place if path_separator is not present in alembic.ini. If this +# option is omitted entirely, fallback logic is as follows: +# +# 1. Parsing of the version_locations option falls back to using the legacy +# "version_path_separator" key, which if absent then falls back to the legacy +# behavior of splitting on spaces and/or commas. +# 2. Parsing of the prepend_sys_path option falls back to the legacy +# behavior of splitting on spaces, commas, or colons. +# +# Valid values for path_separator are: +# +# path_separator = : +# path_separator = ; +# path_separator = space +# path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +path_separator = os + + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +# database URL. This is consumed by the user-maintained env.py script only. +# other means of configuring database URLs may be customized within the env.py +# file. +sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module +# hooks = ruff +# ruff.type = module +# ruff.module = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Alternatively, use the exec runner to execute a binary found on your PATH +# hooks = ruff +# ruff.type = exec +# ruff.executable = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration. This is also consumed by the user-maintained +# env.py script only. +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S + +[settings] +# This section is used to set environment variables for the alembic. +# The env option is used to specify the environment for the alembic. +# It can be set to dev or prod. +env = dev diff --git a/ruoyi-fastapi-backend/alembic/README b/ruoyi-fastapi-backend/alembic/README new file mode 100644 index 0000000..e0d0858 --- /dev/null +++ b/ruoyi-fastapi-backend/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration with an async dbapi. \ No newline at end of file diff --git a/ruoyi-fastapi-backend/alembic/env.py b/ruoyi-fastapi-backend/alembic/env.py new file mode 100644 index 0000000..98828e6 --- /dev/null +++ b/ruoyi-fastapi-backend/alembic/env.py @@ -0,0 +1,116 @@ +import asyncio +import os +from alembic import context +from logging.config import fileConfig +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config +from config.database import Base, ASYNC_SQLALCHEMY_DATABASE_URL +from utils.import_util import ImportUtil + + +# 判断vesrions目录是否存在,如果不存在则创建 +alembic_veresions_path = 'alembic/versions' +if not os.path.exists(alembic_veresions_path): + os.makedirs(alembic_veresions_path) + + +# 自动查找所有模型 +found_models = ImportUtil.find_models(Base) + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +alembic_config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if alembic_config.config_file_name is not None: + fileConfig(alembic_config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +target_metadata = Base.metadata +# ASYNC_SQLALCHEMY_DATABASE_URL = 'mysql+asyncmy://root:mysqlroot@127.0.0.1:3306/ruoyi-fastapi' +# other values from the config, defined by the needs of env.py, +alembic_config.set_main_option('sqlalchemy.url', ASYNC_SQLALCHEMY_DATABASE_URL) + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = alembic_config.get_main_option('sqlalchemy.url') + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={'paramstyle': 'named'}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + def process_revision_directives(context, revision, directives): + script = directives[0] + + # 检查所有操作集是否为空 + all_empty = all(ops.is_empty() for ops in script.upgrade_ops_list) + + if all_empty: + # 如果没有实际变更,不生成迁移文件 + directives[:] = [] + print('❎️ 未检测到模型变更,不生成迁移文件') + else: + print('✅️ 检测到模型变更,生成迁移文件') + + context.configure( + connection=connection, + target_metadata=target_metadata, + compare_type=True, + compare_server_default=True, + transaction_per_migration=True, + process_revision_directives=process_revision_directives, + ) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = async_engine_from_config( + alembic_config.get_section(alembic_config.config_ini_section, {}), + prefix='sqlalchemy.', + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/ruoyi-fastapi-backend/alembic/script.py.mako b/ruoyi-fastapi-backend/alembic/script.py.mako new file mode 100644 index 0000000..1101630 --- /dev/null +++ b/ruoyi-fastapi-backend/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/ruoyi-fastapi-backend/config/env.py b/ruoyi-fastapi-backend/config/env.py index 192a2af..3a2b3af 100644 --- a/ruoyi-fastapi-backend/config/env.py +++ b/ruoyi-fastapi-backend/config/env.py @@ -1,4 +1,5 @@ import argparse +import configparser import os import sys from dotenv import load_dotenv @@ -206,7 +207,15 @@ class GetConfig: """ 解析命令行参数 """ - if 'uvicorn' in sys.argv[0]: + # 检查是否在alembic环境中运行,如果是则跳过参数解析 + if 'alembic' in sys.argv[0] or any('alembic' in arg for arg in sys.argv): + ini_config = configparser.ConfigParser() + ini_config.read('alembic.ini', encoding='utf-8') + if 'settings' in ini_config: + # 获取env选项 + env_value = ini_config['settings'].get('env') + os.environ['APP_ENV'] = env_value if env_value else 'dev' + elif 'uvicorn' in sys.argv[0]: # 使用uvicorn启动时,命令行参数需要按照uvicorn的文档进行配置,无法自定义参数 pass else: diff --git a/ruoyi-fastapi-backend/utils/import_util.py b/ruoyi-fastapi-backend/utils/import_util.py new file mode 100644 index 0000000..4bb2a63 --- /dev/null +++ b/ruoyi-fastapi-backend/utils/import_util.py @@ -0,0 +1,121 @@ +import importlib +import inspect +import os +from pathlib import Path +import sys +from functools import lru_cache +from sqlalchemy import inspect as sa_inspect +from typing import Any, List +from config.database import Base + + +class ImportUtil: + @classmethod + def find_project_root(cls) -> Path: + """ + 查找项目根目录 + + :return: 项目根目录路径 + """ + current_dir = Path(__file__).resolve().parent + while current_dir != current_dir.parent: + if any(current_dir.joinpath(file).exists() for file in ['setup.py', 'pyproject.toml', 'requirements.txt']): + return current_dir + current_dir = current_dir.parent + return Path(__file__).resolve().parent + + @classmethod + def is_valid_model(cls, obj: Any, base_class: Base) -> bool: + """ + 验证是否为有效的SQLAlchemy模型类 + + :param obj: 待验证的对象 + :param base_class: SQLAlchemy的Base类 + :return: 验证结果 + """ + # 必须继承自Base且不是Base本身 + if not (inspect.isclass(obj) and issubclass(obj, base_class) and obj is not base_class): + return False + + # 必须有表名定义(排除抽象基类) + if not hasattr(obj, '__tablename__') or obj.__tablename__ is None: + return False + + # 必须有至少一个列定义 + try: + return len(sa_inspect(obj).columns) > 0 + except Exception: + return False + + @classmethod + @lru_cache(maxsize=256) + def find_models(cls, base_class: Base) -> List[Base]: + """ + 查找并过滤有效的模型类,避免重复和无效定义 + + :param base_class: SQLAlchemy的Base类,用于验证模型类 + :return: 有效模型类列表 + """ + models = [] + # 按类对象去重 + seen_models = set() + # 按表名去重(防止同表名冲突) + seen_tables = set() + project_root = cls.find_project_root() + + sys.path.append(str(project_root)) + print(f"⏰️ 开始在项目根目录 {project_root} 中查找模型...") + + # 排除目录扩展 + exclude_dirs = { + 'venv', + '.env', + '.git', + '__pycache__', + 'migrations', + 'alembic', + 'tests', + 'test', + 'docs', + 'examples', + 'scripts', + } + + for root, dirs, files in os.walk(project_root): + dirs[:] = [d for d in dirs if d not in exclude_dirs] + + for file in files: + if file.endswith('.py') and not file.startswith('__'): + relative_path = Path(root).relative_to(project_root) + module_parts = list(relative_path.parts) + [file[:-3]] + module_name = '.'.join(module_parts) + + try: + module = importlib.import_module(module_name) + + for name, obj in inspect.getmembers(module, inspect.isclass): + # 验证模型有效性 + if not cls.is_valid_model(obj, base_class): + continue + + # 检查类对象重复 + if obj in seen_models: + continue + + # 检查表名重复 + table_name = obj.__tablename__ + if table_name in seen_tables: + continue + + seen_models.add(obj) + seen_tables.add(table_name) + models.append(obj) + print(f'✅️ 找到有效模型: {obj.__module__}.{obj.__name__} (表: {table_name})') + + except ImportError as e: + if 'cannot import name' not in str(e): + print(f'❗️ 警告: 无法导入模块 {module_name}: {e}') + except Exception as e: + print(f'❌️ 处理模块 {module_name} 时出错: {e}') + + return models -- Gitee From 2141dd25b4c9ce859fcbd0dc395a0836b794ff18 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Thu, 21 Aug 2025 17:24:52 +0800 Subject: [PATCH 06/38] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E4=BF=A1=E6=81=AF=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-fastapi-backend/config/get_db.py | 4 ++-- ruoyi-fastapi-backend/config/get_redis.py | 14 +++++++------- ruoyi-fastapi-backend/config/get_scheduler.py | 6 +++--- ruoyi-fastapi-backend/server.py | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/ruoyi-fastapi-backend/config/get_db.py b/ruoyi-fastapi-backend/config/get_db.py index 20986ae..e5930c7 100644 --- a/ruoyi-fastapi-backend/config/get_db.py +++ b/ruoyi-fastapi-backend/config/get_db.py @@ -18,7 +18,7 @@ async def init_create_table(): :return: """ - logger.info('初始化数据库连接...') + logger.info('🔎 初始化数据库连接...') async with async_engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) - logger.info('数据库连接成功') + logger.info('✅️ 数据库连接成功') diff --git a/ruoyi-fastapi-backend/config/get_redis.py b/ruoyi-fastapi-backend/config/get_redis.py index 9d78cad..ee4b6bd 100644 --- a/ruoyi-fastapi-backend/config/get_redis.py +++ b/ruoyi-fastapi-backend/config/get_redis.py @@ -19,7 +19,7 @@ class RedisUtil: :return: Redis连接对象 """ - logger.info('开始连接redis...') + logger.info('🔎 开始连接redis...') redis = await aioredis.from_url( url=f'redis://{RedisConfig.redis_host}', port=RedisConfig.redis_port, @@ -32,15 +32,15 @@ class RedisUtil: try: connection = await redis.ping() if connection: - logger.info('redis连接成功') + logger.info('✅️ redis连接成功') else: - logger.error('redis连接失败') + logger.error('❌️ redis连接失败') except AuthenticationError as e: - logger.error(f'redis用户名或密码错误,详细错误信息:{e}') + logger.error(f'❌️ redis用户名或密码错误,详细错误信息:{e}') except TimeoutError as e: - logger.error(f'redis连接超时,详细错误信息:{e}') + logger.error(f'❌️ redis连接超时,详细错误信息:{e}') except RedisError as e: - logger.error(f'redis连接错误,详细错误信息:{e}') + logger.error(f'❌️ redis连接错误,详细错误信息:{e}') return redis @classmethod @@ -52,7 +52,7 @@ class RedisUtil: :return: """ await app.state.redis.close() - logger.info('关闭redis连接成功') + logger.info('✅️ 关闭redis连接成功') @classmethod async def init_sys_dict(cls, redis): diff --git a/ruoyi-fastapi-backend/config/get_scheduler.py b/ruoyi-fastapi-backend/config/get_scheduler.py index 4b0c298..fadcf02 100644 --- a/ruoyi-fastapi-backend/config/get_scheduler.py +++ b/ruoyi-fastapi-backend/config/get_scheduler.py @@ -131,7 +131,7 @@ class SchedulerUtil: :return: """ - logger.info('开始启动定时任务...') + logger.info('🔎 开始启动定时任务...') scheduler.start() async with AsyncSessionLocal() as session: job_list = await JobDao.get_job_list_for_scheduler(session) @@ -139,7 +139,7 @@ class SchedulerUtil: cls.remove_scheduler_job(job_id=str(item.job_id)) cls.add_scheduler_job(item) scheduler.add_listener(cls.scheduler_event_listener, EVENT_ALL) - logger.info('系统初始定时任务加载成功') + logger.info('✅️ 系统初始定时任务加载成功') @classmethod async def close_system_scheduler(cls): @@ -149,7 +149,7 @@ class SchedulerUtil: :return: """ scheduler.shutdown() - logger.info('关闭定时任务成功') + logger.info('✅️ 关闭定时任务成功') @classmethod def get_scheduler_job(cls, job_id: Union[str, int]): diff --git a/ruoyi-fastapi-backend/server.py b/ruoyi-fastapi-backend/server.py index 5c8ad9c..7a46e77 100644 --- a/ruoyi-fastapi-backend/server.py +++ b/ruoyi-fastapi-backend/server.py @@ -31,14 +31,14 @@ from utils.log_util import logger # 生命周期事件 @asynccontextmanager async def lifespan(app: FastAPI): - logger.info(f'{AppConfig.app_name}开始启动') + logger.info(f'⏰️ {AppConfig.app_name}开始启动') worship() await init_create_table() app.state.redis = await RedisUtil.create_redis_pool() await RedisUtil.init_sys_dict(app.state.redis) await RedisUtil.init_sys_config(app.state.redis) await SchedulerUtil.init_system_scheduler() - logger.info(f'{AppConfig.app_name}启动成功') + logger.info(f'🚀 {AppConfig.app_name}启动成功') yield await RedisUtil.close_redis_pool(app) await SchedulerUtil.close_system_scheduler() -- Gitee From 8003c2bb877bd10f5620c0dfbb4c5e3ed5cdea69 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Fri, 22 Aug 2025 16:14:49 +0800 Subject: [PATCH 07/38] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-fastapi-backend/requirements-pg.txt | 13 +++++++------ ruoyi-fastapi-backend/requirements.txt | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/ruoyi-fastapi-backend/requirements-pg.txt b/ruoyi-fastapi-backend/requirements-pg.txt index 3c9ce65..c9d1c24 100644 --- a/ruoyi-fastapi-backend/requirements-pg.txt +++ b/ruoyi-fastapi-backend/requirements-pg.txt @@ -1,18 +1,19 @@ +alembic==1.16.4 APScheduler==3.11.0 async-lru==2.0.5 asyncpg==0.30.0 DateTime==5.5 -fastapi[all]==0.115.8 +fastapi[all]==0.116.1 loguru==0.7.3 openpyxl==3.1.5 -pandas==2.2.3 +pandas==2.3.2 passlib[bcrypt]==1.7.4 -Pillow==11.1.0 +Pillow==11.3.0 psutil==7.0.0 pydantic-validation-decorator==0.1.4 PyJWT[crypto]==2.10.1 psycopg2==2.9.10 -redis==5.2.1 -SQLAlchemy[asyncio]==2.0.38 -sqlglot[rs]==26.6.0 +redis==6.4.0 +SQLAlchemy[asyncio]==2.0.43 +sqlglot[rs]==27.8.0 user-agents==2.2.0 diff --git a/ruoyi-fastapi-backend/requirements.txt b/ruoyi-fastapi-backend/requirements.txt index 4ddd3af..9040b49 100644 --- a/ruoyi-fastapi-backend/requirements.txt +++ b/ruoyi-fastapi-backend/requirements.txt @@ -1,18 +1,19 @@ +alembic==1.16.4 APScheduler==3.11.0 async-lru==2.0.5 asyncmy==0.2.10 DateTime==5.5 -fastapi[all]==0.115.8 +fastapi[all]==0.116.1 loguru==0.7.3 openpyxl==3.1.5 -pandas==2.2.3 +pandas==2.3.2 passlib[bcrypt]==1.7.4 -Pillow==11.1.0 +Pillow==11.3.0 psutil==7.0.0 pydantic-validation-decorator==0.1.4 PyJWT[crypto]==2.10.1 PyMySQL==1.1.1 -redis==5.2.1 -SQLAlchemy[asyncio]==2.0.38 -sqlglot[rs]==26.6.0 +redis==6.4.0 +SQLAlchemy[asyncio]==2.0.43 +sqlglot[rs]==27.8.0 user-agents==2.2.0 -- Gitee From 2c402eb1bcf97f534ea90c62fd74402068c29012 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Fri, 22 Aug 2025 16:15:32 +0800 Subject: [PATCH 08/38] =?UTF-8?q?refactor:=20=E8=B0=83=E6=95=B4do=E4=B8=8E?= =?UTF-8?q?sql=E4=BD=BF=E5=85=B6=E7=9B=B8=E4=BA=92=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module_admin/dao/job_dao.py | 13 ++- .../module_admin/entity/do/config_do.py | 26 ++++-- .../module_admin/entity/do/dept_do.py | 44 +++++++--- .../module_admin/entity/do/dict_do.py | 66 ++++++++++----- .../module_admin/entity/do/job_do.py | 44 +++++----- .../module_admin/entity/do/log_do.py | 56 +++++++------ .../module_admin/entity/do/menu_do.py | 56 ++++++++----- .../module_admin/entity/do/notice_do.py | 34 +++++--- .../module_admin/entity/do/post_do.py | 20 +++-- .../module_admin/entity/do/role_do.py | 56 +++++++++---- .../module_admin/entity/do/user_do.py | 59 ++++++++----- .../module_admin/service/job_service.py | 2 +- .../module_generator/entity/do/gen_do.py | 83 ++++++++++++------- .../sql/ruoyi-fastapi-pg.sql | 6 +- ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql | 3 +- ruoyi-fastapi-backend/utils/common_util.py | 14 ++++ 16 files changed, 380 insertions(+), 202 deletions(-) diff --git a/ruoyi-fastapi-backend/module_admin/dao/job_dao.py b/ruoyi-fastapi-backend/module_admin/dao/job_dao.py index 805d460..fc30048 100644 --- a/ruoyi-fastapi-backend/module_admin/dao/job_dao.py +++ b/ruoyi-fastapi-backend/module_admin/dao/job_dao.py @@ -104,15 +104,24 @@ class JobDao: return db_job @classmethod - async def edit_job_dao(cls, db: AsyncSession, job: dict): + async def edit_job_dao(cls, db: AsyncSession, job: dict, old_job: JobModel): """ 编辑定时任务数据库操作 :param db: orm对象 :param job: 需要更新的定时任务字典 + :param old_job: 原定时任务对象 :return: """ - await db.execute(update(SysJob), [job]) + await db.execute( + update(SysJob) + .where( + SysJob.job_id == old_job.job_id, + SysJob.job_name == old_job.job_name, + SysJob.job_group == old_job.job_group, + ) + .values(**job) + ) @classmethod async def delete_job_dao(cls, db: AsyncSession, job: JobModel): diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/config_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/config_do.py index 012d2be..8a3235d 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/do/config_do.py +++ b/ruoyi-fastapi-backend/module_admin/entity/do/config_do.py @@ -1,6 +1,8 @@ from datetime import datetime -from sqlalchemy import Column, DateTime, Integer, String +from sqlalchemy import CHAR, Column, DateTime, Integer, String from config.database import Base +from config.env import DataBaseConfig +from utils.common_util import SqlalchemyUtil class SysConfig(Base): @@ -9,14 +11,20 @@ class SysConfig(Base): """ __tablename__ = 'sys_config' + __table_args__ = {'comment': '参数配置表'} - config_id = Column(Integer, primary_key=True, autoincrement=True, comment='参数主键') - config_name = Column(String(100), nullable=True, default='', comment='参数名称') - config_key = Column(String(100), nullable=True, default='', comment='参数键名') - config_value = Column(String(500), nullable=True, default='', comment='参数键值') - config_type = Column(String(1), nullable=True, default='N', comment='系统内置(Y是 N否)') - create_by = Column(String(64), nullable=True, default='', comment='创建者') + config_id = Column(Integer, primary_key=True, nullable=False, autoincrement=True, comment='参数主键') + config_name = Column(String(100), nullable=True, server_default="''", comment='参数名称') + config_key = Column(String(100), nullable=True, server_default="''", comment='参数键名') + config_value = Column(String(500), nullable=True, server_default="''", comment='参数键值') + config_type = Column(CHAR(1), nullable=True, server_default='N', comment='系统内置(Y是 N否)') + create_by = Column(String(64), nullable=True, server_default="''", comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') - update_by = Column(String(64), nullable=True, default='', comment='更新者') + update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') - remark = Column(String(500), nullable=True, default=None, comment='备注') + remark = Column( + String(500), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='备注', + ) diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/dept_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/dept_do.py index 44e2f02..48baf8d 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/do/dept_do.py +++ b/ruoyi-fastapi-backend/module_admin/entity/do/dept_do.py @@ -1,6 +1,8 @@ from datetime import datetime -from sqlalchemy import Column, DateTime, Integer, String +from sqlalchemy import BigInteger, CHAR, Column, DateTime, Integer, String from config.database import Base +from config.env import DataBaseConfig +from utils.common_util import SqlalchemyUtil class SysDept(Base): @@ -9,18 +11,34 @@ class SysDept(Base): """ __tablename__ = 'sys_dept' + __table_args__ = {'comment': '部门表'} - dept_id = Column(Integer, primary_key=True, autoincrement=True, comment='部门id') - parent_id = Column(Integer, default=0, comment='父部门id') - ancestors = Column(String(50), nullable=True, default='', comment='祖级列表') - dept_name = Column(String(30), nullable=True, default='', comment='部门名称') - order_num = Column(Integer, default=0, comment='显示顺序') - leader = Column(String(20), nullable=True, default=None, comment='负责人') - phone = Column(String(11), nullable=True, default=None, comment='联系电话') - email = Column(String(50), nullable=True, default=None, comment='邮箱') - status = Column(String(1), nullable=True, default='0', comment='部门状态(0正常 1停用)') - del_flag = Column(String(1), nullable=True, default='0', comment='删除标志(0代表存在 2代表删除)') - create_by = Column(String(64), nullable=True, default='', comment='创建者') + dept_id = Column(BigInteger, primary_key=True, autoincrement=True, comment='部门id') + parent_id = Column(BigInteger, server_default='0', comment='父部门id') + ancestors = Column(String(50), nullable=True, server_default="''", comment='祖级列表') + dept_name = Column(String(30), nullable=True, server_default="''", comment='部门名称') + order_num = Column(Integer, server_default='0', comment='显示顺序') + leader = Column( + String(20), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='负责人', + ) + phone = Column( + String(11), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='联系电话', + ) + email = Column( + String(50), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='邮箱', + ) + status = Column(CHAR(1), nullable=True, server_default='0', comment='部门状态(0正常 1停用)') + del_flag = Column(CHAR(1), nullable=True, server_default='0', comment='删除标志(0代表存在 2代表删除)') + create_by = Column(String(64), nullable=True, server_default="''", comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') - update_by = Column(String(64), nullable=True, default='', comment='更新者') + update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/dict_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/dict_do.py index 7a155ea..de6a9c4 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/do/dict_do.py +++ b/ruoyi-fastapi-backend/module_admin/entity/do/dict_do.py @@ -1,6 +1,8 @@ from datetime import datetime -from sqlalchemy import Column, DateTime, Integer, String, UniqueConstraint +from sqlalchemy import BigInteger, CHAR, Column, DateTime, Integer, String from config.database import Base +from config.env import DataBaseConfig +from utils.common_util import SqlalchemyUtil class SysDictType(Base): @@ -9,18 +11,22 @@ class SysDictType(Base): """ __tablename__ = 'sys_dict_type' + __table_args__ = {'comment': '字典类型表'} - dict_id = Column(Integer, primary_key=True, autoincrement=True, comment='字典主键') - dict_name = Column(String(100), nullable=True, default='', comment='字典名称') - dict_type = Column(String(100), nullable=True, default='', comment='字典类型') - status = Column(String(1), nullable=True, default='0', comment='状态(0正常 1停用)') - create_by = Column(String(64), nullable=True, default='', comment='创建者') + dict_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='字典主键') + dict_name = Column(String(100), nullable=True, server_default="''", comment='字典名称') + dict_type = Column(String(100), unique=True, nullable=True, server_default="''", comment='字典类型') + status = Column(CHAR(1), nullable=True, server_default='0', comment='状态(0正常 1停用)') + create_by = Column(String(64), nullable=True, server_default="''", comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') - update_by = Column(String(64), nullable=True, default='', comment='更新者') + update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') - remark = Column(String(500), nullable=True, default=None, comment='备注') - - __table_args__ = (UniqueConstraint('dict_type', name='uq_sys_dict_type_dict_type'),) + remark = Column( + String(500), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='备注', + ) class SysDictData(Base): @@ -29,18 +35,34 @@ class SysDictData(Base): """ __tablename__ = 'sys_dict_data' + __table_args__ = {'comment': '字典数据表'} - dict_code = Column(Integer, primary_key=True, autoincrement=True, comment='字典编码') - dict_sort = Column(Integer, nullable=True, default=0, comment='字典排序') - dict_label = Column(String(100), nullable=True, default='', comment='字典标签') - dict_value = Column(String(100), nullable=True, default='', comment='字典键值') - dict_type = Column(String(100), nullable=True, default='', comment='字典类型') - css_class = Column(String(100), nullable=True, default=None, comment='样式属性(其他样式扩展)') - list_class = Column(String(100), nullable=True, default=None, comment='表格回显样式') - is_default = Column(String(1), nullable=True, default='N', comment='是否默认(Y是 N否)') - status = Column(String(1), nullable=True, default='0', comment='状态(0正常 1停用)') - create_by = Column(String(64), nullable=True, default='', comment='创建者') + dict_code = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='字典编码') + dict_sort = Column(Integer, nullable=True, server_default='0', comment='字典排序') + dict_label = Column(String(100), nullable=True, server_default="''", comment='字典标签') + dict_value = Column(String(100), nullable=True, server_default="''", comment='字典键值') + dict_type = Column(String(100), nullable=True, server_default="''", comment='字典类型') + css_class = Column( + String(100), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='样式属性(其他样式扩展)', + ) + list_class = Column( + String(100), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='表格回显样式', + ) + is_default = Column(CHAR(1), nullable=True, server_default='N', comment='是否默认(Y是 N否)') + status = Column(CHAR(1), nullable=True, server_default='0', comment='状态(0正常 1停用)') + create_by = Column(String(64), nullable=True, server_default="''", comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') - update_by = Column(String(64), nullable=True, default='', comment='更新者') + update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') - remark = Column(String(500), nullable=True, default=None, comment='备注') + remark = Column( + String(500), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='备注', + ) diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/job_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/job_do.py index b2cbfb8..8e941b7 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/do/job_do.py +++ b/ruoyi-fastapi-backend/module_admin/entity/do/job_do.py @@ -1,5 +1,5 @@ from datetime import datetime -from sqlalchemy import Column, DateTime, Integer, String +from sqlalchemy import BigInteger, CHAR, Column, DateTime, String from config.database import Base @@ -9,28 +9,29 @@ class SysJob(Base): """ __tablename__ = 'sys_job' + __table_args__ = {'comment': '定时任务调度表'} - job_id = Column(Integer, primary_key=True, autoincrement=True, comment='任务ID') - job_name = Column(String(64), nullable=True, default='', comment='任务名称') - job_group = Column(String(64), nullable=True, default='default', comment='任务组名') - job_executor = Column(String(64), nullable=True, default='default', comment='任务执行器') + job_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='任务ID') + job_name = Column(String(64), primary_key=True, nullable=False, server_default="''", comment='任务名称') + job_group = Column(String(64), primary_key=True, nullable=False, server_default='default', comment='任务组名') + job_executor = Column(String(64), nullable=True, server_default='default', comment='任务执行器') invoke_target = Column(String(500), nullable=False, comment='调用目标字符串') - job_args = Column(String(255), nullable=True, default='', comment='位置参数') - job_kwargs = Column(String(255), nullable=True, default='', comment='关键字参数') - cron_expression = Column(String(255), nullable=True, default='', comment='cron执行表达式') + job_args = Column(String(255), nullable=True, server_default="''", comment='位置参数') + job_kwargs = Column(String(255), nullable=True, server_default="''", comment='关键字参数') + cron_expression = Column(String(255), nullable=True, server_default="''", comment='cron执行表达式') misfire_policy = Column( String(20), nullable=True, - default='3', + server_default='3', comment='计划执行错误策略(1立即执行 2执行一次 3放弃执行)', ) - concurrent = Column(String(1), nullable=True, default='1', comment='是否并发执行(0允许 1禁止)') - status = Column(String(1), nullable=True, default='0', comment='状态(0正常 1暂停)') - create_by = Column(String(64), nullable=True, default='', comment='创建者') + concurrent = Column(CHAR(1), nullable=True, server_default='1', comment='是否并发执行(0允许 1禁止)') + status = Column(CHAR(1), nullable=True, server_default='0', comment='状态(0正常 1暂停)') + create_by = Column(String(64), nullable=True, server_default="''", comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') - update_by = Column(String(64), nullable=True, default='', comment='更新者') + update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') - remark = Column(String(500), nullable=True, default='', comment='备注信息') + remark = Column(String(500), nullable=True, server_default="''", comment='备注信息') class SysJobLog(Base): @@ -39,16 +40,17 @@ class SysJobLog(Base): """ __tablename__ = 'sys_job_log' + __table_args__ = {'comment': '定时任务调度日志表'} - job_log_id = Column(Integer, primary_key=True, autoincrement=True, comment='任务日志ID') + job_log_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='任务日志ID') job_name = Column(String(64), nullable=False, comment='任务名称') job_group = Column(String(64), nullable=False, comment='任务组名') job_executor = Column(String(64), nullable=False, comment='任务执行器') invoke_target = Column(String(500), nullable=False, comment='调用目标字符串') - job_args = Column(String(255), nullable=True, default='', comment='位置参数') - job_kwargs = Column(String(255), nullable=True, default='', comment='关键字参数') - job_trigger = Column(String(255), nullable=True, default='', comment='任务触发器') - job_message = Column(String(500), nullable=True, default='', comment='日志信息') - status = Column(String(1), nullable=True, default='0', comment='执行状态(0正常 1失败)') - exception_info = Column(String(2000), nullable=True, default='', comment='异常信息') + job_args = Column(String(255), nullable=True, server_default="''", comment='位置参数') + job_kwargs = Column(String(255), nullable=True, server_default="''", comment='关键字参数') + job_trigger = Column(String(255), nullable=True, server_default="''", comment='任务触发器') + job_message = Column(String(500), nullable=True, comment='日志信息') + status = Column(CHAR(1), nullable=True, server_default='0', comment='执行状态(0正常 1失败)') + exception_info = Column(String(2000), nullable=True, server_default="''", comment='异常信息') create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/log_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/log_do.py index f9e14ab..981b026 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/do/log_do.py +++ b/ruoyi-fastapi-backend/module_admin/entity/do/log_do.py @@ -1,5 +1,5 @@ from datetime import datetime -from sqlalchemy import BigInteger, Column, DateTime, Index, Integer, String +from sqlalchemy import BigInteger, CHAR, Column, DateTime, Index, Integer, String from config.database import Base @@ -9,15 +9,16 @@ class SysLogininfor(Base): """ __tablename__ = 'sys_logininfor' - - info_id = Column(Integer, primary_key=True, autoincrement=True, comment='访问ID') - user_name = Column(String(50), nullable=True, default='', comment='用户账号') - ipaddr = Column(String(128), nullable=True, default='', comment='登录IP地址') - login_location = Column(String(255), nullable=True, default='', comment='登录地点') - browser = Column(String(50), nullable=True, default='', comment='浏览器类型') - os = Column(String(50), nullable=True, default='', comment='操作系统') - status = Column(String(1), nullable=True, default='0', comment='登录状态(0成功 1失败)') - msg = Column(String(255), nullable=True, default='', comment='提示消息') + __table_args__ = {'comment': '系统访问记录'} + + info_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='访问ID') + user_name = Column(String(50), nullable=True, server_default="''", comment='用户账号') + ipaddr = Column(String(128), nullable=True, server_default="''", comment='登录IP地址') + login_location = Column(String(255), nullable=True, server_default="''", comment='登录地点') + browser = Column(String(50), nullable=True, server_default="''", comment='浏览器类型') + os = Column(String(50), nullable=True, server_default="''", comment='操作系统') + status = Column(CHAR(1), nullable=True, server_default='0', comment='登录状态(0成功 1失败)') + msg = Column(String(255), nullable=True, server_default="''", comment='提示消息') login_time = Column(DateTime, nullable=True, default=datetime.now(), comment='访问时间') idx_sys_logininfor_s = Index('idx_sys_logininfor_s', status) @@ -30,24 +31,25 @@ class SysOperLog(Base): """ __tablename__ = 'sys_oper_log' - - oper_id = Column(BigInteger, primary_key=True, autoincrement=True, comment='日志主键') - title = Column(String(50), nullable=True, default='', comment='模块标题') - business_type = Column(Integer, default=0, comment='业务类型(0其它 1新增 2修改 3删除)') - method = Column(String(100), nullable=True, default='', comment='方法名称') - request_method = Column(String(10), nullable=True, default='', comment='请求方式') - operator_type = Column(Integer, default=0, comment='操作类别(0其它 1后台用户 2手机端用户)') - oper_name = Column(String(50), nullable=True, default='', comment='操作人员') - dept_name = Column(String(50), nullable=True, default='', comment='部门名称') - oper_url = Column(String(255), nullable=True, default='', comment='请求URL') - oper_ip = Column(String(128), nullable=True, default='', comment='主机地址') - oper_location = Column(String(255), nullable=True, default='', comment='操作地点') - oper_param = Column(String(2000), nullable=True, default='', comment='请求参数') - json_result = Column(String(2000), nullable=True, default='', comment='返回参数') - status = Column(Integer, default=0, comment='操作状态(0正常 1异常)') - error_msg = Column(String(2000), nullable=True, default='', comment='错误消息') + __table_args__ = {'comment': '操作日志记录'} + + oper_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='日志主键') + title = Column(String(50), nullable=True, server_default="''", comment='模块标题') + business_type = Column(Integer, nullable=True, server_default='0', comment='业务类型(0其它 1新增 2修改 3删除)') + method = Column(String(100), nullable=True, server_default="''", comment='方法名称') + request_method = Column(String(10), nullable=True, server_default="''", comment='请求方式') + operator_type = Column(Integer, nullable=True, server_default='0', comment='操作类别(0其它 1后台用户 2手机端用户)') + oper_name = Column(String(50), nullable=True, server_default="''", comment='操作人员') + dept_name = Column(String(50), nullable=True, server_default="''", comment='部门名称') + oper_url = Column(String(255), nullable=True, server_default="''", comment='请求URL') + oper_ip = Column(String(128), nullable=True, server_default="''", comment='主机地址') + oper_location = Column(String(255), nullable=True, server_default="''", comment='操作地点') + oper_param = Column(String(2000), nullable=True, server_default="''", comment='请求参数') + json_result = Column(String(2000), nullable=True, server_default="''", comment='返回参数') + status = Column(Integer, nullable=True, server_default='0', comment='操作状态(0正常 1异常)') + error_msg = Column(String(2000), nullable=True, server_default="''", comment='错误消息') oper_time = Column(DateTime, nullable=True, default=datetime.now(), comment='操作时间') - cost_time = Column(BigInteger, default=0, comment='消耗时间') + cost_time = Column(BigInteger, nullable=True, server_default='0', comment='消耗时间') idx_sys_oper_log_bt = Index('idx_sys_oper_log_bt', business_type) idx_sys_oper_log_s = Index('idx_sys_oper_log_s', status) diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/menu_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/menu_do.py index d3ccffa..0a8a836 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/do/menu_do.py +++ b/ruoyi-fastapi-backend/module_admin/entity/do/menu_do.py @@ -1,6 +1,8 @@ from datetime import datetime -from sqlalchemy import Column, DateTime, Integer, String +from sqlalchemy import BigInteger, CHAR, Column, DateTime, Integer, String from config.database import Base +from config.env import DataBaseConfig +from utils.common_util import SqlalchemyUtil class SysMenu(Base): @@ -9,24 +11,40 @@ class SysMenu(Base): """ __tablename__ = 'sys_menu' + __table_args__ = {'comment': '菜单权限表'} - menu_id = Column(Integer, primary_key=True, autoincrement=True, comment='菜单ID') - menu_name = Column(String(50), nullable=False, default='', comment='菜单名称') - parent_id = Column(Integer, default=0, comment='父菜单ID') - order_num = Column(Integer, default=0, comment='显示顺序') - path = Column(String(200), nullable=True, default='', comment='路由地址') - component = Column(String(255), nullable=True, default=None, comment='组件路径') - query = Column(String(255), nullable=True, default=None, comment='路由参数') - route_name = Column(String(50), nullable=True, default='', comment='路由名称') - is_frame = Column(Integer, default=1, comment='是否为外链(0是 1否)') - is_cache = Column(Integer, default=0, comment='是否缓存(0缓存 1不缓存)') - menu_type = Column(String(1), nullable=True, default='', comment='菜单类型(M目录 C菜单 F按钮)') - visible = Column(String(1), nullable=True, default='0', comment='菜单状态(0显示 1隐藏)') - status = Column(String(1), nullable=True, default='0', comment='菜单状态(0正常 1停用)') - perms = Column(String(100), nullable=True, default=None, comment='权限标识') - icon = Column(String(100), nullable=True, default='#', comment='菜单图标') - create_by = Column(String(64), nullable=True, default='', comment='创建者') + menu_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='菜单ID') + menu_name = Column(String(50), nullable=False, comment='菜单名称') + parent_id = Column(BigInteger, nullable=True, server_default='0', comment='父菜单ID') + order_num = Column(Integer, server_default='0', comment='显示顺序') + path = Column(String(200), nullable=True, server_default="''", comment='路由地址') + component = Column( + String(255), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='组件路径', + ) + query = Column( + String(255), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='路由参数', + ) + route_name = Column(String(50), nullable=True, server_default="''", comment='路由名称') + is_frame = Column(Integer, nullable=True, server_default='1', comment='是否为外链(0是 1否)') + is_cache = Column(Integer, nullable=True, server_default='0', comment='是否缓存(0缓存 1不缓存)') + menu_type = Column(CHAR(1), nullable=True, server_default="''", comment='菜单类型(M目录 C菜单 F按钮)') + visible = Column(CHAR(1), nullable=True, server_default='0', comment='菜单状态(0显示 1隐藏)') + status = Column(CHAR(1), nullable=True, server_default='0', comment='菜单状态(0正常 1停用)') + perms = Column( + String(100), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='权限标识', + ) + icon = Column(String(100), nullable=True, server_default='#', comment='菜单图标') + create_by = Column(String(64), nullable=True, server_default="''", comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') - update_by = Column(String(64), nullable=True, default='', comment='更新者') + update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') - remark = Column(String(500), nullable=True, default='', comment='备注') + remark = Column(String(500), nullable=True, server_default="''", comment='备注') diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/notice_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/notice_do.py index 9d1eb98..0d60854 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/do/notice_do.py +++ b/ruoyi-fastapi-backend/module_admin/entity/do/notice_do.py @@ -1,6 +1,9 @@ from datetime import datetime -from sqlalchemy import Column, DateTime, Integer, LargeBinary, String +from sqlalchemy import CHAR, Column, DateTime, Integer, LargeBinary, String +from sqlalchemy.dialects import mysql from config.database import Base +from config.env import DataBaseConfig +from utils.common_util import SqlalchemyUtil class SysNotice(Base): @@ -9,14 +12,25 @@ class SysNotice(Base): """ __tablename__ = 'sys_notice' + __table_args__ = {'comment': '通知公告表'} - notice_id = Column(Integer, primary_key=True, autoincrement=True, comment='公告ID') + notice_id = Column(Integer, primary_key=True, nullable=False, autoincrement=True, comment='公告ID') notice_title = Column(String(50), nullable=False, comment='公告标题') - notice_type = Column(String(1), nullable=False, comment='公告类型(1通知 2公告)') - notice_content = Column(LargeBinary, comment='公告内容') - status = Column(String(1), default='0', comment='公告状态(0正常 1关闭)') - create_by = Column(String(64), default='', comment='创建者') - create_time = Column(DateTime, comment='创建时间', default=datetime.now()) - update_by = Column(String(64), default='', comment='更新者') - update_time = Column(DateTime, comment='更新时间', default=datetime.now()) - remark = Column(String(255), comment='备注') + notice_type = Column(CHAR(1), nullable=False, comment='公告类型(1通知 2公告)') + notice_content = Column( + mysql.LONGBLOB if DataBaseConfig.db_type == 'mysql' else LargeBinary, + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type, False), + comment='公告内容', + ) + status = Column(CHAR(1), nullable=True, server_default='0', comment='公告状态(0正常 1关闭)') + create_by = Column(String(64), nullable=True, server_default="''", comment='创建者') + create_time = Column(DateTime, nullable=True, comment='创建时间', default=datetime.now()) + update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') + update_time = Column(DateTime, nullable=True, comment='更新时间', default=datetime.now()) + remark = Column( + String(255), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='备注', + ) diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/post_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/post_do.py index f231f72..442d0f3 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/do/post_do.py +++ b/ruoyi-fastapi-backend/module_admin/entity/do/post_do.py @@ -1,6 +1,8 @@ from datetime import datetime -from sqlalchemy import Column, DateTime, Integer, String +from sqlalchemy import BigInteger, CHAR, Column, DateTime, Integer, String from config.database import Base +from config.env import DataBaseConfig +from utils.common_util import SqlalchemyUtil class SysPost(Base): @@ -9,14 +11,20 @@ class SysPost(Base): """ __tablename__ = 'sys_post' + __table_args__ = {'comment': '岗位信息表'} - post_id = Column(Integer, primary_key=True, autoincrement=True, comment='岗位ID') + post_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='岗位ID') post_code = Column(String(64), nullable=False, comment='岗位编码') post_name = Column(String(50), nullable=False, comment='岗位名称') post_sort = Column(Integer, nullable=False, comment='显示顺序') - status = Column(String(1), nullable=False, default='0', comment='状态(0正常 1停用)') - create_by = Column(String(64), default='', comment='创建者') + status = Column(CHAR(1), nullable=False, comment='状态(0正常 1停用)') + create_by = Column(String(64), nullable=True, server_default="''", comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') - update_by = Column(String(64), default='', comment='更新者') + update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') - remark = Column(String(500), nullable=True, default=None, comment='备注') + remark = Column( + String(500), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='备注', + ) diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/role_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/role_do.py index 58d4de1..fecf9d1 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/do/role_do.py +++ b/ruoyi-fastapi-backend/module_admin/entity/do/role_do.py @@ -1,6 +1,9 @@ from datetime import datetime -from sqlalchemy import Column, DateTime, Integer, String +from sqlalchemy import BigInteger, CHAR, Column, DateTime, Integer, SmallInteger, String +from sqlalchemy.dialects import mysql from config.database import Base +from config.env import DataBaseConfig +from utils.common_util import SqlalchemyUtil class SysRole(Base): @@ -9,25 +12,42 @@ class SysRole(Base): """ __tablename__ = 'sys_role' + __table_args__ = {'comment': '角色信息表'} - role_id = Column(Integer, primary_key=True, autoincrement=True, comment='角色ID') + role_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='角色ID') role_name = Column(String(30), nullable=False, comment='角色名称') role_key = Column(String(100), nullable=False, comment='角色权限字符串') role_sort = Column(Integer, nullable=False, comment='显示顺序') data_scope = Column( - String(1), - default='1', + CHAR(1), + nullable=True, + server_default='1', comment='数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', ) - menu_check_strictly = Column(Integer, default=1, comment='菜单树选择项是否关联显示') - dept_check_strictly = Column(Integer, default=1, comment='部门树选择项是否关联显示') - status = Column(String(1), nullable=False, default='0', comment='角色状态(0正常 1停用)') - del_flag = Column(String(1), default='0', comment='删除标志(0代表存在 2代表删除)') - create_by = Column(String(64), default='', comment='创建者') - create_time = Column(DateTime, default=datetime.now(), comment='创建时间') - update_by = Column(String(64), default='', comment='更新者') - update_time = Column(DateTime, default=datetime.now(), comment='更新时间') - remark = Column(String(500), default=None, comment='备注') + menu_check_strictly = Column( + mysql.TINYINT(display_width=1) if DataBaseConfig.db_type == 'mysql' else SmallInteger, + nullable=True, + server_default='1', + comment='菜单树选择项是否关联显示', + ) + dept_check_strictly = Column( + mysql.TINYINT(display_width=1) if DataBaseConfig.db_type == 'mysql' else SmallInteger, + nullable=True, + server_default='1', + comment='部门树选择项是否关联显示', + ) + status = Column(CHAR(1), nullable=False, comment='角色状态(0正常 1停用)') + del_flag = Column(CHAR(1), nullable=True, server_default='0', comment='删除标志(0代表存在 2代表删除)') + create_by = Column(String(64), nullable=True, server_default="''", comment='创建者') + create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') + update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') + update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') + remark = Column( + String(500), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='备注', + ) class SysRoleDept(Base): @@ -36,9 +56,10 @@ class SysRoleDept(Base): """ __tablename__ = 'sys_role_dept' + __table_args__ = {'comment': '角色和部门关联表'} - role_id = Column(Integer, primary_key=True, nullable=False, comment='角色ID') - dept_id = Column(Integer, primary_key=True, nullable=False, comment='部门ID') + role_id = Column(BigInteger, primary_key=True, nullable=False, comment='角色ID') + dept_id = Column(BigInteger, primary_key=True, nullable=False, comment='部门ID') class SysRoleMenu(Base): @@ -47,6 +68,7 @@ class SysRoleMenu(Base): """ __tablename__ = 'sys_role_menu' + __table_args__ = {'comment': '角色和菜单关联表'} - role_id = Column(Integer, primary_key=True, nullable=False, comment='角色ID') - menu_id = Column(Integer, primary_key=True, nullable=False, comment='菜单ID') + role_id = Column(BigInteger, primary_key=True, nullable=False, comment='角色ID') + menu_id = Column(BigInteger, primary_key=True, nullable=False, comment='菜单ID') diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/user_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/user_do.py index 2dd0ba0..246461f 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/do/user_do.py +++ b/ruoyi-fastapi-backend/module_admin/entity/do/user_do.py @@ -1,6 +1,8 @@ from datetime import datetime -from sqlalchemy import Column, DateTime, Integer, String +from sqlalchemy import BigInteger, CHAR, Column, DateTime, String from config.database import Base +from config.env import DataBaseConfig +from utils.common_util import SqlalchemyUtil class SysUser(Base): @@ -9,26 +11,37 @@ class SysUser(Base): """ __tablename__ = 'sys_user' + __table_args__ = {'comment': '用户信息表'} - user_id = Column(Integer, primary_key=True, autoincrement=True, comment='用户ID') - dept_id = Column(Integer, default=None, comment='部门ID') + user_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='用户ID') + dept_id = Column( + BigInteger, + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type, False), + comment='部门ID', + ) user_name = Column(String(30), nullable=False, comment='用户账号') nick_name = Column(String(30), nullable=False, comment='用户昵称') - user_type = Column(String(2), default='00', comment='用户类型(00系统用户)') - email = Column(String(50), default='', comment='用户邮箱') - phonenumber = Column(String(11), default='', comment='手机号码') - sex = Column(String(1), default='0', comment='用户性别(0男 1女 2未知)') - avatar = Column(String(100), default='', comment='头像地址') - password = Column(String(100), default='', comment='密码') - status = Column(String(1), default='0', comment='帐号状态(0正常 1停用)') - del_flag = Column(String(1), default='0', comment='删除标志(0代表存在 2代表删除)') - login_ip = Column(String(128), default='', comment='最后登录IP') - login_date = Column(DateTime, comment='最后登录时间') - create_by = Column(String(64), default='', comment='创建者') - create_time = Column(DateTime, comment='创建时间', default=datetime.now()) - update_by = Column(String(64), default='', comment='更新者') - update_time = Column(DateTime, comment='更新时间', default=datetime.now()) - remark = Column(String(500), default=None, comment='备注') + user_type = Column(String(2), nullable=True, server_default='00', comment='用户类型(00系统用户)') + email = Column(String(50), nullable=True, server_default="''", comment='用户邮箱') + phonenumber = Column(String(11), nullable=True, server_default="''", comment='手机号码') + sex = Column(CHAR(1), nullable=True, server_default='0', comment='用户性别(0男 1女 2未知)') + avatar = Column(String(100), nullable=True, server_default="''", comment='头像地址') + password = Column(String(100), nullable=True, server_default="''", comment='密码') + status = Column(CHAR(1), nullable=True, server_default='0', comment='帐号状态(0正常 1停用)') + del_flag = Column(CHAR(1), nullable=True, server_default='0', comment='删除标志(0代表存在 2代表删除)') + login_ip = Column(String(128), nullable=True, server_default="''", comment='最后登录IP') + login_date = Column(DateTime, nullable=True, comment='最后登录时间') + create_by = Column(String(64), nullable=True, server_default="''", comment='创建者') + create_time = Column(DateTime, nullable=True, comment='创建时间', default=datetime.now()) + update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') + update_time = Column(DateTime, nullable=True, comment='更新时间', default=datetime.now()) + remark = Column( + String(500), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='备注', + ) class SysUserRole(Base): @@ -37,9 +50,10 @@ class SysUserRole(Base): """ __tablename__ = 'sys_user_role' + __table_args__ = {'comment': '用户和角色关联表'} - user_id = Column(Integer, primary_key=True, nullable=False, comment='用户ID') - role_id = Column(Integer, primary_key=True, nullable=False, comment='角色ID') + user_id = Column(BigInteger, primary_key=True, nullable=False, comment='用户ID') + role_id = Column(BigInteger, primary_key=True, nullable=False, comment='角色ID') class SysUserPost(Base): @@ -48,6 +62,7 @@ class SysUserPost(Base): """ __tablename__ = 'sys_user_post' + __table_args__ = {'comment': '用户与岗位关联表'} - user_id = Column(Integer, primary_key=True, nullable=False, comment='用户ID') - post_id = Column(Integer, primary_key=True, nullable=False, comment='岗位ID') + user_id = Column(BigInteger, primary_key=True, nullable=False, comment='用户ID') + post_id = Column(BigInteger, primary_key=True, nullable=False, comment='岗位ID') diff --git a/ruoyi-fastapi-backend/module_admin/service/job_service.py b/ruoyi-fastapi-backend/module_admin/service/job_service.py index 55263c1..2b783f0 100644 --- a/ruoyi-fastapi-backend/module_admin/service/job_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/job_service.py @@ -129,7 +129,7 @@ class JobService: elif not await cls.check_job_unique_services(query_db, page_object): raise ServiceException(message=f'修改定时任务{page_object.job_name}失败,定时任务已存在') try: - await JobDao.edit_job_dao(query_db, edit_job) + await JobDao.edit_job_dao(query_db, edit_job, job_info) SchedulerUtil.remove_scheduler_job(job_id=edit_job.get('job_id')) if edit_job.get('status') == '0': job_info = await cls.job_detail_services(query_db, edit_job.get('job_id')) diff --git a/ruoyi-fastapi-backend/module_generator/entity/do/gen_do.py b/ruoyi-fastapi-backend/module_generator/entity/do/gen_do.py index e64d0bf..edd62f6 100644 --- a/ruoyi-fastapi-backend/module_generator/entity/do/gen_do.py +++ b/ruoyi-fastapi-backend/module_generator/entity/do/gen_do.py @@ -1,7 +1,9 @@ from datetime import datetime -from sqlalchemy import Column, DateTime, ForeignKey, Integer, String +from sqlalchemy import BigInteger, CHAR, Column, DateTime, ForeignKey, Integer, String from sqlalchemy.orm import relationship from config.database import Base +from config.env import DataBaseConfig +from utils.common_util import SqlalchemyUtil class GenTable(Base): @@ -10,30 +12,48 @@ class GenTable(Base): """ __tablename__ = 'gen_table' + __table_args__ = {'comment': '代码生成业务表'} - table_id = Column(Integer, primary_key=True, autoincrement=True, comment='编号') - table_name = Column(String(200), nullable=True, default='', comment='表名称') - table_comment = Column(String(500), nullable=True, default='', comment='表描述') - sub_table_name = Column(String(64), nullable=True, comment='关联子表的表名') - sub_table_fk_name = Column(String(64), nullable=True, comment='子表关联的外键名') - class_name = Column(String(100), nullable=True, default='', comment='实体类名称') - tpl_category = Column(String(200), nullable=True, default='crud', comment='使用的模板(crud单表操作 tree树表操作)') + table_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='编号') + table_name = Column(String(200), nullable=True, server_default="''", comment='表名称') + table_comment = Column(String(500), nullable=True, server_default="''", comment='表描述') + sub_table_name = Column( + String(64), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='关联子表的表名', + ) + sub_table_fk_name = Column( + String(64), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='子表关联的外键名', + ) + class_name = Column(String(100), nullable=True, server_default="''", comment='实体类名称') + tpl_category = Column( + String(200), nullable=True, server_default='crud', comment='使用的模板(crud单表操作 tree树表操作)' + ) tpl_web_type = Column( - String(30), nullable=True, default='', comment='前端模板类型(element-ui模版 element-plus模版)' + String(30), nullable=True, server_default="''", comment='前端模板类型(element-ui模版 element-plus模版)' ) package_name = Column(String(100), nullable=True, comment='生成包路径') module_name = Column(String(30), nullable=True, comment='生成模块名') business_name = Column(String(30), nullable=True, comment='生成业务名') - function_name = Column(String(100), nullable=True, comment='生成功能名') - function_author = Column(String(100), nullable=True, comment='生成功能作者') - gen_type = Column(String(1), nullable=True, default='0', comment='生成代码方式(0zip压缩包 1自定义路径)') - gen_path = Column(String(200), nullable=True, default='/', comment='生成路径(不填默认项目路径)') + function_name = Column(String(50), nullable=True, comment='生成功能名') + function_author = Column(String(50), nullable=True, comment='生成功能作者') + gen_type = Column(CHAR(1), nullable=True, server_default='0', comment='生成代码方式(0zip压缩包 1自定义路径)') + gen_path = Column(String(200), nullable=True, server_default='/', comment='生成路径(不填默认项目路径)') options = Column(String(1000), nullable=True, comment='其它生成选项') - create_by = Column(String(64), default='', comment='创建者') + create_by = Column(String(64), nullable=True, server_default="''", comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') - update_by = Column(String(64), default='', comment='更新者') + update_by = Column(String(64), nullable=True, server_default="''", comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') - remark = Column(String(500), nullable=True, default=None, comment='备注') + remark = Column( + String(500), + nullable=True, + server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), + comment='备注', + ) columns = relationship('GenTableColumn', order_by='GenTableColumn.sort', back_populates='tables') @@ -44,31 +64,34 @@ class GenTableColumn(Base): """ __tablename__ = 'gen_table_column' + __table_args__ = {'comment': '代码生成业务表字段'} - column_id = Column(Integer, primary_key=True, autoincrement=True, comment='编号') - table_id = Column(Integer, ForeignKey('gen_table.table_id'), nullable=True, comment='归属表编号') + column_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='编号') + table_id = Column(BigInteger, ForeignKey('gen_table.table_id'), nullable=True, comment='归属表编号') column_name = Column(String(200), nullable=True, comment='列名称') column_comment = Column(String(500), nullable=True, comment='列描述') column_type = Column(String(100), nullable=True, comment='列类型') python_type = Column(String(500), nullable=True, comment='PYTHON类型') python_field = Column(String(200), nullable=True, comment='PYTHON字段名') - is_pk = Column(String(1), nullable=True, comment='是否主键(1是)') - is_increment = Column(String(1), nullable=True, comment='是否自增(1是)') - is_required = Column(String(1), nullable=True, comment='是否必填(1是)') - is_unique = Column(String(1), nullable=True, comment='是否唯一(1是)') - is_insert = Column(String(1), nullable=True, comment='是否为插入字段(1是)') - is_edit = Column(String(1), nullable=True, comment='是否编辑字段(1是)') - is_list = Column(String(1), nullable=True, comment='是否列表字段(1是)') - is_query = Column(String(1), nullable=True, comment='是否查询字段(1是)') - query_type = Column(String(200), nullable=True, default='EQ', comment='查询方式(等于、不等于、大于、小于、范围)') + is_pk = Column(CHAR(1), nullable=True, comment='是否主键(1是)') + is_increment = Column(CHAR(1), nullable=True, comment='是否自增(1是)') + is_required = Column(CHAR(1), nullable=True, comment='是否必填(1是)') + is_unique = Column(CHAR(1), nullable=True, comment='是否唯一(1是)') + is_insert = Column(CHAR(1), nullable=True, comment='是否为插入字段(1是)') + is_edit = Column(CHAR(1), nullable=True, comment='是否编辑字段(1是)') + is_list = Column(CHAR(1), nullable=True, comment='是否列表字段(1是)') + is_query = Column(CHAR(1), nullable=True, comment='是否查询字段(1是)') + query_type = Column( + String(200), nullable=True, server_default='EQ', comment='查询方式(等于、不等于、大于、小于、范围)' + ) html_type = Column( String(200), nullable=True, comment='显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)' ) - dict_type = Column(String(200), nullable=True, default='', comment='字典类型') + dict_type = Column(String(200), nullable=True, server_default="''", comment='字典类型') sort = Column(Integer, nullable=True, comment='排序') - create_by = Column(String(64), default='', comment='创建者') + create_by = Column(String(64), server_default="''", comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') - update_by = Column(String(64), default='', comment='更新者') + update_by = Column(String(64), server_default="''", comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') tables = relationship('GenTable', back_populates='columns') diff --git a/ruoyi-fastapi-backend/sql/ruoyi-fastapi-pg.sql b/ruoyi-fastapi-backend/sql/ruoyi-fastapi-pg.sql index b3731aa..a47c14f 100644 --- a/ruoyi-fastapi-backend/sql/ruoyi-fastapi-pg.sql +++ b/ruoyi-fastapi-backend/sql/ruoyi-fastapi-pg.sql @@ -522,7 +522,7 @@ create table sys_oper_log ( status int4 default 0, error_msg varchar(2000) default '', oper_time timestamp(0), - cost_time int8 default 0, + cost_time bigint default 0, primary key (oper_id) ); alter sequence sys_oper_log_oper_id_seq restart 100; @@ -545,6 +545,7 @@ comment on column sys_oper_log.json_result is '返回参数'; comment on column sys_oper_log.status is '操作状态(0正常 1异常)'; comment on column sys_oper_log.error_msg is '错误消息'; comment on column sys_oper_log.oper_time is '操作时间'; +comment on column sys_oper_log.cost_time is '消耗时间'; comment on table sys_oper_log is '操作日志记录'; -- ---------------------------- @@ -931,7 +932,8 @@ create table gen_table_column ( create_time timestamp(0), update_by varchar(64) default '', update_time timestamp(0), - primary key (column_id) + primary key (column_id), + constraint fk_gen_table_column_table_id foreign key (table_id) references gen_table(table_id) ); comment on column gen_table_column.column_id is '编号'; comment on column gen_table_column.table_id is '归属表编号'; diff --git a/ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql b/ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql index d0ce11a..7b796a7 100644 --- a/ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql +++ b/ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql @@ -709,5 +709,6 @@ create table gen_table_column ( create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', - primary key (column_id) + primary key (column_id), + foreign key (table_id) references gen_table(table_id) ) engine=innodb auto_increment=1 comment = '代码生成业务表字段'; \ No newline at end of file diff --git a/ruoyi-fastapi-backend/utils/common_util.py b/ruoyi-fastapi-backend/utils/common_util.py index b56000e..0ccb546 100644 --- a/ruoyi-fastapi-backend/utils/common_util.py +++ b/ruoyi-fastapi-backend/utils/common_util.py @@ -8,6 +8,7 @@ from openpyxl.utils import get_column_letter from openpyxl.worksheet.datavalidation import DataValidation from sqlalchemy.engine.row import Row from sqlalchemy.orm.collections import InstrumentedList +from sqlalchemy.sql.expression import TextClause, null from typing import Any, Dict, List, Literal, Union from config.database import Base from config.env import CachePathConfig @@ -100,6 +101,19 @@ class SqlalchemyUtil: return result_dict return result + @classmethod + def get_server_default_null(cls, dialect_name: str, need_explicit_null: bool = True) -> Union[TextClause, None]: + """ + 根据数据库方言动态返回值为null的server_default + + :param dialect_name: 数据库方言名称 + :param need_explicit_null: 是否需要显式DEFAULT NULL + :return: 不同数据库方言对应的null_server_default + """ + if need_explicit_null and dialect_name == 'postgresql': + return null() + return None + class CamelCaseUtil: """ -- Gitee From 3adb545ca57bd8d19bc66fa294396cbdba3b108a Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Tue, 2 Sep 2025 14:59:52 +0800 Subject: [PATCH 09/38] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E5=A4=84=E7=90=86=E8=B7=AF=E7=94=B1=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/store/modules/permission.js | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/ruoyi-fastapi-frontend/src/store/modules/permission.js b/ruoyi-fastapi-frontend/src/store/modules/permission.js index b3c216a..1d17498 100644 --- a/ruoyi-fastapi-frontend/src/store/modules/permission.js +++ b/ruoyi-fastapi-frontend/src/store/modules/permission.js @@ -82,28 +82,13 @@ function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) { function filterChildren(childrenMap, lastRouter = false) { var children = [] - childrenMap.forEach((el, index) => { - if (el.children && el.children.length) { - if (el.component === 'ParentView' && !lastRouter) { - el.children.forEach(c => { - c.path = el.path + '/' + c.path - if (c.children && c.children.length) { - children = children.concat(filterChildren(c.children, c)) - return - } - children.push(c) - }) - return - } - } - if (lastRouter) { - el.path = lastRouter.path + '/' + el.path - if (el.children && el.children.length) { - children = children.concat(filterChildren(el.children, el)) - return - } + childrenMap.forEach(el => { + el.path = lastRouter ? lastRouter.path + '/' + el.path : el.path + if (el.children && el.children.length && el.component === 'ParentView') { + children = children.concat(filterChildren(el.children, el)) + } else { + children.push(el) } - children = children.concat(el) }) return children } -- Gitee From cd3d7c84796dd3243eca2a13c3540a00afb69706 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Tue, 2 Sep 2025 15:02:29 +0800 Subject: [PATCH 10/38] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E9=A1=B6?= =?UTF-8?q?=E9=83=A8=E8=8F=9C=E5=8D=95=E6=90=9C=E7=B4=A2=E6=A0=8F=E4=B8=BA?= =?UTF-8?q?=E5=A4=9A=E5=B1=82=E7=BA=A7=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-fastapi-frontend/src/components/HeaderSearch/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruoyi-fastapi-frontend/src/components/HeaderSearch/index.vue b/ruoyi-fastapi-frontend/src/components/HeaderSearch/index.vue index 339a688..3b716a0 100644 --- a/ruoyi-fastapi-frontend/src/components/HeaderSearch/index.vue +++ b/ruoyi-fastapi-frontend/src/components/HeaderSearch/index.vue @@ -37,7 +37,7 @@ export default { }, computed: { routes() { - return this.$store.getters.permission_routes + return this.$store.getters.defaultRoutes } }, watch: { -- Gitee From 61b4de872925dd5ca7557568940125b171bfe469 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Tue, 2 Sep 2025 15:26:38 +0800 Subject: [PATCH 11/38] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E7=9B=91=E6=8E=A7=E5=92=8C=E7=BC=93=E5=AD=98=E7=9B=91?= =?UTF-8?q?=E6=8E=A7=E9=A1=B5=E9=9D=A2,=E9=A1=B5=E8=BE=B9=E8=B7=9D?= =?UTF-8?q?=E4=BF=9D=E6=8C=81=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-fastapi-frontend/src/assets/styles/ruoyi.scss | 2 -- ruoyi-fastapi-frontend/src/views/monitor/cache/index.vue | 2 +- ruoyi-fastapi-frontend/src/views/monitor/server/index.vue | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ruoyi-fastapi-frontend/src/assets/styles/ruoyi.scss b/ruoyi-fastapi-frontend/src/assets/styles/ruoyi.scss index 3d51d6d..ab4a1d2 100644 --- a/ruoyi-fastapi-frontend/src/assets/styles/ruoyi.scss +++ b/ruoyi-fastapi-frontend/src/assets/styles/ruoyi.scss @@ -194,8 +194,6 @@ } .card-box { - padding-right: 15px; - padding-left: 15px; margin-bottom: 10px; } diff --git a/ruoyi-fastapi-frontend/src/views/monitor/cache/index.vue b/ruoyi-fastapi-frontend/src/views/monitor/cache/index.vue index 8d2f378..9a730b8 100644 --- a/ruoyi-fastapi-frontend/src/views/monitor/cache/index.vue +++ b/ruoyi-fastapi-frontend/src/views/monitor/cache/index.vue @@ -1,6 +1,6 @@