diff --git a/README.md b/README.md index 2165fb30ea62eec15aa7706dd6a2d600e3325127..8bfde6b30b967a75effb39788115ad4a3a1fbd12 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@
-
+
diff --git a/ruoyi-fastapi-backend/.env.dev b/ruoyi-fastapi-backend/.env.dev
index 7b4cc7e941daf11ed25db3a48dd85761c9ee3a5e..73e227ff82be3446ecb6413462f1ee56814f5a47 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 dbfec7679b095c9c2389d3929ef7e257b052759a..3f971049a5c29a170c072181940b0851b1c1a323 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-backend/config/env.py b/ruoyi-fastapi-backend/config/env.py
index 52cc8da5d96b9709203b94b929589a054cd6e913..192a2afa75baf0431f299dd2ac209ab6efb016e5 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_admin/annotation/pydantic_annotation.py b/ruoyi-fastapi-backend/module_admin/annotation/pydantic_annotation.py
index 0799f66ad9e39c13646ef0fae5ffd0113a8da4a9..11e8d7ff8ffdd7675521e2abe9e5abc003edd0ef 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模型用于接收表单参数
"""
diff --git a/ruoyi-fastapi-backend/module_generator/dao/gen_dao.py b/ruoyi-fastapi-backend/module_generator/dao/gen_dao.py
index d6e74e94c2a530758e9a5346cdfa20a055de6166..937668a8ba1e4a84dd9918f002ab8bcdaeadc999 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 c22019d9d1f8c59cacccb8c5d7231e3e8ee61ca6..aec0bbf0ced2847eb1bcdc62e59dec2dd3fefefc 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/module_generator/templates/python/dao.py.jinja2 b/ruoyi-fastapi-backend/module_generator/templates/python/dao.py.jinja2
index cb9ba8123bef63cc3dfe7e70ad3b57f4c43a0498..6cf19a137b65bbae22742934d97c3cac9ea499ae 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 %}
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 2b8d5b9ab2a0d0b0302afb58345092cb2335afc6..76349807bec6730c66e27d859a4f6058362bb9f6 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 %}
diff --git a/ruoyi-fastapi-backend/requirements.txt b/ruoyi-fastapi-backend/requirements.txt
index 7a3705b8abdcb3145cce783c9911c2cc403aea77..45ff472a1501b1fb70d6f98c5d819cf43eb9f19c 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
diff --git a/ruoyi-fastapi-frontend/package.json b/ruoyi-fastapi-frontend/package.json
index 4a7dc7b8cd3f55d09939d9b63823b8fe327999e1..1e252dbfed2b31f1e718780998f2a0e461af0f44 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",