From a22b5412b732c1afd3b0c1f49d7ac771a061260c Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Tue, 30 Apr 2024 17:59:58 +0800 Subject: [PATCH 01/53] Add code generator app --- backend/alembic/env.py | 2 + backend/app/generator/__init__.py | 2 + backend/app/generator/api/__init__.py | 2 + backend/app/generator/api/router.py | 9 +++ backend/app/generator/api/v1/__init__.py | 2 + backend/app/generator/api/v1/gen.py | 5 ++ backend/app/generator/conf.py | 22 ++++++ backend/app/generator/crud/__init__.py | 2 + backend/app/generator/crud/crud_gen.py | 2 + backend/app/generator/model/__init__.py | 4 + backend/app/generator/model/gen.py | 50 ++++++++++++ backend/app/generator/schema/__init__.py | 2 + backend/app/generator/schema/gen.py | 2 + backend/app/generator/service/__init__.py | 2 + backend/app/generator/service/gen_service.py | 2 + backend/core/path_conf.py | 3 + backend/pdm.lock | 16 +++- backend/pyproject.toml | 1 + backend/requirements.txt | 1 + backend/templates/py/api.py.jinja | 81 ++++++++++++++++++++ backend/templates/py/crud.py.jinja | 0 backend/templates/py/model.py.jinja | 0 backend/templates/py/schema.py.jinja | 0 backend/templates/py/service.py.jinja | 0 backend/utils/get_templates.py | 20 +++++ 25 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 backend/app/generator/__init__.py create mode 100644 backend/app/generator/api/__init__.py create mode 100644 backend/app/generator/api/router.py create mode 100644 backend/app/generator/api/v1/__init__.py create mode 100644 backend/app/generator/api/v1/gen.py create mode 100644 backend/app/generator/conf.py create mode 100644 backend/app/generator/crud/__init__.py create mode 100644 backend/app/generator/crud/crud_gen.py create mode 100644 backend/app/generator/model/__init__.py create mode 100644 backend/app/generator/model/gen.py create mode 100644 backend/app/generator/schema/__init__.py create mode 100644 backend/app/generator/schema/gen.py create mode 100644 backend/app/generator/service/__init__.py create mode 100644 backend/app/generator/service/gen_service.py create mode 100644 backend/templates/py/api.py.jinja create mode 100644 backend/templates/py/crud.py.jinja create mode 100644 backend/templates/py/model.py.jinja create mode 100644 backend/templates/py/schema.py.jinja create mode 100644 backend/templates/py/service.py.jinja create mode 100644 backend/utils/get_templates.py diff --git a/backend/alembic/env.py b/backend/alembic/env.py index c6352b705..cb05ffd4a 100644 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -30,9 +30,11 @@ # for 'autogenerate' support # https://alembic.sqlalchemy.org/en/latest/autogenerate.html#autogenerating-multiple-metadata-collections from backend.app.admin.model import MappedBase as AdminBase +from backend.app.generator.model import MappedBase as GeneratorBase target_metadata = [ AdminBase.metadata, + GeneratorBase.metadata, ] # other values from the config, defined by the needs of env.py, diff --git a/backend/app/generator/__init__.py b/backend/app/generator/__init__.py new file mode 100644 index 000000000..56fafa58b --- /dev/null +++ b/backend/app/generator/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- diff --git a/backend/app/generator/api/__init__.py b/backend/app/generator/api/__init__.py new file mode 100644 index 000000000..56fafa58b --- /dev/null +++ b/backend/app/generator/api/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- diff --git a/backend/app/generator/api/router.py b/backend/app/generator/api/router.py new file mode 100644 index 000000000..8c3a0a335 --- /dev/null +++ b/backend/app/generator/api/router.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from fastapi import APIRouter + +from backend.app.generator.api.v1.gen import router as gen_router + +v1 = APIRouter() + +v1.include_router(gen_router, prefix='/gen', tags=['代码生成管理']) diff --git a/backend/app/generator/api/v1/__init__.py b/backend/app/generator/api/v1/__init__.py new file mode 100644 index 000000000..56fafa58b --- /dev/null +++ b/backend/app/generator/api/v1/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py new file mode 100644 index 000000000..011d58c89 --- /dev/null +++ b/backend/app/generator/api/v1/gen.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from fastapi import APIRouter + +router = APIRouter() diff --git a/backend/app/generator/conf.py b/backend/app/generator/conf.py new file mode 100644 index 000000000..6fec85e0f --- /dev/null +++ b/backend/app/generator/conf.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from functools import lru_cache + +from pydantic_settings import BaseSettings, SettingsConfigDict + +from backend.core.path_conf import BasePath + + +class GeneratorSettings(BaseSettings): + """Admin Settings""" + + model_config = SettingsConfigDict(env_file=f'{BasePath}/.env', env_file_encoding='utf-8', extra='ignore') + + +@lru_cache +def get_generator_settings() -> GeneratorSettings: + """获取 generator 配置""" + return GeneratorSettings() + + +generator_settings = get_generator_settings() diff --git a/backend/app/generator/crud/__init__.py b/backend/app/generator/crud/__init__.py new file mode 100644 index 000000000..56fafa58b --- /dev/null +++ b/backend/app/generator/crud/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- diff --git a/backend/app/generator/crud/crud_gen.py b/backend/app/generator/crud/crud_gen.py new file mode 100644 index 000000000..56fafa58b --- /dev/null +++ b/backend/app/generator/crud/crud_gen.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- diff --git a/backend/app/generator/model/__init__.py b/backend/app/generator/model/__init__.py new file mode 100644 index 000000000..0a9a0e5d6 --- /dev/null +++ b/backend/app/generator/model/__init__.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from backend.common.model import MappedBase # noqa: I001 +from backend.app.generator.model.gen import GenModel, GenBusiness diff --git a/backend/app/generator/model/gen.py b/backend/app/generator/model/gen.py new file mode 100644 index 000000000..96f1d82d2 --- /dev/null +++ b/backend/app/generator/model/gen.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from sqlalchemy import ForeignKey, String, UniqueConstraint +from sqlalchemy.dialects.mysql import LONGTEXT +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from backend.common.model import Base, DataClassBase, id_key + + +class GenBusiness(Base): + """代码生成业务表""" + + __tablename__ = 'sys_gen_business' + + id: Mapped[id_key] = mapped_column(init=False) + app_name: Mapped[str] = mapped_column(String(50), comment='应用名称(英文)') + model_name: Mapped[str] = mapped_column(String(255), comment='表名称(英文)') + model_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文)') + model_simple_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文简称)') + model_comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='表描述') + relate_model_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='关联表名称') + relate_model_fk: Mapped[str | None] = mapped_column(String(255), default=None, comment='关联表外键') + schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称') + gen_type: Mapped[int] = mapped_column( + default=1, comment='生成代码方式(1:自定义路径, 2:内部写入,3:tar.gz 压缩包)' + ) + gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='生成代码路径(默认为项目根路径)') + remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注') + # 代码生成业务model一对一 + gen_model: Mapped['GenModel'] = relationship(back_populates='gen_business') + + +class GenModel(DataClassBase): + """代码生成model表""" + + __tablename__ = 'sys_gen_model' + __table_args__ = UniqueConstraint('gen_business_id') + + id: Mapped[id_key] = mapped_column(init=False) + name: Mapped[str] = mapped_column(String(50), comment='列名称') + comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='列描述') + type: Mapped[str] = mapped_column(String(20), default='string', comment='列类型') + default: Mapped[str | None] = mapped_column(default=None, comment='列默认值') + length: Mapped[int] = mapped_column(default=0, comment='列长度') + is_pk: Mapped[bool] = mapped_column(default=False, comment='是否主键') + is_nullable = mapped_column(default=False, comment='是否可为空') + + # 代码生成业务model一对一 + gen_business_id: Mapped[int] = mapped_column(ForeignKey('sys_gen_business.id')) + gen_business: Mapped['GenBusiness'] = relationship(back_populates='gen_model', single_parent=True) diff --git a/backend/app/generator/schema/__init__.py b/backend/app/generator/schema/__init__.py new file mode 100644 index 000000000..56fafa58b --- /dev/null +++ b/backend/app/generator/schema/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- diff --git a/backend/app/generator/schema/gen.py b/backend/app/generator/schema/gen.py new file mode 100644 index 000000000..56fafa58b --- /dev/null +++ b/backend/app/generator/schema/gen.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- diff --git a/backend/app/generator/service/__init__.py b/backend/app/generator/service/__init__.py new file mode 100644 index 000000000..56fafa58b --- /dev/null +++ b/backend/app/generator/service/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py new file mode 100644 index 000000000..56fafa58b --- /dev/null +++ b/backend/app/generator/service/gen_service.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- diff --git a/backend/core/path_conf.py b/backend/core/path_conf.py index 0514dbb0d..937a1e0dd 100644 --- a/backend/core/path_conf.py +++ b/backend/core/path_conf.py @@ -19,3 +19,6 @@ # 挂载静态目录 STATIC_DIR = os.path.join(BasePath, 'static') + +# jinja2 模版文件路径 +JINJA2_TEMPLATE_DIR = os.path.join(BasePath, 'templates') diff --git a/backend/pdm.lock b/backend/pdm.lock index 5bde33ed1..0fbcc4bc3 100644 --- a/backend/pdm.lock +++ b/backend/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "lint", "deploy"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:b67b86e4bf27c72aa5b06f77e3eec479a555c5ba560d82ca8d23a8b98edc901d" +content_hash = "sha256:cc7df52b0f3f0d4285ac67f5cec91b84931bea350c12fb8754aef0894c9a2e09" [[package]] name = "aiofiles" @@ -850,6 +850,20 @@ files = [ {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, ] +[[package]] +name = "jinja2" +version = "3.1.3" +requires_python = ">=3.7" +summary = "A very fast and expressive template engine." +groups = ["default"] +dependencies = [ + "MarkupSafe>=2.0", +] +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + [[package]] name = "kombu" version = "5.3.5" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 7a3608c90..221e192ed 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -47,6 +47,7 @@ dependencies = [ "fastapi_oauth20>=0.0.1a1", "flower==2.0.1", "sqlalchemy-crud-plus==0.0.2", + "jinja2>=3.1.3", ] requires-python = ">=3.10" readme = "README.md" diff --git a/backend/requirements.txt b/backend/requirements.txt index 4044bc229..11176edbe 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -49,6 +49,7 @@ identify==2.5.35 idna==3.6 iniconfig==2.0.0 itsdangerous==2.1.2 +jinja2==3.1.3 kombu==5.3.5 loguru==0.7.2 mako==1.3.2 diff --git a/backend/templates/py/api.py.jinja b/backend/templates/py/api.py.jinja new file mode 100644 index 000000000..3d7cdcaef --- /dev/null +++ b/backend/templates/py/api.py.jinja @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from typing import Annotated + +from fastapi import APIRouter, Depends, Path, Query + +from backend.{{ app_name }}.admin.schema.{{ model_name }} import Create{{ schema_name }}Param, Get{{ schema_name }}ListDetails, Update{{ schema_name }}Param +from backend.{{ app_name }}.admin.service.{{ model_name }}_service import {{ model_name }}_service +from backend.common.pagination import DependsPagination, paging_data +from backend.common.response.response_schema import ResponseModel, response_base +from backend.common.security.jwt import DependsJwtAuth +from backend.common.security.permission import RequestPermission +from backend.common.security.rbac import DependsRBAC +from backend.database.db_mysql import CurrentSession +from backend.utils.serializers import select_as_dict + +router = APIRouter() + + +@router.get('/{pk}', summary='获取{{ model_simple_name_zh }}详情', dependencies=[DependsJwtAuth]) +async def get_{{ model_name }}(pk: Annotated[int, Path(...)]) -> ResponseModel: + dict_data = await {{ model_name }}_service.get(pk=pk) + data = GetDictDataListDetails(**await select_as_dict(dict_data)) + return await response_base.success(data=data) + + +@router.get( + '', + summary='(模糊条件)分页获取所有{{ model_simple_name_zh }}', + dependencies=[ + DependsJwtAuth, + DependsPagination, + ], +) +async def get_pagination_{{ model_name }}(db: CurrentSession) -> ResponseModel: + {{ model_name }}_select = await {{ model_name }}_service.get_select() + page_data = await paging_data(db, {{ model_name }}_select, Get{{ schema_name }}ListDetails) + return await response_base.success(data=page_data) + + +@router.post( + '', + summary='创建{{ model_simple_name_zh }}', + dependencies=[ + Depends(RequestPermission('{{ permission_sign }}:add')), + DependsRBAC, + ], +) +async def create_{{ model_name }}(obj: Create{{ schema_name }}Param) -> ResponseModel: + await {{ model_name }}_service.create(obj=obj) + return await response_base.success() + + +@router.put( + '/{pk}', + summary='更新{{ model_simple_name_zh }}', + dependencies=[ + Depends(RequestPermission('{{ permission_sign }}:edit')), + DependsRBAC, + ], +) +async def update_{{ model_name }}(pk: Annotated[int, Path(...)], obj: Update{{ schema_name }}Param) -> ResponseModel: + count = await {{ model_name }}_service.update(pk=pk, obj=obj) + if count > 0: + return await response_base.success() + return await response_base.fail() + + +@router.delete( + '', + summary='(批量)删除{{ model_simple_name_zh }}', + dependencies=[ + Depends(RequestPermission('{{ permission_sign }}:del')), + DependsRBAC, + ], +) +async def delete_{{ model_name }}(pk: Annotated[list[int], Query(...)]) -> ResponseModel: + count = await {{ model_name }}_service.delete(pk=pk) + if count > 0: + return await response_base.success() + return await response_base.fail() diff --git a/backend/templates/py/crud.py.jinja b/backend/templates/py/crud.py.jinja new file mode 100644 index 000000000..e69de29bb diff --git a/backend/templates/py/model.py.jinja b/backend/templates/py/model.py.jinja new file mode 100644 index 000000000..e69de29bb diff --git a/backend/templates/py/schema.py.jinja b/backend/templates/py/schema.py.jinja new file mode 100644 index 000000000..e69de29bb diff --git a/backend/templates/py/service.py.jinja b/backend/templates/py/service.py.jinja new file mode 100644 index 000000000..e69de29bb diff --git a/backend/utils/get_templates.py b/backend/utils/get_templates.py new file mode 100644 index 000000000..35eebe898 --- /dev/null +++ b/backend/utils/get_templates.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from jinja2 import Environment, FileSystemLoader, Template, select_autoescape + +from backend.core.path_conf import JINJA2_TEMPLATE_DIR + + +def get_templates(jinja_file: str) -> 'Template': + """ + 获取模版文件 + + :param jinja_file: + :return: + """ + env = Environment( + loader=FileSystemLoader(JINJA2_TEMPLATE_DIR), + autoescape=select_autoescape(['html', 'xml', 'jinja']), + keep_trailing_newline=True, + ) + return env.get_template(jinja_file) From 14e6c322fc9b3ce2dd0e0a3c1d552adae5d6c4ab Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Tue, 30 Apr 2024 22:36:47 +0800 Subject: [PATCH 02/53] Define the generator interfaces --- backend/app/generator/api/v1/gen.py | 61 +++++++++++++++++++- backend/app/generator/model/gen.py | 8 ++- backend/app/generator/service/gen_service.py | 38 ++++++++++++ 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index 011d58c89..30107cbf3 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -1,5 +1,64 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from fastapi import APIRouter +from typing import Annotated + +from fastapi import APIRouter, Path + +from backend.app.generator.service.gen_service import gen_service +from backend.common.response.response_schema import ResponseModel, response_base router = APIRouter() + + +@router.get('/all', summary='获取代码生成业务表列表') +async def get_all_dbs(): + data = await gen_service.get_all() + return await response_base.success(data=data) + + +@router.get('/{pk}', summary='获取代码生成业务model表详情') +async def get_db(pk: Annotated[int, Path(...)]): + data = await gen_service.get(pk) + return await response_base.success(data=data) + + +@router.post('/db', summary='创建代码生成业务表') +async def create_db(): ... + + +@router.put('/db', summary='更新代码生成业务表') +async def update_db(): ... + + +@router.delete('/db', summary='删除代码生成业务表') +async def delete_db(): ... + + +@router.post('/model', summary='创建代码生成model表') +async def create_model(): ... + + +@router.put('/model', summary='更新代码生成model表') +async def update_model(): ... + + +@router.delete('/model', summary='删除代码生成model表') +async def delete_model(): ... + + +@router.get('/preview', summary='生成代码预览') +async def preview_code() -> ResponseModel: + data = await gen_service.get_preview() + return await response_base.success(data=data) + + +@router.post('/generate', summary='生成代码') +async def generate_code(): + await gen_service.generate() + return await response_base.success() + + +@router.post('/download', summary='下载代码') +async def download_code(): + await gen_service.download() + ... diff --git a/backend/app/generator/model/gen.py b/backend/app/generator/model/gen.py index 96f1d82d2..1f4393030 100644 --- a/backend/app/generator/model/gen.py +++ b/backend/app/generator/model/gen.py @@ -22,9 +22,9 @@ class GenBusiness(Base): relate_model_fk: Mapped[str | None] = mapped_column(String(255), default=None, comment='关联表外键') schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称') gen_type: Mapped[int] = mapped_column( - default=1, comment='生成代码方式(1:自定义路径, 2:内部写入,3:tar.gz 压缩包)' + default=1, comment='代码生成方式(1:自定义路径, 2:内部写入,3:tar.gz 压缩包)' ) - gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='生成代码路径(默认为项目根路径)') + gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='代码生成路径(默认为项目根路径)') remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注') # 代码生成业务model一对一 gen_model: Mapped['GenModel'] = relationship(back_populates='gen_business') @@ -46,5 +46,7 @@ class GenModel(DataClassBase): is_nullable = mapped_column(default=False, comment='是否可为空') # 代码生成业务model一对一 - gen_business_id: Mapped[int] = mapped_column(ForeignKey('sys_gen_business.id')) + gen_business_id: Mapped[int] = mapped_column( + ForeignKey('sys_gen_business.id', default=None, comment='代码生成业务ID') + ) gen_business: Mapped['GenBusiness'] = relationship(back_populates='gen_model', single_parent=True) diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 56fafa58b..24f3a1868 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -1,2 +1,40 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- + + +class GenService: + @staticmethod + async def get_all() -> dict: ... + + @staticmethod + async def get() -> dict: ... + + @staticmethod + async def create_db() -> None: ... + + @staticmethod + async def update_db() -> int: ... + + @staticmethod + async def delete_db() -> int: ... + + @staticmethod + async def create_model() -> None: ... + + @staticmethod + async def update_model() -> int: ... + + @staticmethod + async def delete_model() -> int: ... + + @staticmethod + async def get_preview() -> dict: ... + + @staticmethod + async def generate() -> dict: ... + + @staticmethod + async def download() -> dict: ... + + +gen_service = GenService() From 8c726af5043e57db5ad16ef37d193146b1af9842 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Tue, 30 Apr 2024 22:37:10 +0800 Subject: [PATCH 03/53] Update the name of the template files --- backend/templates/py/{api.py.jinja => api.jinja} | 6 ++---- backend/templates/py/{crud.py.jinja => crud.jinja} | 0 backend/templates/py/{model.py.jinja => model.jinja} | 0 backend/templates/py/{schema.py.jinja => schema.jinja} | 0 backend/templates/py/{service.py.jinja => service.jinja} | 0 5 files changed, 2 insertions(+), 4 deletions(-) rename backend/templates/py/{api.py.jinja => api.jinja} (92%) rename backend/templates/py/{crud.py.jinja => crud.jinja} (100%) rename backend/templates/py/{model.py.jinja => model.jinja} (100%) rename backend/templates/py/{schema.py.jinja => schema.jinja} (100%) rename backend/templates/py/{service.py.jinja => service.jinja} (100%) diff --git a/backend/templates/py/api.py.jinja b/backend/templates/py/api.jinja similarity index 92% rename from backend/templates/py/api.py.jinja rename to backend/templates/py/api.jinja index 3d7cdcaef..845b79679 100644 --- a/backend/templates/py/api.py.jinja +++ b/backend/templates/py/api.jinja @@ -12,16 +12,14 @@ from backend.common.security.jwt import DependsJwtAuth from backend.common.security.permission import RequestPermission from backend.common.security.rbac import DependsRBAC from backend.database.db_mysql import CurrentSession -from backend.utils.serializers import select_as_dict router = APIRouter() @router.get('/{pk}', summary='获取{{ model_simple_name_zh }}详情', dependencies=[DependsJwtAuth]) async def get_{{ model_name }}(pk: Annotated[int, Path(...)]) -> ResponseModel: - dict_data = await {{ model_name }}_service.get(pk=pk) - data = GetDictDataListDetails(**await select_as_dict(dict_data)) - return await response_base.success(data=data) + {{ model_name }} = await {{ model_name }}_service.get(pk=pk) + return await response_base.success(data={{ model_name }}) @router.get( diff --git a/backend/templates/py/crud.py.jinja b/backend/templates/py/crud.jinja similarity index 100% rename from backend/templates/py/crud.py.jinja rename to backend/templates/py/crud.jinja diff --git a/backend/templates/py/model.py.jinja b/backend/templates/py/model.jinja similarity index 100% rename from backend/templates/py/model.py.jinja rename to backend/templates/py/model.jinja diff --git a/backend/templates/py/schema.py.jinja b/backend/templates/py/schema.jinja similarity index 100% rename from backend/templates/py/schema.py.jinja rename to backend/templates/py/schema.jinja diff --git a/backend/templates/py/service.py.jinja b/backend/templates/py/service.jinja similarity index 100% rename from backend/templates/py/service.py.jinja rename to backend/templates/py/service.jinja From 93197a50a86c97b48f7fc02706f7cf358d8410c4 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Sat, 4 May 2024 23:33:17 +0800 Subject: [PATCH 04/53] update lock hash --- backend/pdm.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pdm.lock b/backend/pdm.lock index 6195d3d36..3112f9612 100644 --- a/backend/pdm.lock +++ b/backend/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "lint", "deploy"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:c2a3e19bef234f542b76f4874890f8452272fafc6b2fa06dca7ca6a0a3a74f3a" +content_hash = "sha256:2a2deb7179c7e6eefc4370036ae06edbf34bbb7cb5f43601d52f8f0f45d2d607" [[package]] name = "alembic" From 8ce957773d7f84ff2492cb4671fee4a94f1bdd85 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Mon, 6 May 2024 23:41:56 +0800 Subject: [PATCH 05/53] update route and schemas --- backend/app/generator/api/v1/gen.py | 26 ++++----- backend/app/generator/model/gen.py | 4 +- backend/app/generator/schema/gen.py | 60 ++++++++++++++++++++ backend/app/generator/service/gen_service.py | 8 +-- backend/app/router.py | 2 + 5 files changed, 81 insertions(+), 19 deletions(-) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index 30107cbf3..753f48989 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -11,44 +11,44 @@ @router.get('/all', summary='获取代码生成业务表列表') -async def get_all_dbs(): +async def get_all_businesses(): data = await gen_service.get_all() return await response_base.success(data=data) -@router.get('/{pk}', summary='获取代码生成业务model表详情') -async def get_db(pk: Annotated[int, Path(...)]): +@router.get('/{pk}', summary='获取代码生成业务和model表详情') +async def get_business_and_model(pk: Annotated[int, Path(...)]): data = await gen_service.get(pk) return await response_base.success(data=data) -@router.post('/db', summary='创建代码生成业务表') -async def create_db(): ... +@router.post('/businesses', summary='创建代码生成业务表') +async def create_business(): ... -@router.put('/db', summary='更新代码生成业务表') -async def update_db(): ... +@router.put('/businesses', summary='更新代码生成业务表') +async def update_business(): ... -@router.delete('/db', summary='删除代码生成业务表') -async def delete_db(): ... +@router.delete('/businesses', summary='删除代码生成业务表') +async def delete_business(): ... -@router.post('/model', summary='创建代码生成model表') +@router.post('/models', summary='创建代码生成model表') async def create_model(): ... -@router.put('/model', summary='更新代码生成model表') +@router.put('/models', summary='更新代码生成model表') async def update_model(): ... -@router.delete('/model', summary='删除代码生成model表') +@router.delete('/models', summary='删除代码生成model表') async def delete_model(): ... @router.get('/preview', summary='生成代码预览') async def preview_code() -> ResponseModel: - data = await gen_service.get_preview() + data = await gen_service.preview() return await response_base.success(data=data) diff --git a/backend/app/generator/model/gen.py b/backend/app/generator/model/gen.py index 1f4393030..bedb35679 100644 --- a/backend/app/generator/model/gen.py +++ b/backend/app/generator/model/gen.py @@ -19,7 +19,7 @@ class GenBusiness(Base): model_simple_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文简称)') model_comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='表描述') relate_model_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='关联表名称') - relate_model_fk: Mapped[str | None] = mapped_column(String(255), default=None, comment='关联表外键') + relate_model_fk: Mapped[int | None] = mapped_column(default=None, comment='关联表外键') schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称') gen_type: Mapped[int] = mapped_column( default=1, comment='代码生成方式(1:自定义路径, 2:内部写入,3:tar.gz 压缩包)' @@ -43,7 +43,7 @@ class GenModel(DataClassBase): default: Mapped[str | None] = mapped_column(default=None, comment='列默认值') length: Mapped[int] = mapped_column(default=0, comment='列长度') is_pk: Mapped[bool] = mapped_column(default=False, comment='是否主键') - is_nullable = mapped_column(default=False, comment='是否可为空') + is_nullable: Mapped[bool] = mapped_column(default=False, comment='是否可为空') # 代码生成业务model一对一 gen_business_id: Mapped[int] = mapped_column( diff --git a/backend/app/generator/schema/gen.py b/backend/app/generator/schema/gen.py index 56fafa58b..480c2c2a6 100644 --- a/backend/app/generator/schema/gen.py +++ b/backend/app/generator/schema/gen.py @@ -1,2 +1,62 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from datetime import datetime + +from pydantic import ConfigDict + +from backend.common.schema import SchemaBase + + +class GenBusinessSchemaBase(SchemaBase): + app_name: str + model_name: str + model_name_zh: str + model_simple_name_zh: str + model_comment: str | None = None + relate_model_name: str | None = None + relate_model_fk: int | None = None + schema_name: str | None = None + gen_type: int + gen_path: str | None = None + remark: str | None = None + + +class CreateGenBusinessParam(GenBusinessSchemaBase): + pass + + +class UpdateGenBusinessParam(GenBusinessSchemaBase): + pass + + +class GetGenBusinessListDetails(GenBusinessSchemaBase): + model_config = ConfigDict(from_attributes=True) + + id: int + created_time: datetime + updated_time: datetime | None = None + + +class GenModelSchemaBase(SchemaBase): + name: str + comment: str | None = None + type: str + default: str | None = None + length: int + is_pk: bool + is_nullable: bool + + +class CreateGenModel(GenModelSchemaBase): + pass + + +class UpdateGenModel(GenModelSchemaBase): + pass + + +class GetGenModelListDetails(GenModelSchemaBase): + model_config = ConfigDict(from_attributes=True) + + id: int + gen_business_id: int diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 24f3a1868..565dccf46 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -10,13 +10,13 @@ async def get_all() -> dict: ... async def get() -> dict: ... @staticmethod - async def create_db() -> None: ... + async def create_business() -> None: ... @staticmethod - async def update_db() -> int: ... + async def update_business() -> int: ... @staticmethod - async def delete_db() -> int: ... + async def delete_business() -> int: ... @staticmethod async def create_model() -> None: ... @@ -28,7 +28,7 @@ async def update_model() -> int: ... async def delete_model() -> int: ... @staticmethod - async def get_preview() -> dict: ... + async def preview() -> dict: ... @staticmethod async def generate() -> dict: ... diff --git a/backend/app/router.py b/backend/app/router.py index 26adf14ec..c51dece17 100644 --- a/backend/app/router.py +++ b/backend/app/router.py @@ -3,10 +3,12 @@ from fastapi import APIRouter from backend.app.admin.api.router import v1 as admin_v1 +from backend.app.generator.api.router import v1 as generator_v1 from backend.app.task.api.router import v1 as task_v1 from backend.core.conf import settings route = APIRouter(prefix=settings.API_V1_STR) route.include_router(admin_v1) +route.include_router(generator_v1) route.include_router(task_v1) From 55d67f5657dd716c3adb0ee4a7a3a76987811032 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Thu, 9 May 2024 22:11:28 +0800 Subject: [PATCH 06/53] update gen code structure --- backend/app/generator/crud/crud_gen.py | 2 -- .../app/generator/crud/crud_gen_business.py | 9 ++++++ backend/app/generator/crud/crud_gen_model.py | 9 ++++++ backend/app/generator/model/__init__.py | 3 +- .../model/{gen.py => gen_business.py} | 28 ++--------------- backend/app/generator/model/gen_model.py | 28 +++++++++++++++++ .../schema/{gen.py => gen_business.py} | 25 ---------------- backend/app/generator/schema/gen_model.py | 30 +++++++++++++++++++ .../generator/service/gen_business_service.py | 16 ++++++++++ .../generator/service/gen_model_service.py | 16 ++++++++++ backend/app/generator/service/gen_service.py | 18 ----------- 11 files changed, 113 insertions(+), 71 deletions(-) delete mode 100644 backend/app/generator/crud/crud_gen.py create mode 100644 backend/app/generator/crud/crud_gen_business.py create mode 100644 backend/app/generator/crud/crud_gen_model.py rename backend/app/generator/model/{gen.py => gen_business.py} (56%) create mode 100644 backend/app/generator/model/gen_model.py rename backend/app/generator/schema/{gen.py => gen_business.py} (66%) create mode 100644 backend/app/generator/schema/gen_model.py create mode 100644 backend/app/generator/service/gen_business_service.py create mode 100644 backend/app/generator/service/gen_model_service.py diff --git a/backend/app/generator/crud/crud_gen.py b/backend/app/generator/crud/crud_gen.py deleted file mode 100644 index 56fafa58b..000000000 --- a/backend/app/generator/crud/crud_gen.py +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- diff --git a/backend/app/generator/crud/crud_gen_business.py b/backend/app/generator/crud/crud_gen_business.py new file mode 100644 index 000000000..fe7090cde --- /dev/null +++ b/backend/app/generator/crud/crud_gen_business.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from sqlalchemy_crud_plus import CRUDPlus + +from backend.app.generator.model import GenBusiness + + +class CRUDGenBusiness(CRUDPlus[GenBusiness]): + pass diff --git a/backend/app/generator/crud/crud_gen_model.py b/backend/app/generator/crud/crud_gen_model.py new file mode 100644 index 000000000..f06407bb4 --- /dev/null +++ b/backend/app/generator/crud/crud_gen_model.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from sqlalchemy_crud_plus import CRUDPlus + +from backend.app.generator.model import GenModel + + +class CRUDGenModel(CRUDPlus[GenModel]): + pass diff --git a/backend/app/generator/model/__init__.py b/backend/app/generator/model/__init__.py index 0a9a0e5d6..6ce035a36 100644 --- a/backend/app/generator/model/__init__.py +++ b/backend/app/generator/model/__init__.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from backend.common.model import MappedBase # noqa: I001 -from backend.app.generator.model.gen import GenModel, GenBusiness +from backend.app.generator.model.gen_business import GenBusiness +from backend.app.generator.model.gen_model import GenModel diff --git a/backend/app/generator/model/gen.py b/backend/app/generator/model/gen_business.py similarity index 56% rename from backend/app/generator/model/gen.py rename to backend/app/generator/model/gen_business.py index bedb35679..6baccd591 100644 --- a/backend/app/generator/model/gen.py +++ b/backend/app/generator/model/gen_business.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from sqlalchemy import ForeignKey, String, UniqueConstraint +from sqlalchemy import String from sqlalchemy.dialects.mysql import LONGTEXT from sqlalchemy.orm import Mapped, mapped_column, relationship -from backend.common.model import Base, DataClassBase, id_key +from backend.common.model import Base, id_key class GenBusiness(Base): @@ -27,26 +27,4 @@ class GenBusiness(Base): gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='代码生成路径(默认为项目根路径)') remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注') # 代码生成业务model一对一 - gen_model: Mapped['GenModel'] = relationship(back_populates='gen_business') - - -class GenModel(DataClassBase): - """代码生成model表""" - - __tablename__ = 'sys_gen_model' - __table_args__ = UniqueConstraint('gen_business_id') - - id: Mapped[id_key] = mapped_column(init=False) - name: Mapped[str] = mapped_column(String(50), comment='列名称') - comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='列描述') - type: Mapped[str] = mapped_column(String(20), default='string', comment='列类型') - default: Mapped[str | None] = mapped_column(default=None, comment='列默认值') - length: Mapped[int] = mapped_column(default=0, comment='列长度') - is_pk: Mapped[bool] = mapped_column(default=False, comment='是否主键') - is_nullable: Mapped[bool] = mapped_column(default=False, comment='是否可为空') - - # 代码生成业务model一对一 - gen_business_id: Mapped[int] = mapped_column( - ForeignKey('sys_gen_business.id', default=None, comment='代码生成业务ID') - ) - gen_business: Mapped['GenBusiness'] = relationship(back_populates='gen_model', single_parent=True) + gen_model: Mapped['GenModel'] = relationship(back_populates='gen_business') # noqa: F821 diff --git a/backend/app/generator/model/gen_model.py b/backend/app/generator/model/gen_model.py new file mode 100644 index 000000000..86d2d11e0 --- /dev/null +++ b/backend/app/generator/model/gen_model.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from sqlalchemy import ForeignKey, String, UniqueConstraint +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from backend.common.model import DataClassBase, id_key + + +class GenModel(DataClassBase): + """代码生成model表""" + + __tablename__ = 'sys_gen_model' + __table_args__ = UniqueConstraint('gen_business_id') + + id: Mapped[id_key] = mapped_column(init=False) + name: Mapped[str] = mapped_column(String(50), comment='列名称') + comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='列描述') + type: Mapped[str] = mapped_column(String(20), default='string', comment='列类型') + default: Mapped[str | None] = mapped_column(default=None, comment='列默认值') + length: Mapped[int] = mapped_column(default=0, comment='列长度') + is_pk: Mapped[bool] = mapped_column(default=False, comment='是否主键') + is_nullable: Mapped[bool] = mapped_column(default=False, comment='是否可为空') + + # 代码生成业务model一对一 + gen_business_id: Mapped[int] = mapped_column( + ForeignKey('sys_gen_business.id', default=None, comment='代码生成业务ID') + ) + gen_business: Mapped['GenBusiness'] = relationship(back_populates='gen_model', single_parent=True) # noqa: F821 diff --git a/backend/app/generator/schema/gen.py b/backend/app/generator/schema/gen_business.py similarity index 66% rename from backend/app/generator/schema/gen.py rename to backend/app/generator/schema/gen_business.py index 480c2c2a6..6008f810e 100644 --- a/backend/app/generator/schema/gen.py +++ b/backend/app/generator/schema/gen_business.py @@ -35,28 +35,3 @@ class GetGenBusinessListDetails(GenBusinessSchemaBase): id: int created_time: datetime updated_time: datetime | None = None - - -class GenModelSchemaBase(SchemaBase): - name: str - comment: str | None = None - type: str - default: str | None = None - length: int - is_pk: bool - is_nullable: bool - - -class CreateGenModel(GenModelSchemaBase): - pass - - -class UpdateGenModel(GenModelSchemaBase): - pass - - -class GetGenModelListDetails(GenModelSchemaBase): - model_config = ConfigDict(from_attributes=True) - - id: int - gen_business_id: int diff --git a/backend/app/generator/schema/gen_model.py b/backend/app/generator/schema/gen_model.py new file mode 100644 index 000000000..42078b63d --- /dev/null +++ b/backend/app/generator/schema/gen_model.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from pydantic import ConfigDict + +from backend.common.schema import SchemaBase + + +class GenModelSchemaBase(SchemaBase): + name: str + comment: str | None = None + type: str + default: str | None = None + length: int + is_pk: bool + is_nullable: bool + gen_business_id: int + + +class CreateGenModel(GenModelSchemaBase): + pass + + +class UpdateGenModel(GenModelSchemaBase): + pass + + +class GetGenModelListDetails(GenModelSchemaBase): + model_config = ConfigDict(from_attributes=True) + + id: int diff --git a/backend/app/generator/service/gen_business_service.py b/backend/app/generator/service/gen_business_service.py new file mode 100644 index 000000000..f31807b14 --- /dev/null +++ b/backend/app/generator/service/gen_business_service.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +class GenBusinessService: + @staticmethod + async def create() -> None: ... + + @staticmethod + async def update() -> int: ... + + @staticmethod + async def delete() -> int: ... + + +gen_business_service = GenBusinessService() diff --git a/backend/app/generator/service/gen_model_service.py b/backend/app/generator/service/gen_model_service.py new file mode 100644 index 000000000..889a96480 --- /dev/null +++ b/backend/app/generator/service/gen_model_service.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +class GenModelService: + @staticmethod + async def create() -> None: ... + + @staticmethod + async def update() -> int: ... + + @staticmethod + async def delete() -> int: ... + + +gen_model_service = GenModelService() diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 565dccf46..bd0e7afbf 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -9,24 +9,6 @@ async def get_all() -> dict: ... @staticmethod async def get() -> dict: ... - @staticmethod - async def create_business() -> None: ... - - @staticmethod - async def update_business() -> int: ... - - @staticmethod - async def delete_business() -> int: ... - - @staticmethod - async def create_model() -> None: ... - - @staticmethod - async def update_model() -> int: ... - - @staticmethod - async def delete_model() -> int: ... - @staticmethod async def preview() -> dict: ... From 42d4d7d27be7c9d698e0b3c60e33d3cbf32e106b Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Sun, 12 May 2024 22:54:45 +0800 Subject: [PATCH 07/53] add some gen codes --- backend/app/generator/api/v1/gen.py | 62 +++++++++++------- .../app/generator/crud/crud_gen_business.py | 64 ++++++++++++++++++- backend/app/generator/crud/crud_gen_model.py | 11 +++- backend/app/generator/model/gen_business.py | 6 +- backend/app/generator/model/gen_model.py | 7 +- backend/app/generator/schema/gen_model.py | 1 + .../generator/service/gen_business_service.py | 38 ++++++++++- .../generator/service/gen_model_service.py | 11 ++++ backend/app/generator/service/gen_service.py | 15 +++-- 9 files changed, 176 insertions(+), 39 deletions(-) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index 753f48989..6942991d5 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -2,63 +2,77 @@ # -*- coding: utf-8 -*- from typing import Annotated -from fastapi import APIRouter, Path +from fastapi import APIRouter, Path, Query +from backend.app.generator.schema.gen_business import CreateGenBusinessParam, UpdateGenBusinessParam +from backend.app.generator.service.gen_business_service import gen_business_service from backend.app.generator.service.gen_service import gen_service from backend.common.response.response_schema import ResponseModel, response_base +from backend.common.security.jwt import DependsJwtAuth +from backend.common.security.rbac import DependsRBAC router = APIRouter() -@router.get('/all', summary='获取代码生成业务表列表') -async def get_all_businesses(): - data = await gen_service.get_all() +@router.get('/all', summary='获取所有代码生成业务', dependencies=[DependsJwtAuth]) +async def get_all_businesses() -> ResponseModel: + data = await gen_business_service.get_all() return await response_base.success(data=data) -@router.get('/{pk}', summary='获取代码生成业务和model表详情') -async def get_business_and_model(pk: Annotated[int, Path(...)]): - data = await gen_service.get(pk) +@router.get('/businesses/{pk}', summary='获取代码生成业务详情', dependencies=[DependsJwtAuth]) +async def get_business(pk: Annotated[int, Path(...)]) -> ResponseModel: + data = await gen_service.get_business_and_model(pk) return await response_base.success(data=data) -@router.post('/businesses', summary='创建代码生成业务表') -async def create_business(): ... +@router.post('/businesses', summary='创建代码生成业务', dependencies=[DependsRBAC]) +async def create_business(obj: CreateGenBusinessParam) -> ResponseModel: + await gen_business_service.create(obj=obj) + return await response_base.success() -@router.put('/businesses', summary='更新代码生成业务表') -async def update_business(): ... +@router.put('/businesses/{pk}', summary='更新代码生成业务', dependencies=[DependsRBAC]) +async def update_business(pk: Annotated[int, Path(...)], obj: UpdateGenBusinessParam) -> ResponseModel: + count = await gen_business_service.update(pk=pk, obj=obj) + if count > 0: + return await response_base.success() + return await response_base.fail() -@router.delete('/businesses', summary='删除代码生成业务表') -async def delete_business(): ... +@router.delete('/businesses', summary='删除代码生成业务', dependencies=[DependsRBAC]) +async def delete_business(pk: Annotated[int, Query(...)]) -> ResponseModel: + count = await gen_business_service.delete(pk=pk) + if count > 0: + return await response_base.success() + return await response_base.fail() -@router.post('/models', summary='创建代码生成model表') -async def create_model(): ... +@router.post('/models', summary='创建代码生成模型', dependencies=[DependsRBAC]) +async def create_model() -> ResponseModel: ... -@router.put('/models', summary='更新代码生成model表') -async def update_model(): ... +@router.put('/models', summary='更新代码生成模型', dependencies=[DependsRBAC]) +async def update_model() -> ResponseModel: ... -@router.delete('/models', summary='删除代码生成model表') -async def delete_model(): ... +@router.delete('/models', summary='删除代码生成模型', dependencies=[DependsRBAC]) +async def delete_model() -> ResponseModel: ... -@router.get('/preview', summary='生成代码预览') +@router.get('/preview', summary='生成代码预览', dependencies=[DependsJwtAuth]) async def preview_code() -> ResponseModel: data = await gen_service.preview() return await response_base.success(data=data) -@router.post('/generate', summary='生成代码') -async def generate_code(): +@router.post('/generate', summary='生成代码', dependencies=[DependsRBAC]) +async def generate_code() -> ResponseModel: await gen_service.generate() return await response_base.success() -@router.post('/download', summary='下载代码') -async def download_code(): +@router.post('/download', summary='下载代码', dependencies=[DependsRBAC]) +async def download_code() -> ResponseModel: await gen_service.download() ... diff --git a/backend/app/generator/crud/crud_gen_business.py b/backend/app/generator/crud/crud_gen_business.py index fe7090cde..b0f30e031 100644 --- a/backend/app/generator/crud/crud_gen_business.py +++ b/backend/app/generator/crud/crud_gen_business.py @@ -1,9 +1,71 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy_crud_plus import CRUDPlus from backend.app.generator.model import GenBusiness +from backend.app.generator.schema.gen_business import CreateGenBusinessParam, UpdateGenBusinessParam class CRUDGenBusiness(CRUDPlus[GenBusiness]): - pass + async def get(self, db: AsyncSession, pk: int) -> GenBusiness | None: + """ + 获取代码生成业务表 + + :param db: + :param pk: + :return: + """ + return await self.select_model_by_id(db, pk) + + async def get_by_name(self, db: AsyncSession, name: str) -> GenBusiness | None: + """ + 通过 name 获取代码生成业务表 + + :param db: + :param name: + :return: + """ + return await self.select_model_by_column(db, 'name', name) + + async def get_all(self, db: AsyncSession): + """ + 获取所有代码生成业务表 + + :return: + """ + return await self.select_models(db) + + async def create(self, db: AsyncSession, obj_in: CreateGenBusinessParam) -> None: + """ + 创建代码生成业务表 + + :param db: + :param obj_in: + :return: + """ + await self.create_model(db, obj_in) + + async def update(self, db: AsyncSession, pk: int, obj_in: UpdateGenBusinessParam) -> int: + """ + 更新代码生成业务表 + + :param db: + :param pk: + :param obj_in: + :return: + """ + return await self.update_model(db, pk, obj_in) + + async def delete(self, db: AsyncSession, pk: int) -> int: + """ + 删除代码生成业务表 + + :param db: + :param pk: + :return: + """ + return await self.delete_model(db, pk) + + +gen_business_dao = CRUDGenBusiness(GenBusiness) diff --git a/backend/app/generator/crud/crud_gen_model.py b/backend/app/generator/crud/crud_gen_model.py index f06407bb4..b17b486a5 100644 --- a/backend/app/generator/crud/crud_gen_model.py +++ b/backend/app/generator/crud/crud_gen_model.py @@ -1,9 +1,18 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from typing import Sequence + +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy_crud_plus import CRUDPlus from backend.app.generator.model import GenModel class CRUDGenModel(CRUDPlus[GenModel]): - pass + async def get_with_relation(self, db: AsyncSession, business_id: int) -> Sequence[GenModel]: + gen_model = await db.execute(select(self.model).where(self.model.gen_business_id == business_id)) + return gen_model.scalars().all() + + +gen_model_dao = CRUDGenModel(GenModel) diff --git a/backend/app/generator/model/gen_business.py b/backend/app/generator/model/gen_business.py index 6baccd591..ae9b8df87 100644 --- a/backend/app/generator/model/gen_business.py +++ b/backend/app/generator/model/gen_business.py @@ -14,7 +14,7 @@ class GenBusiness(Base): id: Mapped[id_key] = mapped_column(init=False) app_name: Mapped[str] = mapped_column(String(50), comment='应用名称(英文)') - model_name: Mapped[str] = mapped_column(String(255), comment='表名称(英文)') + model_name: Mapped[str] = mapped_column(String(255), unique=True, comment='表名称(英文)') model_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文)') model_simple_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文简称)') model_comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='表描述') @@ -26,5 +26,5 @@ class GenBusiness(Base): ) gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='代码生成路径(默认为项目根路径)') remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注') - # 代码生成业务model一对一 - gen_model: Mapped['GenModel'] = relationship(back_populates='gen_business') # noqa: F821 + # 代码生成业务模型一对多 + gen_model: Mapped[list['GenModel']] = relationship(init=False, back_populates='gen_business') # noqa: F821 diff --git a/backend/app/generator/model/gen_model.py b/backend/app/generator/model/gen_model.py index 86d2d11e0..d15df1462 100644 --- a/backend/app/generator/model/gen_model.py +++ b/backend/app/generator/model/gen_model.py @@ -7,7 +7,7 @@ class GenModel(DataClassBase): - """代码生成model表""" + """代码生成模型表""" __tablename__ = 'sys_gen_model' __table_args__ = UniqueConstraint('gen_business_id') @@ -17,12 +17,13 @@ class GenModel(DataClassBase): comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='列描述') type: Mapped[str] = mapped_column(String(20), default='string', comment='列类型') default: Mapped[str | None] = mapped_column(default=None, comment='列默认值') + sort: Mapped[int | None] = mapped_column(default=1, comment='列排序') length: Mapped[int] = mapped_column(default=0, comment='列长度') is_pk: Mapped[bool] = mapped_column(default=False, comment='是否主键') is_nullable: Mapped[bool] = mapped_column(default=False, comment='是否可为空') - # 代码生成业务model一对一 + # 代码生成业务model一对多 gen_business_id: Mapped[int] = mapped_column( ForeignKey('sys_gen_business.id', default=None, comment='代码生成业务ID') ) - gen_business: Mapped['GenBusiness'] = relationship(back_populates='gen_model', single_parent=True) # noqa: F821 + gen_business: Mapped[Union['GenBusiness', None]] = relationship(init=False, back_populates='gen_model') # noqa: F821 diff --git a/backend/app/generator/schema/gen_model.py b/backend/app/generator/schema/gen_model.py index 42078b63d..cc7179a40 100644 --- a/backend/app/generator/schema/gen_model.py +++ b/backend/app/generator/schema/gen_model.py @@ -10,6 +10,7 @@ class GenModelSchemaBase(SchemaBase): comment: str | None = None type: str default: str | None = None + sort: int length: int is_pk: bool is_nullable: bool diff --git a/backend/app/generator/service/gen_business_service.py b/backend/app/generator/service/gen_business_service.py index f31807b14..c7597c7c1 100644 --- a/backend/app/generator/service/gen_business_service.py +++ b/backend/app/generator/service/gen_business_service.py @@ -1,16 +1,48 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from typing import Sequence + +from backend.app.generator.crud.crud_gen_business import gen_business_dao +from backend.app.generator.model import GenBusiness +from backend.app.generator.schema.gen_business import CreateGenBusinessParam, UpdateGenBusinessParam +from backend.common.exception import errors +from backend.database.db_mysql import async_db_session class GenBusinessService: @staticmethod - async def create() -> None: ... + async def get(*, pk: int) -> GenBusiness: + async with async_db_session() as db: + business = await gen_business_dao.get(db, pk) + if not business: + raise errors.NotFoundError(msg='代码生成业务不存在') + return business + + @staticmethod + async def get_all() -> Sequence[GenBusiness]: + async with async_db_session() as db: + businesses = await gen_business_dao.get_all(db) + return businesses + + @staticmethod + async def create(*, obj: CreateGenBusinessParam) -> None: + async with async_db_session.begin() as db: + business = gen_business_dao.get_by_name(db, obj.model_name) + if business: + raise errors.ForbiddenError(msg='代码生成业务已存在') + await gen_business_dao.create(db, obj) @staticmethod - async def update() -> int: ... + async def update(*, pk: int, obj: UpdateGenBusinessParam) -> int: + async with async_db_session.begin() as db: + count = await gen_business_dao.update(db, pk, obj) + return count @staticmethod - async def delete() -> int: ... + async def delete(*, pk: int) -> int: + async with async_db_session.begin() as db: + count = await gen_business_dao.delete(db, pk) + return count gen_business_service = GenBusinessService() diff --git a/backend/app/generator/service/gen_model_service.py b/backend/app/generator/service/gen_model_service.py index 889a96480..8aff5c159 100644 --- a/backend/app/generator/service/gen_model_service.py +++ b/backend/app/generator/service/gen_model_service.py @@ -1,8 +1,19 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from typing import Sequence + +from backend.app.generator.crud.crud_gen_model import gen_model_dao +from backend.app.generator.model import GenModel +from backend.database.db_mysql import async_db_session class GenModelService: + @staticmethod + async def get_with_relation(*, business_id: int) -> Sequence[GenModel]: + async with async_db_session() as db: + gen_model = await gen_model_dao.get_with_relation(db, business_id) + return gen_model + @staticmethod async def create() -> None: ... diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index bd0e7afbf..29d7eee34 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -1,13 +1,20 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from backend.app.generator.service.gen_business_service import gen_business_service +from backend.app.generator.service.gen_model_service import gen_model_service +from backend.utils.serializers import select_as_dict, select_list_serialize class GenService: @staticmethod - async def get_all() -> dict: ... - - @staticmethod - async def get() -> dict: ... + async def get_business_and_model(*, pk: int) -> dict: + gen_business = await gen_business_service.get(pk=pk) + gen_model = await gen_model_service.get_with_relation(pk=pk) + business_data = await select_as_dict(gen_business) + if gen_model: + model_data = await select_list_serialize(gen_model) + business_data.update({'models': model_data}) + return business_data @staticmethod async def preview() -> dict: ... From a24b9ca1a1967d7e457e7e86ea316933fefd5258 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Fri, 17 May 2024 12:33:44 +0800 Subject: [PATCH 08/53] upgrade SQLAlchemy to release --- backend/pdm.lock | 110 ++++++++++++++++++++--------------------- backend/pyproject.toml | 4 +- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/backend/pdm.lock b/backend/pdm.lock index 3112f9612..29a1f9b0c 100644 --- a/backend/pdm.lock +++ b/backend/pdm.lock @@ -5,11 +5,11 @@ groups = ["default", "lint", "deploy"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:2a2deb7179c7e6eefc4370036ae06edbf34bbb7cb5f43601d52f8f0f45d2d607" +content_hash = "sha256:88b398f2701d30a784bc7e0902ec33f6999c0210fcb9dfafe284661772e26785" [[package]] name = "alembic" -version = "1.13.0" +version = "1.13.1" requires_python = ">=3.8" summary = "A database migration tool for SQLAlchemy." groups = ["default"] @@ -19,8 +19,8 @@ dependencies = [ "typing-extensions>=4", ] files = [ - {file = "alembic-1.13.0-py3-none-any.whl", hash = "sha256:a23974ea301c3ee52705db809c7413cecd165290c6679b9998dd6c74342ca23a"}, - {file = "alembic-1.13.0.tar.gz", hash = "sha256:ab4b3b94d2e1e5f81e34be8a9b7b7575fc9dd5398fccb0bef351ec9b14872623"}, + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, ] [[package]] @@ -880,7 +880,7 @@ files = [ [[package]] name = "jinja2" -version = "3.1.3" +version = "3.1.4" requires_python = ">=3.7" summary = "A very fast and expressive template engine." groups = ["default"] @@ -888,8 +888,8 @@ dependencies = [ "MarkupSafe>=2.0", ] files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [[package]] @@ -1600,28 +1600,28 @@ files = [ [[package]] name = "ruff" -version = "0.4.2" +version = "0.4.4" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["lint"] files = [ - {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d14dc8953f8af7e003a485ef560bbefa5f8cc1ad994eebb5b12136049bbccc5"}, - {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:24016ed18db3dc9786af103ff49c03bdf408ea253f3cb9e3638f39ac9cf2d483"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2e06459042ac841ed510196c350ba35a9b24a643e23db60d79b2db92af0c2b"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3afabaf7ba8e9c485a14ad8f4122feff6b2b93cc53cd4dad2fd24ae35112d5c5"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:799eb468ea6bc54b95527143a4ceaf970d5aa3613050c6cff54c85fda3fde480"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ec4ba9436a51527fb6931a8839af4c36a5481f8c19e8f5e42c2f7ad3a49f5069"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a2243f8f434e487c2a010c7252150b1fdf019035130f41b77626f5655c9ca22"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8772130a063f3eebdf7095da00c0b9898bd1774c43b336272c3e98667d4fb8fa"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab165ef5d72392b4ebb85a8b0fbd321f69832a632e07a74794c0e598e7a8376"}, - {file = "ruff-0.4.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1f32cadf44c2020e75e0c56c3408ed1d32c024766bd41aedef92aa3ca28eef68"}, - {file = "ruff-0.4.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:22e306bf15e09af45ca812bc42fa59b628646fa7c26072555f278994890bc7ac"}, - {file = "ruff-0.4.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:82986bb77ad83a1719c90b9528a9dd663c9206f7c0ab69282af8223566a0c34e"}, - {file = "ruff-0.4.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:652e4ba553e421a6dc2a6d4868bc3b3881311702633eb3672f9f244ded8908cd"}, - {file = "ruff-0.4.2-py3-none-win32.whl", hash = "sha256:7891ee376770ac094da3ad40c116258a381b86c7352552788377c6eb16d784fe"}, - {file = "ruff-0.4.2-py3-none-win_amd64.whl", hash = "sha256:5ec481661fb2fd88a5d6cf1f83403d388ec90f9daaa36e40e2c003de66751798"}, - {file = "ruff-0.4.2-py3-none-win_arm64.whl", hash = "sha256:cbd1e87c71bca14792948c4ccb51ee61c3296e164019d2d484f3eaa2d360dfaf"}, - {file = "ruff-0.4.2.tar.gz", hash = "sha256:33bcc160aee2520664bc0859cfeaebc84bb7323becff3f303b8f1f2d81cb4edc"}, + {file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"}, + {file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"}, + {file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"}, + {file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"}, + {file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"}, + {file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"}, ] [[package]] @@ -1680,41 +1680,41 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.23" +version = "2.0.30" requires_python = ">=3.7" summary = "Database Abstraction Library" groups = ["default"] dependencies = [ "greenlet!=0.4.17; platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\"", - "typing-extensions>=4.2.0", -] -files = [ - {file = "SQLAlchemy-2.0.23-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:638c2c0b6b4661a4fd264f6fb804eccd392745c5887f9317feb64bb7cb03b3ea"}, - {file = "SQLAlchemy-2.0.23-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3b5036aa326dc2df50cba3c958e29b291a80f604b1afa4c8ce73e78e1c9f01d"}, - {file = "SQLAlchemy-2.0.23-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:787af80107fb691934a01889ca8f82a44adedbf5ef3d6ad7d0f0b9ac557e0c34"}, - {file = "SQLAlchemy-2.0.23-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c14eba45983d2f48f7546bb32b47937ee2cafae353646295f0e99f35b14286ab"}, - {file = "SQLAlchemy-2.0.23-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0666031df46b9badba9bed00092a1ffa3aa063a5e68fa244acd9f08070e936d3"}, - {file = "SQLAlchemy-2.0.23-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89a01238fcb9a8af118eaad3ffcc5dedaacbd429dc6fdc43fe430d3a941ff965"}, - {file = "SQLAlchemy-2.0.23-cp310-cp310-win32.whl", hash = "sha256:cabafc7837b6cec61c0e1e5c6d14ef250b675fa9c3060ed8a7e38653bd732ff8"}, - {file = "SQLAlchemy-2.0.23-cp310-cp310-win_amd64.whl", hash = "sha256:87a3d6b53c39cd173990de2f5f4b83431d534a74f0e2f88bd16eabb5667e65c6"}, - {file = "SQLAlchemy-2.0.23-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d5578e6863eeb998980c212a39106ea139bdc0b3f73291b96e27c929c90cd8e1"}, - {file = "SQLAlchemy-2.0.23-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62d9e964870ea5ade4bc870ac4004c456efe75fb50404c03c5fd61f8bc669a72"}, - {file = "SQLAlchemy-2.0.23-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c80c38bd2ea35b97cbf7c21aeb129dcbebbf344ee01a7141016ab7b851464f8e"}, - {file = "SQLAlchemy-2.0.23-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75eefe09e98043cff2fb8af9796e20747ae870c903dc61d41b0c2e55128f958d"}, - {file = "SQLAlchemy-2.0.23-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd45a5b6c68357578263d74daab6ff9439517f87da63442d244f9f23df56138d"}, - {file = "SQLAlchemy-2.0.23-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a86cb7063e2c9fb8e774f77fbf8475516d270a3e989da55fa05d08089d77f8c4"}, - {file = "SQLAlchemy-2.0.23-cp311-cp311-win32.whl", hash = "sha256:b41f5d65b54cdf4934ecede2f41b9c60c9f785620416e8e6c48349ab18643855"}, - {file = "SQLAlchemy-2.0.23-cp311-cp311-win_amd64.whl", hash = "sha256:9ca922f305d67605668e93991aaf2c12239c78207bca3b891cd51a4515c72e22"}, - {file = "SQLAlchemy-2.0.23-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0f7fb0c7527c41fa6fcae2be537ac137f636a41b4c5a4c58914541e2f436b45"}, - {file = "SQLAlchemy-2.0.23-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7c424983ab447dab126c39d3ce3be5bee95700783204a72549c3dceffe0fc8f4"}, - {file = "SQLAlchemy-2.0.23-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f508ba8f89e0a5ecdfd3761f82dda2a3d7b678a626967608f4273e0dba8f07ac"}, - {file = "SQLAlchemy-2.0.23-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6463aa765cf02b9247e38b35853923edbf2f6fd1963df88706bc1d02410a5577"}, - {file = "SQLAlchemy-2.0.23-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e599a51acf3cc4d31d1a0cf248d8f8d863b6386d2b6782c5074427ebb7803bda"}, - {file = "SQLAlchemy-2.0.23-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd54601ef9cc455a0c61e5245f690c8a3ad67ddb03d3b91c361d076def0b4c60"}, - {file = "SQLAlchemy-2.0.23-cp312-cp312-win32.whl", hash = "sha256:42d0b0290a8fb0165ea2c2781ae66e95cca6e27a2fbe1016ff8db3112ac1e846"}, - {file = "SQLAlchemy-2.0.23-cp312-cp312-win_amd64.whl", hash = "sha256:227135ef1e48165f37590b8bfc44ed7ff4c074bf04dc8d6f8e7f1c14a94aa6ca"}, - {file = "SQLAlchemy-2.0.23-py3-none-any.whl", hash = "sha256:31952bbc527d633b9479f5f81e8b9dfada00b91d6baba021a869095f1a97006d"}, - {file = "SQLAlchemy-2.0.23.tar.gz", hash = "sha256:c1bda93cbbe4aa2aa0aa8655c5aeda505cd219ff3e8da91d1d329e143e4aff69"}, + "typing-extensions>=4.6.0", +] +files = [ + {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-win32.whl", hash = "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-win32.whl", hash = "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-win_amd64.whl", hash = "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"}, + {file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"}, + {file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"}, ] [[package]] diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 1b1b7b1be..3bf633265 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -6,7 +6,7 @@ authors = [ { name = "Wu Clan", email = "jianhengwu0407@gmail.com" }, ] dependencies = [ - "alembic==1.13.0", + "alembic>=1.13.0", "asgiref==3.7.2", "asyncmy==0.2.9", "bcrypt==4.0.1", @@ -34,7 +34,7 @@ dependencies = [ "python-jose==3.3.0", "pytz==2023.3", "redis[hiredis]==5.0.1", - "SQLAlchemy==2.0.23", + "SQLAlchemy>=2.0.23", "user-agents==2.2.0", "uvicorn[standard]==0.29.0", "XdbSearchIP==1.0.2", From 76b6d05f989b0ab926d1f99e27f0f68f06152967 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Fri, 17 May 2024 12:34:13 +0800 Subject: [PATCH 09/53] update template util --- backend/utils/gen_template.py | 23 +++++++++++++++++++++++ backend/utils/get_templates.py | 20 -------------------- 2 files changed, 23 insertions(+), 20 deletions(-) create mode 100644 backend/utils/gen_template.py delete mode 100644 backend/utils/get_templates.py diff --git a/backend/utils/gen_template.py b/backend/utils/gen_template.py new file mode 100644 index 000000000..d38fd8c4f --- /dev/null +++ b/backend/utils/gen_template.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from jinja2 import Environment, FileSystemLoader, Template, select_autoescape + +from backend.core.path_conf import JINJA2_TEMPLATE_DIR + + +class GenTemplate: + + @staticmethod + def get_template(jinja_file: str) -> 'Template': + """ + 获取模版文件 + + :param jinja_file: + :return: + """ + env = Environment( + loader=FileSystemLoader(JINJA2_TEMPLATE_DIR), + autoescape=select_autoescape(['html', 'xml', 'jinja']), + keep_trailing_newline=True, + ) + return env.get_template(jinja_file) diff --git a/backend/utils/get_templates.py b/backend/utils/get_templates.py deleted file mode 100644 index 35eebe898..000000000 --- a/backend/utils/get_templates.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -from jinja2 import Environment, FileSystemLoader, Template, select_autoescape - -from backend.core.path_conf import JINJA2_TEMPLATE_DIR - - -def get_templates(jinja_file: str) -> 'Template': - """ - 获取模版文件 - - :param jinja_file: - :return: - """ - env = Environment( - loader=FileSystemLoader(JINJA2_TEMPLATE_DIR), - autoescape=select_autoescape(['html', 'xml', 'jinja']), - keep_trailing_newline=True, - ) - return env.get_template(jinja_file) From 82f1447738ac40db6a36ffedc9057dabaa8945f9 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 22 May 2024 00:19:39 +0800 Subject: [PATCH 10/53] Fix lint error --- backend/requirements.txt | 8 ++++---- backend/utils/gen_template.py | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index b49bc1bed..da5cf4c0f 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,7 +1,7 @@ # This file is @generated by PDM. # Please do not edit it manually. -alembic==1.13.0 +alembic==1.13.1 amqp==5.2.0 annotated-types==0.6.0 anyio==4.3.0 @@ -48,7 +48,7 @@ identify==2.5.35 idna==3.6 iniconfig==2.0.0 itsdangerous==2.1.2 -jinja2==3.1.3 +jinja2==3.1.4 kombu==5.3.5 loguru==0.7.2 mako==1.3.2 @@ -87,13 +87,13 @@ pyyaml==6.0.1 redis==5.0.1 rich==13.7.1 rsa==4.9 -ruff==0.4.2 +ruff==0.4.4 setuptools==69.2.0 shellingham==1.5.4 simpleeval==0.9.13 six==1.16.0 sniffio==1.3.1 -sqlalchemy==2.0.23 +sqlalchemy==2.0.30 sqlalchemy-crud-plus==0.0.2 starlette==0.37.2 supervisor==4.2.5 diff --git a/backend/utils/gen_template.py b/backend/utils/gen_template.py index d38fd8c4f..e9431ba23 100644 --- a/backend/utils/gen_template.py +++ b/backend/utils/gen_template.py @@ -6,7 +6,6 @@ class GenTemplate: - @staticmethod def get_template(jinja_file: str) -> 'Template': """ From b4e9415bbf23849c6acfbca2e0783f12c30a21e5 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Sun, 26 May 2024 15:33:51 +0800 Subject: [PATCH 11/53] Add template rendering code --- backend/app/generator/api/v1/gen.py | 6 +- backend/app/generator/crud/crud_gen_model.py | 6 +- backend/app/generator/model/gen_model.py | 2 +- backend/app/generator/schema/gen_model.py | 2 +- .../generator/service/gen_model_service.py | 4 +- backend/app/generator/service/gen_service.py | 30 ++++++++-- backend/pdm.lock | 10 ++-- backend/pyproject.toml | 2 +- backend/requirements.txt | 2 +- backend/utils/gen_template.py | 57 ++++++++++++++++--- 10 files changed, 93 insertions(+), 28 deletions(-) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index 6942991d5..cb4e84a99 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -60,9 +60,9 @@ async def update_model() -> ResponseModel: ... async def delete_model() -> ResponseModel: ... -@router.get('/preview', summary='生成代码预览', dependencies=[DependsJwtAuth]) -async def preview_code() -> ResponseModel: - data = await gen_service.preview() +@router.get('/preview/{pk}', summary='生成代码预览', dependencies=[DependsJwtAuth]) +async def preview_code(pk: Annotated[int, Path(..., description='业务ID')]) -> ResponseModel: + data = await gen_service.preview(pk=pk) return await response_base.success(data=data) diff --git a/backend/app/generator/crud/crud_gen_model.py b/backend/app/generator/crud/crud_gen_model.py index b17b486a5..d5ca4ae05 100644 --- a/backend/app/generator/crud/crud_gen_model.py +++ b/backend/app/generator/crud/crud_gen_model.py @@ -10,8 +10,10 @@ class CRUDGenModel(CRUDPlus[GenModel]): - async def get_with_relation(self, db: AsyncSession, business_id: int) -> Sequence[GenModel]: - gen_model = await db.execute(select(self.model).where(self.model.gen_business_id == business_id)) + async def get_by_business_id(self, db: AsyncSession, business_id: int) -> Sequence[GenModel]: + gen_model = await db.execute( + select(self.model).where(self.model.gen_business_id == business_id).order_by(self.model.sort) + ) return gen_model.scalars().all() diff --git a/backend/app/generator/model/gen_model.py b/backend/app/generator/model/gen_model.py index d15df1462..dff4a23da 100644 --- a/backend/app/generator/model/gen_model.py +++ b/backend/app/generator/model/gen_model.py @@ -23,7 +23,7 @@ class GenModel(DataClassBase): is_nullable: Mapped[bool] = mapped_column(default=False, comment='是否可为空') # 代码生成业务model一对多 - gen_business_id: Mapped[int] = mapped_column( + gen_business_id: Mapped[int | None] = mapped_column( ForeignKey('sys_gen_business.id', default=None, comment='代码生成业务ID') ) gen_business: Mapped[Union['GenBusiness', None]] = relationship(init=False, back_populates='gen_model') # noqa: F821 diff --git a/backend/app/generator/schema/gen_model.py b/backend/app/generator/schema/gen_model.py index cc7179a40..ab4c1d03f 100644 --- a/backend/app/generator/schema/gen_model.py +++ b/backend/app/generator/schema/gen_model.py @@ -14,7 +14,7 @@ class GenModelSchemaBase(SchemaBase): length: int is_pk: bool is_nullable: bool - gen_business_id: int + gen_business_id: int | None class CreateGenModel(GenModelSchemaBase): diff --git a/backend/app/generator/service/gen_model_service.py b/backend/app/generator/service/gen_model_service.py index 8aff5c159..f822b2fc2 100644 --- a/backend/app/generator/service/gen_model_service.py +++ b/backend/app/generator/service/gen_model_service.py @@ -9,9 +9,9 @@ class GenModelService: @staticmethod - async def get_with_relation(*, business_id: int) -> Sequence[GenModel]: + async def get_by_business(*, business_id: int) -> Sequence[GenModel]: async with async_db_session() as db: - gen_model = await gen_model_dao.get_with_relation(db, business_id) + gen_model = await gen_model_dao.get_by_business_id(db, business_id) return gen_model @staticmethod diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 29d7eee34..07b66cb03 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -1,7 +1,13 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from backend.app.generator.crud.crud_gen_business import gen_business_dao +from backend.app.generator.crud.crud_gen_model import gen_model_dao +from backend.app.generator.model import GenBusiness from backend.app.generator.service.gen_business_service import gen_business_service from backend.app.generator.service.gen_model_service import gen_model_service +from backend.common.exception import errors +from backend.database.db_mysql import async_db_session +from backend.utils.gen_template import gen_template from backend.utils.serializers import select_as_dict, select_list_serialize @@ -9,15 +15,31 @@ class GenService: @staticmethod async def get_business_and_model(*, pk: int) -> dict: gen_business = await gen_business_service.get(pk=pk) - gen_model = await gen_model_service.get_with_relation(pk=pk) + gen_models = await gen_model_service.get_by_business(pk=pk) business_data = await select_as_dict(gen_business) - if gen_model: - model_data = await select_list_serialize(gen_model) + if gen_models: + model_data = await select_list_serialize(gen_models) business_data.update({'models': model_data}) return business_data @staticmethod - async def preview() -> dict: ... + async def render_tpl_code(business: GenBusiness) -> dict: + gen_models = await gen_model_dao.get_by_business(business.id) + if not gen_models: + raise errors.NotFoundError(msg='代码生成模型表为空') + gen_vars = gen_template.get_vars(business, gen_models) + tpl_code_map = {} + for tpl_path in gen_template.get_template_paths(): + tpl_code_map[tpl_path] = await gen_template.get_template(tpl_path).render(**gen_vars) + return tpl_code_map + + async def preview(self, *, pk: int) -> dict: + async with async_db_session() as db: + business = await gen_business_dao.get(db, pk) + if not business: + raise errors.NotFoundError(msg='业务不存在') + tpl_code_map = await self.render_tpl_code(business) + return {tpl.replace('.tpl', ''): code for tpl, code in tpl_code_map.items()} @staticmethod async def generate() -> dict: ... diff --git a/backend/pdm.lock b/backend/pdm.lock index 29a1f9b0c..1554e08fc 100644 --- a/backend/pdm.lock +++ b/backend/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "lint", "deploy"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:88b398f2701d30a784bc7e0902ec33f6999c0210fcb9dfafe284661772e26785" +content_hash = "sha256:daef4659b78e2f299c142b06a0d20977bee23bd168c00d37d5a29e4bb9aae0aa" [[package]] name = "alembic" @@ -67,16 +67,16 @@ files = [ [[package]] name = "asgiref" -version = "3.7.2" -requires_python = ">=3.7" +version = "3.8.1" +requires_python = ">=3.8" summary = "ASGI specs, helper code, and adapters" groups = ["default"] dependencies = [ "typing-extensions>=4; python_version < \"3.11\"", ] files = [ - {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, - {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, + {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, + {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, ] [[package]] diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 3bf633265..c28713013 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -7,7 +7,7 @@ authors = [ ] dependencies = [ "alembic>=1.13.0", - "asgiref==3.7.2", + "asgiref>=3.8.0", "asyncmy==0.2.9", "bcrypt==4.0.1", "casbin==1.34.0", diff --git a/backend/requirements.txt b/backend/requirements.txt index da5cf4c0f..3c7a0c729 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -5,7 +5,7 @@ alembic==1.13.1 amqp==5.2.0 annotated-types==0.6.0 anyio==4.3.0 -asgiref==3.7.2 +asgiref==3.8.1 async-timeout==4.0.3; python_full_version <= "3.11.2" asyncmy==0.2.9 attrs==23.2.0 diff --git a/backend/utils/gen_template.py b/backend/utils/gen_template.py index e9431ba23..997cca2f3 100644 --- a/backend/utils/gen_template.py +++ b/backend/utils/gen_template.py @@ -2,21 +2,62 @@ # -*- coding: utf-8 -*- from jinja2 import Environment, FileSystemLoader, Template, select_autoescape +from backend.app.generator.model import GenBusiness, GenModel from backend.core.path_conf import JINJA2_TEMPLATE_DIR class GenTemplate: - @staticmethod - def get_template(jinja_file: str) -> 'Template': + def __init__(self): + self.env = Environment( + loader=FileSystemLoader(JINJA2_TEMPLATE_DIR), + autoescape=select_autoescape(['html', 'xml', 'jinja']), + keep_trailing_newline=True, + ) + + def get_template(self, jinja_file: str) -> Template: """ 获取模版文件 :param jinja_file: :return: """ - env = Environment( - loader=FileSystemLoader(JINJA2_TEMPLATE_DIR), - autoescape=select_autoescape(['html', 'xml', 'jinja']), - keep_trailing_newline=True, - ) - return env.get_template(jinja_file) + + return self.env.get_template(jinja_file) + + @staticmethod + def get_template_paths(self) -> list[str]: + """ + 获取模版文件路径 + + :return: + """ + return [ + 'py/api.jinja', + 'py/crud.jinja', + 'py/model.jinja', + 'py/schema.jinja', + 'py/service.jinja', + ] + + @staticmethod + def get_vars(business: GenBusiness, models: list[GenModel]) -> dict: + """ + 获取模版变量 + + :param business: + :param models: + :return: + """ + for model in models: + pass + return { + 'app_name': business.app_name, + 'model_name': business.model_name, + 'schema_name': business.schema_name, + 'model_simple_name_zh': business.model_simple_name_zh, + 'permission_sign': str(business.__tablename__.replace('_', ':')), + # TODO + } + + +gen_template = GenTemplate() From b95303de8e7e395fbc57c27409ca1e1ce187fafd Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Mon, 27 May 2024 12:26:09 +0800 Subject: [PATCH 12/53] Add gen model code --- backend/app/generator/api/v1/gen.py | 26 ++++++++---- backend/app/generator/crud/crud_gen_model.py | 41 +++++++++++++++++++ backend/app/generator/schema/gen_model.py | 4 +- .../generator/service/gen_model_service.py | 24 +++++++++-- 4 files changed, 83 insertions(+), 12 deletions(-) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index cb4e84a99..b3d529bc4 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -5,7 +5,9 @@ from fastapi import APIRouter, Path, Query from backend.app.generator.schema.gen_business import CreateGenBusinessParam, UpdateGenBusinessParam +from backend.app.generator.schema.gen_model import CreateGenModelParam, UpdateGenModelParam from backend.app.generator.service.gen_business_service import gen_business_service +from backend.app.generator.service.gen_model_service import gen_model_service from backend.app.generator.service.gen_service import gen_service from backend.common.response.response_schema import ResponseModel, response_base from backend.common.security.jwt import DependsJwtAuth @@ -49,15 +51,25 @@ async def delete_business(pk: Annotated[int, Query(...)]) -> ResponseModel: @router.post('/models', summary='创建代码生成模型', dependencies=[DependsRBAC]) -async def create_model() -> ResponseModel: ... +async def create_model(obj: CreateGenModelParam) -> ResponseModel: + await gen_model_service.create(obj) + return await response_base.success() -@router.put('/models', summary='更新代码生成模型', dependencies=[DependsRBAC]) -async def update_model() -> ResponseModel: ... +@router.put('/models/{pk}', summary='更新代码生成模型', dependencies=[DependsRBAC]) +async def update_model(pk: Annotated[int, Path(...)], obj: UpdateGenModelParam) -> ResponseModel: + count = await gen_model_service.update(pk=pk, obj=obj) + if count > 0: + return await response_base.success() + return await response_base.fail() -@router.delete('/models', summary='删除代码生成模型', dependencies=[DependsRBAC]) -async def delete_model() -> ResponseModel: ... +@router.delete('/models/{pk}', summary='删除代码生成模型', dependencies=[DependsRBAC]) +async def delete_model(pk: Annotated[int, Path(...)]) -> ResponseModel: + count = await gen_model_service.delete(pk=pk) + if count > 0: + return await response_base.success() + return await response_base.fail() @router.get('/preview/{pk}', summary='生成代码预览', dependencies=[DependsJwtAuth]) @@ -66,7 +78,7 @@ async def preview_code(pk: Annotated[int, Path(..., description='业务ID')]) -> return await response_base.success(data=data) -@router.post('/generate', summary='生成代码', dependencies=[DependsRBAC]) +@router.post('/generate', summary='生成代码', description='此接口会写入文件,请谨慎操作', dependencies=[DependsRBAC]) async def generate_code() -> ResponseModel: await gen_service.generate() return await response_base.success() @@ -75,4 +87,4 @@ async def generate_code() -> ResponseModel: @router.post('/download', summary='下载代码', dependencies=[DependsRBAC]) async def download_code() -> ResponseModel: await gen_service.download() - ... + return await response_base.success() diff --git a/backend/app/generator/crud/crud_gen_model.py b/backend/app/generator/crud/crud_gen_model.py index d5ca4ae05..7df2755b1 100644 --- a/backend/app/generator/crud/crud_gen_model.py +++ b/backend/app/generator/crud/crud_gen_model.py @@ -7,14 +7,55 @@ from sqlalchemy_crud_plus import CRUDPlus from backend.app.generator.model import GenModel +from backend.app.generator.schema.gen_model import CreateGenModelParam, UpdateGenModelParam class CRUDGenModel(CRUDPlus[GenModel]): + async def get_by_name(self, db: AsyncSession, name: str) -> GenModel | None: + """ + 通过 name 获取代码生成模型表 + :param db: + :param name: + :return: + """ + return await self.select_model_by_column(db, 'name', name) + async def get_by_business_id(self, db: AsyncSession, business_id: int) -> Sequence[GenModel]: gen_model = await db.execute( select(self.model).where(self.model.gen_business_id == business_id).order_by(self.model.sort) ) return gen_model.scalars().all() + async def create(self, db: AsyncSession, obj_in: CreateGenModelParam) -> None: + """ + 创建代码生成模型表 + + :param db: + :param obj_in: + :return: + """ + return await self.create_model(db, obj_in) + + async def update(self, db: AsyncSession, pk: int, obj_in: UpdateGenModelParam) -> int: + """ + 更细代码生成模型表 + + :param db: + :param pk: + :param obj_in: + :return: + """ + return await self.update_model(db, pk, obj_in) + + async def delete(self, db: AsyncSession, pk: int) -> int: + """ + 删除代码生成模型表 + + :param db: + :param pk: + :return: + """ + return await self.delete_model(db, pk) + gen_model_dao = CRUDGenModel(GenModel) diff --git a/backend/app/generator/schema/gen_model.py b/backend/app/generator/schema/gen_model.py index ab4c1d03f..c61b30991 100644 --- a/backend/app/generator/schema/gen_model.py +++ b/backend/app/generator/schema/gen_model.py @@ -17,11 +17,11 @@ class GenModelSchemaBase(SchemaBase): gen_business_id: int | None -class CreateGenModel(GenModelSchemaBase): +class CreateGenModelParam(GenModelSchemaBase): pass -class UpdateGenModel(GenModelSchemaBase): +class UpdateGenModelParam(GenModelSchemaBase): pass diff --git a/backend/app/generator/service/gen_model_service.py b/backend/app/generator/service/gen_model_service.py index f822b2fc2..2c62e1710 100644 --- a/backend/app/generator/service/gen_model_service.py +++ b/backend/app/generator/service/gen_model_service.py @@ -4,6 +4,8 @@ from backend.app.generator.crud.crud_gen_model import gen_model_dao from backend.app.generator.model import GenModel +from backend.app.generator.schema.gen_model import CreateGenModelParam, UpdateGenModelParam +from backend.common.exception import errors from backend.database.db_mysql import async_db_session @@ -15,13 +17,29 @@ async def get_by_business(*, business_id: int) -> Sequence[GenModel]: return gen_model @staticmethod - async def create() -> None: ... + async def create(obj: CreateGenModelParam) -> None: + async with async_db_session.begin() as db: + gen_models = await gen_model_dao.get_by_business_id(obj.gen_business_id) + if gen_models: + if obj.name in [name.name for name in gen_models]: + raise errors.ForbiddenError(msg='禁止添加相同列到模型表') + await gen_model_dao.create(db, obj) @staticmethod - async def update() -> int: ... + async def update(pk: int, obj: UpdateGenModelParam) -> int: + async with async_db_session.begin() as db: + gen_models = await gen_model_dao.get_by_business_id(obj.gen_business_id) + if gen_models: + if obj.name in [name.name for name in gen_models]: + raise errors.ForbiddenError(msg='禁止添加相同列到模型表') + count = await gen_model_dao.update(db, pk, obj) + return count @staticmethod - async def delete() -> int: ... + async def delete(pk: int) -> int: + async with async_db_session.begin() as db: + count = await gen_model_dao.delete(db, pk) + return count gen_model_service = GenModelService() From 2b33441b913138cefe576a557348f521d341d0e5 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Mon, 27 May 2024 22:44:47 +0800 Subject: [PATCH 13/53] Fix gen model table --- backend/app/generator/model/gen_model.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/app/generator/model/gen_model.py b/backend/app/generator/model/gen_model.py index dff4a23da..3be8a7d67 100644 --- a/backend/app/generator/model/gen_model.py +++ b/backend/app/generator/model/gen_model.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from sqlalchemy import ForeignKey, String, UniqueConstraint +from typing import Union + +from sqlalchemy import ForeignKey, String from sqlalchemy.orm import Mapped, mapped_column, relationship from backend.common.model import DataClassBase, id_key @@ -10,13 +12,12 @@ class GenModel(DataClassBase): """代码生成模型表""" __tablename__ = 'sys_gen_model' - __table_args__ = UniqueConstraint('gen_business_id') id: Mapped[id_key] = mapped_column(init=False) name: Mapped[str] = mapped_column(String(50), comment='列名称') comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='列描述') type: Mapped[str] = mapped_column(String(20), default='string', comment='列类型') - default: Mapped[str | None] = mapped_column(default=None, comment='列默认值') + default: Mapped[str | None] = mapped_column(String(50), default=None, comment='列默认值') sort: Mapped[int | None] = mapped_column(default=1, comment='列排序') length: Mapped[int] = mapped_column(default=0, comment='列长度') is_pk: Mapped[bool] = mapped_column(default=False, comment='是否主键') @@ -24,6 +25,6 @@ class GenModel(DataClassBase): # 代码生成业务model一对多 gen_business_id: Mapped[int | None] = mapped_column( - ForeignKey('sys_gen_business.id', default=None, comment='代码生成业务ID') + ForeignKey('sys_gen_business.id', ondelete='CASCADE'), default=None, comment='代码生成业务ID' ) gen_business: Mapped[Union['GenBusiness', None]] = relationship(init=False, back_populates='gen_model') # noqa: F821 From 75259261e3c02a0dda2370c8981ab1b53ace90b8 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Mon, 27 May 2024 23:21:19 +0800 Subject: [PATCH 14/53] Update business model column naming --- backend/app/generator/model/gen_business.py | 8 ++++---- backend/app/generator/schema/gen_business.py | 8 ++++---- backend/app/generator/service/gen_business_service.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/app/generator/model/gen_business.py b/backend/app/generator/model/gen_business.py index ae9b8df87..bba07d97a 100644 --- a/backend/app/generator/model/gen_business.py +++ b/backend/app/generator/model/gen_business.py @@ -14,10 +14,10 @@ class GenBusiness(Base): id: Mapped[id_key] = mapped_column(init=False) app_name: Mapped[str] = mapped_column(String(50), comment='应用名称(英文)') - model_name: Mapped[str] = mapped_column(String(255), unique=True, comment='表名称(英文)') - model_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文)') - model_simple_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文简称)') - model_comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='表描述') + table_name: Mapped[str] = mapped_column(String(255), unique=True, comment='表名称(英文)') + table_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文)') + table_simple_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文简称)') + table_comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='表描述') relate_model_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='关联表名称') relate_model_fk: Mapped[int | None] = mapped_column(default=None, comment='关联表外键') schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称') diff --git a/backend/app/generator/schema/gen_business.py b/backend/app/generator/schema/gen_business.py index 6008f810e..2bafacabc 100644 --- a/backend/app/generator/schema/gen_business.py +++ b/backend/app/generator/schema/gen_business.py @@ -9,10 +9,10 @@ class GenBusinessSchemaBase(SchemaBase): app_name: str - model_name: str - model_name_zh: str - model_simple_name_zh: str - model_comment: str | None = None + table_name: str + table_name_zh: str + table_simple_name_zh: str + table_comment: str | None = None relate_model_name: str | None = None relate_model_fk: int | None = None schema_name: str | None = None diff --git a/backend/app/generator/service/gen_business_service.py b/backend/app/generator/service/gen_business_service.py index c7597c7c1..8a638ec33 100644 --- a/backend/app/generator/service/gen_business_service.py +++ b/backend/app/generator/service/gen_business_service.py @@ -27,7 +27,7 @@ async def get_all() -> Sequence[GenBusiness]: @staticmethod async def create(*, obj: CreateGenBusinessParam) -> None: async with async_db_session.begin() as db: - business = gen_business_dao.get_by_name(db, obj.model_name) + business = gen_business_dao.get_by_name(db, obj.table_name) if business: raise errors.ForbiddenError(msg='代码生成业务已存在') await gen_business_dao.create(db, obj) From 70f0fd96dbc05aaefe5ac93a7f029bf824762dc4 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Mon, 27 May 2024 23:21:53 +0800 Subject: [PATCH 15/53] Update any route --- backend/app/admin/api/router.py | 2 ++ backend/app/admin/api/v1/auth/__init__.py | 8 +++----- backend/app/admin/api/v1/auth2/__init__.py | 9 +++++++++ .../app/admin/api/v1/{auth => auth2}/github.py | 0 backend/app/admin/api/v1/log/__init__.py | 4 ++-- backend/app/admin/api/v1/monitor/__init__.py | 6 +++--- backend/app/admin/api/v1/sys/__init__.py | 16 ++++++++-------- backend/app/generator/api/router.py | 2 +- backend/app/task/api/router.py | 2 +- 9 files changed, 29 insertions(+), 20 deletions(-) create mode 100644 backend/app/admin/api/v1/auth2/__init__.py rename backend/app/admin/api/v1/{auth => auth2}/github.py (100%) diff --git a/backend/app/admin/api/router.py b/backend/app/admin/api/router.py index b832a471a..03a1804bd 100644 --- a/backend/app/admin/api/router.py +++ b/backend/app/admin/api/router.py @@ -3,6 +3,7 @@ from fastapi import APIRouter from backend.app.admin.api.v1.auth import router as auth_router +from backend.app.admin.api.v1.auth2 import router as auth2_router from backend.app.admin.api.v1.log import router as log_router from backend.app.admin.api.v1.monitor import router as monitor_router from backend.app.admin.api.v1.sys import router as sys_router @@ -10,6 +11,7 @@ v1 = APIRouter() v1.include_router(auth_router) +v1.include_router(auth2_router) v1.include_router(sys_router) v1.include_router(log_router) v1.include_router(monitor_router) diff --git a/backend/app/admin/api/v1/auth/__init__.py b/backend/app/admin/api/v1/auth/__init__.py index 2bd73d868..b13b1aa58 100644 --- a/backend/app/admin/api/v1/auth/__init__.py +++ b/backend/app/admin/api/v1/auth/__init__.py @@ -4,10 +4,8 @@ from backend.app.admin.api.v1.auth.auth import router as auth_router from backend.app.admin.api.v1.auth.captcha import router as captcha_router -from backend.app.admin.api.v1.auth.github import router as github_router -router = APIRouter(prefix='/auth', tags=['授权管理']) +router = APIRouter(prefix='/auth') -router.include_router(auth_router) -router.include_router(captcha_router, prefix='/captcha') -router.include_router(github_router, prefix='/github') +router.include_router(auth_router, tags=['授权']) +router.include_router(captcha_router, prefix='/captcha', tags=['验证码']) diff --git a/backend/app/admin/api/v1/auth2/__init__.py b/backend/app/admin/api/v1/auth2/__init__.py new file mode 100644 index 000000000..8c0642a27 --- /dev/null +++ b/backend/app/admin/api/v1/auth2/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from fastapi import APIRouter + +from backend.app.admin.api.v1.auth2.github import router as github_router + +router = APIRouter(prefix='/auth2') + +router.include_router(github_router, prefix='/github', tags=['OAuth2']) diff --git a/backend/app/admin/api/v1/auth/github.py b/backend/app/admin/api/v1/auth2/github.py similarity index 100% rename from backend/app/admin/api/v1/auth/github.py rename to backend/app/admin/api/v1/auth2/github.py diff --git a/backend/app/admin/api/v1/log/__init__.py b/backend/app/admin/api/v1/log/__init__.py index e3ee952b2..bf00542f9 100644 --- a/backend/app/admin/api/v1/log/__init__.py +++ b/backend/app/admin/api/v1/log/__init__.py @@ -7,5 +7,5 @@ router = APIRouter(prefix='/logs') -router.include_router(login_log, prefix='/login', tags=['登录日志管理']) -router.include_router(opera_log, prefix='/opera', tags=['操作日志管理']) +router.include_router(login_log, prefix='/login', tags=['登录日志']) +router.include_router(opera_log, prefix='/opera', tags=['操作日志']) diff --git a/backend/app/admin/api/v1/monitor/__init__.py b/backend/app/admin/api/v1/monitor/__init__.py index 119bdb0da..d9d4fa247 100644 --- a/backend/app/admin/api/v1/monitor/__init__.py +++ b/backend/app/admin/api/v1/monitor/__init__.py @@ -5,7 +5,7 @@ from backend.app.admin.api.v1.monitor.redis import router as redis_router from backend.app.admin.api.v1.monitor.server import router as server_router -router = APIRouter(prefix='/monitors', tags=['监控管理']) +router = APIRouter(prefix='/monitors') -router.include_router(redis_router, prefix='/redis') -router.include_router(server_router, prefix='/server') +router.include_router(redis_router, prefix='/redis', tags=['redis监控']) +router.include_router(server_router, prefix='/server', tags=['服务器监控']) diff --git a/backend/app/admin/api/v1/sys/__init__.py b/backend/app/admin/api/v1/sys/__init__.py index 9ec211260..d757bf1a9 100644 --- a/backend/app/admin/api/v1/sys/__init__.py +++ b/backend/app/admin/api/v1/sys/__init__.py @@ -13,11 +13,11 @@ router = APIRouter(prefix='/sys') -router.include_router(api_router, prefix='/apis', tags=['API管理']) -router.include_router(casbin_router, prefix='/casbin', tags=['Casbin权限管理']) -router.include_router(dept_router, prefix='/depts', tags=['部门管理']) -router.include_router(dict_data_router, prefix='/dict_datas', tags=['字典数据管理']) -router.include_router(dict_type_router, prefix='/dict_types', tags=['字典类型管理']) -router.include_router(menu_router, prefix='/menus', tags=['目录管理']) -router.include_router(role_router, prefix='/roles', tags=['角色管理']) -router.include_router(user_router, prefix='/users', tags=['用户管理']) +router.include_router(api_router, prefix='/apis', tags=['系统API']) +router.include_router(casbin_router, prefix='/casbin', tags=['系统Casbin权限']) +router.include_router(dept_router, prefix='/depts', tags=['系统部门']) +router.include_router(dict_data_router, prefix='/dict_datas', tags=['系统字典数据']) +router.include_router(dict_type_router, prefix='/dict_types', tags=['系统字典类型']) +router.include_router(menu_router, prefix='/menus', tags=['系统目录']) +router.include_router(role_router, prefix='/roles', tags=['系统角色']) +router.include_router(user_router, prefix='/users', tags=['系统用户']) diff --git a/backend/app/generator/api/router.py b/backend/app/generator/api/router.py index 8c3a0a335..f5837532c 100644 --- a/backend/app/generator/api/router.py +++ b/backend/app/generator/api/router.py @@ -6,4 +6,4 @@ v1 = APIRouter() -v1.include_router(gen_router, prefix='/gen', tags=['代码生成管理']) +v1.include_router(gen_router, prefix='/gen', tags=['代码生成']) diff --git a/backend/app/task/api/router.py b/backend/app/task/api/router.py index b038e511a..e7ec4fcb1 100644 --- a/backend/app/task/api/router.py +++ b/backend/app/task/api/router.py @@ -6,4 +6,4 @@ v1 = APIRouter() -v1.include_router(task_router, prefix='/tasks', tags=['任务管理']) +v1.include_router(task_router, prefix='/tasks', tags=['任务']) From bca4405e68a57d0b1f284762103fd6b68f4da182 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Mon, 27 May 2024 23:23:00 +0800 Subject: [PATCH 16/53] Fix api ninja args --- backend/templates/py/api.jinja | 38 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/backend/templates/py/api.jinja b/backend/templates/py/api.jinja index 845b79679..e11a9d7a0 100644 --- a/backend/templates/py/api.jinja +++ b/backend/templates/py/api.jinja @@ -4,8 +4,8 @@ from typing import Annotated from fastapi import APIRouter, Depends, Path, Query -from backend.{{ app_name }}.admin.schema.{{ model_name }} import Create{{ schema_name }}Param, Get{{ schema_name }}ListDetails, Update{{ schema_name }}Param -from backend.{{ app_name }}.admin.service.{{ model_name }}_service import {{ model_name }}_service +from backend.{{ app_name }}.admin.schema.{{ table_name }} import Create{{ schema_name }}Param, Get{{ schema_name }}ListDetails, Update{{ schema_name }}Param +from backend.{{ app_name }}.admin.service.{{ table_name }}_service import {{ table_name }}_service from backend.common.pagination import DependsPagination, paging_data from backend.common.response.response_schema import ResponseModel, response_base from backend.common.security.jwt import DependsJwtAuth @@ -16,49 +16,49 @@ from backend.database.db_mysql import CurrentSession router = APIRouter() -@router.get('/{pk}', summary='获取{{ model_simple_name_zh }}详情', dependencies=[DependsJwtAuth]) -async def get_{{ model_name }}(pk: Annotated[int, Path(...)]) -> ResponseModel: - {{ model_name }} = await {{ model_name }}_service.get(pk=pk) - return await response_base.success(data={{ model_name }}) +@router.get('/{pk}', summary='获取{{ table_simple_name_zh }}详情', dependencies=[DependsJwtAuth]) +async def get_{{ table_name }}(pk: Annotated[int, Path(...)]) -> ResponseModel: + {{ table_name }} = await {{ table_name }}_service.get(pk=pk) + return await response_base.success(data={{ table_name }}) @router.get( '', - summary='(模糊条件)分页获取所有{{ model_simple_name_zh }}', + summary='(模糊条件)分页获取所有{{ table_simple_name_zh }}', dependencies=[ DependsJwtAuth, DependsPagination, ], ) -async def get_pagination_{{ model_name }}(db: CurrentSession) -> ResponseModel: - {{ model_name }}_select = await {{ model_name }}_service.get_select() - page_data = await paging_data(db, {{ model_name }}_select, Get{{ schema_name }}ListDetails) +async def get_pagination_{{ table_name }}(db: CurrentSession) -> ResponseModel: + {{ table_name }}_select = await {{ table_name }}_service.get_select() + page_data = await paging_data(db, {{ table_name }}_select, Get{{ schema_name }}ListDetails) return await response_base.success(data=page_data) @router.post( '', - summary='创建{{ model_simple_name_zh }}', + summary='创建{{ table_simple_name_zh }}', dependencies=[ Depends(RequestPermission('{{ permission_sign }}:add')), DependsRBAC, ], ) -async def create_{{ model_name }}(obj: Create{{ schema_name }}Param) -> ResponseModel: - await {{ model_name }}_service.create(obj=obj) +async def create_{{ table_name }}(obj: Create{{ schema_name }}Param) -> ResponseModel: + await {{ table_name }}_service.create(obj=obj) return await response_base.success() @router.put( '/{pk}', - summary='更新{{ model_simple_name_zh }}', + summary='更新{{ table_simple_name_zh }}', dependencies=[ Depends(RequestPermission('{{ permission_sign }}:edit')), DependsRBAC, ], ) -async def update_{{ model_name }}(pk: Annotated[int, Path(...)], obj: Update{{ schema_name }}Param) -> ResponseModel: - count = await {{ model_name }}_service.update(pk=pk, obj=obj) +async def update_{{ table_name }}(pk: Annotated[int, Path(...)], obj: Update{{ schema_name }}Param) -> ResponseModel: + count = await {{ table_name }}_service.update(pk=pk, obj=obj) if count > 0: return await response_base.success() return await response_base.fail() @@ -66,14 +66,14 @@ async def update_{{ model_name }}(pk: Annotated[int, Path(...)], obj: Update{{ s @router.delete( '', - summary='(批量)删除{{ model_simple_name_zh }}', + summary='(批量)删除{{ table_simple_name_zh }}', dependencies=[ Depends(RequestPermission('{{ permission_sign }}:del')), DependsRBAC, ], ) -async def delete_{{ model_name }}(pk: Annotated[list[int], Query(...)]) -> ResponseModel: - count = await {{ model_name }}_service.delete(pk=pk) +async def delete_{{ table_name }}(pk: Annotated[list[int], Query(...)]) -> ResponseModel: + count = await {{ table_name }}_service.delete(pk=pk) if count > 0: return await response_base.success() return await response_base.fail() From c5efb5f2f764964104cb0eadb0552e2eb5e6e5d5 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Tue, 28 May 2024 23:54:31 +0800 Subject: [PATCH 17/53] Update gen business model --- backend/app/generator/model/gen_business.py | 10 +++++----- backend/app/generator/schema/gen_business.py | 11 ++++++----- backend/common/enums.py | 8 ++++++++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/backend/app/generator/model/gen_business.py b/backend/app/generator/model/gen_business.py index bba07d97a..830ddf6ed 100644 --- a/backend/app/generator/model/gen_business.py +++ b/backend/app/generator/model/gen_business.py @@ -14,15 +14,15 @@ class GenBusiness(Base): id: Mapped[id_key] = mapped_column(init=False) app_name: Mapped[str] = mapped_column(String(50), comment='应用名称(英文)') - table_name: Mapped[str] = mapped_column(String(255), unique=True, comment='表名称(英文)') + table_name_en: Mapped[str] = mapped_column(String(255), unique=True, comment='表名称(英文)') table_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文)') table_simple_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文简称)') table_comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='表描述') - relate_model_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='关联表名称') - relate_model_fk: Mapped[int | None] = mapped_column(default=None, comment='关联表外键') - schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称') + # relate_model_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='关联表名称') + # relate_model_fk: Mapped[int | None] = mapped_column(default=None, comment='关联表外键') + schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称 (默认为英文表名称)') gen_type: Mapped[int] = mapped_column( - default=1, comment='代码生成方式(1:自定义路径, 2:内部写入,3:tar.gz 压缩包)' + default=1, comment='代码生成方式(0:自定义路径, 1:内部写入,2:tar.gz 压缩包)' ) gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='代码生成路径(默认为项目根路径)') remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注') diff --git a/backend/app/generator/schema/gen_business.py b/backend/app/generator/schema/gen_business.py index 2bafacabc..e66720fef 100644 --- a/backend/app/generator/schema/gen_business.py +++ b/backend/app/generator/schema/gen_business.py @@ -2,21 +2,22 @@ # -*- coding: utf-8 -*- from datetime import datetime -from pydantic import ConfigDict +from pydantic import ConfigDict, Field +from backend.common.enums import GenType from backend.common.schema import SchemaBase class GenBusinessSchemaBase(SchemaBase): app_name: str - table_name: str + table_name_en: str table_name_zh: str table_simple_name_zh: str table_comment: str | None = None - relate_model_name: str | None = None - relate_model_fk: int | None = None + # relate_model_name: str | None = None + # relate_model_fk: int | None = None schema_name: str | None = None - gen_type: int + gen_type: GenType = Field(default=GenType.gz, description='代码生成类型(0:自定义路径 1:内部写入 2:压缩包)') gen_path: str | None = None remark: str | None = None diff --git a/backend/common/enums.py b/backend/common/enums.py index 95cde903d..2e6742e3f 100644 --- a/backend/common/enums.py +++ b/backend/common/enums.py @@ -87,3 +87,11 @@ class UserSocialType(StrEnum): """用户社交类型""" github = 'GitHub' + + +class GenType(IntEnum): + """代码生成类型""" + + custom = 0 + internal = 1 + gz = 2 From 244963202d134c2619319e1fa176ad439ae91eeb Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 29 May 2024 01:28:34 +0800 Subject: [PATCH 18/53] Fix interface services logic --- backend/app/generator/api/v1/gen.py | 8 +++++--- backend/app/generator/crud/crud_gen_business.py | 6 ++++-- backend/app/generator/model/gen_business.py | 1 + backend/app/generator/service/gen_business_service.py | 2 +- backend/app/generator/service/gen_model_service.py | 6 +++--- backend/app/generator/service/gen_service.py | 8 ++++---- backend/templates/py/api.jinja | 4 ++-- 7 files changed, 20 insertions(+), 15 deletions(-) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index b3d529bc4..2cf377302 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -12,19 +12,21 @@ from backend.common.response.response_schema import ResponseModel, response_base from backend.common.security.jwt import DependsJwtAuth from backend.common.security.rbac import DependsRBAC +from backend.utils.serializers import select_list_serialize router = APIRouter() @router.get('/all', summary='获取所有代码生成业务', dependencies=[DependsJwtAuth]) async def get_all_businesses() -> ResponseModel: - data = await gen_business_service.get_all() + businesses = await gen_business_service.get_all() + data = await select_list_serialize(businesses) return await response_base.success(data=data) @router.get('/businesses/{pk}', summary='获取代码生成业务详情', dependencies=[DependsJwtAuth]) async def get_business(pk: Annotated[int, Path(...)]) -> ResponseModel: - data = await gen_service.get_business_and_model(pk) + data = await gen_service.get_business_and_model(pk=pk) return await response_base.success(data=data) @@ -52,7 +54,7 @@ async def delete_business(pk: Annotated[int, Query(...)]) -> ResponseModel: @router.post('/models', summary='创建代码生成模型', dependencies=[DependsRBAC]) async def create_model(obj: CreateGenModelParam) -> ResponseModel: - await gen_model_service.create(obj) + await gen_model_service.create(obj=obj) return await response_base.success() diff --git a/backend/app/generator/crud/crud_gen_business.py b/backend/app/generator/crud/crud_gen_business.py index b0f30e031..6f539c08b 100644 --- a/backend/app/generator/crud/crud_gen_business.py +++ b/backend/app/generator/crud/crud_gen_business.py @@ -1,5 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from typing import Sequence + from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy_crud_plus import CRUDPlus @@ -26,9 +28,9 @@ async def get_by_name(self, db: AsyncSession, name: str) -> GenBusiness | None: :param name: :return: """ - return await self.select_model_by_column(db, 'name', name) + return await self.select_model_by_column(db, 'table_name_en', name) - async def get_all(self, db: AsyncSession): + async def get_all(self, db: AsyncSession) -> Sequence[GenBusiness]: """ 获取所有代码生成业务表 diff --git a/backend/app/generator/model/gen_business.py b/backend/app/generator/model/gen_business.py index 830ddf6ed..631d9c6ac 100644 --- a/backend/app/generator/model/gen_business.py +++ b/backend/app/generator/model/gen_business.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- + from sqlalchemy import String from sqlalchemy.dialects.mysql import LONGTEXT from sqlalchemy.orm import Mapped, mapped_column, relationship diff --git a/backend/app/generator/service/gen_business_service.py b/backend/app/generator/service/gen_business_service.py index 8a638ec33..102d4ac6d 100644 --- a/backend/app/generator/service/gen_business_service.py +++ b/backend/app/generator/service/gen_business_service.py @@ -27,7 +27,7 @@ async def get_all() -> Sequence[GenBusiness]: @staticmethod async def create(*, obj: CreateGenBusinessParam) -> None: async with async_db_session.begin() as db: - business = gen_business_dao.get_by_name(db, obj.table_name) + business = await gen_business_dao.get_by_name(db, obj.table_name_en) if business: raise errors.ForbiddenError(msg='代码生成业务已存在') await gen_business_dao.create(db, obj) diff --git a/backend/app/generator/service/gen_model_service.py b/backend/app/generator/service/gen_model_service.py index 2c62e1710..f73fef03b 100644 --- a/backend/app/generator/service/gen_model_service.py +++ b/backend/app/generator/service/gen_model_service.py @@ -17,7 +17,7 @@ async def get_by_business(*, business_id: int) -> Sequence[GenModel]: return gen_model @staticmethod - async def create(obj: CreateGenModelParam) -> None: + async def create(*, obj: CreateGenModelParam) -> None: async with async_db_session.begin() as db: gen_models = await gen_model_dao.get_by_business_id(obj.gen_business_id) if gen_models: @@ -26,7 +26,7 @@ async def create(obj: CreateGenModelParam) -> None: await gen_model_dao.create(db, obj) @staticmethod - async def update(pk: int, obj: UpdateGenModelParam) -> int: + async def update(*, pk: int, obj: UpdateGenModelParam) -> int: async with async_db_session.begin() as db: gen_models = await gen_model_dao.get_by_business_id(obj.gen_business_id) if gen_models: @@ -36,7 +36,7 @@ async def update(pk: int, obj: UpdateGenModelParam) -> int: return count @staticmethod - async def delete(pk: int) -> int: + async def delete(*, pk: int) -> int: async with async_db_session.begin() as db: count = await gen_model_dao.delete(db, pk) return count diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 07b66cb03..3cce73f79 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -15,7 +15,7 @@ class GenService: @staticmethod async def get_business_and_model(*, pk: int) -> dict: gen_business = await gen_business_service.get(pk=pk) - gen_models = await gen_model_service.get_by_business(pk=pk) + gen_models = await gen_model_service.get_by_business(business_id=pk) business_data = await select_as_dict(gen_business) if gen_models: model_data = await select_list_serialize(gen_models) @@ -23,8 +23,8 @@ async def get_business_and_model(*, pk: int) -> dict: return business_data @staticmethod - async def render_tpl_code(business: GenBusiness) -> dict: - gen_models = await gen_model_dao.get_by_business(business.id) + async def render_tpl_code(*, business: GenBusiness) -> dict: + gen_models = await gen_model_service.get_by_business(business_id=business.id) if not gen_models: raise errors.NotFoundError(msg='代码生成模型表为空') gen_vars = gen_template.get_vars(business, gen_models) @@ -38,7 +38,7 @@ async def preview(self, *, pk: int) -> dict: business = await gen_business_dao.get(db, pk) if not business: raise errors.NotFoundError(msg='业务不存在') - tpl_code_map = await self.render_tpl_code(business) + tpl_code_map = await self.render_tpl_code(business=business) return {tpl.replace('.tpl', ''): code for tpl, code in tpl_code_map.items()} @staticmethod diff --git a/backend/templates/py/api.jinja b/backend/templates/py/api.jinja index e11a9d7a0..494918ccc 100644 --- a/backend/templates/py/api.jinja +++ b/backend/templates/py/api.jinja @@ -4,8 +4,8 @@ from typing import Annotated from fastapi import APIRouter, Depends, Path, Query -from backend.{{ app_name }}.admin.schema.{{ table_name }} import Create{{ schema_name }}Param, Get{{ schema_name }}ListDetails, Update{{ schema_name }}Param -from backend.{{ app_name }}.admin.service.{{ table_name }}_service import {{ table_name }}_service +from backend.app.{{ app_name }}.schema.{{ table_name }} import Create{{ schema_name }}Param, Get{{ schema_name }}ListDetails, Update{{ schema_name }}Param +from backend.app.{{ app_name }}.service.{{ table_name }}_service import {{ table_name }}_service from backend.common.pagination import DependsPagination, paging_data from backend.common.response.response_schema import ResponseModel, response_base from backend.common.security.jwt import DependsJwtAuth From e9672bb88d2cfce6f9fd65809d8f9b79866b0e10 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 29 May 2024 02:56:34 +0800 Subject: [PATCH 19/53] Fix template render --- backend/app/generator/schema/gen_model.py | 11 ++++---- .../generator/service/gen_model_service.py | 2 +- backend/app/generator/service/gen_service.py | 8 ++++-- backend/common/enums.py | 16 +++++++++++ backend/templates/py/api.jinja | 28 +++++++++---------- backend/utils/gen_template.py | 10 ++++--- 6 files changed, 48 insertions(+), 27 deletions(-) diff --git a/backend/app/generator/schema/gen_model.py b/backend/app/generator/schema/gen_model.py index c61b30991..f56c4a31b 100644 --- a/backend/app/generator/schema/gen_model.py +++ b/backend/app/generator/schema/gen_model.py @@ -1,20 +1,21 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from pydantic import ConfigDict +from pydantic import ConfigDict, Field +from backend.common.enums import GenModelType from backend.common.schema import SchemaBase class GenModelSchemaBase(SchemaBase): name: str comment: str | None = None - type: str + type: GenModelType = Field(GenModelType.String, description='模型 column 类型') default: str | None = None sort: int length: int - is_pk: bool - is_nullable: bool - gen_business_id: int | None + is_pk: bool = Field(default=False) + is_nullable: bool = Field(default=False) + gen_business_id: int | None = Field(ge=1) class CreateGenModelParam(GenModelSchemaBase): diff --git a/backend/app/generator/service/gen_model_service.py b/backend/app/generator/service/gen_model_service.py index f73fef03b..08df933fc 100644 --- a/backend/app/generator/service/gen_model_service.py +++ b/backend/app/generator/service/gen_model_service.py @@ -19,7 +19,7 @@ async def get_by_business(*, business_id: int) -> Sequence[GenModel]: @staticmethod async def create(*, obj: CreateGenModelParam) -> None: async with async_db_session.begin() as db: - gen_models = await gen_model_dao.get_by_business_id(obj.gen_business_id) + gen_models = await gen_model_dao.get_by_business_id(db, obj.gen_business_id) if gen_models: if obj.name in [name.name for name in gen_models]: raise errors.ForbiddenError(msg='禁止添加相同列到模型表') diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 3cce73f79..919863cc2 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from backend.app.generator.crud.crud_gen_business import gen_business_dao -from backend.app.generator.crud.crud_gen_model import gen_model_dao from backend.app.generator.model import GenBusiness from backend.app.generator.service.gen_business_service import gen_business_service from backend.app.generator.service.gen_model_service import gen_model_service @@ -30,7 +29,7 @@ async def render_tpl_code(*, business: GenBusiness) -> dict: gen_vars = gen_template.get_vars(business, gen_models) tpl_code_map = {} for tpl_path in gen_template.get_template_paths(): - tpl_code_map[tpl_path] = await gen_template.get_template(tpl_path).render(**gen_vars) + tpl_code_map[tpl_path] = await gen_template.get_template(tpl_path).render_async(**gen_vars) return tpl_code_map async def preview(self, *, pk: int) -> dict: @@ -39,7 +38,10 @@ async def preview(self, *, pk: int) -> dict: if not business: raise errors.NotFoundError(msg='业务不存在') tpl_code_map = await self.render_tpl_code(business=business) - return {tpl.replace('.tpl', ''): code for tpl, code in tpl_code_map.items()} + return { + tpl.replace('.jinja', '.py') if tpl.startswith('py') else tpl.replace('.jinja', ''): code + for tpl, code in tpl_code_map.items() + } @staticmethod async def generate() -> dict: ... diff --git a/backend/common/enums.py b/backend/common/enums.py index 2e6742e3f..b2863a7d7 100644 --- a/backend/common/enums.py +++ b/backend/common/enums.py @@ -95,3 +95,19 @@ class GenType(IntEnum): custom = 0 internal = 1 gz = 2 + + +class GenModelType(StrEnum): + """代码生成模型类型""" + + Integer = 'Integer' + String = 'String' + Text = 'Text' + Float = 'Float' + Boolean = 'Boolean' + Date = 'Date' + DateTime = 'DateTime' + Time = 'Time' + Numeric = 'Numeric' + LargeBinary = 'LargeBinary' + UUID = 'UUID' diff --git a/backend/templates/py/api.jinja b/backend/templates/py/api.jinja index 494918ccc..56169e30f 100644 --- a/backend/templates/py/api.jinja +++ b/backend/templates/py/api.jinja @@ -4,8 +4,8 @@ from typing import Annotated from fastapi import APIRouter, Depends, Path, Query -from backend.app.{{ app_name }}.schema.{{ table_name }} import Create{{ schema_name }}Param, Get{{ schema_name }}ListDetails, Update{{ schema_name }}Param -from backend.app.{{ app_name }}.service.{{ table_name }}_service import {{ table_name }}_service +from backend.app.{{ app_name }}.schema.{{ table_name_en }} import Create{{ schema_name }}Param, Get{{ schema_name }}ListDetails, Update{{ schema_name }}Param +from backend.app.{{ app_name }}.service.{{ table_name_en }}_service import {{ table_name_en }}_service from backend.common.pagination import DependsPagination, paging_data from backend.common.response.response_schema import ResponseModel, response_base from backend.common.security.jwt import DependsJwtAuth @@ -17,9 +17,9 @@ router = APIRouter() @router.get('/{pk}', summary='获取{{ table_simple_name_zh }}详情', dependencies=[DependsJwtAuth]) -async def get_{{ table_name }}(pk: Annotated[int, Path(...)]) -> ResponseModel: - {{ table_name }} = await {{ table_name }}_service.get(pk=pk) - return await response_base.success(data={{ table_name }}) +async def get_{{ table_name_en }}(pk: Annotated[int, Path(...)]) -> ResponseModel: + {{ table_name_en }} = await {{ table_name_en }}_service.get(pk=pk) + return await response_base.success(data={{ table_name_en }}) @router.get( @@ -30,9 +30,9 @@ async def get_{{ table_name }}(pk: Annotated[int, Path(...)]) -> ResponseModel: DependsPagination, ], ) -async def get_pagination_{{ table_name }}(db: CurrentSession) -> ResponseModel: - {{ table_name }}_select = await {{ table_name }}_service.get_select() - page_data = await paging_data(db, {{ table_name }}_select, Get{{ schema_name }}ListDetails) +async def get_pagination_{{ table_name_en }}(db: CurrentSession) -> ResponseModel: + {{ table_name_en }}_select = await {{ table_name_en }}_service.get_select() + page_data = await paging_data(db, {{ table_name_en }}_select, Get{{ schema_name }}ListDetails) return await response_base.success(data=page_data) @@ -44,8 +44,8 @@ async def get_pagination_{{ table_name }}(db: CurrentSession) -> ResponseModel: DependsRBAC, ], ) -async def create_{{ table_name }}(obj: Create{{ schema_name }}Param) -> ResponseModel: - await {{ table_name }}_service.create(obj=obj) +async def create_{{ table_name_en }}(obj: Create{{ schema_name }}Param) -> ResponseModel: + await {{ table_name_en }}_service.create(obj=obj) return await response_base.success() @@ -57,8 +57,8 @@ async def create_{{ table_name }}(obj: Create{{ schema_name }}Param) -> Response DependsRBAC, ], ) -async def update_{{ table_name }}(pk: Annotated[int, Path(...)], obj: Update{{ schema_name }}Param) -> ResponseModel: - count = await {{ table_name }}_service.update(pk=pk, obj=obj) +async def update_{{ table_name_en }}(pk: Annotated[int, Path(...)], obj: Update{{ schema_name }}Param) -> ResponseModel: + count = await {{ table_name_en }}_service.update(pk=pk, obj=obj) if count > 0: return await response_base.success() return await response_base.fail() @@ -72,8 +72,8 @@ async def update_{{ table_name }}(pk: Annotated[int, Path(...)], obj: Update{{ s DependsRBAC, ], ) -async def delete_{{ table_name }}(pk: Annotated[list[int], Query(...)]) -> ResponseModel: - count = await {{ table_name }}_service.delete(pk=pk) +async def delete_{{ table_name_en }}(pk: Annotated[list[int], Query(...)]) -> ResponseModel: + count = await {{ table_name_en }}_service.delete(pk=pk) if count > 0: return await response_base.success() return await response_base.fail() diff --git a/backend/utils/gen_template.py b/backend/utils/gen_template.py index 997cca2f3..b0698be0a 100644 --- a/backend/utils/gen_template.py +++ b/backend/utils/gen_template.py @@ -11,7 +11,9 @@ def __init__(self): self.env = Environment( loader=FileSystemLoader(JINJA2_TEMPLATE_DIR), autoescape=select_autoescape(['html', 'xml', 'jinja']), - keep_trailing_newline=True, + trim_blocks=True, + lstrip_blocks=True, + enable_async=True, ) def get_template(self, jinja_file: str) -> Template: @@ -25,7 +27,7 @@ def get_template(self, jinja_file: str) -> Template: return self.env.get_template(jinja_file) @staticmethod - def get_template_paths(self) -> list[str]: + def get_template_paths() -> list[str]: """ 获取模版文件路径 @@ -52,9 +54,9 @@ def get_vars(business: GenBusiness, models: list[GenModel]) -> dict: pass return { 'app_name': business.app_name, - 'model_name': business.model_name, + 'table_name_en': business.table_name_en, 'schema_name': business.schema_name, - 'model_simple_name_zh': business.model_simple_name_zh, + 'table_simple_name_zh': business.table_simple_name_zh, 'permission_sign': str(business.__tablename__.replace('_', ':')), # TODO } From 1248a936f8f6fc53c940aea63361db5086558a72 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Thu, 30 May 2024 01:06:23 +0800 Subject: [PATCH 20/53] Add more code template --- backend/templates/py/crud.jinja | 66 ++++++++++++++++++++++++++++++ backend/templates/py/schema.jinja | 27 ++++++++++++ backend/templates/py/service.jinja | 50 ++++++++++++++++++++++ 3 files changed, 143 insertions(+) diff --git a/backend/templates/py/crud.jinja b/backend/templates/py/crud.jinja index e69de29bb..d227c249c 100644 --- a/backend/templates/py/crud.jinja +++ b/backend/templates/py/crud.jinja @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from typing import Sequence + +from sqlalchemy import delete, select +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy_crud_plus import CRUDPlus + +from backend.app.{{ app_name }}.model import {{ schema_name }} +from backend.app.{{ app_name }}.schema.api import Create{{ schema_name }}Param, Update{{ schema_name }}Param + + +class CRUDApi(CRUDPlus[{{ schema_name }}]): + async def get(self, db: AsyncSession, pk: int) -> Api | None: + """ + 获取 {{ schema_name }} + + :param db: + :param pk: + :return: + """ + return await self.select_model_by_id(db, pk) + + async def get_all(self, db: AsyncSession) -> Sequence[{{ schema_name }}]: + """ + 获取所有 {{ schema_name }} + + :param db: + :return: + """ + return await self.select_models(db) + + async def create(self, db: AsyncSession, obj_in: Create{{ schema_name }}Param) -> None: + """ + 创建 {{ schema_name }} + + :param db: + :param obj_in: + :return: + """ + await self.create_model(db, obj_in) + + async def update(self, db: AsyncSession, pk: int, obj_in: Update{{ schema_name }}Param) -> int: + """ + 更新 {{ schema_name }} + + :param db: + :param pk: + :param obj_in: + :return: + """ + return await self.update_model(db, pk, obj_in) + + async def delete(self, db: AsyncSession, pk: list[int]) -> int: + """ + 删除 {{ schema_name }} + + :param db: + :param pk: + :return: + """ + {{ table_name_en }}s = await db.execute(delete(self.model).where(self.model.id.in_(pk))) + return {{ table_name_en }}s.rowcount + + +{{ table_name_en }}_dao: CRUD{{ schema_name }} = CRUD{{ schema_name }}({{ schema_name }}) diff --git a/backend/templates/py/schema.jinja b/backend/templates/py/schema.jinja index e69de29bb..60c37abeb 100644 --- a/backend/templates/py/schema.jinja +++ b/backend/templates/py/schema.jinja @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from datetime import datetime + +from pydantic import ConfigDict, Field + +from backend.common.schema import SchemaBase + + +class {{ schema_name }}SchemaBase(SchemaBase): + # todo + + +class Create{{ schema_name }}Param({{ schema_name }}SchemaBase): + pass + + +class Update{{ schema_name }}Param({{ schema_name }}SchemaBase): + pass + + +class Get{{ schema_name }}ListDetails({{ schema_name }}SchemaBase): + model_config = ConfigDict(from_attributes=True) + + id: int + created_time: datetime + updated_time: datetime | None = None diff --git a/backend/templates/py/service.jinja b/backend/templates/py/service.jinja index e69de29bb..0d46ed13f 100644 --- a/backend/templates/py/service.jinja +++ b/backend/templates/py/service.jinja @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from typing import Sequence + +from sqlalchemy import Select + +from backend.app.{{ app_name }}.crud.crud_{{ table_name_en }} import {{ table_name_en }}_dao +from backend.app.{{ app_name }}.model import {{ table_name_en }} +from backend.app.{{ app_name }}.schema.{{ table_name_en }} import Create{{ schema_name }}Param, Update{{ schema_name }}Param +from backend.common.exception import errors +from backend.database.db_mysql import async_db_session + + +class {{ schema_name }}Service: + @staticmethod + async def get(*, pk: int) -> Api: + async with async_db_session() as db: + {{ table_name_en }} = await {{ table_name_en }}_dao.get(db, pk) + if not {{ table_name_en }}: + raise errors.NotFoundError(msg='接口不存在') + return {{ table_name_en }} + + @staticmethod + async def get_all() -> Sequence[Api]: + async with async_db_session() as db: + {{ table_name_en }}s = await {{ table_name_en }}_dao.get_all(db) + return {{ table_name_en }}s + + @staticmethod + async def create(*, obj: Create{{ schema_name }}Param) -> None: + async with async_db_session.begin() as db: + {{ table_name_en }} = await {{ table_name_en }}_dao.get_by_name(db, obj.name) + if {{ table_name_en }}: + raise errors.ForbiddenError(msg='{{ table_simple_name_zh }}已存在') + await {{ table_name_en }}_dao.create(db, obj) + + @staticmethod + async def update(*, pk: int, obj: Update{{ schema_name }}Param) -> int: + async with async_db_session.begin() as db: + count = await {{ table_name_en }}_dao.update(db, pk, obj) + return count + + @staticmethod + async def delete(*, pk: list[int]) -> int: + async with async_db_session.begin() as db: + count = await {{ table_name_en }}_dao.delete(db, pk) + return count + + +{{ table_name_en }}_service = {{ schema_name }}Service() From 95a756cf76b6ed294cac7fee0fd58694de7f3c9b Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Sun, 23 Jun 2024 16:41:56 +0800 Subject: [PATCH 21/53] Add model auto gen template --- backend/templates/py/model.jinja | 14 ++++++++++++++ backend/utils/gen_template.py | 14 ++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/backend/templates/py/model.jinja b/backend/templates/py/model.jinja index e69de29bb..a006848e8 100644 --- a/backend/templates/py/model.jinja +++ b/backend/templates/py/model.jinja @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from sqlalchemy.orm import Mapped, mapped_column + +from backend.common.model import Base, id_key + +class {{ table_name_en }}(Base): + """{{ table_name_zh }}""" + + __tablename__ = '{{ table_name_en }}' + + {% for model in models %} + {{ model.name }}: {% if model.is_nullable %}Mapped[{{ model.type | None }}]{% else %}Mapped[{{ model.type }}]{% endif %} = mapped_column({% if model.type == 'str' %}{{ model_type_map.get(model.type) }}({{ model.length }}){% else %}{{ model_type_map.get(model.type) or model.type}}(){% endif %}, default={{ model.default }}, sort_order={{ model.sort }}, comment={{ model.comment }}) + {% endfor %} diff --git a/backend/utils/gen_template.py b/backend/utils/gen_template.py index b0698be0a..b88238a40 100644 --- a/backend/utils/gen_template.py +++ b/backend/utils/gen_template.py @@ -52,13 +52,23 @@ def get_vars(business: GenBusiness, models: list[GenModel]) -> dict: """ for model in models: pass + # python 类型对应的 sqla 类型 + model_type_map = { + 'str', 'String', + 'float', 'Float', + 'int', 'Integer', + 'bool', 'Boolean', + } return { 'app_name': business.app_name, 'table_name_en': business.table_name_en, - 'schema_name': business.schema_name, + 'table_name_zh': business.table_name_zh, 'table_simple_name_zh': business.table_simple_name_zh, + 'table_comment': business.table_comment, + 'schema_name': business.schema_name, 'permission_sign': str(business.__tablename__.replace('_', ':')), - # TODO + 'models': models, + 'model_type_map': model_type_map, } From c03d6ced9b85891e80c7ced6c47e34a2ef5b164e Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Sun, 23 Jun 2024 16:43:02 +0800 Subject: [PATCH 22/53] Fix model type map style --- backend/utils/gen_template.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/utils/gen_template.py b/backend/utils/gen_template.py index b88238a40..74248de0d 100644 --- a/backend/utils/gen_template.py +++ b/backend/utils/gen_template.py @@ -54,10 +54,10 @@ def get_vars(business: GenBusiness, models: list[GenModel]) -> dict: pass # python 类型对应的 sqla 类型 model_type_map = { - 'str', 'String', - 'float', 'Float', - 'int', 'Integer', - 'bool', 'Boolean', + 'str': 'String', + 'float': 'Float', + 'int': 'Integer', + 'bool': 'Boolean', } return { 'app_name': business.app_name, From 27dee1ccc7ae88242517c2243efc0d32cae22fa3 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 26 Jun 2024 20:00:24 +0800 Subject: [PATCH 23/53] Update schema and model templates --- backend/app/generator/model/gen_model.py | 1 + backend/templates/py/model.jinja | 4 ++-- backend/templates/py/schema.jinja | 7 ++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/backend/app/generator/model/gen_model.py b/backend/app/generator/model/gen_model.py index 3be8a7d67..05561b0c3 100644 --- a/backend/app/generator/model/gen_model.py +++ b/backend/app/generator/model/gen_model.py @@ -22,6 +22,7 @@ class GenModel(DataClassBase): length: Mapped[int] = mapped_column(default=0, comment='列长度') is_pk: Mapped[bool] = mapped_column(default=False, comment='是否主键') is_nullable: Mapped[bool] = mapped_column(default=False, comment='是否可为空') + have_datetime_column: Mapped[bool] = mapped_column(default=True, comment='存在默认时间列') # 代码生成业务model一对多 gen_business_id: Mapped[int | None] = mapped_column( diff --git a/backend/templates/py/model.jinja b/backend/templates/py/model.jinja index a006848e8..4168d3d00 100644 --- a/backend/templates/py/model.jinja +++ b/backend/templates/py/model.jinja @@ -2,9 +2,9 @@ # -*- coding: utf-8 -*- from sqlalchemy.orm import Mapped, mapped_column -from backend.common.model import Base, id_key +from backend.common.model import {% if model.have_datetime_column %}Base{% else %}MappedBase, id_key -class {{ table_name_en }}(Base): +class {{ table_name_en }}({% if model.have_datetime_column %}Base{% else %}MappedBase): """{{ table_name_zh }}""" __tablename__ = '{{ table_name_en }}' diff --git a/backend/templates/py/schema.jinja b/backend/templates/py/schema.jinja index 60c37abeb..7f748626c 100644 --- a/backend/templates/py/schema.jinja +++ b/backend/templates/py/schema.jinja @@ -8,7 +8,10 @@ from backend.common.schema import SchemaBase class {{ schema_name }}SchemaBase(SchemaBase): - # todo + {% for model in models %} + {{ model.name }}: {% if model.nullable %}{{ model.type }} | None = None{% else %}{{ model.type }}{% endif %} + {% endfor %} + class Create{{ schema_name }}Param({{ schema_name }}SchemaBase): @@ -23,5 +26,7 @@ class Get{{ schema_name }}ListDetails({{ schema_name }}SchemaBase): model_config = ConfigDict(from_attributes=True) id: int + {% if model.have_datetime_column %} created_time: datetime updated_time: datetime | None = None + {% endif %} From 1ca84c5749f487dd2faa04126fece9e8eb9eda3f Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 26 Jun 2024 20:13:00 +0800 Subject: [PATCH 24/53] Update gen_model schema --- backend/app/generator/schema/gen_model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/app/generator/schema/gen_model.py b/backend/app/generator/schema/gen_model.py index f56c4a31b..b0d3a800d 100644 --- a/backend/app/generator/schema/gen_model.py +++ b/backend/app/generator/schema/gen_model.py @@ -15,6 +15,7 @@ class GenModelSchemaBase(SchemaBase): length: int is_pk: bool = Field(default=False) is_nullable: bool = Field(default=False) + have_datetime_column: bool = Field(default=False) gen_business_id: int | None = Field(ge=1) From 30f5bda9600d421c70f994f98ef889d93e441d07 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 26 Jun 2024 20:53:22 +0800 Subject: [PATCH 25/53] Fix model template if --- backend/templates/py/model.jinja | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/templates/py/model.jinja b/backend/templates/py/model.jinja index 4168d3d00..8e4eda663 100644 --- a/backend/templates/py/model.jinja +++ b/backend/templates/py/model.jinja @@ -2,9 +2,9 @@ # -*- coding: utf-8 -*- from sqlalchemy.orm import Mapped, mapped_column -from backend.common.model import {% if model.have_datetime_column %}Base{% else %}MappedBase, id_key +from backend.common.model import {% if model.have_datetime_column %}Base{% else %}MappedBase{% endif %}, id_key -class {{ table_name_en }}({% if model.have_datetime_column %}Base{% else %}MappedBase): +class {{ table_name_en }}({% if model.have_datetime_column %}Base{% else %}MappedBase{% endif %}): """{{ table_name_zh }}""" __tablename__ = '{{ table_name_en }}' From 0cc216ee55abe2acfbf58c8942515d88f9f2bcf6 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 26 Jun 2024 23:24:28 +0800 Subject: [PATCH 26/53] Update have_datetime_column field from gen_model to gen_business table --- backend/app/generator/model/gen_business.py | 2 +- backend/app/generator/model/gen_model.py | 1 - backend/app/generator/schema/gen_business.py | 1 + backend/app/generator/schema/gen_model.py | 28 ++++++++++- backend/common/enums.py | 50 +++++++++++++++++--- backend/templates/py/model.jinja | 6 +-- backend/templates/py/schema.jinja | 2 +- backend/utils/gen_template.py | 9 ++-- 8 files changed, 80 insertions(+), 19 deletions(-) diff --git a/backend/app/generator/model/gen_business.py b/backend/app/generator/model/gen_business.py index 631d9c6ac..1f3ee8245 100644 --- a/backend/app/generator/model/gen_business.py +++ b/backend/app/generator/model/gen_business.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- - from sqlalchemy import String from sqlalchemy.dialects.mysql import LONGTEXT from sqlalchemy.orm import Mapped, mapped_column, relationship @@ -22,6 +21,7 @@ class GenBusiness(Base): # relate_model_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='关联表名称') # relate_model_fk: Mapped[int | None] = mapped_column(default=None, comment='关联表外键') schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称 (默认为英文表名称)') + have_datetime_column: Mapped[bool] = mapped_column(default=True, comment='存在默认时间列') gen_type: Mapped[int] = mapped_column( default=1, comment='代码生成方式(0:自定义路径, 1:内部写入,2:tar.gz 压缩包)' ) diff --git a/backend/app/generator/model/gen_model.py b/backend/app/generator/model/gen_model.py index 05561b0c3..3be8a7d67 100644 --- a/backend/app/generator/model/gen_model.py +++ b/backend/app/generator/model/gen_model.py @@ -22,7 +22,6 @@ class GenModel(DataClassBase): length: Mapped[int] = mapped_column(default=0, comment='列长度') is_pk: Mapped[bool] = mapped_column(default=False, comment='是否主键') is_nullable: Mapped[bool] = mapped_column(default=False, comment='是否可为空') - have_datetime_column: Mapped[bool] = mapped_column(default=True, comment='存在默认时间列') # 代码生成业务model一对多 gen_business_id: Mapped[int | None] = mapped_column( diff --git a/backend/app/generator/schema/gen_business.py b/backend/app/generator/schema/gen_business.py index e66720fef..af865ed19 100644 --- a/backend/app/generator/schema/gen_business.py +++ b/backend/app/generator/schema/gen_business.py @@ -17,6 +17,7 @@ class GenBusinessSchemaBase(SchemaBase): # relate_model_name: str | None = None # relate_model_fk: int | None = None schema_name: str | None = None + have_datetime_column: bool = Field(default=False) gen_type: GenType = Field(default=GenType.gz, description='代码生成类型(0:自定义路径 1:内部写入 2:压缩包)') gen_path: str | None = None remark: str | None = None diff --git a/backend/app/generator/schema/gen_model.py b/backend/app/generator/schema/gen_model.py index b0d3a800d..db04a4cc9 100644 --- a/backend/app/generator/schema/gen_model.py +++ b/backend/app/generator/schema/gen_model.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, field_validator from backend.common.enums import GenModelType from backend.common.schema import SchemaBase @@ -15,9 +15,33 @@ class GenModelSchemaBase(SchemaBase): length: int is_pk: bool = Field(default=False) is_nullable: bool = Field(default=False) - have_datetime_column: bool = Field(default=False) gen_business_id: int | None = Field(ge=1) + @field_validator('type') + @classmethod + def sql_type_to_python(cls, v: GenModelType): + type_mapping = { + GenModelType.String: 'str', + GenModelType.TEXT: 'str', + GenModelType.Text: 'str', + GenModelType.LONGTEXT: 'str', + GenModelType.UnicodeText: 'str', + GenModelType.INT: 'int', + GenModelType.INTEGER: 'int', + GenModelType.Integer: 'int', + GenModelType.BigInteger: 'int', + GenModelType.SmallInteger: 'int', + GenModelType.BIGINT: 'int', + GenModelType.SMALLINT: 'int', + GenModelType.FLOAT: 'float', + GenModelType.Float: 'float', + GenModelType.Boolean: 'bool', + GenModelType.DECIMAL: 'decimal', + GenModelType.UUID: 'UUID', + GenModelType.Uuid: 'UUID', + } + return type_mapping.get(v) or v + class CreateGenModelParam(GenModelSchemaBase): pass diff --git a/backend/common/enums.py b/backend/common/enums.py index b2863a7d7..c8c3dabd2 100644 --- a/backend/common/enums.py +++ b/backend/common/enums.py @@ -100,14 +100,52 @@ class GenType(IntEnum): class GenModelType(StrEnum): """代码生成模型类型""" - Integer = 'Integer' - String = 'String' - Text = 'Text' - Float = 'Float' + ARRAY = 'ARRAY' + BIGINT = 'BIGINT' + BigInteger = 'BigInteger' + BINARY = 'BINARY' + BLOB = 'BLOB' + BOOLEAN = 'BOOLEAN' Boolean = 'Boolean' + CHAR = 'CHAR' + CLOB = 'CLOB' + DATE = 'DATE' Date = 'Date' + DATETIME = 'DATETIME' DateTime = 'DateTime' - Time = 'Time' - Numeric = 'Numeric' + DECIMAL = 'DECIMAL' + DOUBLE = 'DOUBLE' + Double = 'Double' + DOUBLE_PRECISION = 'DOUBLE_PRECISION' + Enum = 'Enum' + FLOAT = 'FLOAT' + Float = 'Float' + INT = 'INT' + INTEGER = 'INTEGER' + Integer = 'Integer' + Interval = 'Interval' + JSON = 'JSON' LargeBinary = 'LargeBinary' + LONGTEXT = 'LONGTEXT' + NCHAR = 'NCHAR' + NUMERIC = 'NUMERIC' + Numeric = 'Numeric' + NVARCHAR = 'NVARCHAR' + PickleType = 'PickleType' + REAL = 'REAL' + SMALLINT = 'SMALLINT' + SmallInteger = 'SmallInteger' + String = 'String' + TEXT = 'TEXT' + Text = 'Text' + TIME = 'TIME' + Time = 'Time' + TIMESTAMP = 'TIMESTAMP' + TupleType = 'TupleType' + TypeDecorator = 'TypeDecorator' + Unicode = 'Unicode' + UnicodeText = 'UnicodeText' UUID = 'UUID' + Uuid = 'Uuid' + VARBINARY = 'VARBINARY' + VARCHAR = 'VARCHAR' diff --git a/backend/templates/py/model.jinja b/backend/templates/py/model.jinja index 8e4eda663..2290224d6 100644 --- a/backend/templates/py/model.jinja +++ b/backend/templates/py/model.jinja @@ -2,13 +2,13 @@ # -*- coding: utf-8 -*- from sqlalchemy.orm import Mapped, mapped_column -from backend.common.model import {% if model.have_datetime_column %}Base{% else %}MappedBase{% endif %}, id_key +from backend.common.model import {% if have_datetime_column %}Base{% else %}MappedBase{% endif %}, id_key -class {{ table_name_en }}({% if model.have_datetime_column %}Base{% else %}MappedBase{% endif %}): +class {{ table_name_en }}({% if have_datetime_column %}Base{% else %}MappedBase{% endif %}): """{{ table_name_zh }}""" __tablename__ = '{{ table_name_en }}' {% for model in models %} - {{ model.name }}: {% if model.is_nullable %}Mapped[{{ model.type | None }}]{% else %}Mapped[{{ model.type }}]{% endif %} = mapped_column({% if model.type == 'str' %}{{ model_type_map.get(model.type) }}({{ model.length }}){% else %}{{ model_type_map.get(model.type) or model.type}}(){% endif %}, default={{ model.default }}, sort_order={{ model.sort }}, comment={{ model.comment }}) + {{ model.name }}: {% if model.is_nullable %}Mapped[{{ model.type | None }}]{% else %}Mapped[{{ model.type }}]{% endif %} = mapped_column({% if model.type == 'str' %}{{ model_type_mapping.get(model.type) }}({{ model.length }}){% else %}{{ model_type_mapping.get(model.type) or model.type}}(){% endif %}, default={{ model.default }}, sort_order={{ model.sort }}, comment={{ model.comment }}) {% endfor %} diff --git a/backend/templates/py/schema.jinja b/backend/templates/py/schema.jinja index 7f748626c..9897b71a1 100644 --- a/backend/templates/py/schema.jinja +++ b/backend/templates/py/schema.jinja @@ -26,7 +26,7 @@ class Get{{ schema_name }}ListDetails({{ schema_name }}SchemaBase): model_config = ConfigDict(from_attributes=True) id: int - {% if model.have_datetime_column %} + {% if have_datetime_column %} created_time: datetime updated_time: datetime | None = None {% endif %} diff --git a/backend/utils/gen_template.py b/backend/utils/gen_template.py index 74248de0d..940897519 100644 --- a/backend/utils/gen_template.py +++ b/backend/utils/gen_template.py @@ -50,10 +50,8 @@ def get_vars(business: GenBusiness, models: list[GenModel]) -> dict: :param models: :return: """ - for model in models: - pass - # python 类型对应的 sqla 类型 - model_type_map = { + # python 类型对应的 sqlalchemy 类型 + model_type_mapping = { 'str': 'String', 'float': 'Float', 'int': 'Integer', @@ -66,9 +64,10 @@ def get_vars(business: GenBusiness, models: list[GenModel]) -> dict: 'table_simple_name_zh': business.table_simple_name_zh, 'table_comment': business.table_comment, 'schema_name': business.schema_name, + 'have_datetime_column': business.have_datetime_column, 'permission_sign': str(business.__tablename__.replace('_', ':')), 'models': models, - 'model_type_map': model_type_map, + 'model_type_mapping': model_type_mapping, } From 1320ff95cec9f172a760184ed9442529464e7f6f Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Thu, 27 Jun 2024 00:18:36 +0800 Subject: [PATCH 27/53] Update the name of the zip --- backend/app/generator/model/gen_business.py | 2 +- backend/app/generator/schema/gen_business.py | 2 +- backend/common/enums.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/app/generator/model/gen_business.py b/backend/app/generator/model/gen_business.py index 1f3ee8245..d2df0adcd 100644 --- a/backend/app/generator/model/gen_business.py +++ b/backend/app/generator/model/gen_business.py @@ -23,7 +23,7 @@ class GenBusiness(Base): schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称 (默认为英文表名称)') have_datetime_column: Mapped[bool] = mapped_column(default=True, comment='存在默认时间列') gen_type: Mapped[int] = mapped_column( - default=1, comment='代码生成方式(0:自定义路径, 1:内部写入,2:tar.gz 压缩包)' + default=1, comment='代码生成方式(0:自定义路径, 1:内部写入,2:zip 压缩包)' ) gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='代码生成路径(默认为项目根路径)') remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注') diff --git a/backend/app/generator/schema/gen_business.py b/backend/app/generator/schema/gen_business.py index af865ed19..6dac1b3e5 100644 --- a/backend/app/generator/schema/gen_business.py +++ b/backend/app/generator/schema/gen_business.py @@ -18,7 +18,7 @@ class GenBusinessSchemaBase(SchemaBase): # relate_model_fk: int | None = None schema_name: str | None = None have_datetime_column: bool = Field(default=False) - gen_type: GenType = Field(default=GenType.gz, description='代码生成类型(0:自定义路径 1:内部写入 2:压缩包)') + gen_type: GenType = Field(default=GenType.zip) gen_path: str | None = None remark: str | None = None diff --git a/backend/common/enums.py b/backend/common/enums.py index c8c3dabd2..dd48c0710 100644 --- a/backend/common/enums.py +++ b/backend/common/enums.py @@ -94,7 +94,7 @@ class GenType(IntEnum): custom = 0 internal = 1 - gz = 2 + zip = 2 class GenModelType(StrEnum): From edcc782228ebf1a8c55d47af9199496bf9209701 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Thu, 27 Jun 2024 01:01:44 +0800 Subject: [PATCH 28/53] Update model template vars --- backend/templates/py/model.jinja | 2 +- backend/utils/gen_template.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/backend/templates/py/model.jinja b/backend/templates/py/model.jinja index 2290224d6..f4c14bba2 100644 --- a/backend/templates/py/model.jinja +++ b/backend/templates/py/model.jinja @@ -4,7 +4,7 @@ from sqlalchemy.orm import Mapped, mapped_column from backend.common.model import {% if have_datetime_column %}Base{% else %}MappedBase{% endif %}, id_key -class {{ table_name_en }}({% if have_datetime_column %}Base{% else %}MappedBase{% endif %}): +class {{ table_name_class }}({% if have_datetime_column %}Base{% else %}MappedBase{% endif %}): """{{ table_name_zh }}""" __tablename__ = '{{ table_name_en }}' diff --git a/backend/utils/gen_template.py b/backend/utils/gen_template.py index 940897519..4e4a33865 100644 --- a/backend/utils/gen_template.py +++ b/backend/utils/gen_template.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from jinja2 import Environment, FileSystemLoader, Template, select_autoescape +from pydantic.alias_generators import to_pascal, to_snake from backend.app.generator.model import GenBusiness, GenModel from backend.core.path_conf import JINJA2_TEMPLATE_DIR @@ -59,11 +60,12 @@ def get_vars(business: GenBusiness, models: list[GenModel]) -> dict: } return { 'app_name': business.app_name, - 'table_name_en': business.table_name_en, + 'table_name_en': to_snake(business.table_name_en), + 'table_name_class': to_pascal(business.table_name_en), 'table_name_zh': business.table_name_zh, 'table_simple_name_zh': business.table_simple_name_zh, 'table_comment': business.table_comment, - 'schema_name': business.schema_name, + 'schema_name': to_pascal(business.schema_name), 'have_datetime_column': business.have_datetime_column, 'permission_sign': str(business.__tablename__.replace('_', ':')), 'models': models, From 840b47cedd6bf1307e4ec7ae13a67d16073ee728 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Mon, 1 Jul 2024 02:31:04 +0800 Subject: [PATCH 29/53] Add download generate code --- backend/app/generator/api/v1/gen.py | 13 ++++++++---- backend/app/generator/model/gen_business.py | 4 +--- backend/app/generator/service/gen_service.py | 22 +++++++++++++++++--- backend/common/enums.py | 5 ++--- backend/utils/gen_template.py | 16 ++++++++++++++ 5 files changed, 47 insertions(+), 13 deletions(-) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index 2cf377302..088cc64c5 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -3,6 +3,7 @@ from typing import Annotated from fastapi import APIRouter, Path, Query +from fastapi.responses import StreamingResponse from backend.app.generator.schema.gen_business import CreateGenBusinessParam, UpdateGenBusinessParam from backend.app.generator.schema.gen_model import CreateGenModelParam, UpdateGenModelParam @@ -86,7 +87,11 @@ async def generate_code() -> ResponseModel: return await response_base.success() -@router.post('/download', summary='下载代码', dependencies=[DependsRBAC]) -async def download_code() -> ResponseModel: - await gen_service.download() - return await response_base.success() +@router.post('/download/{pk}', summary='下载代码', dependencies=[DependsRBAC]) +async def download_code(pk: Annotated[int, Path(..., description='业务ID')]): + bio = await gen_service.download(pk=pk) + return StreamingResponse( + bio, + media_type='application/x-zip-compressed', + headers={'Content-Disposition': 'attachment; filename=fba_generator.zip'}, + ) diff --git a/backend/app/generator/model/gen_business.py b/backend/app/generator/model/gen_business.py index d2df0adcd..f50e249cf 100644 --- a/backend/app/generator/model/gen_business.py +++ b/backend/app/generator/model/gen_business.py @@ -22,9 +22,7 @@ class GenBusiness(Base): # relate_model_fk: Mapped[int | None] = mapped_column(default=None, comment='关联表外键') schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称 (默认为英文表名称)') have_datetime_column: Mapped[bool] = mapped_column(default=True, comment='存在默认时间列') - gen_type: Mapped[int] = mapped_column( - default=1, comment='代码生成方式(0:自定义路径, 1:内部写入,2:zip 压缩包)' - ) + gen_type: Mapped[int] = mapped_column(default=1, comment='代码生成方式(0:路径写入,1:zip 压缩包)') gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='代码生成路径(默认为项目根路径)') remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注') # 代码生成业务模型一对多 diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 919863cc2..6b04b5ff7 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -1,5 +1,8 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import io +import zipfile + from backend.app.generator.crud.crud_gen_business import gen_business_dao from backend.app.generator.model import GenBusiness from backend.app.generator.service.gen_business_service import gen_business_service @@ -39,15 +42,28 @@ async def preview(self, *, pk: int) -> dict: raise errors.NotFoundError(msg='业务不存在') tpl_code_map = await self.render_tpl_code(business=business) return { - tpl.replace('.jinja', '.py') if tpl.startswith('py') else tpl.replace('.jinja', ''): code + tpl.replace('.jinja', '.py') if tpl.startswith('py') else ...: code for tpl, code in tpl_code_map.items() } @staticmethod async def generate() -> dict: ... - @staticmethod - async def download() -> dict: ... + async def download(self, *, pk: int) -> io.BytesIO: + async with async_db_session() as db: + business = await gen_business_dao.get(db, pk) + if not business: + raise errors.NotFoundError(msg='业务不存在') + bio = io.BytesIO() + zf = zipfile.ZipFile(bio, 'w') + app_name = business.app_name + tpl_code_map = await self.render_tpl_code(business=business) + for code_path, code in tpl_code_map.items(): + new_code_path = gen_template.get_code_gen_path(code_path, app_name) + zf.writestr(new_code_path, code) + zf.close() + bio.seek(0) + return bio gen_service = GenService() diff --git a/backend/common/enums.py b/backend/common/enums.py index dd48c0710..944c83335 100644 --- a/backend/common/enums.py +++ b/backend/common/enums.py @@ -92,9 +92,8 @@ class UserSocialType(StrEnum): class GenType(IntEnum): """代码生成类型""" - custom = 0 - internal = 1 - zip = 2 + path = 0 + zip = 1 class GenModelType(StrEnum): diff --git a/backend/utils/gen_template.py b/backend/utils/gen_template.py index 4e4a33865..1c3d369c0 100644 --- a/backend/utils/gen_template.py +++ b/backend/utils/gen_template.py @@ -42,6 +42,22 @@ def get_template_paths() -> list[str]: 'py/service.jinja', ] + @staticmethod + def get_code_gen_path(tpl_path: str, app_name: str) -> str: + """ + 获取代码生成路径 + + :return: + """ + code_gen_path_mapping = { + 'py/api.jinja': f'py/{app_name}/api.py', + 'py/crud.jinja': f'py/{app_name}/crud.py', + 'py/model.jinja': f'py/{app_name}/model.py', + 'py/schema.jinja': f'py/{app_name}/schema.py', + 'py/service.jinja': f'py/{app_name}/service.py', + } + return code_gen_path_mapping.get(tpl_path) + @staticmethod def get_vars(business: GenBusiness, models: list[GenModel]) -> dict: """ From 88d51394ab95dbf3326035880295f7b8adb40d51 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Mon, 1 Jul 2024 13:12:16 +0800 Subject: [PATCH 30/53] Add preview code encoding --- backend/app/generator/service/gen_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 6b04b5ff7..32668263d 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -42,7 +42,7 @@ async def preview(self, *, pk: int) -> dict: raise errors.NotFoundError(msg='业务不存在') tpl_code_map = await self.render_tpl_code(business=business) return { - tpl.replace('.jinja', '.py') if tpl.startswith('py') else ...: code + tpl.replace('.jinja', '.py') if tpl.startswith('py') else ...: code.encode("utf-8") for tpl, code in tpl_code_map.items() } From 87bcd67f534a15b3c533771445d34558e23c882e Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Mon, 1 Jul 2024 13:16:18 +0800 Subject: [PATCH 31/53] Update service code template --- backend/templates/py/service.jinja | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/templates/py/service.jinja b/backend/templates/py/service.jinja index 0d46ed13f..29f4dae3d 100644 --- a/backend/templates/py/service.jinja +++ b/backend/templates/py/service.jinja @@ -2,8 +2,6 @@ # -*- coding: utf-8 -*- from typing import Sequence -from sqlalchemy import Select - from backend.app.{{ app_name }}.crud.crud_{{ table_name_en }} import {{ table_name_en }}_dao from backend.app.{{ app_name }}.model import {{ table_name_en }} from backend.app.{{ app_name }}.schema.{{ table_name_en }} import Create{{ schema_name }}Param, Update{{ schema_name }}Param From 4271b4f1714dac1ce297820a428faff949dac2e5 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Mon, 1 Jul 2024 13:17:55 +0800 Subject: [PATCH 32/53] fix lint --- backend/app/generator/service/gen_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 32668263d..c665c6cd0 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -42,7 +42,7 @@ async def preview(self, *, pk: int) -> dict: raise errors.NotFoundError(msg='业务不存在') tpl_code_map = await self.render_tpl_code(business=business) return { - tpl.replace('.jinja', '.py') if tpl.startswith('py') else ...: code.encode("utf-8") + tpl.replace('.jinja', '.py') if tpl.startswith('py') else ...: code.encode('utf-8') for tpl, code in tpl_code_map.items() } From f45faf3245362b83c8187575f4c377bfde56d165 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Tue, 2 Jul 2024 09:40:22 +0800 Subject: [PATCH 33/53] Update github auth tag --- backend/app/admin/api/v1/auth2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/admin/api/v1/auth2/__init__.py b/backend/app/admin/api/v1/auth2/__init__.py index 8c0642a27..5c97a67bc 100644 --- a/backend/app/admin/api/v1/auth2/__init__.py +++ b/backend/app/admin/api/v1/auth2/__init__.py @@ -6,4 +6,4 @@ router = APIRouter(prefix='/auth2') -router.include_router(github_router, prefix='/github', tags=['OAuth2']) +router.include_router(github_router, prefix='/github', tags=['GitHub OAuth2']) From c16b364cc6e6c76c0cb1838fd54f0959a545cb04 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Tue, 2 Jul 2024 12:38:41 +0800 Subject: [PATCH 34/53] Update requirements --- backend/pdm.lock | 160 +++++++++++++++++++-------------------- backend/pyproject.toml | 6 +- backend/requirements.txt | 6 +- 3 files changed, 84 insertions(+), 88 deletions(-) diff --git a/backend/pdm.lock b/backend/pdm.lock index 1554e08fc..2e15836ce 100644 --- a/backend/pdm.lock +++ b/backend/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "lint", "deploy"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:daef4659b78e2f299c142b06a0d20977bee23bd168c00d37d5a29e4bb9aae0aa" +content_hash = "sha256:931bd86972c2e8eb16efd53edb7dab0753b4ece67357035b00fd0ef3f0582418" [[package]] name = "alembic" @@ -1275,99 +1275,95 @@ files = [ [[package]] name = "pydantic" -version = "2.5.2" -requires_python = ">=3.7" +version = "2.8.0" +requires_python = ">=3.8" summary = "Data validation using Python type hints" groups = ["default"] dependencies = [ "annotated-types>=0.4.0", - "pydantic-core==2.14.5", - "typing-extensions>=4.6.1", + "pydantic-core==2.20.0", + "typing-extensions>=4.12.2; python_version >= \"3.13\"", + "typing-extensions>=4.6.1; python_version < \"3.13\"", ] files = [ - {file = "pydantic-2.5.2-py3-none-any.whl", hash = "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0"}, - {file = "pydantic-2.5.2.tar.gz", hash = "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"}, + {file = "pydantic-2.8.0-py3-none-any.whl", hash = "sha256:ead4f3a1e92386a734ca1411cb25d94147cf8778ed5be6b56749047676d6364e"}, + {file = "pydantic-2.8.0.tar.gz", hash = "sha256:d970ffb9d030b710795878940bd0489842c638e7252fc4a19c3ae2f7da4d6141"}, ] [[package]] name = "pydantic-core" -version = "2.14.5" -requires_python = ">=3.7" -summary = "" +version = "2.20.0" +requires_python = ">=3.8" +summary = "Core functionality for Pydantic validation and serialization" groups = ["default"] dependencies = [ "typing-extensions!=4.7.0,>=4.6.0", ] files = [ - {file = "pydantic_core-2.14.5-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd"}, - {file = "pydantic_core-2.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66"}, - {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997"}, - {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093"}, - {file = "pydantic_core-2.14.5-cp310-none-win32.whl", hash = "sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720"}, - {file = "pydantic_core-2.14.5-cp310-none-win_amd64.whl", hash = "sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b"}, - {file = "pydantic_core-2.14.5-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459"}, - {file = "pydantic_core-2.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4"}, - {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada"}, - {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda"}, - {file = "pydantic_core-2.14.5-cp311-none-win32.whl", hash = "sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651"}, - {file = "pydantic_core-2.14.5-cp311-none-win_amd64.whl", hash = "sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077"}, - {file = "pydantic_core-2.14.5-cp311-none-win_arm64.whl", hash = "sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf"}, - {file = "pydantic_core-2.14.5-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093"}, - {file = "pydantic_core-2.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e"}, - {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69"}, - {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d"}, - {file = "pydantic_core-2.14.5-cp312-none-win32.whl", hash = "sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260"}, - {file = "pydantic_core-2.14.5-cp312-none-win_amd64.whl", hash = "sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36"}, - {file = "pydantic_core-2.14.5-cp312-none-win_arm64.whl", hash = "sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3"}, - {file = "pydantic_core-2.14.5.tar.gz", hash = "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71"}, + {file = "pydantic_core-2.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e9dcd7fb34f7bfb239b5fa420033642fff0ad676b765559c3737b91f664d4fa9"}, + {file = "pydantic_core-2.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:649a764d9b0da29816889424697b2a3746963ad36d3e0968784ceed6e40c6355"}, + {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7701df088d0b05f3460f7ba15aec81ac8b0fb5690367dfd072a6c38cf5b7fdb5"}, + {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab760f17c3e792225cdaef31ca23c0aea45c14ce80d8eff62503f86a5ab76bff"}, + {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb1ad5b4d73cde784cf64580166568074f5ccd2548d765e690546cff3d80937d"}, + {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b81ec2efc04fc1dbf400647d4357d64fb25543bae38d2d19787d69360aad21c9"}, + {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4a9732a5cad764ba37f3aa873dccb41b584f69c347a57323eda0930deec8e10"}, + {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6dc85b9e10cc21d9c1055f15684f76fa4facadddcb6cd63abab702eb93c98943"}, + {file = "pydantic_core-2.20.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:21d9f7e24f63fdc7118e6cc49defaab8c1d27570782f7e5256169d77498cf7c7"}, + {file = "pydantic_core-2.20.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8b315685832ab9287e6124b5d74fc12dda31e6421d7f6b08525791452844bc2d"}, + {file = "pydantic_core-2.20.0-cp310-none-win32.whl", hash = "sha256:c3dc8ec8b87c7ad534c75b8855168a08a7036fdb9deeeed5705ba9410721c84d"}, + {file = "pydantic_core-2.20.0-cp310-none-win_amd64.whl", hash = "sha256:85770b4b37bb36ef93a6122601795231225641003e0318d23c6233c59b424279"}, + {file = "pydantic_core-2.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:58e251bb5a5998f7226dc90b0b753eeffa720bd66664eba51927c2a7a2d5f32c"}, + {file = "pydantic_core-2.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:78d584caac52c24240ef9ecd75de64c760bbd0e20dbf6973631815e3ef16ef8b"}, + {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5084ec9721f82bef5ff7c4d1ee65e1626783abb585f8c0993833490b63fe1792"}, + {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d0f52684868db7c218437d260e14d37948b094493f2646f22d3dda7229bbe3f"}, + {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1def125d59a87fe451212a72ab9ed34c118ff771e5473fef4f2f95d8ede26d75"}, + {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b34480fd6778ab356abf1e9086a4ced95002a1e195e8d2fd182b0def9d944d11"}, + {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d42669d319db366cb567c3b444f43caa7ffb779bf9530692c6f244fc635a41eb"}, + {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:53b06aea7a48919a254b32107647be9128c066aaa6ee6d5d08222325f25ef175"}, + {file = "pydantic_core-2.20.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1f038156b696a1c39d763b2080aeefa87ddb4162c10aa9fabfefffc3dd8180fa"}, + {file = "pydantic_core-2.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3f0f3a4a23717280a5ee3ac4fb1f81d6fde604c9ec5100f7f6f987716bb8c137"}, + {file = "pydantic_core-2.20.0-cp311-none-win32.whl", hash = "sha256:316fe7c3fec017affd916a0c83d6f1ec697cbbbdf1124769fa73328e7907cc2e"}, + {file = "pydantic_core-2.20.0-cp311-none-win_amd64.whl", hash = "sha256:2d06a7fa437f93782e3f32d739c3ec189f82fca74336c08255f9e20cea1ed378"}, + {file = "pydantic_core-2.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:d6f8c49657f3eb7720ed4c9b26624063da14937fc94d1812f1e04a2204db3e17"}, + {file = "pydantic_core-2.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad1bd2f377f56fec11d5cfd0977c30061cd19f4fa199bf138b200ec0d5e27eeb"}, + {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed741183719a5271f97d93bbcc45ed64619fa38068aaa6e90027d1d17e30dc8d"}, + {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d82e5ed3a05f2dcb89c6ead2fd0dbff7ac09bc02c1b4028ece2d3a3854d049ce"}, + {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2ba34a099576234671f2e4274e5bc6813b22e28778c216d680eabd0db3f7dad"}, + {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:879ae6bb08a063b3e1b7ac8c860096d8fd6b48dd9b2690b7f2738b8c835e744b"}, + {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b0eefc7633a04c0694340aad91fbfd1986fe1a1e0c63a22793ba40a18fcbdc8"}, + {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73deadd6fd8a23e2f40b412b3ac617a112143c8989a4fe265050fd91ba5c0608"}, + {file = "pydantic_core-2.20.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:35681445dc85446fb105943d81ae7569aa7e89de80d1ca4ac3229e05c311bdb1"}, + {file = "pydantic_core-2.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0f6dd3612a3b9f91f2e63924ea18a4476656c6d01843ca20a4c09e00422195af"}, + {file = "pydantic_core-2.20.0-cp312-none-win32.whl", hash = "sha256:7e37b6bb6e90c2b8412b06373c6978d9d81e7199a40e24a6ef480e8acdeaf918"}, + {file = "pydantic_core-2.20.0-cp312-none-win_amd64.whl", hash = "sha256:7d4df13d1c55e84351fab51383520b84f490740a9f1fec905362aa64590b7a5d"}, + {file = "pydantic_core-2.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d43e7ab3b65e4dc35a7612cfff7b0fd62dce5bc11a7cd198310b57f39847fd6c"}, + {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b6a24d7b5893392f2b8e3b7a0031ae3b14c6c1942a4615f0d8794fdeeefb08b"}, + {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b2f13c3e955a087c3ec86f97661d9f72a76e221281b2262956af381224cfc243"}, + {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72432fd6e868c8d0a6849869e004b8bcae233a3c56383954c228316694920b38"}, + {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d70a8ff2d4953afb4cbe6211f17268ad29c0b47e73d3372f40e7775904bc28fc"}, + {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e49524917b8d3c2f42cd0d2df61178e08e50f5f029f9af1f402b3ee64574392"}, + {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4f0f71653b1c1bad0350bc0b4cc057ab87b438ff18fa6392533811ebd01439c"}, + {file = "pydantic_core-2.20.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:16197e6f4fdecb9892ed2436e507e44f0a1aa2cff3b9306d1c879ea2f9200997"}, + {file = "pydantic_core-2.20.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:763602504bf640b3ded3bba3f8ed8a1cc2fc6a87b8d55c1c5689f428c49c947e"}, + {file = "pydantic_core-2.20.0-cp313-none-win32.whl", hash = "sha256:a3f243f318bd9523277fa123b3163f4c005a3e8619d4b867064de02f287a564d"}, + {file = "pydantic_core-2.20.0-cp313-none-win_amd64.whl", hash = "sha256:03aceaf6a5adaad3bec2233edc5a7905026553916615888e53154807e404545c"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cafde15a6f7feaec2f570646e2ffc5b73412295d29134a29067e70740ec6ee20"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2aec8eeea0b08fd6bc2213d8e86811a07491849fd3d79955b62d83e32fa2ad5f"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:840200827984f1c4e114008abc2f5ede362d6e11ed0b5931681884dd41852ff1"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ea1d8b7df522e5ced34993c423c3bf3735c53df8b2a15688a2f03a7d678800"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5b8376a867047bf08910573deb95d3c8dfb976eb014ee24f3b5a61ccc5bee1b"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d08264b4460326cefacc179fc1411304d5af388a79910832835e6f641512358b"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7a3639011c2e8a9628466f616ed7fb413f30032b891898e10895a0a8b5857d6c"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05e83ce2f7eba29e627dd8066aa6c4c0269b2d4f889c0eba157233a353053cea"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:603a843fea76a595c8f661cd4da4d2281dff1e38c4a836a928eac1a2f8fe88e4"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac76f30d5d3454f4c28826d891fe74d25121a346c69523c9810ebba43f3b1cec"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e3b1d4b1b3f6082849f9b28427ef147a5b46a6132a3dbaf9ca1baa40c88609"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2761f71faed820e25ec62eacba670d1b5c2709bb131a19fcdbfbb09884593e5a"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a0586cddbf4380e24569b8a05f234e7305717cc8323f50114dfb2051fcbce2a3"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:b8c46a8cf53e849eea7090f331ae2202cd0f1ceb090b00f5902c423bd1e11805"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b4a085bd04af7245e140d1b95619fe8abb445a3d7fdf219b3f80c940853268ef"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:116b326ac82c8b315e7348390f6d30bcfe6e688a7d3f1de50ff7bcc2042a23c2"}, + {file = "pydantic_core-2.20.0.tar.gz", hash = "sha256:366be8e64e0cb63d87cf79b4e1765c0703dd6313c729b22e7b9e378db6b96877"}, ] [[package]] @@ -1810,13 +1806,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.10.0" +version = "4.12.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" groups = ["default"] files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] diff --git a/backend/pyproject.toml b/backend/pyproject.toml index c28713013..0bd1e4fb6 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -28,20 +28,20 @@ dependencies = [ "phonenumbers==8.13.27", "pre-commit==3.2.2", "psutil==5.9.6", - "pydantic==2.5.2", + "pydantic==2.8.0", "pytest==7.2.2", "pytest-pretty==1.2.0", "python-jose==3.3.0", "pytz==2023.3", "redis[hiredis]==5.0.1", - "SQLAlchemy>=2.0.23", + "SQLAlchemy==2.0.30", "user-agents==2.2.0", "uvicorn[standard]==0.29.0", "XdbSearchIP==1.0.2", "fastapi_oauth20>=0.0.1a1", "flower==2.0.1", "sqlalchemy-crud-plus==0.0.2", - "jinja2>=3.1.3", + "jinja2==3.1.4", ] requires-python = ">=3.10" readme = "README.md" diff --git a/backend/requirements.txt b/backend/requirements.txt index 3c7a0c729..2dd8e6d48 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -71,8 +71,8 @@ prompt-toolkit==3.0.43 psutil==5.9.6 pyasn1==0.5.1 pycparser==2.21 -pydantic==2.5.2 -pydantic-core==2.14.5 +pydantic==2.8.0 +pydantic-core==2.20.0 pydantic-extra-types==2.2.0 pydantic-settings==2.1.0 pygments==2.17.2 @@ -100,7 +100,7 @@ supervisor==4.2.5 tomli==2.0.1; python_version < "3.11" tornado==6.4 typer==0.12.3 -typing-extensions==4.10.0 +typing-extensions==4.12.2 tzdata==2024.1 ua-parser==0.18.0 ujson==5.9.0 From 9764c385299e63f1ec54ce8ade7db2804dcf2174 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Tue, 2 Jul 2024 15:15:02 +0800 Subject: [PATCH 35/53] Add code generation write --- backend/app/generator/model/gen_business.py | 4 +-- backend/app/generator/schema/gen_business.py | 3 +-- backend/app/generator/service/gen_service.py | 27 +++++++++++++++++--- backend/common/enums.py | 7 ----- backend/pdm.lock | 13 +++++++++- backend/pyproject.toml | 1 + backend/requirements.txt | 1 + backend/utils/gen_template.py | 12 ++++----- 8 files changed, 47 insertions(+), 21 deletions(-) diff --git a/backend/app/generator/model/gen_business.py b/backend/app/generator/model/gen_business.py index f50e249cf..43ded60fd 100644 --- a/backend/app/generator/model/gen_business.py +++ b/backend/app/generator/model/gen_business.py @@ -22,8 +22,8 @@ class GenBusiness(Base): # relate_model_fk: Mapped[int | None] = mapped_column(default=None, comment='关联表外键') schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称 (默认为英文表名称)') have_datetime_column: Mapped[bool] = mapped_column(default=True, comment='存在默认时间列') - gen_type: Mapped[int] = mapped_column(default=1, comment='代码生成方式(0:路径写入,1:zip 压缩包)') - gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='代码生成路径(默认为项目根路径)') + api_version: Mapped[str] = mapped_column(default='v1', comment='代码生成 api 版本,默认为 v1') + gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='代码生成路径(默认为 app 根路径)') remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注') # 代码生成业务模型一对多 gen_model: Mapped[list['GenModel']] = relationship(init=False, back_populates='gen_business') # noqa: F821 diff --git a/backend/app/generator/schema/gen_business.py b/backend/app/generator/schema/gen_business.py index 6dac1b3e5..e0eb26911 100644 --- a/backend/app/generator/schema/gen_business.py +++ b/backend/app/generator/schema/gen_business.py @@ -4,7 +4,6 @@ from pydantic import ConfigDict, Field -from backend.common.enums import GenType from backend.common.schema import SchemaBase @@ -18,7 +17,7 @@ class GenBusinessSchemaBase(SchemaBase): # relate_model_fk: int | None = None schema_name: str | None = None have_datetime_column: bool = Field(default=False) - gen_type: GenType = Field(default=GenType.zip) + api_version: str = Field(default='v1') gen_path: str | None = None remark: str | None = None diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index c665c6cd0..140a9b9e3 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -1,13 +1,17 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import io +import os.path import zipfile +import aiofiles + from backend.app.generator.crud.crud_gen_business import gen_business_dao from backend.app.generator.model import GenBusiness from backend.app.generator.service.gen_business_service import gen_business_service from backend.app.generator.service.gen_model_service import gen_model_service from backend.common.exception import errors +from backend.core.path_conf import BasePath from backend.database.db_mysql import async_db_session from backend.utils.gen_template import gen_template from backend.utils.serializers import select_as_dict, select_list_serialize @@ -47,7 +51,24 @@ async def preview(self, *, pk: int) -> dict: } @staticmethod - async def generate() -> dict: ... + async def generate(self, *, pk: int) -> None: + async with async_db_session() as db: + business = await gen_business_dao.get(db, pk) + if not business: + raise errors.NotFoundError(msg='业务不存在') + tpl_code_map = await self.render_tpl_code(business=business) + gen_path = business.gen_path + if not gen_path: + gen_path = os.path.join(BasePath, 'app') + for tpl_path, code in tpl_code_map.items(): + code_filepath = os.path.join( + gen_path, + gen_template.get_code_gen_path(tpl_path, business.app_name, business.api_version).split('/')[1:], + ) + if not os.path.exists(code_filepath): + os.makedirs(code_filepath, exist_ok=True) + async with aiofiles.open(code_filepath, 'w', encoding='utf-8') as f: + await f.write(code) async def download(self, *, pk: int) -> io.BytesIO: async with async_db_session() as db: @@ -58,8 +79,8 @@ async def download(self, *, pk: int) -> io.BytesIO: zf = zipfile.ZipFile(bio, 'w') app_name = business.app_name tpl_code_map = await self.render_tpl_code(business=business) - for code_path, code in tpl_code_map.items(): - new_code_path = gen_template.get_code_gen_path(code_path, app_name) + for tpl_path, code in tpl_code_map.items(): + new_code_path = gen_template.get_code_gen_path(tpl_path, app_name, business.api_version) zf.writestr(new_code_path, code) zf.close() bio.seek(0) diff --git a/backend/common/enums.py b/backend/common/enums.py index 944c83335..e1292df33 100644 --- a/backend/common/enums.py +++ b/backend/common/enums.py @@ -89,13 +89,6 @@ class UserSocialType(StrEnum): github = 'GitHub' -class GenType(IntEnum): - """代码生成类型""" - - path = 0 - zip = 1 - - class GenModelType(StrEnum): """代码生成模型类型""" diff --git a/backend/pdm.lock b/backend/pdm.lock index 2e15836ce..2152e048a 100644 --- a/backend/pdm.lock +++ b/backend/pdm.lock @@ -5,7 +5,18 @@ groups = ["default", "lint", "deploy"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:931bd86972c2e8eb16efd53edb7dab0753b4ece67357035b00fd0ef3f0582418" +content_hash = "sha256:04116a878cef951f3a27a703a9e0f80caaa1db69b3984613b17f8860d0f77fbc" + +[[package]] +name = "aiofiles" +version = "24.1.0" +requires_python = ">=3.8" +summary = "File support for asyncio." +groups = ["default"] +files = [ + {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, + {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, +] [[package]] name = "alembic" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 0bd1e4fb6..47350a9f2 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -42,6 +42,7 @@ dependencies = [ "flower==2.0.1", "sqlalchemy-crud-plus==0.0.2", "jinja2==3.1.4", + "aiofiles==24.1.0", ] requires-python = ">=3.10" readme = "README.md" diff --git a/backend/requirements.txt b/backend/requirements.txt index 2dd8e6d48..66657d208 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,6 +1,7 @@ # This file is @generated by PDM. # Please do not edit it manually. +aiofiles==24.1.0 alembic==1.13.1 amqp==5.2.0 annotated-types==0.6.0 diff --git a/backend/utils/gen_template.py b/backend/utils/gen_template.py index 1c3d369c0..346829c26 100644 --- a/backend/utils/gen_template.py +++ b/backend/utils/gen_template.py @@ -43,18 +43,18 @@ def get_template_paths() -> list[str]: ] @staticmethod - def get_code_gen_path(tpl_path: str, app_name: str) -> str: + def get_code_gen_path(tpl_path: str, app_name: str, api_version: str) -> str: """ 获取代码生成路径 :return: """ code_gen_path_mapping = { - 'py/api.jinja': f'py/{app_name}/api.py', - 'py/crud.jinja': f'py/{app_name}/crud.py', - 'py/model.jinja': f'py/{app_name}/model.py', - 'py/schema.jinja': f'py/{app_name}/schema.py', - 'py/service.jinja': f'py/{app_name}/service.py', + 'py/api.jinja': f'py/{app_name}/api/{api_version}/api.py', + 'py/crud.jinja': f'py/{app_name}/crud/crud.py', + 'py/model.jinja': f'py/{app_name}/model/model.py', + 'py/schema.jinja': f'py/{app_name}/schema/schema.py', + 'py/service.jinja': f'py/{app_name}/service/service.py', } return code_gen_path_mapping.get(tpl_path) From 799224c1ce4f4f8ff2ec315d5c4869921a85fdea Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Tue, 2 Jul 2024 15:28:03 +0800 Subject: [PATCH 36/53] Update generate interface and zip conf --- backend/app/generator/api/v1/gen.py | 9 +++++---- backend/app/generator/conf.py | 3 +++ backend/app/generator/model/gen_business.py | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index 088cc64c5..c397f2da3 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -5,6 +5,7 @@ from fastapi import APIRouter, Path, Query from fastapi.responses import StreamingResponse +from backend.app.generator.conf import generator_settings from backend.app.generator.schema.gen_business import CreateGenBusinessParam, UpdateGenBusinessParam from backend.app.generator.schema.gen_model import CreateGenModelParam, UpdateGenModelParam from backend.app.generator.service.gen_business_service import gen_business_service @@ -81,9 +82,9 @@ async def preview_code(pk: Annotated[int, Path(..., description='业务ID')]) -> return await response_base.success(data=data) -@router.post('/generate', summary='生成代码', description='此接口会写入文件,请谨慎操作', dependencies=[DependsRBAC]) -async def generate_code() -> ResponseModel: - await gen_service.generate() +@router.post('/generate/{pk}', summary='生成代码', description='文件磁盘写入,请谨慎操作', dependencies=[DependsRBAC]) +async def generate_code(pk: Annotated[int, Path(..., description='业务ID')]) -> ResponseModel: + await gen_service.generate(pk=pk) return await response_base.success() @@ -93,5 +94,5 @@ async def download_code(pk: Annotated[int, Path(..., description='业务ID')]): return StreamingResponse( bio, media_type='application/x-zip-compressed', - headers={'Content-Disposition': 'attachment; filename=fba_generator.zip'}, + headers={'Content-Disposition': f'attachment; filename={generator_settings.ZIP_FILENAME}.zip'}, ) diff --git a/backend/app/generator/conf.py b/backend/app/generator/conf.py index 6fec85e0f..a30213525 100644 --- a/backend/app/generator/conf.py +++ b/backend/app/generator/conf.py @@ -12,6 +12,9 @@ class GeneratorSettings(BaseSettings): model_config = SettingsConfigDict(env_file=f'{BasePath}/.env', env_file_encoding='utf-8', extra='ignore') + # 代码下载 + ZIP_FILENAME: str = 'fba_generator' + @lru_cache def get_generator_settings() -> GeneratorSettings: diff --git a/backend/app/generator/model/gen_business.py b/backend/app/generator/model/gen_business.py index 43ded60fd..39e480146 100644 --- a/backend/app/generator/model/gen_business.py +++ b/backend/app/generator/model/gen_business.py @@ -22,7 +22,7 @@ class GenBusiness(Base): # relate_model_fk: Mapped[int | None] = mapped_column(default=None, comment='关联表外键') schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称 (默认为英文表名称)') have_datetime_column: Mapped[bool] = mapped_column(default=True, comment='存在默认时间列') - api_version: Mapped[str] = mapped_column(default='v1', comment='代码生成 api 版本,默认为 v1') + api_version: Mapped[str] = mapped_column(String(20), default='v1', comment='代码生成 api 版本,默认为 v1') gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='代码生成路径(默认为 app 根路径)') remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注') # 代码生成业务模型一对多 From fccc2f7faf6415ce8486bc79c85064795ebbd858 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Tue, 2 Jul 2024 21:34:11 +0800 Subject: [PATCH 37/53] Optimize templates details --- backend/app/generator/service/gen_service.py | 13 +++++++------ backend/templates/py/crud.jinja | 10 +++++----- backend/templates/py/model.jinja | 2 ++ backend/templates/py/service.jinja | 6 +++--- backend/utils/gen_template.py | 14 ++++++++------ 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 140a9b9e3..441a5414e 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -4,6 +4,8 @@ import os.path import zipfile +from pathlib import Path + import aiofiles from backend.app.generator.crud.crud_gen_business import gen_business_dao @@ -50,7 +52,6 @@ async def preview(self, *, pk: int) -> dict: for tpl, code in tpl_code_map.items() } - @staticmethod async def generate(self, *, pk: int) -> None: async with async_db_session() as db: business = await gen_business_dao.get(db, pk) @@ -63,10 +64,11 @@ async def generate(self, *, pk: int) -> None: for tpl_path, code in tpl_code_map.items(): code_filepath = os.path.join( gen_path, - gen_template.get_code_gen_path(tpl_path, business.app_name, business.api_version).split('/')[1:], + *gen_template.get_code_gen_path(tpl_path, business).split('/')[1:], ) - if not os.path.exists(code_filepath): - os.makedirs(code_filepath, exist_ok=True) + code_folder = Path(str(code_filepath)).parent + if not code_folder.exists(): + code_folder.mkdir(parents=True, exist_ok=True) async with aiofiles.open(code_filepath, 'w', encoding='utf-8') as f: await f.write(code) @@ -77,10 +79,9 @@ async def download(self, *, pk: int) -> io.BytesIO: raise errors.NotFoundError(msg='业务不存在') bio = io.BytesIO() zf = zipfile.ZipFile(bio, 'w') - app_name = business.app_name tpl_code_map = await self.render_tpl_code(business=business) for tpl_path, code in tpl_code_map.items(): - new_code_path = gen_template.get_code_gen_path(tpl_path, app_name, business.api_version) + new_code_path = gen_template.get_code_gen_path(tpl_path, business) zf.writestr(new_code_path, code) zf.close() bio.seek(0) diff --git a/backend/templates/py/crud.jinja b/backend/templates/py/crud.jinja index d227c249c..bd2bcc8dc 100644 --- a/backend/templates/py/crud.jinja +++ b/backend/templates/py/crud.jinja @@ -2,16 +2,16 @@ # -*- coding: utf-8 -*- from typing import Sequence -from sqlalchemy import delete, select +from sqlalchemy import delete from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy_crud_plus import CRUDPlus -from backend.app.{{ app_name }}.model import {{ schema_name }} -from backend.app.{{ app_name }}.schema.api import Create{{ schema_name }}Param, Update{{ schema_name }}Param +from backend.app.{{ app_name }}.model import {{ table_name_class }} +from backend.app.{{ app_name }}.schema.{{ table_name_en }} import Create{{ schema_name }}Param, Update{{ schema_name }}Param -class CRUDApi(CRUDPlus[{{ schema_name }}]): - async def get(self, db: AsyncSession, pk: int) -> Api | None: +class CRUD{{ table_name_class }}(CRUDPlus[{{ schema_name }}]): + async def get(self, db: AsyncSession, pk: int) -> {{ table_name_class }} | None: """ 获取 {{ schema_name }} diff --git a/backend/templates/py/model.jinja b/backend/templates/py/model.jinja index f4c14bba2..20acda476 100644 --- a/backend/templates/py/model.jinja +++ b/backend/templates/py/model.jinja @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from sqlalchemy import String from sqlalchemy.orm import Mapped, mapped_column from backend.common.model import {% if have_datetime_column %}Base{% else %}MappedBase{% endif %}, id_key @@ -9,6 +10,7 @@ class {{ table_name_class }}({% if have_datetime_column %}Base{% else %}MappedBa __tablename__ = '{{ table_name_en }}' + id: Mapped[id_key] = mapped_column(init=False) {% for model in models %} {{ model.name }}: {% if model.is_nullable %}Mapped[{{ model.type | None }}]{% else %}Mapped[{{ model.type }}]{% endif %} = mapped_column({% if model.type == 'str' %}{{ model_type_mapping.get(model.type) }}({{ model.length }}){% else %}{{ model_type_mapping.get(model.type) or model.type}}(){% endif %}, default={{ model.default }}, sort_order={{ model.sort }}, comment={{ model.comment }}) {% endfor %} diff --git a/backend/templates/py/service.jinja b/backend/templates/py/service.jinja index 29f4dae3d..664c034f3 100644 --- a/backend/templates/py/service.jinja +++ b/backend/templates/py/service.jinja @@ -3,7 +3,7 @@ from typing import Sequence from backend.app.{{ app_name }}.crud.crud_{{ table_name_en }} import {{ table_name_en }}_dao -from backend.app.{{ app_name }}.model import {{ table_name_en }} +from backend.app.{{ app_name }}.model import {{ table_name_class }} from backend.app.{{ app_name }}.schema.{{ table_name_en }} import Create{{ schema_name }}Param, Update{{ schema_name }}Param from backend.common.exception import errors from backend.database.db_mysql import async_db_session @@ -11,7 +11,7 @@ from backend.database.db_mysql import async_db_session class {{ schema_name }}Service: @staticmethod - async def get(*, pk: int) -> Api: + async def get(*, pk: int) -> {{ table_name_class }}: async with async_db_session() as db: {{ table_name_en }} = await {{ table_name_en }}_dao.get(db, pk) if not {{ table_name_en }}: @@ -19,7 +19,7 @@ class {{ schema_name }}Service: return {{ table_name_en }} @staticmethod - async def get_all() -> Sequence[Api]: + async def get_all() -> Sequence[{{ table_name_class }}]: async with async_db_session() as db: {{ table_name_en }}s = await {{ table_name_en }}_dao.get_all(db) return {{ table_name_en }}s diff --git a/backend/utils/gen_template.py b/backend/utils/gen_template.py index 346829c26..567d2aef3 100644 --- a/backend/utils/gen_template.py +++ b/backend/utils/gen_template.py @@ -43,18 +43,20 @@ def get_template_paths() -> list[str]: ] @staticmethod - def get_code_gen_path(tpl_path: str, app_name: str, api_version: str) -> str: + def get_code_gen_path(tpl_path: str, business: GenBusiness) -> str: """ 获取代码生成路径 :return: """ + app_name = business.app_name + module_name = business.table_name_en code_gen_path_mapping = { - 'py/api.jinja': f'py/{app_name}/api/{api_version}/api.py', - 'py/crud.jinja': f'py/{app_name}/crud/crud.py', - 'py/model.jinja': f'py/{app_name}/model/model.py', - 'py/schema.jinja': f'py/{app_name}/schema/schema.py', - 'py/service.jinja': f'py/{app_name}/service/service.py', + 'py/api.jinja': f'py/{app_name}/api/{business.api_version}/{module_name}.py', + 'py/crud.jinja': f'py/{app_name}/crud/crud_{module_name}.py', + 'py/model.jinja': f'py/{app_name}/model/{module_name}.py', + 'py/schema.jinja': f'py/{app_name}/schema/{module_name}.py', + 'py/service.jinja': f'py/{app_name}/service/{module_name}_service.py', } return code_gen_path_mapping.get(tpl_path) From e6554f146a6768459dc56f51e5fdc65388d2db12 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 3 Jul 2024 16:21:32 +0800 Subject: [PATCH 38/53] Update model relation column conf --- backend/app/admin/model/sys_dict_data.py | 2 +- backend/app/generator/model/gen_model.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/app/admin/model/sys_dict_data.py b/backend/app/admin/model/sys_dict_data.py index 718747682..884bdc6b5 100644 --- a/backend/app/admin/model/sys_dict_data.py +++ b/backend/app/admin/model/sys_dict_data.py @@ -19,5 +19,5 @@ class DictData(Base): status: Mapped[int] = mapped_column(default=1, comment='状态(0停用 1正常)') remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注') # 字典类型一对多 - type_id: Mapped[int] = mapped_column(ForeignKey('sys_dict_type.id'), default=None, comment='字典类型关联ID') + type_id: Mapped[int] = mapped_column(ForeignKey('sys_dict_type.id'), default=0, comment='字典类型关联ID') type: Mapped['DictType'] = relationship(init=False, back_populates='datas') # noqa: F821 diff --git a/backend/app/generator/model/gen_model.py b/backend/app/generator/model/gen_model.py index 3be8a7d67..d9d11570b 100644 --- a/backend/app/generator/model/gen_model.py +++ b/backend/app/generator/model/gen_model.py @@ -23,8 +23,8 @@ class GenModel(DataClassBase): is_pk: Mapped[bool] = mapped_column(default=False, comment='是否主键') is_nullable: Mapped[bool] = mapped_column(default=False, comment='是否可为空') - # 代码生成业务model一对多 - gen_business_id: Mapped[int | None] = mapped_column( - ForeignKey('sys_gen_business.id', ondelete='CASCADE'), default=None, comment='代码生成业务ID' + # 代码生成业务模型一对多 + gen_business_id: Mapped[int] = mapped_column( + ForeignKey('sys_gen_business.id', ondelete='CASCADE'), default=0, comment='代码生成业务ID' ) gen_business: Mapped[Union['GenBusiness', None]] = relationship(init=False, back_populates='gen_model') # noqa: F821 From 1bb39d6579cd716a7db0608b9e865b7bb4becf57 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 3 Jul 2024 17:01:14 +0800 Subject: [PATCH 39/53] Add import tables module --- backend/app/generator/api/v1/gen.py | 6 ++++++ backend/app/generator/service/gen_service.py | 3 +++ 2 files changed, 9 insertions(+) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index c397f2da3..a7c39cc44 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -76,6 +76,12 @@ async def delete_model(pk: Annotated[int, Path(...)]) -> ResponseModel: return await response_base.fail() +@router.post('/import', summary='导入代码生成业务和模型列', dependencies=[DependsRBAC]) +async def import_table(tables: Annotated[list, Query(..., description='数据库表名')]) -> ResponseModel: + await gen_service.import_bm(tables=tables) + return await response_base.success() + + @router.get('/preview/{pk}', summary='生成代码预览', dependencies=[DependsJwtAuth]) async def preview_code(pk: Annotated[int, Path(..., description='业务ID')]) -> ResponseModel: data = await gen_service.preview(pk=pk) diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 441a5414e..8325a621d 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -30,6 +30,9 @@ async def get_business_and_model(*, pk: int) -> dict: business_data.update({'models': model_data}) return business_data + @staticmethod + async def import_bm(*, tables: list): ... + @staticmethod async def render_tpl_code(*, business: GenBusiness) -> dict: gen_models = await gen_model_service.get_by_business(business_id=business.id) From 63406a1660a355f38476f8e995f83968036afd8f Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 3 Jul 2024 18:07:39 +0800 Subject: [PATCH 40/53] Add get all databases interface --- backend/app/generator/api/v1/gen.py | 9 ++++++++- backend/app/generator/service/gen_service.py | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index a7c39cc44..fac54af40 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -76,7 +76,14 @@ async def delete_model(pk: Annotated[int, Path(...)]) -> ResponseModel: return await response_base.fail() -@router.post('/import', summary='导入代码生成业务和模型列', dependencies=[DependsRBAC]) +@router.get('/tables', summary='获取数据库表', dependencies=[DependsRBAC]) +async def get_all_tables(table_schema: Annotated[str, Query('fba', description='数据库名')]) -> ResponseModel: + tables = await gen_service.get_all(table_schema=table_schema) + data = await select_list_serialize(tables) + return await response_base.success(data=data) + + +@router.post('/import', summary='导入数据库代码生成业务和模型列', dependencies=[DependsRBAC]) async def import_table(tables: Annotated[list, Query(..., description='数据库表名')]) -> ResponseModel: await gen_service.import_bm(tables=tables) return await response_base.success() diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 8325a621d..bf7accd0a 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -5,9 +5,12 @@ import zipfile from pathlib import Path +from typing import Sequence import aiofiles +from sqlalchemy import text + from backend.app.generator.crud.crud_gen_business import gen_business_dao from backend.app.generator.model import GenBusiness from backend.app.generator.service.gen_business_service import gen_business_service @@ -30,6 +33,18 @@ async def get_business_and_model(*, pk: int) -> dict: business_data.update({'models': model_data}) return business_data + @staticmethod + async def get_all(*, table_schema: str) -> Sequence[str]: + async with async_db_session() as db: + stmt = await db.execute( + text( + 'select table_name as table_name, table_comment as table_comment ' + 'from information_schema.tables ' + f"where table_name not like 'sys_gen_%' and table_schema = '{table_schema}';" + ) + ) + return stmt.scalars().all() + @staticmethod async def import_bm(*, tables: list): ... From cfa9378b2b5e78f68c3319bd3f3a6ed58f4cf7c8 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Thu, 4 Jul 2024 00:27:58 +0800 Subject: [PATCH 41/53] Fix get tables interface --- backend/app/generator/api/v1/gen.py | 9 ++++----- backend/app/generator/service/gen_service.py | 7 +++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index fac54af40..99334cae6 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -77,15 +77,14 @@ async def delete_model(pk: Annotated[int, Path(...)]) -> ResponseModel: @router.get('/tables', summary='获取数据库表', dependencies=[DependsRBAC]) -async def get_all_tables(table_schema: Annotated[str, Query('fba', description='数据库名')]) -> ResponseModel: - tables = await gen_service.get_all(table_schema=table_schema) - data = await select_list_serialize(tables) +async def get_all_tables(table_schema: Annotated[str | None, Query(description='数据库名')] = 'fba') -> ResponseModel: + data = await gen_service.get_tables(table_schema=table_schema) return await response_base.success(data=data) -@router.post('/import', summary='导入数据库代码生成业务和模型列', dependencies=[DependsRBAC]) +@router.post('/import', summary='导入代码生成业务和模型列', dependencies=[DependsRBAC]) async def import_table(tables: Annotated[list, Query(..., description='数据库表名')]) -> ResponseModel: - await gen_service.import_bm(tables=tables) + await gen_service.import_business_and_model(tables=tables) return await response_base.success() diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index bf7accd0a..82ac23b7b 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -34,19 +34,18 @@ async def get_business_and_model(*, pk: int) -> dict: return business_data @staticmethod - async def get_all(*, table_schema: str) -> Sequence[str]: + async def get_tables(*, table_schema: str) -> Sequence[str]: async with async_db_session() as db: stmt = await db.execute( text( - 'select table_name as table_name, table_comment as table_comment ' - 'from information_schema.tables ' + 'select table_name as table_name from information_schema.tables ' f"where table_name not like 'sys_gen_%' and table_schema = '{table_schema}';" ) ) return stmt.scalars().all() @staticmethod - async def import_bm(*, tables: list): ... + async def import_business_and_model(*, tables: list): ... @staticmethod async def render_tpl_code(*, business: GenBusiness) -> dict: From 3182ff6f6b6dc05d28960ae5d7ad108c80f3923a Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Thu, 4 Jul 2024 13:52:38 +0800 Subject: [PATCH 42/53] Bump pydantic to 2.8.1 --- backend/pdm.lock | 141 ++++++++++++++++++++------------------- backend/pyproject.toml | 2 +- backend/requirements.txt | 4 +- 3 files changed, 74 insertions(+), 73 deletions(-) diff --git a/backend/pdm.lock b/backend/pdm.lock index 2152e048a..7c51a7060 100644 --- a/backend/pdm.lock +++ b/backend/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "lint", "deploy"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:04116a878cef951f3a27a703a9e0f80caaa1db69b3984613b17f8860d0f77fbc" +content_hash = "sha256:686fd98f45b746eaea57476437ab02bb3724101c323d271b2596932080a36ef5" [[package]] name = "aiofiles" @@ -1286,24 +1286,24 @@ files = [ [[package]] name = "pydantic" -version = "2.8.0" +version = "2.8.1" requires_python = ">=3.8" summary = "Data validation using Python type hints" groups = ["default"] dependencies = [ "annotated-types>=0.4.0", - "pydantic-core==2.20.0", + "pydantic-core==2.20.1", "typing-extensions>=4.12.2; python_version >= \"3.13\"", "typing-extensions>=4.6.1; python_version < \"3.13\"", ] files = [ - {file = "pydantic-2.8.0-py3-none-any.whl", hash = "sha256:ead4f3a1e92386a734ca1411cb25d94147cf8778ed5be6b56749047676d6364e"}, - {file = "pydantic-2.8.0.tar.gz", hash = "sha256:d970ffb9d030b710795878940bd0489842c638e7252fc4a19c3ae2f7da4d6141"}, + {file = "pydantic-2.8.1-py3-none-any.whl", hash = "sha256:a7e968e55cc934de69163cce4e90870326f57575984a698d5a96ad1e97762dbe"}, + {file = "pydantic-2.8.1.tar.gz", hash = "sha256:5ac6a29cc27108917fb0923c28542471087bf90b2dc2c3a64d779818dffc1f6f"}, ] [[package]] name = "pydantic-core" -version = "2.20.0" +version = "2.20.1" requires_python = ">=3.8" summary = "Core functionality for Pydantic validation and serialization" groups = ["default"] @@ -1311,70 +1311,71 @@ dependencies = [ "typing-extensions!=4.7.0,>=4.6.0", ] files = [ - {file = "pydantic_core-2.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e9dcd7fb34f7bfb239b5fa420033642fff0ad676b765559c3737b91f664d4fa9"}, - {file = "pydantic_core-2.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:649a764d9b0da29816889424697b2a3746963ad36d3e0968784ceed6e40c6355"}, - {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7701df088d0b05f3460f7ba15aec81ac8b0fb5690367dfd072a6c38cf5b7fdb5"}, - {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab760f17c3e792225cdaef31ca23c0aea45c14ce80d8eff62503f86a5ab76bff"}, - {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb1ad5b4d73cde784cf64580166568074f5ccd2548d765e690546cff3d80937d"}, - {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b81ec2efc04fc1dbf400647d4357d64fb25543bae38d2d19787d69360aad21c9"}, - {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4a9732a5cad764ba37f3aa873dccb41b584f69c347a57323eda0930deec8e10"}, - {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6dc85b9e10cc21d9c1055f15684f76fa4facadddcb6cd63abab702eb93c98943"}, - {file = "pydantic_core-2.20.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:21d9f7e24f63fdc7118e6cc49defaab8c1d27570782f7e5256169d77498cf7c7"}, - {file = "pydantic_core-2.20.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8b315685832ab9287e6124b5d74fc12dda31e6421d7f6b08525791452844bc2d"}, - {file = "pydantic_core-2.20.0-cp310-none-win32.whl", hash = "sha256:c3dc8ec8b87c7ad534c75b8855168a08a7036fdb9deeeed5705ba9410721c84d"}, - {file = "pydantic_core-2.20.0-cp310-none-win_amd64.whl", hash = "sha256:85770b4b37bb36ef93a6122601795231225641003e0318d23c6233c59b424279"}, - {file = "pydantic_core-2.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:58e251bb5a5998f7226dc90b0b753eeffa720bd66664eba51927c2a7a2d5f32c"}, - {file = "pydantic_core-2.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:78d584caac52c24240ef9ecd75de64c760bbd0e20dbf6973631815e3ef16ef8b"}, - {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5084ec9721f82bef5ff7c4d1ee65e1626783abb585f8c0993833490b63fe1792"}, - {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d0f52684868db7c218437d260e14d37948b094493f2646f22d3dda7229bbe3f"}, - {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1def125d59a87fe451212a72ab9ed34c118ff771e5473fef4f2f95d8ede26d75"}, - {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b34480fd6778ab356abf1e9086a4ced95002a1e195e8d2fd182b0def9d944d11"}, - {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d42669d319db366cb567c3b444f43caa7ffb779bf9530692c6f244fc635a41eb"}, - {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:53b06aea7a48919a254b32107647be9128c066aaa6ee6d5d08222325f25ef175"}, - {file = "pydantic_core-2.20.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1f038156b696a1c39d763b2080aeefa87ddb4162c10aa9fabfefffc3dd8180fa"}, - {file = "pydantic_core-2.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3f0f3a4a23717280a5ee3ac4fb1f81d6fde604c9ec5100f7f6f987716bb8c137"}, - {file = "pydantic_core-2.20.0-cp311-none-win32.whl", hash = "sha256:316fe7c3fec017affd916a0c83d6f1ec697cbbbdf1124769fa73328e7907cc2e"}, - {file = "pydantic_core-2.20.0-cp311-none-win_amd64.whl", hash = "sha256:2d06a7fa437f93782e3f32d739c3ec189f82fca74336c08255f9e20cea1ed378"}, - {file = "pydantic_core-2.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:d6f8c49657f3eb7720ed4c9b26624063da14937fc94d1812f1e04a2204db3e17"}, - {file = "pydantic_core-2.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad1bd2f377f56fec11d5cfd0977c30061cd19f4fa199bf138b200ec0d5e27eeb"}, - {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed741183719a5271f97d93bbcc45ed64619fa38068aaa6e90027d1d17e30dc8d"}, - {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d82e5ed3a05f2dcb89c6ead2fd0dbff7ac09bc02c1b4028ece2d3a3854d049ce"}, - {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2ba34a099576234671f2e4274e5bc6813b22e28778c216d680eabd0db3f7dad"}, - {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:879ae6bb08a063b3e1b7ac8c860096d8fd6b48dd9b2690b7f2738b8c835e744b"}, - {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b0eefc7633a04c0694340aad91fbfd1986fe1a1e0c63a22793ba40a18fcbdc8"}, - {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73deadd6fd8a23e2f40b412b3ac617a112143c8989a4fe265050fd91ba5c0608"}, - {file = "pydantic_core-2.20.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:35681445dc85446fb105943d81ae7569aa7e89de80d1ca4ac3229e05c311bdb1"}, - {file = "pydantic_core-2.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0f6dd3612a3b9f91f2e63924ea18a4476656c6d01843ca20a4c09e00422195af"}, - {file = "pydantic_core-2.20.0-cp312-none-win32.whl", hash = "sha256:7e37b6bb6e90c2b8412b06373c6978d9d81e7199a40e24a6ef480e8acdeaf918"}, - {file = "pydantic_core-2.20.0-cp312-none-win_amd64.whl", hash = "sha256:7d4df13d1c55e84351fab51383520b84f490740a9f1fec905362aa64590b7a5d"}, - {file = "pydantic_core-2.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d43e7ab3b65e4dc35a7612cfff7b0fd62dce5bc11a7cd198310b57f39847fd6c"}, - {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b6a24d7b5893392f2b8e3b7a0031ae3b14c6c1942a4615f0d8794fdeeefb08b"}, - {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b2f13c3e955a087c3ec86f97661d9f72a76e221281b2262956af381224cfc243"}, - {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72432fd6e868c8d0a6849869e004b8bcae233a3c56383954c228316694920b38"}, - {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d70a8ff2d4953afb4cbe6211f17268ad29c0b47e73d3372f40e7775904bc28fc"}, - {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e49524917b8d3c2f42cd0d2df61178e08e50f5f029f9af1f402b3ee64574392"}, - {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4f0f71653b1c1bad0350bc0b4cc057ab87b438ff18fa6392533811ebd01439c"}, - {file = "pydantic_core-2.20.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:16197e6f4fdecb9892ed2436e507e44f0a1aa2cff3b9306d1c879ea2f9200997"}, - {file = "pydantic_core-2.20.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:763602504bf640b3ded3bba3f8ed8a1cc2fc6a87b8d55c1c5689f428c49c947e"}, - {file = "pydantic_core-2.20.0-cp313-none-win32.whl", hash = "sha256:a3f243f318bd9523277fa123b3163f4c005a3e8619d4b867064de02f287a564d"}, - {file = "pydantic_core-2.20.0-cp313-none-win_amd64.whl", hash = "sha256:03aceaf6a5adaad3bec2233edc5a7905026553916615888e53154807e404545c"}, - {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cafde15a6f7feaec2f570646e2ffc5b73412295d29134a29067e70740ec6ee20"}, - {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2aec8eeea0b08fd6bc2213d8e86811a07491849fd3d79955b62d83e32fa2ad5f"}, - {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:840200827984f1c4e114008abc2f5ede362d6e11ed0b5931681884dd41852ff1"}, - {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ea1d8b7df522e5ced34993c423c3bf3735c53df8b2a15688a2f03a7d678800"}, - {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5b8376a867047bf08910573deb95d3c8dfb976eb014ee24f3b5a61ccc5bee1b"}, - {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d08264b4460326cefacc179fc1411304d5af388a79910832835e6f641512358b"}, - {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7a3639011c2e8a9628466f616ed7fb413f30032b891898e10895a0a8b5857d6c"}, - {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05e83ce2f7eba29e627dd8066aa6c4c0269b2d4f889c0eba157233a353053cea"}, - {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:603a843fea76a595c8f661cd4da4d2281dff1e38c4a836a928eac1a2f8fe88e4"}, - {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac76f30d5d3454f4c28826d891fe74d25121a346c69523c9810ebba43f3b1cec"}, - {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e3b1d4b1b3f6082849f9b28427ef147a5b46a6132a3dbaf9ca1baa40c88609"}, - {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2761f71faed820e25ec62eacba670d1b5c2709bb131a19fcdbfbb09884593e5a"}, - {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a0586cddbf4380e24569b8a05f234e7305717cc8323f50114dfb2051fcbce2a3"}, - {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:b8c46a8cf53e849eea7090f331ae2202cd0f1ceb090b00f5902c423bd1e11805"}, - {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b4a085bd04af7245e140d1b95619fe8abb445a3d7fdf219b3f80c940853268ef"}, - {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:116b326ac82c8b315e7348390f6d30bcfe6e688a7d3f1de50ff7bcc2042a23c2"}, - {file = "pydantic_core-2.20.0.tar.gz", hash = "sha256:366be8e64e0cb63d87cf79b4e1765c0703dd6313c729b22e7b9e378db6b96877"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"}, + {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"}, + {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"}, + {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"}, + {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"}, + {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"}, + {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"}, + {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"}, + {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"}, + {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"}, ] [[package]] diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 47350a9f2..23593cc28 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "phonenumbers==8.13.27", "pre-commit==3.2.2", "psutil==5.9.6", - "pydantic==2.8.0", + "pydantic==2.8.1", "pytest==7.2.2", "pytest-pretty==1.2.0", "python-jose==3.3.0", diff --git a/backend/requirements.txt b/backend/requirements.txt index 66657d208..4e049247f 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -72,8 +72,8 @@ prompt-toolkit==3.0.43 psutil==5.9.6 pyasn1==0.5.1 pycparser==2.21 -pydantic==2.8.0 -pydantic-core==2.20.0 +pydantic==2.8.1 +pydantic-core==2.20.1 pydantic-extra-types==2.2.0 pydantic-settings==2.1.0 pygments==2.17.2 From 7d6e83b02e05139b33d8ccd4984dccf8e5df2461 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Mon, 8 Jul 2024 17:42:51 +0800 Subject: [PATCH 43/53] Upgrading lint dependencies --- .pre-commit-config.yaml | 6 +++--- backend/pdm.lock | 39 ++++++++++++++++++++------------------- backend/pyproject.toml | 2 +- backend/requirements.txt | 8 ++++---- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f911ed4d7..8b6acf803 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.6.0 hooks: - id: check-added-large-files - id: end-of-file-fixer @@ -8,7 +8,7 @@ repos: - id: check-toml - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.4.2 + rev: v0.5.1 hooks: - id: ruff args: @@ -20,7 +20,7 @@ repos: - id: ruff-format - repo: https://github.com/pdm-project/pdm - rev: 2.12.4 + rev: 2.16.1 hooks: - id: pdm-export args: diff --git a/backend/pdm.lock b/backend/pdm.lock index 110eb194e..6aa955260 100644 --- a/backend/pdm.lock +++ b/backend/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "lint", "deploy"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:686fd98f45b746eaea57476437ab02bb3724101c323d271b2596932080a36ef5" +content_hash = "sha256:7dfcccec290d3aae8c2588285424b0ee04672fd60d1740b3bccf433a946fc9ee" [[package]] name = "aiofiles" @@ -1608,28 +1608,29 @@ files = [ [[package]] name = "ruff" -version = "0.4.4" +version = "0.5.1" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["lint"] files = [ - {file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"}, - {file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"}, - {file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"}, - {file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"}, - {file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"}, - {file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"}, + {file = "ruff-0.5.1-py3-none-linux_armv6l.whl", hash = "sha256:6ecf968fcf94d942d42b700af18ede94b07521bd188aaf2cd7bc898dd8cb63b6"}, + {file = "ruff-0.5.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:204fb0a472f00f2e6280a7c8c7c066e11e20e23a37557d63045bf27a616ba61c"}, + {file = "ruff-0.5.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d235968460e8758d1e1297e1de59a38d94102f60cafb4d5382033c324404ee9d"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38beace10b8d5f9b6bdc91619310af6d63dd2019f3fb2d17a2da26360d7962fa"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e478d2f09cf06add143cf8c4540ef77b6599191e0c50ed976582f06e588c994"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0368d765eec8247b8550251c49ebb20554cc4e812f383ff9f5bf0d5d94190b0"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3a9a9a1b582e37669b0138b7c1d9d60b9edac880b80eb2baba6d0e566bdeca4d"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bdd9f723e16003623423affabcc0a807a66552ee6a29f90eddad87a40c750b78"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:be9fd62c1e99539da05fcdc1e90d20f74aec1b7a1613463ed77870057cd6bd96"}, + {file = "ruff-0.5.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e216fc75a80ea1fbd96af94a6233d90190d5b65cc3d5dfacf2bd48c3e067d3e1"}, + {file = "ruff-0.5.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c4c2112e9883a40967827d5c24803525145e7dab315497fae149764979ac7929"}, + {file = "ruff-0.5.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dfaf11c8a116394da3b65cd4b36de30d8552fa45b8119b9ef5ca6638ab964fa3"}, + {file = "ruff-0.5.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d7ceb9b2fe700ee09a0c6b192c5ef03c56eb82a0514218d8ff700f6ade004108"}, + {file = "ruff-0.5.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:bac6288e82f6296f82ed5285f597713acb2a6ae26618ffc6b429c597b392535c"}, + {file = "ruff-0.5.1-py3-none-win32.whl", hash = "sha256:5c441d9c24ec09e1cb190a04535c5379b36b73c4bc20aa180c54812c27d1cca4"}, + {file = "ruff-0.5.1-py3-none-win_amd64.whl", hash = "sha256:b1789bf2cd3d1b5a7d38397cac1398ddf3ad7f73f4de01b1e913e2abc7dfc51d"}, + {file = "ruff-0.5.1-py3-none-win_arm64.whl", hash = "sha256:2875b7596a740cbbd492f32d24be73e545a4ce0a3daf51e4f4e609962bfd3cd2"}, + {file = "ruff-0.5.1.tar.gz", hash = "sha256:3164488aebd89b1745b47fd00604fb4358d774465f20d1fcd907f9c0fc1b0655"}, ] [[package]] diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 66e82cd39..0ff5df1e7 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -50,7 +50,7 @@ license = { text = "MIT" } [tool.pdm.dev-dependencies] lint = [ - "ruff>=0.4.2", + "ruff>=0.5.0", ] deploy = [ "supervisor>=4.2.5", diff --git a/backend/requirements.txt b/backend/requirements.txt index 9568a52d8..7d9dfdf13 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -30,11 +30,11 @@ ecdsa==0.18.0 email-validator==2.0.0 exceptiongroup==1.2.0; python_version < "3.11" fast-captcha==0.2.1 -fastapi==0.111.0 fastapi-cli==0.0.2 fastapi-limiter==0.1.6 fastapi-oauth20==0.0.1a2 fastapi-pagination==0.12.13 +fastapi[all]==0.111.0 filelock==3.13.1 flower==2.0.1 greenlet==3.0.3; platform_machine == "win32" or platform_machine == "WIN32" or platform_machine == "AMD64" or platform_machine == "amd64" or platform_machine == "x86_64" or platform_machine == "ppc64le" or platform_machine == "aarch64" @@ -85,10 +85,10 @@ python-jose==3.3.0 python-multipart==0.0.9 pytz==2023.3 pyyaml==6.0.1 -redis==5.0.1 +redis[hiredis]==5.0.1 rich==13.7.1 rsa==4.9 -ruff==0.4.4 +ruff==0.5.1 setuptools==69.2.0 shellingham==1.5.4 simpleeval==0.9.13 @@ -106,7 +106,7 @@ tzdata==2024.1 ua-parser==0.18.0 ujson==5.9.0 user-agents==2.2.0 -uvicorn==0.29.0 +uvicorn[standard]==0.29.0 uvloop==0.19.0; (sys_platform != "cygwin" and sys_platform != "win32") and platform_python_implementation != "PyPy" vine==5.1.0 virtualenv==20.25.1 From 7c68c3b95ffc9de6c785e89676159435724ef732 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 10 Jul 2024 09:49:48 +0800 Subject: [PATCH 44/53] Add import business and model implement --- backend/.ruff.toml | 1 - backend/app/generator/api/v1/gen.py | 7 +- backend/app/generator/model/gen_business.py | 2 +- backend/app/generator/schema/gen_business.py | 8 +- backend/app/generator/schema/gen_model.py | 2 + backend/app/generator/service/gen_service.py | 53 ++++++++++- backend/common/enums.py | 98 ++++++++++---------- 7 files changed, 116 insertions(+), 55 deletions(-) diff --git a/backend/.ruff.toml b/backend/.ruff.toml index 4dc4e3856..fa3922e16 100644 --- a/backend/.ruff.toml +++ b/backend/.ruff.toml @@ -26,7 +26,6 @@ select = [ "UP007" ] preview = true -ignore-init-module-imports = true ignore = ["FURB101"] [lint.flake8-pytest-style] diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index 99334cae6..5f4758173 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -83,8 +83,11 @@ async def get_all_tables(table_schema: Annotated[str | None, Query(description=' @router.post('/import', summary='导入代码生成业务和模型列', dependencies=[DependsRBAC]) -async def import_table(tables: Annotated[list, Query(..., description='数据库表名')]) -> ResponseModel: - await gen_service.import_business_and_model(tables=tables) +async def import_table( + app: Annotated[str, Query(..., description='应用名称,用于代码生成到指定 app')], + table: Annotated[str, Query(..., description='数据库表名')], +) -> ResponseModel: + await gen_service.import_business_and_model(app=app, table=table) return await response_base.success() diff --git a/backend/app/generator/model/gen_business.py b/backend/app/generator/model/gen_business.py index 39e480146..0ff4bbc78 100644 --- a/backend/app/generator/model/gen_business.py +++ b/backend/app/generator/model/gen_business.py @@ -21,7 +21,7 @@ class GenBusiness(Base): # relate_model_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='关联表名称') # relate_model_fk: Mapped[int | None] = mapped_column(default=None, comment='关联表外键') schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称 (默认为英文表名称)') - have_datetime_column: Mapped[bool] = mapped_column(default=True, comment='存在默认时间列') + have_datetime_column: Mapped[bool] = mapped_column(default=True, comment='是否存在默认时间列') api_version: Mapped[str] = mapped_column(String(20), default='v1', comment='代码生成 api 版本,默认为 v1') gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='代码生成路径(默认为 app 根路径)') remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注') diff --git a/backend/app/generator/schema/gen_business.py b/backend/app/generator/schema/gen_business.py index e0eb26911..acbc31674 100644 --- a/backend/app/generator/schema/gen_business.py +++ b/backend/app/generator/schema/gen_business.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from datetime import datetime -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, model_validator from backend.common.schema import SchemaBase @@ -21,6 +21,12 @@ class GenBusinessSchemaBase(SchemaBase): gen_path: str | None = None remark: str | None = None + @model_validator(mode='after') + def check_schema_name(self): + if self.schema_name is None: + self.schema_name = self.table_name_en + return self + class CreateGenBusinessParam(GenBusinessSchemaBase): pass diff --git a/backend/app/generator/schema/gen_model.py b/backend/app/generator/schema/gen_model.py index db04a4cc9..813ef498a 100644 --- a/backend/app/generator/schema/gen_model.py +++ b/backend/app/generator/schema/gen_model.py @@ -21,6 +21,8 @@ class GenModelSchemaBase(SchemaBase): @classmethod def sql_type_to_python(cls, v: GenModelType): type_mapping = { + GenModelType.CHAR: 'str', + GenModelType.VARCHAR: 'str', GenModelType.String: 'str', GenModelType.TEXT: 'str', GenModelType.Text: 'str', diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 82ac23b7b..189685bf2 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -12,9 +12,13 @@ from sqlalchemy import text from backend.app.generator.crud.crud_gen_business import gen_business_dao +from backend.app.generator.crud.crud_gen_model import gen_model_dao from backend.app.generator.model import GenBusiness +from backend.app.generator.schema.gen_business import CreateGenBusinessParam +from backend.app.generator.schema.gen_model import CreateGenModelParam from backend.app.generator.service.gen_business_service import gen_business_service from backend.app.generator.service.gen_model_service import gen_model_service +from backend.common.enums import GenModelType from backend.common.exception import errors from backend.core.path_conf import BasePath from backend.database.db_mysql import async_db_session @@ -45,7 +49,54 @@ async def get_tables(*, table_schema: str) -> Sequence[str]: return stmt.scalars().all() @staticmethod - async def import_business_and_model(*, tables: list): ... + async def import_business_and_model(*, app: str, table: str) -> None: + async with async_db_session.begin() as db: + stmt = await db.execute( + text( + 'select table_name as table_name, table_comment as table_comment from information_schema.tables ' + f"where table_name not like 'sys_gen_%' and table_name = '{table}';" + ) + ) + table_info = stmt.fetchone() + if not table_info: + raise errors.NotFoundError(msg='数据库表不存在') + table_name = table_info[0] + business_data = { + 'app_name': app, + 'table_name_en': table_name, + 'table_name_zh': table_info[1] or ' '.join(table_name.split('_')), + 'table_simple_name_zh': table_info[1] or table_name.split('_')[-1], + 'table_comment': table_info[1], + } + new_business = GenBusiness(**CreateGenBusinessParam(**business_data).model_dump()) + db.add(new_business) + await db.flush() + stmt = await db.execute( + text( + 'select column_name AS column_name, ' + 'case when column_key = "PRI" then 1 else 0 end as is_pk, ' + 'case when is_nullable = "NO" or column_key = "PRI" then 0 else 1 end as is_nullable, ' + 'ordinal_position as sort, column_comment as column_comment, column_type as column_type ' + f'from information_schema.columns where table_name = "{table}" and column_name != "id" ' + 'order by sort;' + ) + ) + column_info = stmt.fetchall() + for column in column_info: + column_type = column[-1].split('(')[0].lower() + model_data = { + 'name': column[0], + 'comment': column[-2], + 'type': column_type, + 'sort': column[-2], + 'length': column[-1].split('(')[1][:-1] + if column_type == GenModelType.CHAR or column_type == GenModelType.VARCHAR + else 0, + 'is_pk': column[1], + 'is_nullable': column[2], + 'gen_business_id': new_business.id, + } + await gen_model_dao.create(db, CreateGenModelParam(**model_data)) @staticmethod async def render_tpl_code(*, business: GenBusiness) -> dict: diff --git a/backend/common/enums.py b/backend/common/enums.py index 269378247..be848912c 100644 --- a/backend/common/enums.py +++ b/backend/common/enums.py @@ -93,52 +93,52 @@ class UserSocialType(StrEnum): class GenModelType(StrEnum): """代码生成模型类型""" - ARRAY = 'ARRAY' - BIGINT = 'BIGINT' - BigInteger = 'BigInteger' - BINARY = 'BINARY' - BLOB = 'BLOB' - BOOLEAN = 'BOOLEAN' - Boolean = 'Boolean' - CHAR = 'CHAR' - CLOB = 'CLOB' - DATE = 'DATE' - Date = 'Date' - DATETIME = 'DATETIME' - DateTime = 'DateTime' - DECIMAL = 'DECIMAL' - DOUBLE = 'DOUBLE' - Double = 'Double' - DOUBLE_PRECISION = 'DOUBLE_PRECISION' - Enum = 'Enum' - FLOAT = 'FLOAT' - Float = 'Float' - INT = 'INT' - INTEGER = 'INTEGER' - Integer = 'Integer' - Interval = 'Interval' - JSON = 'JSON' - LargeBinary = 'LargeBinary' - LONGTEXT = 'LONGTEXT' - NCHAR = 'NCHAR' - NUMERIC = 'NUMERIC' - Numeric = 'Numeric' - NVARCHAR = 'NVARCHAR' - PickleType = 'PickleType' - REAL = 'REAL' - SMALLINT = 'SMALLINT' - SmallInteger = 'SmallInteger' - String = 'String' - TEXT = 'TEXT' - Text = 'Text' - TIME = 'TIME' - Time = 'Time' - TIMESTAMP = 'TIMESTAMP' - TupleType = 'TupleType' - TypeDecorator = 'TypeDecorator' - Unicode = 'Unicode' - UnicodeText = 'UnicodeText' - UUID = 'UUID' - Uuid = 'Uuid' - VARBINARY = 'VARBINARY' - VARCHAR = 'VARCHAR' + ARRAY = 'array' + BIGINT = 'bigint' + BigInteger = 'biginteger' + BINARY = 'binary' + BLOB = 'blob' + BOOLEAN = 'boolean' + Boolean = 'boolean' + CHAR = 'char' + CLOB = 'clob' + DATE = 'date' + Date = 'date' + DATETIME = 'datetime' + DateTime = 'datetime' + DECIMAL = 'decimal' + DOUBLE = 'double' + Double = 'double' + DOUBLE_PRECISION = 'double_precision' + Enum = 'enum' + FLOAT = 'float' + Float = 'float' + INT = 'int' + INTEGER = 'integer' + Integer = 'integer' + Interval = 'interval' + JSON = 'json' + LargeBinary = 'largebinary' + LONGTEXT = 'longtext' + NCHAR = 'nchar' + NUMERIC = 'numeric' + Numeric = 'numeric' + NVARCHAR = 'nvarchar' + PickleType = 'pickletype' + REAL = 'real' + SMALLINT = 'smallint' + SmallInteger = 'smallinteger' + String = 'string' + TEXT = 'text' + Text = 'text' + TIME = 'time' + Time = 'time' + TIMESTAMP = 'timestamp' + TupleType = 'tupletype' + TypeDecorator = 'typedecorator' + Unicode = 'unicode' + UnicodeText = 'unicodetext' + UUID = 'uuid' + Uuid = 'uuid' + VARBINARY = 'varbinary' + VARCHAR = 'varchar' From 56e2ee8b9ad539c6f29e2031dbcd1c08109d052a Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 10 Jul 2024 10:08:02 +0800 Subject: [PATCH 45/53] Split import database execution code --- backend/app/generator/api/v1/gen.py | 4 +- backend/app/generator/crud/crud_gen.py | 45 ++++++++++++++++++++ backend/app/generator/service/gen_service.py | 33 +++----------- 3 files changed, 52 insertions(+), 30 deletions(-) create mode 100644 backend/app/generator/crud/crud_gen.py diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index 5f4758173..62a4ed043 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -85,9 +85,9 @@ async def get_all_tables(table_schema: Annotated[str | None, Query(description=' @router.post('/import', summary='导入代码生成业务和模型列', dependencies=[DependsRBAC]) async def import_table( app: Annotated[str, Query(..., description='应用名称,用于代码生成到指定 app')], - table: Annotated[str, Query(..., description='数据库表名')], + table_name: Annotated[str, Query(..., description='数据库表名')], ) -> ResponseModel: - await gen_service.import_business_and_model(app=app, table=table) + await gen_service.import_business_and_model(app=app, table_name=table_name) return await response_base.success() diff --git a/backend/app/generator/crud/crud_gen.py b/backend/app/generator/crud/crud_gen.py new file mode 100644 index 000000000..bb39ce99b --- /dev/null +++ b/backend/app/generator/crud/crud_gen.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from typing import Sequence + +from sqlalchemy import Row, text +from sqlalchemy.ext.asyncio import AsyncSession + + +class CRUDGen: + @staticmethod + async def get_all_tables(db: AsyncSession, table_schema: str) -> Sequence[str]: + stmt = await db.execute( + text( + 'select table_name as table_name from information_schema.tables ' + f"where table_name not like 'sys_gen_%' and table_schema = '{table_schema}';" + ) + ) + return stmt.scalars().all() + + @staticmethod + async def get_table(db: AsyncSession, table_name: str) -> Row[tuple]: + stmt = await db.execute( + text( + 'select table_name as table_name, table_comment as table_comment from information_schema.tables ' + f"where table_name not like 'sys_gen_%' and table_name = '{table_name}';" + ) + ) + return stmt.fetchone() + + @staticmethod + async def get_all_columns(db: AsyncSession, table_name: str) -> Sequence[Row[tuple]]: + stmt = await db.execute( + text( + 'select column_name AS column_name, ' + 'case when column_key = "PRI" then 1 else 0 end as is_pk, ' + 'case when is_nullable = "NO" or column_key = "PRI" then 0 else 1 end as is_nullable, ' + 'ordinal_position as sort, column_comment as column_comment, column_type as column_type ' + f'from information_schema.columns where table_name = "{table_name}" and column_name != "id" ' + 'order by sort;' + ) + ) + return stmt.fetchall() + + +gen_dao = CRUDGen() diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 189685bf2..84f91df8b 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -9,8 +9,7 @@ import aiofiles -from sqlalchemy import text - +from backend.app.generator.crud.crud_gen import gen_dao from backend.app.generator.crud.crud_gen_business import gen_business_dao from backend.app.generator.crud.crud_gen_model import gen_model_dao from backend.app.generator.model import GenBusiness @@ -40,24 +39,12 @@ async def get_business_and_model(*, pk: int) -> dict: @staticmethod async def get_tables(*, table_schema: str) -> Sequence[str]: async with async_db_session() as db: - stmt = await db.execute( - text( - 'select table_name as table_name from information_schema.tables ' - f"where table_name not like 'sys_gen_%' and table_schema = '{table_schema}';" - ) - ) - return stmt.scalars().all() + return await gen_dao.get_all_tables(db, table_schema) @staticmethod - async def import_business_and_model(*, app: str, table: str) -> None: + async def import_business_and_model(*, app: str, table_name: str) -> None: async with async_db_session.begin() as db: - stmt = await db.execute( - text( - 'select table_name as table_name, table_comment as table_comment from information_schema.tables ' - f"where table_name not like 'sys_gen_%' and table_name = '{table}';" - ) - ) - table_info = stmt.fetchone() + table_info = await gen_dao.get_table(db, table_name) if not table_info: raise errors.NotFoundError(msg='数据库表不存在') table_name = table_info[0] @@ -71,17 +58,7 @@ async def import_business_and_model(*, app: str, table: str) -> None: new_business = GenBusiness(**CreateGenBusinessParam(**business_data).model_dump()) db.add(new_business) await db.flush() - stmt = await db.execute( - text( - 'select column_name AS column_name, ' - 'case when column_key = "PRI" then 1 else 0 end as is_pk, ' - 'case when is_nullable = "NO" or column_key = "PRI" then 0 else 1 end as is_nullable, ' - 'ordinal_position as sort, column_comment as column_comment, column_type as column_type ' - f'from information_schema.columns where table_name = "{table}" and column_name != "id" ' - 'order by sort;' - ) - ) - column_info = stmt.fetchall() + column_info = await gen_dao.get_all_columns(db, table_name) for column in column_info: column_type = column[-1].split('(')[0].lower() model_data = { From f39d383c231c35191460d5f4809314098b27aec5 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 10 Jul 2024 10:29:12 +0800 Subject: [PATCH 46/53] Update text SQL execution to prevent SQL injection --- backend/app/generator/crud/crud_gen.py | 41 +++++++++----------- backend/app/generator/service/gen_service.py | 2 +- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/backend/app/generator/crud/crud_gen.py b/backend/app/generator/crud/crud_gen.py index bb39ce99b..03236ce09 100644 --- a/backend/app/generator/crud/crud_gen.py +++ b/backend/app/generator/crud/crud_gen.py @@ -9,36 +9,33 @@ class CRUDGen: @staticmethod async def get_all_tables(db: AsyncSession, table_schema: str) -> Sequence[str]: - stmt = await db.execute( - text( - 'select table_name as table_name from information_schema.tables ' - f"where table_name not like 'sys_gen_%' and table_schema = '{table_schema}';" - ) - ) + t = text( + 'select table_name as table_name from information_schema.tables ' + 'where table_name not like "sys_gen_%" and table_schema = :table_schema;' + ).bindparams(table_schema=table_schema) + stmt = await db.execute(t) return stmt.scalars().all() @staticmethod async def get_table(db: AsyncSession, table_name: str) -> Row[tuple]: - stmt = await db.execute( - text( - 'select table_name as table_name, table_comment as table_comment from information_schema.tables ' - f"where table_name not like 'sys_gen_%' and table_name = '{table_name}';" - ) - ) + t = text( + 'select table_name as table_name, table_comment as table_comment from information_schema.tables ' + 'where table_name not like "sys_gen_%" and table_name = :table_name;' + ).bindparams(table_name=table_name) + stmt = await db.execute(t) return stmt.fetchone() @staticmethod async def get_all_columns(db: AsyncSession, table_name: str) -> Sequence[Row[tuple]]: - stmt = await db.execute( - text( - 'select column_name AS column_name, ' - 'case when column_key = "PRI" then 1 else 0 end as is_pk, ' - 'case when is_nullable = "NO" or column_key = "PRI" then 0 else 1 end as is_nullable, ' - 'ordinal_position as sort, column_comment as column_comment, column_type as column_type ' - f'from information_schema.columns where table_name = "{table_name}" and column_name != "id" ' - 'order by sort;' - ) - ) + t = text( + 'select column_name AS column_name, ' + 'case when column_key = "PRI" then 1 else 0 end as is_pk, ' + 'case when is_nullable = "NO" or column_key = "PRI" then 0 else 1 end as is_nullable, ' + 'ordinal_position as sort, column_comment as column_comment, column_type as column_type ' + 'from information_schema.columns where table_name = :table_name and column_name != "id" ' + 'order by sort;' + ).bindparams(table_name=table_name) + stmt = await db.execute(t) return stmt.fetchall() diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index 84f91df8b..bbd168261 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -65,7 +65,7 @@ async def import_business_and_model(*, app: str, table_name: str) -> None: 'name': column[0], 'comment': column[-2], 'type': column_type, - 'sort': column[-2], + 'sort': column[-3], 'length': column[-1].split('(')[1][:-1] if column_type == GenModelType.CHAR or column_type == GenModelType.VARCHAR else 0, From 1b4b20ed3d957c8adf391ded6fa282b733b56e34 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 10 Jul 2024 10:34:35 +0800 Subject: [PATCH 47/53] Optimize schema name generation --- backend/app/generator/model/gen_business.py | 2 +- backend/app/generator/schema/gen_business.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/app/generator/model/gen_business.py b/backend/app/generator/model/gen_business.py index 0ff4bbc78..172ff07bb 100644 --- a/backend/app/generator/model/gen_business.py +++ b/backend/app/generator/model/gen_business.py @@ -20,7 +20,7 @@ class GenBusiness(Base): table_comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='表描述') # relate_model_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='关联表名称') # relate_model_fk: Mapped[int | None] = mapped_column(default=None, comment='关联表外键') - schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称 (默认为英文表名称)') + schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称 (默认为英文表驼峰)') have_datetime_column: Mapped[bool] = mapped_column(default=True, comment='是否存在默认时间列') api_version: Mapped[str] = mapped_column(String(20), default='v1', comment='代码生成 api 版本,默认为 v1') gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='代码生成路径(默认为 app 根路径)') diff --git a/backend/app/generator/schema/gen_business.py b/backend/app/generator/schema/gen_business.py index acbc31674..cd7d8a6bd 100644 --- a/backend/app/generator/schema/gen_business.py +++ b/backend/app/generator/schema/gen_business.py @@ -3,6 +3,7 @@ from datetime import datetime from pydantic import ConfigDict, Field, model_validator +from pydantic.alias_generators import to_pascal from backend.common.schema import SchemaBase @@ -24,7 +25,7 @@ class GenBusinessSchemaBase(SchemaBase): @model_validator(mode='after') def check_schema_name(self): if self.schema_name is None: - self.schema_name = self.table_name_en + self.schema_name = to_pascal(self.table_name_en) return self From b237a94a5d6dd41fca7df469864a7856e1ee5b51 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 10 Jul 2024 10:36:07 +0800 Subject: [PATCH 48/53] clean code --- backend/app/generator/model/gen_business.py | 1 - backend/app/generator/schema/gen_business.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/backend/app/generator/model/gen_business.py b/backend/app/generator/model/gen_business.py index 172ff07bb..e27e8d2ad 100644 --- a/backend/app/generator/model/gen_business.py +++ b/backend/app/generator/model/gen_business.py @@ -18,7 +18,6 @@ class GenBusiness(Base): table_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文)') table_simple_name_zh: Mapped[str] = mapped_column(String(255), comment='表名称(中文简称)') table_comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='表描述') - # relate_model_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='关联表名称') # relate_model_fk: Mapped[int | None] = mapped_column(default=None, comment='关联表外键') schema_name: Mapped[str | None] = mapped_column(String(255), default=None, comment='Schema 名称 (默认为英文表驼峰)') have_datetime_column: Mapped[bool] = mapped_column(default=True, comment='是否存在默认时间列') diff --git a/backend/app/generator/schema/gen_business.py b/backend/app/generator/schema/gen_business.py index cd7d8a6bd..5596bd7cf 100644 --- a/backend/app/generator/schema/gen_business.py +++ b/backend/app/generator/schema/gen_business.py @@ -14,8 +14,6 @@ class GenBusinessSchemaBase(SchemaBase): table_name_zh: str table_simple_name_zh: str table_comment: str | None = None - # relate_model_name: str | None = None - # relate_model_fk: int | None = None schema_name: str | None = None have_datetime_column: bool = Field(default=False) api_version: str = Field(default='v1') From fb4acdbae1c89f9c4ee30b5b36d03acc9d2064de Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 10 Jul 2024 10:59:51 +0800 Subject: [PATCH 49/53] Update create tables sql scripts --- backend/sql/create_tables.sql | 80 +++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 18 deletions(-) diff --git a/backend/sql/create_tables.sql b/backend/sql/create_tables.sql index 713bfa91d..12bba367e 100644 --- a/backend/sql/create_tables.sql +++ b/backend/sql/create_tables.sql @@ -48,13 +48,35 @@ CREATE TABLE `sys_dept` `created_time` datetime NOT NULL COMMENT '创建时间', `updated_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`), - KEY `ix_sys_dept_parent_id` (`parent_id`), KEY `ix_sys_dept_id` (`id`), + KEY `ix_sys_dept_parent_id` (`parent_id`), CONSTRAINT `sys_dept_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `sys_dept` (`id`) ON DELETE SET NULL ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; +-- sys_dict_data: table +CREATE TABLE `sys_dict_data` +( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id', + `label` varchar(32) NOT NULL COMMENT '字典标签', + `value` varchar(32) NOT NULL COMMENT '字典值', + `sort` int NOT NULL COMMENT '排序', + `status` int NOT NULL COMMENT '状态(0停用 1正常)', + `remark` longtext COMMENT '备注', + `type_id` int NOT NULL COMMENT '字典类型关联ID', + `created_time` datetime NOT NULL COMMENT '创建时间', + `updated_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `label` (`label`), + UNIQUE KEY `value` (`value`), + KEY `type_id` (`type_id`), + KEY `ix_sys_dict_data_id` (`id`), + CONSTRAINT `sys_dict_data_ibfk_1` FOREIGN KEY (`type_id`) REFERENCES `sys_dict_type` (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_0900_ai_ci; + -- sys_dict_type: table CREATE TABLE `sys_dict_type` ( @@ -73,24 +95,46 @@ CREATE TABLE `sys_dict_type` DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; --- sys_dict_data: table -CREATE TABLE `sys_dict_data` +-- sys_gen_business: table +CREATE TABLE `sys_gen_business` ( - `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id', - `label` varchar(32) NOT NULL COMMENT '字典标签', - `value` varchar(32) NOT NULL COMMENT '字典值', - `sort` int NOT NULL COMMENT '排序', - `status` int NOT NULL COMMENT '状态(0停用 1正常)', - `remark` longtext COMMENT '备注', - `type_id` int NOT NULL COMMENT '字典类型关联ID', - `created_time` datetime NOT NULL COMMENT '创建时间', - `updated_time` datetime DEFAULT NULL COMMENT '更新时间', + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id', + `app_name` varchar(50) NOT NULL COMMENT '应用名称(英文)', + `table_name_en` varchar(255) NOT NULL COMMENT '表名称(英文)', + `table_name_zh` varchar(255) NOT NULL COMMENT '表名称(中文)', + `table_simple_name_zh` varchar(255) NOT NULL COMMENT '表名称(中文简称)', + `table_comment` varchar(255) DEFAULT NULL COMMENT '表描述', + `schema_name` varchar(255) DEFAULT NULL COMMENT 'Schema 名称 (默认为英文表驼峰)', + `have_datetime_column` tinyint(1) NOT NULL COMMENT '是否存在默认时间列', + `api_version` varchar(20) NOT NULL COMMENT '代码生成 api 版本,默认为 v1', + `gen_path` varchar(255) DEFAULT NULL COMMENT '代码生成路径(默认为 app 根路径)', + `remark` longtext COMMENT '备注', + `created_time` datetime NOT NULL COMMENT '创建时间', + `updated_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`), - UNIQUE KEY `label` (`label`), - UNIQUE KEY `value` (`value`), - KEY `type_id` (`type_id`), - KEY `ix_sys_dict_data_id` (`id`), - CONSTRAINT `sys_dict_data_ibfk_1` FOREIGN KEY (`type_id`) REFERENCES `sys_dict_type` (`id`) + UNIQUE KEY `table_name_en` (`table_name_en`), + KEY `ix_sys_gen_business_id` (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_0900_ai_ci; + +-- sys_gen_model: table +CREATE TABLE `sys_gen_model` +( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id', + `name` varchar(50) NOT NULL COMMENT '列名称', + `comment` varchar(255) DEFAULT NULL COMMENT '列描述', + `type` varchar(20) NOT NULL COMMENT '列类型', + `default` varchar(50) DEFAULT NULL COMMENT '列默认值', + `sort` int DEFAULT NULL COMMENT '列排序', + `length` int NOT NULL COMMENT '列长度', + `is_pk` tinyint(1) NOT NULL COMMENT '是否主键', + `is_nullable` tinyint(1) NOT NULL COMMENT '是否可为空', + `gen_business_id` int NOT NULL COMMENT '代码生成业务ID', + PRIMARY KEY (`id`), + KEY `gen_business_id` (`gen_business_id`), + KEY `ix_sys_gen_model_id` (`id`), + CONSTRAINT `sys_gen_model_ibfk_1` FOREIGN KEY (`gen_business_id`) REFERENCES `sys_gen_business` (`id`) ON DELETE CASCADE ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; @@ -233,8 +277,8 @@ CREATE TABLE `sys_user` PRIMARY KEY (`id`), UNIQUE KEY `uuid` (`uuid`), UNIQUE KEY `nickname` (`nickname`), - UNIQUE KEY `ix_sys_user_username` (`username`), UNIQUE KEY `ix_sys_user_email` (`email`), + UNIQUE KEY `ix_sys_user_username` (`username`), KEY `dept_id` (`dept_id`), KEY `ix_sys_user_id` (`id`), CONSTRAINT `sys_user_ibfk_1` FOREIGN KEY (`dept_id`) REFERENCES `sys_dept` (`id`) ON DELETE SET NULL From 9764d6548b8ed41e1ba5e42cfe8baab295518c2d Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 10 Jul 2024 11:19:17 +0800 Subject: [PATCH 50/53] Update import sql query conditions --- backend/app/generator/api/v1/gen.py | 5 ++-- backend/app/generator/crud/crud_gen.py | 27 ++++++++++++++------ backend/app/generator/service/gen_service.py | 4 +-- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index 62a4ed043..69fa84351 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -77,7 +77,7 @@ async def delete_model(pk: Annotated[int, Path(...)]) -> ResponseModel: @router.get('/tables', summary='获取数据库表', dependencies=[DependsRBAC]) -async def get_all_tables(table_schema: Annotated[str | None, Query(description='数据库名')] = 'fba') -> ResponseModel: +async def get_all_tables(table_schema: Annotated[str, Query(..., description='数据库名')] = 'fba') -> ResponseModel: data = await gen_service.get_tables(table_schema=table_schema) return await response_base.success(data=data) @@ -86,8 +86,9 @@ async def get_all_tables(table_schema: Annotated[str | None, Query(description=' async def import_table( app: Annotated[str, Query(..., description='应用名称,用于代码生成到指定 app')], table_name: Annotated[str, Query(..., description='数据库表名')], + table_schema: Annotated[str, Query(..., description='数据库名')] = 'fba', ) -> ResponseModel: - await gen_service.import_business_and_model(app=app, table_name=table_name) + await gen_service.import_business_and_model(app=app, table_schema=table_schema, table_name=table_name) return await response_base.success() diff --git a/backend/app/generator/crud/crud_gen.py b/backend/app/generator/crud/crud_gen.py index 03236ce09..ecef470ed 100644 --- a/backend/app/generator/crud/crud_gen.py +++ b/backend/app/generator/crud/crud_gen.py @@ -10,8 +10,10 @@ class CRUDGen: @staticmethod async def get_all_tables(db: AsyncSession, table_schema: str) -> Sequence[str]: t = text( - 'select table_name as table_name from information_schema.tables ' - 'where table_name not like "sys_gen_%" and table_schema = :table_schema;' + 'select table_name as table_name ' + 'from information_schema.tables ' + 'where table_name not like "sys_gen_%" ' + 'and table_schema = :table_schema;' ).bindparams(table_schema=table_schema) stmt = await db.execute(t) return stmt.scalars().all() @@ -19,22 +21,31 @@ async def get_all_tables(db: AsyncSession, table_schema: str) -> Sequence[str]: @staticmethod async def get_table(db: AsyncSession, table_name: str) -> Row[tuple]: t = text( - 'select table_name as table_name, table_comment as table_comment from information_schema.tables ' - 'where table_name not like "sys_gen_%" and table_name = :table_name;' + 'select table_name as table_name, table_comment as table_comment ' + 'from information_schema.tables ' + 'where table_name not like "sys_gen_%" ' + 'and table_name = :table_name;' ).bindparams(table_name=table_name) stmt = await db.execute(t) return stmt.fetchone() @staticmethod - async def get_all_columns(db: AsyncSession, table_name: str) -> Sequence[Row[tuple]]: + async def get_all_columns(db: AsyncSession, table_schema: str, table_name: str) -> Sequence[Row[tuple]]: t = text( 'select column_name AS column_name, ' 'case when column_key = "PRI" then 1 else 0 end as is_pk, ' 'case when is_nullable = "NO" or column_key = "PRI" then 0 else 1 end as is_nullable, ' - 'ordinal_position as sort, column_comment as column_comment, column_type as column_type ' - 'from information_schema.columns where table_name = :table_name and column_name != "id" ' + 'ordinal_position as sort, ' + 'column_comment as column_comment, ' + 'column_type as column_type ' + 'from information_schema.columns ' + 'where table_schema = :table_schema ' + 'and table_name = :table_name ' + 'and column_name != "id" ' + 'and column_name != "created_time" ' + 'and column_name != "updated_time" ' 'order by sort;' - ).bindparams(table_name=table_name) + ).bindparams(table_schema=table_schema, table_name=table_name) stmt = await db.execute(t) return stmt.fetchall() diff --git a/backend/app/generator/service/gen_service.py b/backend/app/generator/service/gen_service.py index bbd168261..cd9220120 100644 --- a/backend/app/generator/service/gen_service.py +++ b/backend/app/generator/service/gen_service.py @@ -42,7 +42,7 @@ async def get_tables(*, table_schema: str) -> Sequence[str]: return await gen_dao.get_all_tables(db, table_schema) @staticmethod - async def import_business_and_model(*, app: str, table_name: str) -> None: + async def import_business_and_model(*, app: str, table_schema: str, table_name: str) -> None: async with async_db_session.begin() as db: table_info = await gen_dao.get_table(db, table_name) if not table_info: @@ -58,7 +58,7 @@ async def import_business_and_model(*, app: str, table_name: str) -> None: new_business = GenBusiness(**CreateGenBusinessParam(**business_data).model_dump()) db.add(new_business) await db.flush() - column_info = await gen_dao.get_all_columns(db, table_name) + column_info = await gen_dao.get_all_columns(db, table_schema, table_name) for column in column_info: column_type = column[-1].split('(')[0].lower() model_data = { From 77d153c2f2dd233709d80959c12c95ab189a9343 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 10 Jul 2024 11:23:41 +0800 Subject: [PATCH 51/53] Add todo comments --- backend/common/enums.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/common/enums.py b/backend/common/enums.py index be848912c..9ce64ef2c 100644 --- a/backend/common/enums.py +++ b/backend/common/enums.py @@ -93,6 +93,8 @@ class UserSocialType(StrEnum): class GenModelType(StrEnum): """代码生成模型类型""" + # 待优化 + # https://github.com/zy7y/dfs-generate/blob/master/dfs_generate/types_map.py ARRAY = 'array' BIGINT = 'bigint' BigInteger = 'biginteger' From ada250db04814c46433003c34d308ebbf1056e53 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 10 Jul 2024 11:39:18 +0800 Subject: [PATCH 52/53] Mark business creation interface --- backend/app/generator/api/v1/gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/generator/api/v1/gen.py b/backend/app/generator/api/v1/gen.py index 69fa84351..6a1647971 100644 --- a/backend/app/generator/api/v1/gen.py +++ b/backend/app/generator/api/v1/gen.py @@ -32,7 +32,7 @@ async def get_business(pk: Annotated[int, Path(...)]) -> ResponseModel: return await response_base.success(data=data) -@router.post('/businesses', summary='创建代码生成业务', dependencies=[DependsRBAC]) +@router.post('/businesses', summary='创建代码生成业务', deprecated=True, dependencies=[DependsRBAC]) async def create_business(obj: CreateGenBusinessParam) -> ResponseModel: await gen_business_service.create(obj=obj) return await response_base.success() From 9f8c7d15d3637b1d8600d0ed29d8dc885ca77037 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Wed, 10 Jul 2024 12:10:02 +0800 Subject: [PATCH 53/53] Update features in README --- README.md | 23 ++++++++++++----------- README.zh-CN.md | 13 +++++++------ 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index f01c274af..8e7173567 100644 --- a/README.md +++ b/README.md @@ -70,17 +70,18 @@ Luckily, we now have a demo site: [FBA UI](https://fba.xwboy.top/) ## Built-in features -1. [x] User management: system user role management, permission assignment -2. [x] Department Management: Configure the system organization (company, department, group...) -3. [x] Menu Management: Configuration of system menus, user menus, button privilege identification -4. [x] Role Management: Assign role menu privileges, assign role routing privileges -5. [x] Dictionary Management: Maintain common fixed data or parameters within the system. -6. [x] Operation Logs: logging and querying of normal and abnormal system operations. -7. [x] Login Authentication: graphical authentication code background authentication login -8. [x] Login Logs: Logging and querying of normal and abnormal user logins -9. [x] Service Monitoring: server hardware device information and status -10. [x] Scheduled tasks: automated tasks, asynchronous tasks, and function invocation are supported -11. [x] Interface Documentation: Automatically generate online interactive API interface documentation. +1. [x] User management: management of system user roles, assignment of permissions +2. [x] Departmental management: Configuration of the system organization (company, department, group, ...) +3. [x] Menu management: Configuration of system menus, user menus, button permission labels +4. [x] Role management: assignment of role menu privileges, assignment of role routing privileges +5. [x] Dictionary management: maintenance of commonly used fixed data or parameters within the system +6. [x] Code generation: back-end code is automatically generated, supporting preview, write and download. +7. [x] Operation log: logging and querying of normal and abnormal system operations. +8. [x] Login authentication: graphical captcha backend authentication login +9. [x] Logging: logging and querying of normal and abnormal user logins +10. [x] Service monitoring: server hardware device information and status +11. [x] Timed tasks: automated tasks, asynchronous tasks, support for function calls +12. [x] Interface Documentation: Automatically generate online interactive API interface documentation. ## Local development diff --git a/README.zh-CN.md b/README.zh-CN.md index 20e7ce563..0c0592f0b 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -68,12 +68,13 @@ mvc 架构作为常规设计模式,在 python web 中也很常见,但是三 3. [x] 菜单管理:配置系统菜单,用户菜单,按钮权限标识 4. [x] 角色管理:角色菜单权限分配,角色路由权限分配 5. [x] 字典管理:维护系统内部常用固定数据或参数 -6. [x] 操作日志:系统正常操作和异常操作日志记录和查询 -7. [x] 登录认证:图形验证码后台认证登录 -8. [x] 登录日志:用户正常登录和异常登录的日志记录与查询 -9. [x] 服务监控:服务器硬件设备信息与状态 -10. [x] 定时任务:自动化任务,异步任务,支持函数调用 -11. [x] 接口文档:自动生成在线交互式 API 接口文档 +6. [x] 代码生成:后端代码自动生成,支持预览,写入及下载 +7. [x] 操作日志:系统正常和异常操作的日志记录与查询 +8. [x] 登录认证:图形验证码后台认证登录 +9. [x] 登录日志:用户正常和异常登录的日志记录与查询 +10. [x] 服务监控:服务器硬件设备信息与状态 +11. [x] 定时任务:自动化任务,异步任务,支持函数调用 +12. [x] 接口文档:自动生成在线交互式 API 接口文档 ## 本地开发