From ca641055e079169be9e44b356be14cf1a482071c Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Fri, 21 Feb 2025 15:40:47 +0800 Subject: [PATCH 1/6] =?UTF-8?q?fix:=20=E5=BC=95=E5=85=A5sqlglot=E4=BF=AE?= =?UTF-8?q?=E5=A4=8Dsql=E8=AF=AD=E5=8F=A5=E8=A7=A3=E6=9E=90=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-fastapi-backend/config/env.py | 8 ++++ .../module_generator/dao/gen_dao.py | 9 ++-- .../module_generator/service/gen_service.py | 45 +++++++++++++------ ruoyi-fastapi-backend/requirements.txt | 1 + 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/ruoyi-fastapi-backend/config/env.py b/ruoyi-fastapi-backend/config/env.py index 52cc8da..192a2af 100644 --- a/ruoyi-fastapi-backend/config/env.py +++ b/ruoyi-fastapi-backend/config/env.py @@ -3,6 +3,7 @@ import os import sys from dotenv import load_dotenv from functools import lru_cache +from pydantic import computed_field from pydantic_settings import BaseSettings from typing import Literal @@ -51,6 +52,13 @@ class DataBaseSettings(BaseSettings): db_pool_recycle: int = 3600 db_pool_timeout: int = 30 + @computed_field + @property + def sqlglot_parse_dialect(self) -> str: + if self.db_type == 'postgresql': + return 'postgres' + return self.db_type + class RedisSettings(BaseSettings): """ diff --git a/ruoyi-fastapi-backend/module_generator/dao/gen_dao.py b/ruoyi-fastapi-backend/module_generator/dao/gen_dao.py index d6e74e9..937668a 100644 --- a/ruoyi-fastapi-backend/module_generator/dao/gen_dao.py +++ b/ruoyi-fastapi-backend/module_generator/dao/gen_dao.py @@ -2,6 +2,7 @@ from datetime import datetime, time from sqlalchemy import delete, func, select, text, update from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload +from sqlglot.expressions import Expression from typing import List from config.env import DataBaseConfig from module_generator.entity.do.gen_do import GenTable, GenTableColumn @@ -75,15 +76,17 @@ class GenTableDao: return gen_table_all @classmethod - async def create_table_by_sql_dao(cls, db: AsyncSession, sql: str): + async def create_table_by_sql_dao(cls, db: AsyncSession, sql_statements: List[Expression]): """ 根据sql语句创建表结构 :param db: orm对象 - :param sql: sql语句 + :param sql_statements: sql语句的ast列表 :return: """ - await db.execute(text(sql)) + for sql_statement in sql_statements: + sql = sql_statement.sql(dialect=DataBaseConfig.sqlglot_parse_dialect) + await db.execute(text(sql)) @classmethod async def get_gen_table_list(cls, db: AsyncSession, query_object: GenTablePageQueryModel, is_page: bool = False): diff --git a/ruoyi-fastapi-backend/module_generator/service/gen_service.py b/ruoyi-fastapi-backend/module_generator/service/gen_service.py index c22019d..aec0bbf 100644 --- a/ruoyi-fastapi-backend/module_generator/service/gen_service.py +++ b/ruoyi-fastapi-backend/module_generator/service/gen_service.py @@ -1,13 +1,14 @@ import io import json import os -import re import zipfile from datetime import datetime from sqlalchemy.ext.asyncio import AsyncSession +from sqlglot import parse as sqlglot_parse +from sqlglot.expressions import Add, Alter, Create, Delete, Drop, Expression, Insert, Table, TruncateTable, Update from typing import List from config.constant import GenConstant -from config.env import GenConfig +from config.env import DataBaseConfig, GenConfig from exceptions.exception import ServiceException from module_admin.entity.vo.common_vo import CrudResponseModel from module_admin.entity.vo.user_vo import CurrentUserModel @@ -197,10 +198,11 @@ class GenTableService: :param current_user: 当前用户信息对象 :return: 创建表结构结果 """ - if cls.__is_valid_create_table(sql): + sql_statements = sqlglot_parse(sql, dialect=DataBaseConfig.sqlglot_parse_dialect) + if cls.__is_valid_create_table(sql_statements): try: - table_names = re.findall(r'create\s+table\s+(\w+)', sql, re.IGNORECASE) - await GenTableDao.create_table_by_sql_dao(query_db, sql) + table_names = cls.__get_table_names(sql_statements) + await GenTableDao.create_table_by_sql_dao(query_db, sql_statements) gen_table_list = await cls.get_gen_db_table_list_by_name_services(query_db, table_names) await cls.import_gen_table_services(query_db, gen_table_list, current_user) @@ -211,22 +213,39 @@ class GenTableService: raise ServiceException(message='建表语句不合法') @classmethod - def __is_valid_create_table(cls, sql: str): + def __is_valid_create_table(cls, sql_statements: List[Expression]): """ 校验sql语句是否为合法的建表语句 - :param sql: sql语句 + :param sql_statements: sql语句的ast列表 :return: 校验结果 """ - create_table_pattern = r'^\s*CREATE\s+TABLE\s+' - if not re.search(create_table_pattern, sql, re.IGNORECASE): + validate_create = [isinstance(sql_statement, Create) for sql_statement in sql_statements] + validate_forbidden_keywords = [ + isinstance( + sql_statement, + (Add, Alter, Delete, Drop, Insert, TruncateTable, Update), + ) + for sql_statement in sql_statements + ] + if not any(validate_create) or any(validate_forbidden_keywords): return False - forbidden_keywords = ['INSERT', 'UPDATE', 'DELETE', 'DROP', 'ALTER', 'TRUNCATE'] - for keyword in forbidden_keywords: - if re.search(rf'\b{keyword}\b', sql, re.IGNORECASE): - return False return True + @classmethod + def __get_table_names(cls, sql_statements: List[Expression]): + """ + 获取sql语句中所有的建表表名 + + :param sql_statements: sql语句的ast列表 + :return: 建表表名列表 + """ + table_names = [] + for sql_statement in sql_statements: + if isinstance(sql_statement, Create): + table_names.append(sql_statement.find(Table).name) + return table_names + @classmethod async def preview_code_services(cls, query_db: AsyncSession, table_id: int): """ diff --git a/ruoyi-fastapi-backend/requirements.txt b/ruoyi-fastapi-backend/requirements.txt index 7a3705b..45ff472 100644 --- a/ruoyi-fastapi-backend/requirements.txt +++ b/ruoyi-fastapi-backend/requirements.txt @@ -14,4 +14,5 @@ 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 8ce598ad540248c45cb9998687de5b9816245fa7 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Wed, 26 Feb 2025 09:00:35 +0800 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=AD=97=E6=AE=B5=E5=94=AF=E4=B8=80=E6=80=A7?= =?UTF-8?q?=E6=A0=A1=E9=AA=8Cdao=E5=B1=82=E6=A8=A1=E6=9D=BF=E5=88=A4?= =?UTF-8?q?=E6=96=AD=E5=BC=82=E5=B8=B8=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_generator/templates/python/dao.py.jinja2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruoyi-fastapi-backend/module_generator/templates/python/dao.py.jinja2 b/ruoyi-fastapi-backend/module_generator/templates/python/dao.py.jinja2 index cb9ba81..6cf19a1 100644 --- a/ruoyi-fastapi-backend/module_generator/templates/python/dao.py.jinja2 +++ b/ruoyi-fastapi-backend/module_generator/templates/python/dao.py.jinja2 @@ -70,7 +70,7 @@ class {{ BusinessName }}Dao: await db.execute( select({{ ClassName }}).where( {% for column in columns %} - {% if column.required %} + {% if column.unique %} {{ ClassName }}.{{ column.python_field | camel_to_snake }} == {{ businessName }}.{{ column.python_field | camel_to_snake }} if {{ businessName }}.{{ column.python_field | camel_to_snake }} else True, {% endif %} {% endfor %} -- Gitee From b4d5619b1b5d097db98d2fa8ad720f8e6117bc7a Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Fri, 28 Feb 2025 10:36:29 +0800 Subject: [PATCH 3/6] =?UTF-8?q?fix:=20=E5=BC=95=E5=85=A5=E6=B3=9B=E5=9E=8B?= =?UTF-8?q?=E4=BF=AE=E5=A4=8Das=5Fquery=E5=92=8Cas=5Fform=E8=A3=85?= =?UTF-8?q?=E9=A5=B0=E6=A8=A1=E5=9E=8B=E6=96=87=E6=A1=A3=E4=B8=A2=E5=A4=B1?= =?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/annotation/pydantic_annotation.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ruoyi-fastapi-backend/module_admin/annotation/pydantic_annotation.py b/ruoyi-fastapi-backend/module_admin/annotation/pydantic_annotation.py index 0799f66..11e8d7f 100644 --- a/ruoyi-fastapi-backend/module_admin/annotation/pydantic_annotation.py +++ b/ruoyi-fastapi-backend/module_admin/annotation/pydantic_annotation.py @@ -2,10 +2,13 @@ import inspect from fastapi import Form, Query from pydantic import BaseModel from pydantic.fields import FieldInfo -from typing import Type +from typing import Type, TypeVar -def as_query(cls: Type[BaseModel]): +BaseModelVar = TypeVar('BaseModelVar', bound=BaseModel) + + +def as_query(cls: Type[BaseModelVar]) -> Type[BaseModelVar]: """ pydantic模型查询参数装饰器,将pydantic模型用于接收查询参数 """ @@ -43,7 +46,7 @@ def as_query(cls: Type[BaseModel]): return cls -def as_form(cls: Type[BaseModel]): +def as_form(cls: Type[BaseModelVar]) -> Type[BaseModelVar]: """ pydantic模型表单参数装饰器,将pydantic模型用于接收表单参数 """ -- Gitee From 9412e3f34414391644cadbe1a6fceb9784d9afc1 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Fri, 28 Feb 2025 10:46:45 +0800 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=94=9F=E6=88=90=E4=B8=BB=E5=AD=90=E8=A1=A8vo=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=8F=AF=E8=83=BD=E7=BC=BA=E5=A4=B1NotBlank=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module_generator/templates/python/vo.py.jinja2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruoyi-fastapi-backend/module_generator/templates/python/vo.py.jinja2 b/ruoyi-fastapi-backend/module_generator/templates/python/vo.py.jinja2 index 2b8d5b9..7634980 100644 --- a/ruoyi-fastapi-backend/module_generator/templates/python/vo.py.jinja2 +++ b/ruoyi-fastapi-backend/module_generator/templates/python/vo.py.jinja2 @@ -8,8 +8,8 @@ {% set vo_field_required.has_required = True %} {% endif %} {% endfor %} -{% if table.sub %} {% set sub_vo_field_required = namespace(has_required=False) %} +{% if table.sub %} {% for sub_column in subTable.columns %} {% if sub_column.required %} {% set sub_vo_field_required.has_required = True %} @@ -21,7 +21,7 @@ {% endfor %} from pydantic import BaseModel, ConfigDict, Field from pydantic.alias_generators import to_camel -{% if vo_field_required.has_required %} +{% if vo_field_required.has_required or sub_vo_field_required.has_required %} from pydantic_validation_decorator import NotBlank {% endif %} {% if table.sub %} -- Gitee From 28aab8c7d462015e6eede5abad706a8d04b2cd47 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Mon, 3 Mar 2025 16:43:47 +0800 Subject: [PATCH 5/6] =?UTF-8?q?chore:=20=E5=8D=87=E7=BA=A7=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E8=87=B31.6.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-fastapi-backend/.env.dev | 2 +- ruoyi-fastapi-backend/.env.prod | 2 +- ruoyi-fastapi-frontend/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ruoyi-fastapi-backend/.env.dev b/ruoyi-fastapi-backend/.env.dev index 7b4cc7e..73e227f 100644 --- a/ruoyi-fastapi-backend/.env.dev +++ b/ruoyi-fastapi-backend/.env.dev @@ -10,7 +10,7 @@ APP_HOST = '0.0.0.0' # 应用端口 APP_PORT = 9099 # 应用版本 -APP_VERSION= '1.6.0' +APP_VERSION= '1.6.1' # 应用是否开启热重载 APP_RELOAD = true # 应用是否开启IP归属区域查询 diff --git a/ruoyi-fastapi-backend/.env.prod b/ruoyi-fastapi-backend/.env.prod index dbfec76..3f97104 100644 --- a/ruoyi-fastapi-backend/.env.prod +++ b/ruoyi-fastapi-backend/.env.prod @@ -10,7 +10,7 @@ APP_HOST = '0.0.0.0' # 应用端口 APP_PORT = 9099 # 应用版本 -APP_VERSION= '1.6.0' +APP_VERSION= '1.6.1' # 应用是否开启热重载 APP_RELOAD = false # 应用是否开启IP归属区域查询 diff --git a/ruoyi-fastapi-frontend/package.json b/ruoyi-fastapi-frontend/package.json index 4a7dc7b..1e252db 100644 --- a/ruoyi-fastapi-frontend/package.json +++ b/ruoyi-fastapi-frontend/package.json @@ -1,6 +1,6 @@ { "name": "vfadmin", - "version": "1.6.0", + "version": "1.6.1", "description": "vfadmin管理系统", "author": "insistence", "license": "MIT", -- Gitee From b708d86effa371674ecb5d75385071c407334c25 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Mon, 3 Mar 2025 16:44:31 +0800 Subject: [PATCH 6/6] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0README=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2165fb3..8bfde6b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@

logo

-

RuoYi-Vue3-FastAPI v1.6.0

+

RuoYi-Vue3-FastAPI v1.6.1

基于RuoYi-Vue3+FastAPI前后端分离的快速开发框架

- + -- Gitee