mirror of
https://github.com/fastapi-practices/fastapi-best-architecture.git
synced 2026-09-21 13:12:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f8ae74ea5 | ||
|
|
da8e7c1b01 | ||
|
|
f513b98f15 | ||
|
|
80cef6dde7 | ||
|
|
2d504f281e | ||
|
|
fc34c98f0a | ||
|
|
1cc020efdd | ||
|
|
bb33e99e33 | ||
|
|
6c70b34ae6 | ||
|
|
960d9f6215 | ||
|
|
085f08ec3b |
@@ -1,3 +1,46 @@
|
|||||||
|
<a id="v1.1.0"></a>
|
||||||
|
# [v1.1.0](https://github.com/fastapi-practices/fastapi_best_architecture/releases/tag/v1.1.0) - 2025-04-17
|
||||||
|
|
||||||
|
## What's Changed
|
||||||
|
* Update the changelog for v1.0.5 by [@wu-clan](https://github.com/wu-clan) in [#572](https://github.com/fastapi-practices/fastapi_best_architecture/pull/572)
|
||||||
|
* Update the default value for some functions by [@wu-clan](https://github.com/wu-clan) in [#573](https://github.com/fastapi-practices/fastapi_best_architecture/pull/573)
|
||||||
|
* Optimize the file structure of code generator by [@wu-clan](https://github.com/wu-clan) in [#574](https://github.com/fastapi-practices/fastapi_best_architecture/pull/574)
|
||||||
|
* Update casbin RBAC verify to dynamic import by [@wu-clan](https://github.com/wu-clan) in [#576](https://github.com/fastapi-practices/fastapi_best_architecture/pull/576)
|
||||||
|
* Update unique columns in dict models by [@wu-clan](https://github.com/wu-clan) in [#577](https://github.com/fastapi-practices/fastapi_best_architecture/pull/577)
|
||||||
|
* Update the code generator to plugin by [@wu-clan](https://github.com/wu-clan) in [#578](https://github.com/fastapi-practices/fastapi_best_architecture/pull/578)
|
||||||
|
* Fix avatar url type of update avatar by [@huyuwei1996](https://github.com/huyuwei1996) in [#575](https://github.com/fastapi-practices/fastapi_best_architecture/pull/575)
|
||||||
|
* Update code generator file and table naming by [@wu-clan](https://github.com/wu-clan) in [#579](https://github.com/fastapi-practices/fastapi_best_architecture/pull/579)
|
||||||
|
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/fastapi-practices/fastapi_best_architecture/compare/v1.0.5...v1.1.0
|
||||||
|
|
||||||
|
[Changes][v1.1.0]
|
||||||
|
|
||||||
|
|
||||||
|
<a id="v1.0.5"></a>
|
||||||
|
# [v1.0.5](https://github.com/fastapi-practices/fastapi_best_architecture/releases/tag/v1.0.5) - 2025-04-09
|
||||||
|
|
||||||
|
## What's Changed
|
||||||
|
* Update the changelog for v1.0.4 by [@wu-clan](https://github.com/wu-clan) in [#558](https://github.com/fastapi-practices/fastapi_best_architecture/pull/558)
|
||||||
|
* Bump dependencies and pre-commits by [@wu-clan](https://github.com/wu-clan) in [#559](https://github.com/fastapi-practices/fastapi_best_architecture/pull/559)
|
||||||
|
* Add python 3.13 to GitHub ci by [@wu-clan](https://github.com/wu-clan) in [#560](https://github.com/fastapi-practices/fastapi_best_architecture/pull/560)
|
||||||
|
* Update the system config to plugin by [@wu-clan](https://github.com/wu-clan) in [#561](https://github.com/fastapi-practices/fastapi_best_architecture/pull/561)
|
||||||
|
* Update dict data and type to plugin by [@wu-clan](https://github.com/wu-clan) in [#562](https://github.com/fastapi-practices/fastapi_best_architecture/pull/562)
|
||||||
|
* Update menu and add vben5 compatibility by [@wu-clan](https://github.com/wu-clan) in [#563](https://github.com/fastapi-practices/fastapi_best_architecture/pull/563)
|
||||||
|
* Update the vben5 tree data structure by [@wu-clan](https://github.com/wu-clan) in [#564](https://github.com/fastapi-practices/fastapi_best_architecture/pull/564)
|
||||||
|
* Update custom validation error messages by [@wu-clan](https://github.com/wu-clan) in [#566](https://github.com/fastapi-practices/fastapi_best_architecture/pull/566)
|
||||||
|
* Update the number of pagination le by [@wu-clan](https://github.com/wu-clan) in [#565](https://github.com/fastapi-practices/fastapi_best_architecture/pull/565)
|
||||||
|
* Fix the login password verification by [@wu-clan](https://github.com/wu-clan) in [#568](https://github.com/fastapi-practices/fastapi_best_architecture/pull/568)
|
||||||
|
* Fix the failure hook of celery task by [@wu-clan](https://github.com/wu-clan) in [#569](https://github.com/fastapi-practices/fastapi_best_architecture/pull/569)
|
||||||
|
* Bump fastapi oauth2 from 0.0.1a2 to 0.0.1 by [@wu-clan](https://github.com/wu-clan) in [#570](https://github.com/fastapi-practices/fastapi_best_architecture/pull/570)
|
||||||
|
* Fix the log rule in gitignore by [@wu-clan](https://github.com/wu-clan) in [#571](https://github.com/fastapi-practices/fastapi_best_architecture/pull/571)
|
||||||
|
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/fastapi-practices/fastapi_best_architecture/compare/v1.0.4...v1.0.5
|
||||||
|
|
||||||
|
[Changes][v1.0.5]
|
||||||
|
|
||||||
|
|
||||||
<a id="v1.0.4"></a>
|
<a id="v1.0.4"></a>
|
||||||
# [v1.0.4](https://github.com/fastapi-practices/fastapi_best_architecture/releases/tag/v1.0.4) - 2025-03-28
|
# [v1.0.4](https://github.com/fastapi-practices/fastapi_best_architecture/releases/tag/v1.0.4) - 2025-03-28
|
||||||
|
|
||||||
@@ -403,6 +446,8 @@
|
|||||||
[Changes][v1.0.0]
|
[Changes][v1.0.0]
|
||||||
|
|
||||||
|
|
||||||
|
[v1.1.0]: https://github.com/fastapi-practices/fastapi_best_architecture/compare/v1.0.5...v1.1.0
|
||||||
|
[v1.0.5]: https://github.com/fastapi-practices/fastapi_best_architecture/compare/v1.0.4...v1.0.5
|
||||||
[v1.0.4]: https://github.com/fastapi-practices/fastapi_best_architecture/compare/v1.0.3...v1.0.4
|
[v1.0.4]: https://github.com/fastapi-practices/fastapi_best_architecture/compare/v1.0.3...v1.0.4
|
||||||
[v1.0.3]: https://github.com/fastapi-practices/fastapi_best_architecture/compare/v1.0.2...v1.0.3
|
[v1.0.3]: https://github.com/fastapi-practices/fastapi_best_architecture/compare/v1.0.2...v1.0.3
|
||||||
[v1.0.2]: https://github.com/fastapi-practices/fastapi_best_architecture/compare/v1.0.1...v1.0.2
|
[v1.0.2]: https://github.com/fastapi-practices/fastapi_best_architecture/compare/v1.0.1...v1.0.2
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from backend.plugin.tools import get_plugin_models
|
|||||||
|
|
||||||
# import your new model here
|
# import your new model here
|
||||||
from backend.app.admin.model import * # noqa: F401
|
from backend.app.admin.model import * # noqa: F401
|
||||||
from backend.app.generator.model import * # noqa: F401
|
from backend.plugin.code_generator.model import * # noqa: F401
|
||||||
|
|
||||||
# import plugin model
|
# import plugin model
|
||||||
for cls in get_plugin_models():
|
for cls in get_plugin_models():
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class CRUDDataRule(CRUDPlus[DataRule]):
|
|||||||
"""
|
"""
|
||||||
return await self.select_model(db, pk)
|
return await self.select_model(db, pk)
|
||||||
|
|
||||||
async def get_list(self, name: str | None = None) -> Select:
|
async def get_list(self, name: str | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取规则列表
|
获取规则列表
|
||||||
|
|
||||||
|
|||||||
@@ -35,12 +35,7 @@ class CRUDDept(CRUDPlus[Dept]):
|
|||||||
return await self.select_model_by_column(db, name=name, del_flag=0)
|
return await self.select_model_by_column(db, name=name, del_flag=0)
|
||||||
|
|
||||||
async def get_all(
|
async def get_all(
|
||||||
self,
|
self, db: AsyncSession, name: str | None, leader: str | None, phone: str | None, status: int | None
|
||||||
db: AsyncSession,
|
|
||||||
name: str | None = None,
|
|
||||||
leader: str | None = None,
|
|
||||||
phone: str | None = None,
|
|
||||||
status: int | None = None,
|
|
||||||
) -> Sequence[Dept]:
|
) -> Sequence[Dept]:
|
||||||
"""
|
"""
|
||||||
获取所有部门
|
获取所有部门
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from backend.app.admin.schema.login_log import CreateLoginLogParam
|
|||||||
class CRUDLoginLog(CRUDPlus[LoginLog]):
|
class CRUDLoginLog(CRUDPlus[LoginLog]):
|
||||||
"""登录日志数据库操作类"""
|
"""登录日志数据库操作类"""
|
||||||
|
|
||||||
async def get_list(self, username: str | None = None, status: int | None = None, ip: str | None = None) -> Select:
|
async def get_list(self, username: str | None, status: int | None, ip: str | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取登录日志列表
|
获取登录日志列表
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class CRUDMenu(CRUDPlus[Menu]):
|
|||||||
"""
|
"""
|
||||||
return await self.select_model_by_column(db, title=title, menu_type__ne=2)
|
return await self.select_model_by_column(db, title=title, menu_type__ne=2)
|
||||||
|
|
||||||
async def get_all(self, db: AsyncSession, title: str | None = None, status: int | None = None) -> Sequence[Menu]:
|
async def get_all(self, db: AsyncSession, title: str | None, status: int | None) -> Sequence[Menu]:
|
||||||
"""
|
"""
|
||||||
获取菜单列表
|
获取菜单列表
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from backend.app.admin.schema.opera_log import CreateOperaLogParam
|
|||||||
class CRUDOperaLogDao(CRUDPlus[OperaLog]):
|
class CRUDOperaLogDao(CRUDPlus[OperaLog]):
|
||||||
"""操作日志数据库操作类"""
|
"""操作日志数据库操作类"""
|
||||||
|
|
||||||
async def get_list(self, username: str | None = None, status: int | None = None, ip: str | None = None) -> Select:
|
async def get_list(self, username: str | None, status: int | None, ip: str | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取操作日志列表
|
获取操作日志列表
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class CRUDRole(CRUDPlus[Role]):
|
|||||||
roles = await db.execute(stmt)
|
roles = await db.execute(stmt)
|
||||||
return roles.scalars().all()
|
return roles.scalars().all()
|
||||||
|
|
||||||
async def get_list(self, name: str | None = None, status: int | None = None) -> Select:
|
async def get_list(self, name: str | None, status: int | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取角色列表
|
获取角色列表
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ class CRUDUser(CRUDPlus[User]):
|
|||||||
:param avatar: 头像地址
|
:param avatar: 头像地址
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return await self.update_model(db, input_user, {'avatar': avatar.url})
|
return await self.update_model(db, input_user, {'avatar': str(avatar.url)})
|
||||||
|
|
||||||
async def delete(self, db: AsyncSession, user_id: int) -> int:
|
async def delete(self, db: AsyncSession, user_id: int) -> int:
|
||||||
"""
|
"""
|
||||||
@@ -175,9 +175,7 @@ class CRUDUser(CRUDPlus[User]):
|
|||||||
"""
|
"""
|
||||||
return await self.update_model(db, pk, {'password': new_pwd})
|
return await self.update_model(db, pk, {'password': new_pwd})
|
||||||
|
|
||||||
async def get_list(
|
async def get_list(self, dept: int | None, username: str | None, phone: str | None, status: int | None) -> Select:
|
||||||
self, dept: int | None = None, username: str | None = None, phone: str | None = None, status: int | None = None
|
|
||||||
) -> Select:
|
|
||||||
"""
|
"""
|
||||||
获取用户列表
|
获取用户列表
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class DataRuleService:
|
|||||||
return model_columns
|
return model_columns
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_select(*, name: str | None = None) -> Select:
|
async def get_select(*, name: str | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取数据规则列表查询条件
|
获取数据规则列表查询条件
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class DeptService:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_dept_tree(
|
async def get_dept_tree(
|
||||||
*, name: str | None = None, leader: str | None = None, phone: str | None = None, status: int | None = None
|
*, name: str | None, leader: str | None, phone: str | None, status: int | None
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
获取部门树形结构
|
获取部门树形结构
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class LoginLogService:
|
|||||||
"""登录日志服务类"""
|
"""登录日志服务类"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_select(*, username: str | None = None, status: int | None = None, ip: str | None = None) -> Select:
|
async def get_select(*, username: str | None, status: int | None, ip: str | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取登录日志列表查询条件
|
获取登录日志列表查询条件
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class MenuService:
|
|||||||
return menu
|
return menu
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_menu_tree(*, title: str | None = None, status: int | None = None) -> list[dict[str, Any]]:
|
async def get_menu_tree(*, title: str | None, status: int | None) -> list[dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
获取菜单树形结构
|
获取菜单树形结构
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class OperaLogService:
|
|||||||
"""操作日志服务类"""
|
"""操作日志服务类"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_select(*, username: str | None = None, status: int | None = None, ip: str | None = None) -> Select:
|
async def get_select(*, username: str | None, status: int | None, ip: str | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取操作日志列表查询条件
|
获取操作日志列表查询条件
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class RoleService:
|
|||||||
return roles
|
return roles
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_select(*, name: str | None = None, status: int | None = None) -> Select:
|
async def get_select(*, name: str | None, status: int | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取角色列表查询条件
|
获取角色列表查询条件
|
||||||
|
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ class UserService:
|
|||||||
return count
|
return count
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_select(*, dept: int, username: str = None, phone: str = None, status: int = None) -> Select:
|
async def get_select(*, dept: int, username: str, phone: str, status: int) -> Select:
|
||||||
"""
|
"""
|
||||||
获取用户列表查询条件
|
获取用户列表查询条件
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from fastapi import APIRouter
|
|
||||||
|
|
||||||
from backend.app.generator.api.v1.gen import router as gen_router
|
|
||||||
from backend.app.generator.api.v1.gen_business import router as gen_business_router
|
|
||||||
from backend.app.generator.api.v1.gen_model import router as gen_model_router
|
|
||||||
from backend.core.conf import settings
|
|
||||||
|
|
||||||
v1 = APIRouter(prefix=f'{settings.FASTAPI_API_V1_PATH}/gen', tags=['代码生成'])
|
|
||||||
|
|
||||||
v1.include_router(gen_router)
|
|
||||||
v1.include_router(gen_business_router, prefix='/businesses')
|
|
||||||
v1.include_router(gen_model_router, prefix='/models')
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from backend.app.generator.model.gen_business import GenBusiness
|
|
||||||
from backend.app.generator.model.gen_model import GenModel
|
|
||||||
@@ -3,11 +3,9 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from backend.app.admin.api.router import v1 as admin_v1
|
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.app.task.api.router import v1 as task_v1
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
router.include_router(admin_v1)
|
router.include_router(admin_v1)
|
||||||
router.include_router(generator_v1)
|
|
||||||
router.include_router(task_v1)
|
router.include_router(task_v1)
|
||||||
|
|||||||
@@ -119,153 +119,3 @@ class FileType(StrEnum):
|
|||||||
|
|
||||||
image = 'image'
|
image = 'image'
|
||||||
video = 'video'
|
video = 'video'
|
||||||
|
|
||||||
|
|
||||||
class GenModelMySQLColumnType(StrEnum):
|
|
||||||
"""代码生成模型列类型(MySQL)"""
|
|
||||||
|
|
||||||
# Python 类型映射
|
|
||||||
BIGINT = 'int'
|
|
||||||
BigInteger = 'int' # BIGINT
|
|
||||||
BINARY = 'bytes'
|
|
||||||
BLOB = 'bytes'
|
|
||||||
BOOLEAN = 'bool' # BOOL
|
|
||||||
Boolean = 'bool' # BOOL
|
|
||||||
CHAR = 'str'
|
|
||||||
CLOB = 'str'
|
|
||||||
DATE = 'date'
|
|
||||||
Date = 'date' # DATE
|
|
||||||
DATETIME = 'datetime'
|
|
||||||
DateTime = 'datetime' # DATETIME
|
|
||||||
DECIMAL = 'Decimal'
|
|
||||||
DOUBLE = 'float'
|
|
||||||
Double = 'float' # DOUBLE
|
|
||||||
DOUBLE_PRECISION = 'float'
|
|
||||||
Enum = 'Enum' # Enum()
|
|
||||||
FLOAT = 'float'
|
|
||||||
Float = 'float' # FLOAT
|
|
||||||
INT = 'int' # INTEGER
|
|
||||||
INTEGER = 'int'
|
|
||||||
Integer = 'int' # INTEGER
|
|
||||||
Interval = 'timedelta' # DATETIME
|
|
||||||
JSON = 'dict'
|
|
||||||
LargeBinary = 'bytes' # BLOB
|
|
||||||
NCHAR = 'str'
|
|
||||||
NUMERIC = 'Decimal'
|
|
||||||
Numeric = 'Decimal' # NUMERIC
|
|
||||||
NVARCHAR = 'str' # String
|
|
||||||
PickleType = 'bytes' # BLOB
|
|
||||||
REAL = 'float'
|
|
||||||
SMALLINT = 'int'
|
|
||||||
SmallInteger = 'int' # SMALLINT
|
|
||||||
String = 'str' # String
|
|
||||||
TEXT = 'str'
|
|
||||||
Text = 'str' # TEXT
|
|
||||||
TIME = 'time'
|
|
||||||
Time = 'time' # TIME
|
|
||||||
TIMESTAMP = 'datetime'
|
|
||||||
Unicode = 'str' # String
|
|
||||||
UnicodeText = 'str' # TEXT
|
|
||||||
UUID = 'str | UUID'
|
|
||||||
Uuid = 'str' # CHAR(32)
|
|
||||||
VARBINARY = 'bytes'
|
|
||||||
VARCHAR = 'str' # String
|
|
||||||
|
|
||||||
# sa.dialects.mysql 导入
|
|
||||||
BIT = 'bool'
|
|
||||||
ENUM = 'Enum'
|
|
||||||
LONGBLOB = 'bytes'
|
|
||||||
LONGTEXT = 'str'
|
|
||||||
MEDIUMBLOB = 'bytes'
|
|
||||||
MEDIUMINT = 'int'
|
|
||||||
MEDIUMTEXT = 'str'
|
|
||||||
SET = 'list[str]'
|
|
||||||
TINYBLOB = 'bytes'
|
|
||||||
TINYINT = 'int'
|
|
||||||
TINYTEXT = 'str'
|
|
||||||
YEAR = 'int'
|
|
||||||
|
|
||||||
|
|
||||||
class GenModelPostgreSQLColumnType(StrEnum):
|
|
||||||
"""代码生成模型列类型(PostgreSQL)"""
|
|
||||||
|
|
||||||
# Python 类型映射
|
|
||||||
BIGINT = 'int'
|
|
||||||
BigInteger = 'int' # BIGINT
|
|
||||||
BINARY = 'bytes'
|
|
||||||
BLOB = 'bytes'
|
|
||||||
BOOLEAN = 'bool'
|
|
||||||
Boolean = 'bool' # BOOLEAN
|
|
||||||
CHAR = 'str'
|
|
||||||
CLOB = 'str'
|
|
||||||
DATE = 'date'
|
|
||||||
Date = 'date' # DATE
|
|
||||||
DATETIME = 'datetime'
|
|
||||||
DateTime = 'datetime' # TIMESTAMP WITHOUT TIME ZONE
|
|
||||||
DECIMAL = 'Decimal'
|
|
||||||
DOUBLE = 'float'
|
|
||||||
Double = 'float' # DOUBLE PRECISION
|
|
||||||
DOUBLE_PRECISION = 'float' # DOUBLE PRECISION
|
|
||||||
Enum = 'Enum' # Enum(name='enum')
|
|
||||||
FLOAT = 'float'
|
|
||||||
Float = 'float' # FLOAT
|
|
||||||
INT = 'int' # INTEGER
|
|
||||||
INTEGER = 'int'
|
|
||||||
Integer = 'int' # INTEGER
|
|
||||||
Interval = 'timedelta' # INTERVAL
|
|
||||||
JSON = 'dict'
|
|
||||||
LargeBinary = 'bytes' # BYTEA
|
|
||||||
NCHAR = 'str'
|
|
||||||
NUMERIC = 'Decimal'
|
|
||||||
Numeric = 'Decimal' # NUMERIC
|
|
||||||
NVARCHAR = 'str' # String
|
|
||||||
PickleType = 'bytes' # BYTEA
|
|
||||||
REAL = 'float'
|
|
||||||
SMALLINT = 'int'
|
|
||||||
SmallInteger = 'int' # SMALLINT
|
|
||||||
String = 'str' # String
|
|
||||||
TEXT = 'str'
|
|
||||||
Text = 'str' # TEXT
|
|
||||||
TIME = 'time' # TIME WITHOUT TIME ZONE
|
|
||||||
Time = 'time' # TIME WITHOUT TIME ZONE
|
|
||||||
TIMESTAMP = 'datetime' # TIMESTAMP WITHOUT TIME ZONE
|
|
||||||
Unicode = 'str' # String
|
|
||||||
UnicodeText = 'str' # TEXT
|
|
||||||
UUID = 'str | UUID'
|
|
||||||
Uuid = 'str'
|
|
||||||
VARBINARY = 'bytes'
|
|
||||||
VARCHAR = 'str' # String
|
|
||||||
|
|
||||||
# sa.dialects.postgresql 导入
|
|
||||||
ARRAY = 'list'
|
|
||||||
BIT = 'bool'
|
|
||||||
BYTEA = 'bytes'
|
|
||||||
CIDR = 'str'
|
|
||||||
CITEXT = 'str'
|
|
||||||
DATEMULTIRANGE = 'list[date]'
|
|
||||||
DATERANGE = 'tuple[date, date]'
|
|
||||||
DOMAIN = 'str'
|
|
||||||
ENUM = 'Enum'
|
|
||||||
HSTORE = 'dict'
|
|
||||||
INET = 'str'
|
|
||||||
INT4MULTIRANGE = 'list[int]'
|
|
||||||
INT4RANGE = 'tuple[int, int]'
|
|
||||||
INT8MULTIRANGE = 'list[int]'
|
|
||||||
INT8RANGE = 'tuple[int, int]'
|
|
||||||
INTERVAL = 'timedelta'
|
|
||||||
JSONB = 'dict'
|
|
||||||
JSONPATH = 'str'
|
|
||||||
MACADDR = 'str'
|
|
||||||
MACADDR8 = 'str'
|
|
||||||
MONEY = 'Decimal'
|
|
||||||
NUMMULTIRANGE = 'list[Decimal]'
|
|
||||||
NUMRANGE = 'tuple[Decimal, Decimal]'
|
|
||||||
OID = 'int'
|
|
||||||
REGCLASS = 'str'
|
|
||||||
REGCONFIG = 'str'
|
|
||||||
TSMULTIRANGE = 'list[datetime]'
|
|
||||||
TSQUERY = 'str'
|
|
||||||
TSRANGE = 'tuple[datetime, datetime]'
|
|
||||||
TSTZMULTIRANGE = 'list[datetime]'
|
|
||||||
TSTZRANGE = 'tuple[datetime, datetime]'
|
|
||||||
TSVECTOR = 'str'
|
|
||||||
|
|||||||
@@ -68,7 +68,9 @@ class ResponseBase:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __response(
|
def __response(
|
||||||
*, res: CustomResponseCode | CustomResponse = None, data: Any | None = None
|
*,
|
||||||
|
res: CustomResponseCode | CustomResponse,
|
||||||
|
data: Any | None,
|
||||||
) -> ResponseModel | ResponseSchemaModel:
|
) -> ResponseModel | ResponseSchemaModel:
|
||||||
"""
|
"""
|
||||||
请求返回通用方法
|
请求返回通用方法
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
from fastapi import Depends, Request
|
from fastapi import Depends, Request
|
||||||
|
|
||||||
from backend.common.enums import MethodType, StatusType
|
from backend.common.enums import MethodType, StatusType
|
||||||
|
from backend.common.exception import errors
|
||||||
from backend.common.exception.errors import AuthorizationError, TokenError
|
from backend.common.exception.errors import AuthorizationError, TokenError
|
||||||
|
from backend.common.log import log
|
||||||
from backend.common.security.jwt import DependsJwtAuth
|
from backend.common.security.jwt import DependsJwtAuth
|
||||||
from backend.core.conf import settings
|
from backend.core.conf import settings
|
||||||
from backend.plugin.casbin.utils.rbac import casbin_verify
|
from backend.utils.import_parse import import_module_cached
|
||||||
|
|
||||||
|
|
||||||
async def rbac_verify(request: Request, _token: str = DependsJwtAuth) -> None:
|
async def rbac_verify(request: Request, _token: str = DependsJwtAuth) -> None:
|
||||||
@@ -67,6 +69,13 @@ async def rbac_verify(request: Request, _token: str = DependsJwtAuth) -> None:
|
|||||||
if path_auth_perm not in allow_perms:
|
if path_auth_perm not in allow_perms:
|
||||||
raise AuthorizationError
|
raise AuthorizationError
|
||||||
else:
|
else:
|
||||||
|
try:
|
||||||
|
casbin_rbac = import_module_cached('backend.plugin.casbin.utils.rbac')
|
||||||
|
casbin_verify = getattr(casbin_rbac, 'casbin_verify')
|
||||||
|
except (ImportError, AttributeError) as e:
|
||||||
|
log.error(f'正在通过 casbin 执行 RBAC 权限校验,但此插件不存在: {e}')
|
||||||
|
raise errors.ServerError(msg='权限校验失败,请联系系统管理员')
|
||||||
|
|
||||||
await casbin_verify(request)
|
await casbin_verify(request)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ STATIC_DIR = BASE_PATH / 'static'
|
|||||||
# 上传文件目录
|
# 上传文件目录
|
||||||
UPLOAD_DIR = STATIC_DIR / 'upload'
|
UPLOAD_DIR = STATIC_DIR / 'upload'
|
||||||
|
|
||||||
# jinja2 模版文件路径
|
|
||||||
JINJA2_TEMPLATE_DIR = BASE_PATH / 'templates'
|
|
||||||
|
|
||||||
# 插件目录
|
# 插件目录
|
||||||
PLUGIN_DIR = BASE_PATH / 'plugin'
|
PLUGIN_DIR = BASE_PATH / 'plugin'
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from backend.database.redis import redis_client
|
|||||||
from backend.middleware.jwt_auth_middleware import JwtAuthMiddleware
|
from backend.middleware.jwt_auth_middleware import JwtAuthMiddleware
|
||||||
from backend.middleware.opera_log_middleware import OperaLogMiddleware
|
from backend.middleware.opera_log_middleware import OperaLogMiddleware
|
||||||
from backend.middleware.state_middleware import StateMiddleware
|
from backend.middleware.state_middleware import StateMiddleware
|
||||||
from backend.plugin.tools import plugin_router_inject
|
from backend.plugin.tools import build_final_router
|
||||||
from backend.utils.demo_site import demo_site
|
from backend.utils.demo_site import demo_site
|
||||||
from backend.utils.health_check import ensure_unique_route_names, http_limit_callback
|
from backend.utils.health_check import ensure_unique_route_names, http_limit_callback
|
||||||
from backend.utils.openapi import simplify_operation_ids
|
from backend.utils.openapi import simplify_operation_ids
|
||||||
@@ -157,12 +157,8 @@ def register_router(app: FastAPI) -> None:
|
|||||||
"""
|
"""
|
||||||
dependencies = [Depends(demo_site)] if settings.DEMO_MODE else None
|
dependencies = [Depends(demo_site)] if settings.DEMO_MODE else None
|
||||||
|
|
||||||
# 插件路由
|
# API
|
||||||
plugin_router_inject()
|
router = build_final_router()
|
||||||
|
|
||||||
# 系统路由(必须在插件路由注入后导入)
|
|
||||||
from backend.app.router import router
|
|
||||||
|
|
||||||
app.include_router(router, dependencies=dependencies)
|
app.include_router(router, dependencies=dependencies)
|
||||||
|
|
||||||
# Extra
|
# Extra
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class CasbinService:
|
|||||||
return await casbin_dao.get_list(ptype, sub)
|
return await casbin_dao.get_list(ptype, sub)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_policy_list(*, role: int | None = None) -> list:
|
async def get_policy_list(*, role: int | None) -> list:
|
||||||
"""
|
"""
|
||||||
获取 P 策略列表
|
获取 P 策略列表
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from backend.core.conf import settings
|
||||||
|
from backend.plugin.code_generator.api.v1.business import router as business_router
|
||||||
|
from backend.plugin.code_generator.api.v1.column import router as model_router
|
||||||
|
from backend.plugin.code_generator.api.v1.gen import router as gen_router
|
||||||
|
|
||||||
|
v1 = APIRouter(prefix=f'{settings.FASTAPI_API_V1_PATH}/gen', tags=['代码生成'])
|
||||||
|
|
||||||
|
v1.include_router(gen_router)
|
||||||
|
v1.include_router(business_router, prefix='/businesses')
|
||||||
|
v1.include_router(model_router, prefix='/models')
|
||||||
+8
-8
@@ -4,18 +4,18 @@ from typing import Annotated
|
|||||||
|
|
||||||
from fastapi import APIRouter, Depends, Path
|
from fastapi import APIRouter, Depends, Path
|
||||||
|
|
||||||
from backend.app.generator.schema.gen_business import (
|
|
||||||
CreateGenBusinessParam,
|
|
||||||
GetGenBusinessDetail,
|
|
||||||
UpdateGenBusinessParam,
|
|
||||||
)
|
|
||||||
from backend.app.generator.schema.gen_model import GetGenModelDetail
|
|
||||||
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.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
|
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
|
||||||
from backend.common.security.jwt import DependsJwtAuth
|
from backend.common.security.jwt import DependsJwtAuth
|
||||||
from backend.common.security.permission import RequestPermission
|
from backend.common.security.permission import RequestPermission
|
||||||
from backend.common.security.rbac import DependsRBAC
|
from backend.common.security.rbac import DependsRBAC
|
||||||
|
from backend.plugin.code_generator.schema.business import (
|
||||||
|
CreateGenBusinessParam,
|
||||||
|
GetGenBusinessDetail,
|
||||||
|
UpdateGenBusinessParam,
|
||||||
|
)
|
||||||
|
from backend.plugin.code_generator.schema.column import GetGenModelDetail
|
||||||
|
from backend.plugin.code_generator.service.business_service import gen_business_service
|
||||||
|
from backend.plugin.code_generator.service.column_service import gen_model_service
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
+2
-2
@@ -4,12 +4,12 @@ from typing import Annotated
|
|||||||
|
|
||||||
from fastapi import APIRouter, Depends, Path
|
from fastapi import APIRouter, Depends, Path
|
||||||
|
|
||||||
from backend.app.generator.schema.gen_model import CreateGenModelParam, GetGenModelDetail, UpdateGenModelParam
|
|
||||||
from backend.app.generator.service.gen_model_service import gen_model_service
|
|
||||||
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
|
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
|
||||||
from backend.common.security.jwt import DependsJwtAuth
|
from backend.common.security.jwt import DependsJwtAuth
|
||||||
from backend.common.security.permission import RequestPermission
|
from backend.common.security.permission import RequestPermission
|
||||||
from backend.common.security.rbac import DependsRBAC
|
from backend.common.security.rbac import DependsRBAC
|
||||||
|
from backend.plugin.code_generator.schema.column import CreateGenModelParam, GetGenModelDetail, UpdateGenModelParam
|
||||||
|
from backend.plugin.code_generator.service.column_service import gen_model_service
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@@ -5,13 +5,13 @@ from typing import Annotated
|
|||||||
from fastapi import APIRouter, Depends, Path, Query
|
from fastapi import APIRouter, Depends, Path, Query
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
|
|
||||||
from backend.app.generator.conf import generator_settings
|
|
||||||
from backend.app.generator.schema.gen import ImportParam
|
|
||||||
from backend.app.generator.service.gen_service import gen_service
|
|
||||||
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
|
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
|
||||||
from backend.common.security.jwt import DependsJwtAuth
|
from backend.common.security.jwt import DependsJwtAuth
|
||||||
from backend.common.security.permission import RequestPermission
|
from backend.common.security.permission import RequestPermission
|
||||||
from backend.common.security.rbac import DependsRBAC
|
from backend.common.security.rbac import DependsRBAC
|
||||||
|
from backend.plugin.code_generator.conf import generator_settings
|
||||||
|
from backend.plugin.code_generator.schema.gen import ImportParam
|
||||||
|
from backend.plugin.code_generator.service.gen_service import gen_service
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@@ -8,9 +8,6 @@ from pydantic_settings import BaseSettings
|
|||||||
class GeneratorSettings(BaseSettings):
|
class GeneratorSettings(BaseSettings):
|
||||||
"""代码生成配置"""
|
"""代码生成配置"""
|
||||||
|
|
||||||
# 模版
|
|
||||||
TEMPLATE_BACKEND_DIR_NAME: str = 'py'
|
|
||||||
|
|
||||||
# 代码下载
|
# 代码下载
|
||||||
DOWNLOAD_ZIP_FILENAME: str = 'fba_generator'
|
DOWNLOAD_ZIP_FILENAME: str = 'fba_generator'
|
||||||
|
|
||||||
+2
-2
@@ -5,8 +5,8 @@ from typing import Sequence
|
|||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy_crud_plus import CRUDPlus
|
from sqlalchemy_crud_plus import CRUDPlus
|
||||||
|
|
||||||
from backend.app.generator.model import GenBusiness
|
from backend.plugin.code_generator.model import GenBusiness
|
||||||
from backend.app.generator.schema.gen_business import CreateGenBusinessParam, UpdateGenBusinessParam
|
from backend.plugin.code_generator.schema.business import CreateGenBusinessParam, UpdateGenBusinessParam
|
||||||
|
|
||||||
|
|
||||||
class CRUDGenBusiness(CRUDPlus[GenBusiness]):
|
class CRUDGenBusiness(CRUDPlus[GenBusiness]):
|
||||||
+8
-8
@@ -5,14 +5,14 @@ from typing import Sequence
|
|||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy_crud_plus import CRUDPlus
|
from sqlalchemy_crud_plus import CRUDPlus
|
||||||
|
|
||||||
from backend.app.generator.model import GenModel
|
from backend.plugin.code_generator.model import GenColumn
|
||||||
from backend.app.generator.schema.gen_model import CreateGenModelParam, UpdateGenModelParam
|
from backend.plugin.code_generator.schema.column import CreateGenModelParam, UpdateGenModelParam
|
||||||
|
|
||||||
|
|
||||||
class CRUDGenModel(CRUDPlus[GenModel]):
|
class CRUDGenModel(CRUDPlus[GenColumn]):
|
||||||
"""代码生成模型 CRUD 类"""
|
"""代码生成模型 CRUD 类"""
|
||||||
|
|
||||||
async def get(self, db: AsyncSession, pk: int) -> GenModel | None:
|
async def get(self, db: AsyncSession, pk: int) -> GenColumn | None:
|
||||||
"""
|
"""
|
||||||
获取代码生成模型列
|
获取代码生成模型列
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ class CRUDGenModel(CRUDPlus[GenModel]):
|
|||||||
"""
|
"""
|
||||||
return await self.select_model(db, pk)
|
return await self.select_model(db, pk)
|
||||||
|
|
||||||
async def get_all_by_business(self, db: AsyncSession, business_id: int) -> Sequence[GenModel]:
|
async def get_all_by_business(self, db: AsyncSession, business_id: int) -> Sequence[GenColumn]:
|
||||||
"""
|
"""
|
||||||
获取所有代码生成模型列
|
获取所有代码生成模型列
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ class CRUDGenModel(CRUDPlus[GenModel]):
|
|||||||
"""
|
"""
|
||||||
return await self.select_models_order(db, sort_columns='sort', gen_business_id=business_id)
|
return await self.select_models_order(db, sort_columns='sort', gen_business_id=business_id)
|
||||||
|
|
||||||
async def create(self, db: AsyncSession, obj: CreateGenModelParam, pd_type: str | None = None) -> None:
|
async def create(self, db: AsyncSession, obj: CreateGenModelParam, pd_type: str | None) -> None:
|
||||||
"""
|
"""
|
||||||
创建代码生成模型
|
创建代码生成模型
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ class CRUDGenModel(CRUDPlus[GenModel]):
|
|||||||
"""
|
"""
|
||||||
await self.create_model(db, obj, pd_type=pd_type)
|
await self.create_model(db, obj, pd_type=pd_type)
|
||||||
|
|
||||||
async def update(self, db: AsyncSession, pk: int, obj: UpdateGenModelParam, pd_type: str | None = None) -> int:
|
async def update(self, db: AsyncSession, pk: int, obj: UpdateGenModelParam, pd_type: str | None) -> int:
|
||||||
"""
|
"""
|
||||||
更新代码生成模型
|
更新代码生成模型
|
||||||
|
|
||||||
@@ -66,4 +66,4 @@ class CRUDGenModel(CRUDPlus[GenModel]):
|
|||||||
return await self.delete_model(db, pk)
|
return await self.delete_model(db, pk)
|
||||||
|
|
||||||
|
|
||||||
gen_model_dao: CRUDGenModel = CRUDGenModel(GenModel)
|
gen_model_dao: CRUDGenModel = CRUDGenModel(GenColumn)
|
||||||
+1
-1
@@ -12,7 +12,7 @@ class CRUDGen:
|
|||||||
"""代码生成 CRUD 类"""
|
"""代码生成 CRUD 类"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_all_tables(db: AsyncSession, table_schema: str) -> list[str]:
|
async def get_all_tables(db: AsyncSession, table_schema: str) -> Sequence[str]:
|
||||||
"""
|
"""
|
||||||
获取所有表名
|
获取所有表名
|
||||||
|
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from backend.common.enums import StrEnum
|
||||||
|
|
||||||
|
|
||||||
|
class GenModelMySQLColumnType(StrEnum):
|
||||||
|
"""代码生成模型列类型(MySQL)"""
|
||||||
|
|
||||||
|
# Python 类型映射
|
||||||
|
BIGINT = 'int'
|
||||||
|
BigInteger = 'int' # BIGINT
|
||||||
|
BINARY = 'bytes'
|
||||||
|
BLOB = 'bytes'
|
||||||
|
BOOLEAN = 'bool' # BOOL
|
||||||
|
Boolean = 'bool' # BOOL
|
||||||
|
CHAR = 'str'
|
||||||
|
CLOB = 'str'
|
||||||
|
DATE = 'date'
|
||||||
|
Date = 'date' # DATE
|
||||||
|
DATETIME = 'datetime'
|
||||||
|
DateTime = 'datetime' # DATETIME
|
||||||
|
DECIMAL = 'Decimal'
|
||||||
|
DOUBLE = 'float'
|
||||||
|
Double = 'float' # DOUBLE
|
||||||
|
DOUBLE_PRECISION = 'float'
|
||||||
|
Enum = 'Enum' # Enum()
|
||||||
|
FLOAT = 'float'
|
||||||
|
Float = 'float' # FLOAT
|
||||||
|
INT = 'int' # INTEGER
|
||||||
|
INTEGER = 'int'
|
||||||
|
Integer = 'int' # INTEGER
|
||||||
|
Interval = 'timedelta' # DATETIME
|
||||||
|
JSON = 'dict'
|
||||||
|
LargeBinary = 'bytes' # BLOB
|
||||||
|
NCHAR = 'str'
|
||||||
|
NUMERIC = 'Decimal'
|
||||||
|
Numeric = 'Decimal' # NUMERIC
|
||||||
|
NVARCHAR = 'str' # String
|
||||||
|
PickleType = 'bytes' # BLOB
|
||||||
|
REAL = 'float'
|
||||||
|
SMALLINT = 'int'
|
||||||
|
SmallInteger = 'int' # SMALLINT
|
||||||
|
String = 'str' # String
|
||||||
|
TEXT = 'str'
|
||||||
|
Text = 'str' # TEXT
|
||||||
|
TIME = 'time'
|
||||||
|
Time = 'time' # TIME
|
||||||
|
TIMESTAMP = 'datetime'
|
||||||
|
Unicode = 'str' # String
|
||||||
|
UnicodeText = 'str' # TEXT
|
||||||
|
UUID = 'str | UUID'
|
||||||
|
Uuid = 'str' # CHAR(32)
|
||||||
|
VARBINARY = 'bytes'
|
||||||
|
VARCHAR = 'str' # String
|
||||||
|
|
||||||
|
# sa.dialects.mysql 导入
|
||||||
|
BIT = 'bool'
|
||||||
|
ENUM = 'Enum'
|
||||||
|
LONGBLOB = 'bytes'
|
||||||
|
LONGTEXT = 'str'
|
||||||
|
MEDIUMBLOB = 'bytes'
|
||||||
|
MEDIUMINT = 'int'
|
||||||
|
MEDIUMTEXT = 'str'
|
||||||
|
SET = 'list[str]'
|
||||||
|
TINYBLOB = 'bytes'
|
||||||
|
TINYINT = 'int'
|
||||||
|
TINYTEXT = 'str'
|
||||||
|
YEAR = 'int'
|
||||||
|
|
||||||
|
|
||||||
|
class GenModelPostgreSQLColumnType(StrEnum):
|
||||||
|
"""代码生成模型列类型(PostgreSQL)"""
|
||||||
|
|
||||||
|
# Python 类型映射
|
||||||
|
BIGINT = 'int'
|
||||||
|
BigInteger = 'int' # BIGINT
|
||||||
|
BINARY = 'bytes'
|
||||||
|
BLOB = 'bytes'
|
||||||
|
BOOLEAN = 'bool'
|
||||||
|
Boolean = 'bool' # BOOLEAN
|
||||||
|
CHAR = 'str'
|
||||||
|
CLOB = 'str'
|
||||||
|
DATE = 'date'
|
||||||
|
Date = 'date' # DATE
|
||||||
|
DATETIME = 'datetime'
|
||||||
|
DateTime = 'datetime' # TIMESTAMP WITHOUT TIME ZONE
|
||||||
|
DECIMAL = 'Decimal'
|
||||||
|
DOUBLE = 'float'
|
||||||
|
Double = 'float' # DOUBLE PRECISION
|
||||||
|
DOUBLE_PRECISION = 'float' # DOUBLE PRECISION
|
||||||
|
Enum = 'Enum' # Enum(name='enum')
|
||||||
|
FLOAT = 'float'
|
||||||
|
Float = 'float' # FLOAT
|
||||||
|
INT = 'int' # INTEGER
|
||||||
|
INTEGER = 'int'
|
||||||
|
Integer = 'int' # INTEGER
|
||||||
|
Interval = 'timedelta' # INTERVAL
|
||||||
|
JSON = 'dict'
|
||||||
|
LargeBinary = 'bytes' # BYTEA
|
||||||
|
NCHAR = 'str'
|
||||||
|
NUMERIC = 'Decimal'
|
||||||
|
Numeric = 'Decimal' # NUMERIC
|
||||||
|
NVARCHAR = 'str' # String
|
||||||
|
PickleType = 'bytes' # BYTEA
|
||||||
|
REAL = 'float'
|
||||||
|
SMALLINT = 'int'
|
||||||
|
SmallInteger = 'int' # SMALLINT
|
||||||
|
String = 'str' # String
|
||||||
|
TEXT = 'str'
|
||||||
|
Text = 'str' # TEXT
|
||||||
|
TIME = 'time' # TIME WITHOUT TIME ZONE
|
||||||
|
Time = 'time' # TIME WITHOUT TIME ZONE
|
||||||
|
TIMESTAMP = 'datetime' # TIMESTAMP WITHOUT TIME ZONE
|
||||||
|
Unicode = 'str' # String
|
||||||
|
UnicodeText = 'str' # TEXT
|
||||||
|
UUID = 'str | UUID'
|
||||||
|
Uuid = 'str'
|
||||||
|
VARBINARY = 'bytes'
|
||||||
|
VARCHAR = 'str' # String
|
||||||
|
|
||||||
|
# sa.dialects.postgresql 导入
|
||||||
|
ARRAY = 'list'
|
||||||
|
BIT = 'bool'
|
||||||
|
BYTEA = 'bytes'
|
||||||
|
CIDR = 'str'
|
||||||
|
CITEXT = 'str'
|
||||||
|
DATEMULTIRANGE = 'list[date]'
|
||||||
|
DATERANGE = 'tuple[date, date]'
|
||||||
|
DOMAIN = 'str'
|
||||||
|
ENUM = 'Enum'
|
||||||
|
HSTORE = 'dict'
|
||||||
|
INET = 'str'
|
||||||
|
INT4MULTIRANGE = 'list[int]'
|
||||||
|
INT4RANGE = 'tuple[int, int]'
|
||||||
|
INT8MULTIRANGE = 'list[int]'
|
||||||
|
INT8RANGE = 'tuple[int, int]'
|
||||||
|
INTERVAL = 'timedelta'
|
||||||
|
JSONB = 'dict'
|
||||||
|
JSONPATH = 'str'
|
||||||
|
MACADDR = 'str'
|
||||||
|
MACADDR8 = 'str'
|
||||||
|
MONEY = 'Decimal'
|
||||||
|
NUMMULTIRANGE = 'list[Decimal]'
|
||||||
|
NUMRANGE = 'tuple[Decimal, Decimal]'
|
||||||
|
OID = 'int'
|
||||||
|
REGCLASS = 'str'
|
||||||
|
REGCONFIG = 'str'
|
||||||
|
TSMULTIRANGE = 'list[datetime]'
|
||||||
|
TSQUERY = 'str'
|
||||||
|
TSRANGE = 'tuple[datetime, datetime]'
|
||||||
|
TSTZMULTIRANGE = 'list[datetime]'
|
||||||
|
TSTZRANGE = 'tuple[datetime, datetime]'
|
||||||
|
TSVECTOR = 'str'
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from backend.plugin.code_generator.model.business import GenBusiness
|
||||||
|
from backend.plugin.code_generator.model.column import GenColumn
|
||||||
+4
-3
@@ -10,13 +10,13 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|||||||
from backend.common.model import Base, id_key
|
from backend.common.model import Base, id_key
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from backend.app.generator.model import GenModel
|
from backend.plugin.code_generator.model import GenColumn
|
||||||
|
|
||||||
|
|
||||||
class GenBusiness(Base):
|
class GenBusiness(Base):
|
||||||
"""代码生成业务表"""
|
"""代码生成业务表"""
|
||||||
|
|
||||||
__tablename__ = 'sys_gen_business'
|
__tablename__ = 'gen_business'
|
||||||
|
|
||||||
id: Mapped[id_key] = mapped_column(init=False)
|
id: Mapped[id_key] = mapped_column(init=False)
|
||||||
app_name: Mapped[str] = mapped_column(String(50), comment='应用名称(英文)')
|
app_name: Mapped[str] = mapped_column(String(50), comment='应用名称(英文)')
|
||||||
@@ -26,6 +26,7 @@ class GenBusiness(Base):
|
|||||||
table_comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='表描述')
|
table_comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='表描述')
|
||||||
# relate_model_fk: Mapped[int | None] = mapped_column(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 名称 (默认为英文表名称)')
|
||||||
|
filename: Mapped[str | None] = mapped_column(String(20), default=None, comment='基础文件名(默认为英文表名称)')
|
||||||
default_datetime_column: Mapped[bool] = mapped_column(default=True, comment='是否存在默认时间列')
|
default_datetime_column: Mapped[bool] = mapped_column(default=True, comment='是否存在默认时间列')
|
||||||
api_version: Mapped[str] = mapped_column(String(20), 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 根路径)')
|
gen_path: Mapped[str | None] = mapped_column(String(255), default=None, comment='代码生成路径(默认为 app 根路径)')
|
||||||
@@ -33,4 +34,4 @@ class GenBusiness(Base):
|
|||||||
LONGTEXT().with_variant(TEXT, 'postgresql'), default=None, comment='备注'
|
LONGTEXT().with_variant(TEXT, 'postgresql'), default=None, comment='备注'
|
||||||
)
|
)
|
||||||
# 代码生成业务模型一对多
|
# 代码生成业务模型一对多
|
||||||
gen_model: Mapped[list['GenModel']] = relationship(init=False, back_populates='gen_business')
|
gen_column: Mapped[list['GenColumn']] = relationship(init=False, back_populates='gen_business')
|
||||||
+7
-7
@@ -10,18 +10,18 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|||||||
from backend.common.model import DataClassBase, id_key
|
from backend.common.model import DataClassBase, id_key
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from backend.app.generator.model import GenBusiness
|
from backend.plugin.code_generator.model import GenBusiness
|
||||||
|
|
||||||
|
|
||||||
class GenModel(DataClassBase):
|
class GenColumn(DataClassBase):
|
||||||
"""代码生成模型表"""
|
"""代码生成模型列表"""
|
||||||
|
|
||||||
__tablename__ = 'sys_gen_model'
|
__tablename__ = 'gen_column'
|
||||||
|
|
||||||
id: Mapped[id_key] = mapped_column(init=False)
|
id: Mapped[id_key] = mapped_column(init=False)
|
||||||
name: Mapped[str] = mapped_column(String(50), comment='列名称')
|
name: Mapped[str] = mapped_column(String(50), comment='列名称')
|
||||||
comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='列描述')
|
comment: Mapped[str | None] = mapped_column(String(255), default=None, comment='列描述')
|
||||||
type: Mapped[str] = mapped_column(String(20), default='str', comment='SQLA 模型列类型')
|
type: Mapped[str] = mapped_column(String(20), default='String', comment='SQLA 模型列类型')
|
||||||
pd_type: Mapped[str] = mapped_column(String(20), default='str', comment='列类型对应的 pydantic 类型')
|
pd_type: Mapped[str] = mapped_column(String(20), default='str', comment='列类型对应的 pydantic 类型')
|
||||||
default: Mapped[str | None] = mapped_column(
|
default: Mapped[str | None] = mapped_column(
|
||||||
LONGTEXT().with_variant(TEXT, 'postgresql'), default=None, comment='列默认值'
|
LONGTEXT().with_variant(TEXT, 'postgresql'), default=None, comment='列默认值'
|
||||||
@@ -33,6 +33,6 @@ class GenModel(DataClassBase):
|
|||||||
|
|
||||||
# 代码生成业务模型一对多
|
# 代码生成业务模型一对多
|
||||||
gen_business_id: Mapped[int] = mapped_column(
|
gen_business_id: Mapped[int] = mapped_column(
|
||||||
ForeignKey('sys_gen_business.id', ondelete='CASCADE'), default=0, comment='代码生成业务ID'
|
ForeignKey('gen_business.id', ondelete='CASCADE'), default=0, comment='代码生成业务ID'
|
||||||
)
|
)
|
||||||
gen_business: Mapped[Union['GenBusiness', None]] = relationship(init=False, back_populates='gen_model')
|
gen_business: Mapped[Union['GenBusiness', None]] = relationship(init=False, back_populates='gen_column')
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from backend.core.path_conf import PLUGIN_DIR
|
||||||
|
|
||||||
|
# jinja2 模版文件路径
|
||||||
|
JINJA2_TEMPLATE_DIR = PLUGIN_DIR / 'code_generator' / 'templates'
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[app]
|
||||||
|
router = ['v1']
|
||||||
+2
-9
@@ -2,8 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from pydantic import ConfigDict, Field, model_validator
|
from pydantic import ConfigDict, Field
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from backend.common.schema import SchemaBase
|
from backend.common.schema import SchemaBase
|
||||||
|
|
||||||
@@ -17,18 +16,12 @@ class GenBusinessSchemaBase(SchemaBase):
|
|||||||
table_simple_name_zh: str = Field(description='表名称(中文简称)')
|
table_simple_name_zh: str = Field(description='表名称(中文简称)')
|
||||||
table_comment: str | None = Field(None, description='表描述')
|
table_comment: str | None = Field(None, description='表描述')
|
||||||
schema_name: str | None = Field(None, description='Schema 名称 (默认为英文表名称)')
|
schema_name: str | None = Field(None, description='Schema 名称 (默认为英文表名称)')
|
||||||
|
filename: str | None = Field(None, description='基础文件名(默认为英文表名称)')
|
||||||
default_datetime_column: bool = Field(True, description='是否存在默认时间列')
|
default_datetime_column: bool = Field(True, description='是否存在默认时间列')
|
||||||
api_version: str = Field('v1', description='代码生成 api 版本')
|
api_version: str = Field('v1', description='代码生成 api 版本')
|
||||||
gen_path: str | None = Field(None, description='代码生成路径(默认为 app 根路径)')
|
gen_path: str | None = Field(None, description='代码生成路径(默认为 app 根路径)')
|
||||||
remark: str | None = Field(None, description='备注')
|
remark: str | None = Field(None, description='备注')
|
||||||
|
|
||||||
@model_validator(mode='after')
|
|
||||||
def check_schema_name(self) -> Self:
|
|
||||||
"""检查并设置 schema 名称"""
|
|
||||||
if self.schema_name is None:
|
|
||||||
self.schema_name = self.table_name_en
|
|
||||||
return self
|
|
||||||
|
|
||||||
|
|
||||||
class CreateGenBusinessParam(GenBusinessSchemaBase):
|
class CreateGenBusinessParam(GenBusinessSchemaBase):
|
||||||
"""创建代码生成业务参数"""
|
"""创建代码生成业务参数"""
|
||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
from pydantic import ConfigDict, Field, field_validator
|
from pydantic import ConfigDict, Field, field_validator
|
||||||
|
|
||||||
from backend.common.schema import SchemaBase
|
from backend.common.schema import SchemaBase
|
||||||
from backend.utils.type_conversion import sql_type_to_sqlalchemy
|
from backend.plugin.code_generator.utils.type_conversion import sql_type_to_sqlalchemy
|
||||||
|
|
||||||
|
|
||||||
class GenModelSchemaBase(SchemaBase):
|
class GenModelSchemaBase(SchemaBase):
|
||||||
@@ -9,5 +9,5 @@ class ImportParam(SchemaBase):
|
|||||||
"""导入参数"""
|
"""导入参数"""
|
||||||
|
|
||||||
app: str = Field(description='应用名称,用于代码生成到指定 app')
|
app: str = Field(description='应用名称,用于代码生成到指定 app')
|
||||||
table_name: str = Field(description='数据库表名')
|
|
||||||
table_schema: str = Field(description='数据库名')
|
table_schema: str = Field(description='数据库名')
|
||||||
|
table_name: str = Field(description='数据库表名')
|
||||||
+3
-3
@@ -2,11 +2,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from typing import Sequence
|
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.common.exception import errors
|
||||||
from backend.database.db import async_db_session
|
from backend.database.db import async_db_session
|
||||||
|
from backend.plugin.code_generator.crud.crud_business import gen_business_dao
|
||||||
|
from backend.plugin.code_generator.model import GenBusiness
|
||||||
|
from backend.plugin.code_generator.schema.business import CreateGenBusinessParam, UpdateGenBusinessParam
|
||||||
|
|
||||||
|
|
||||||
class GenBusinessService:
|
class GenBusinessService:
|
||||||
+7
-7
@@ -2,20 +2,20 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from typing import Sequence
|
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.app.generator.schema.gen_model import CreateGenModelParam, UpdateGenModelParam
|
|
||||||
from backend.common.enums import GenModelMySQLColumnType
|
|
||||||
from backend.common.exception import errors
|
from backend.common.exception import errors
|
||||||
from backend.database.db import async_db_session
|
from backend.database.db import async_db_session
|
||||||
from backend.utils.type_conversion import sql_type_to_pydantic
|
from backend.plugin.code_generator.crud.crud_column import gen_model_dao
|
||||||
|
from backend.plugin.code_generator.enums import GenModelMySQLColumnType
|
||||||
|
from backend.plugin.code_generator.model import GenColumn
|
||||||
|
from backend.plugin.code_generator.schema.column import CreateGenModelParam, UpdateGenModelParam
|
||||||
|
from backend.plugin.code_generator.utils.type_conversion import sql_type_to_pydantic
|
||||||
|
|
||||||
|
|
||||||
class GenModelService:
|
class GenModelService:
|
||||||
"""代码生成模型服务类"""
|
"""代码生成模型服务类"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get(*, pk: int) -> GenModel:
|
async def get(*, pk: int) -> GenColumn:
|
||||||
"""
|
"""
|
||||||
获取指定 ID 的模型
|
获取指定 ID 的模型
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ class GenModelService:
|
|||||||
return types
|
return types
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_by_business(*, business_id: int) -> Sequence[GenModel]:
|
async def get_by_business(*, business_id: int) -> Sequence[GenColumn]:
|
||||||
"""
|
"""
|
||||||
获取指定业务的所有模型
|
获取指定业务的所有模型
|
||||||
|
|
||||||
+84
-65
@@ -5,31 +5,32 @@ import os.path
|
|||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
import aiofiles
|
import aiofiles
|
||||||
|
|
||||||
from pydantic.alias_generators import to_pascal
|
from pydantic.alias_generators import to_pascal
|
||||||
|
|
||||||
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
|
|
||||||
from backend.app.generator.schema.gen import ImportParam
|
|
||||||
from backend.app.generator.schema.gen_business import CreateGenBusinessParam
|
|
||||||
from backend.app.generator.schema.gen_model import CreateGenModelParam
|
|
||||||
from backend.app.generator.service.gen_model_service import gen_model_service
|
|
||||||
from backend.common.exception import errors
|
from backend.common.exception import errors
|
||||||
from backend.core.path_conf import BASE_PATH
|
from backend.core.path_conf import BASE_PATH
|
||||||
from backend.database.db import async_db_session
|
from backend.database.db import async_db_session
|
||||||
from backend.utils.gen_template import gen_template
|
from backend.plugin.code_generator.crud.crud_business import gen_business_dao
|
||||||
from backend.utils.type_conversion import sql_type_to_pydantic
|
from backend.plugin.code_generator.crud.crud_column import gen_model_dao
|
||||||
|
from backend.plugin.code_generator.crud.crud_gen import gen_dao
|
||||||
|
from backend.plugin.code_generator.model import GenBusiness
|
||||||
|
from backend.plugin.code_generator.schema.business import CreateGenBusinessParam
|
||||||
|
from backend.plugin.code_generator.schema.column import CreateGenModelParam
|
||||||
|
from backend.plugin.code_generator.schema.gen import ImportParam
|
||||||
|
from backend.plugin.code_generator.service.column_service import gen_model_service
|
||||||
|
from backend.plugin.code_generator.utils.gen_template import gen_template
|
||||||
|
from backend.plugin.code_generator.utils.type_conversion import sql_type_to_pydantic
|
||||||
|
|
||||||
|
|
||||||
class GenService:
|
class GenService:
|
||||||
"""代码生成服务类"""
|
"""代码生成服务类"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_tables(*, table_schema: str) -> list[str]:
|
async def get_tables(*, table_schema: str) -> Sequence[str]:
|
||||||
"""
|
"""
|
||||||
获取指定 schema 下的所有表名
|
获取指定 schema 下的所有表名
|
||||||
|
|
||||||
@@ -57,14 +58,17 @@ class GenService:
|
|||||||
raise errors.ForbiddenError(msg='已存在相同数据库表业务')
|
raise errors.ForbiddenError(msg='已存在相同数据库表业务')
|
||||||
|
|
||||||
table_name = table_info[0]
|
table_name = table_info[0]
|
||||||
business_data = {
|
new_business = GenBusiness(
|
||||||
'app_name': obj.app,
|
**CreateGenBusinessParam(
|
||||||
'table_name_en': table_name,
|
app_name=obj.app,
|
||||||
'table_name_zh': table_info[1] or ' '.join(table_name.split('_')),
|
table_name_en=table_name,
|
||||||
'table_simple_name_zh': table_info[1] or table_name.split('_')[-1],
|
table_name_zh=table_info[1] or ' '.join(table_name.split('_')),
|
||||||
'table_comment': table_info[1],
|
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())
|
schema_name=table_name,
|
||||||
|
filename=table_name,
|
||||||
|
).model_dump()
|
||||||
|
)
|
||||||
db.add(new_business)
|
db.add(new_business)
|
||||||
await db.flush()
|
await db.flush()
|
||||||
|
|
||||||
@@ -72,17 +76,20 @@ class GenService:
|
|||||||
for column in column_info:
|
for column in column_info:
|
||||||
column_type = column[-1].split('(')[0].upper()
|
column_type = column[-1].split('(')[0].upper()
|
||||||
pd_type = sql_type_to_pydantic(column_type)
|
pd_type = sql_type_to_pydantic(column_type)
|
||||||
model_data = {
|
await gen_model_dao.create(
|
||||||
'name': column[0],
|
db,
|
||||||
'comment': column[-2],
|
CreateGenModelParam(
|
||||||
'type': column_type,
|
name=column[0],
|
||||||
'sort': column[-3],
|
comment=column[-2],
|
||||||
'length': column[-1].split('(')[1][:-1] if pd_type == 'str' and '(' in column[-1] else 0,
|
type=column_type,
|
||||||
'is_pk': column[1],
|
sort=column[-3],
|
||||||
'is_nullable': column[2],
|
length=column[-1].split('(')[1][:-1] if pd_type == 'str' and '(' in column[-1] else 0,
|
||||||
'gen_business_id': new_business.id,
|
is_pk=column[1],
|
||||||
}
|
is_nullable=column[2],
|
||||||
await gen_model_dao.create(db, CreateGenModelParam(**model_data), pd_type=pd_type)
|
gen_business_id=new_business.id,
|
||||||
|
),
|
||||||
|
pd_type=pd_type,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def render_tpl_code(*, business: GenBusiness) -> dict[str, str]:
|
async def render_tpl_code(*, business: GenBusiness) -> dict[str, str]:
|
||||||
@@ -99,7 +106,7 @@ class GenService:
|
|||||||
gen_vars = gen_template.get_vars(business, gen_models)
|
gen_vars = gen_template.get_vars(business, gen_models)
|
||||||
return {
|
return {
|
||||||
tpl_path: await gen_template.get_template(tpl_path).render_async(**gen_vars)
|
tpl_path: await gen_template.get_template(tpl_path).render_async(**gen_vars)
|
||||||
for tpl_path in gen_template.get_template_paths()
|
for tpl_path in gen_template.get_template_files()
|
||||||
}
|
}
|
||||||
|
|
||||||
async def preview(self, *, pk: int) -> dict[str, bytes]:
|
async def preview(self, *, pk: int) -> dict[str, bytes]:
|
||||||
@@ -115,10 +122,13 @@ class GenService:
|
|||||||
raise errors.NotFoundError(msg='业务不存在')
|
raise errors.NotFoundError(msg='业务不存在')
|
||||||
|
|
||||||
tpl_code_map = await self.render_tpl_code(business=business)
|
tpl_code_map = await self.render_tpl_code(business=business)
|
||||||
return {
|
|
||||||
tpl.replace('.jinja', '.py') if tpl.startswith('py') else ...: code.encode('utf-8')
|
codes = {}
|
||||||
for tpl, code in tpl_code_map.items()
|
for tpl, code in tpl_code_map.items():
|
||||||
}
|
if tpl.startswith('python'):
|
||||||
|
codes[tpl.replace('.jinja', '.py').split('/')[-1]] = code.encode('utf-8')
|
||||||
|
|
||||||
|
return codes
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_generate_path(*, pk: int) -> list[str]:
|
async def get_generate_path(*, pk: int) -> list[str]:
|
||||||
@@ -133,9 +143,10 @@ class GenService:
|
|||||||
if not business:
|
if not business:
|
||||||
raise errors.NotFoundError(msg='业务不存在')
|
raise errors.NotFoundError(msg='业务不存在')
|
||||||
|
|
||||||
gen_path = business.gen_path or 'fba-backend-app-path'
|
gen_path = business.gen_path or 'fba-backend-app-dir'
|
||||||
target_files = gen_template.get_code_gen_paths(business)
|
target_files = gen_template.get_code_gen_paths(business)
|
||||||
return [os.path.join(gen_path, *target_file.split('/')[1:]) for target_file in target_files]
|
|
||||||
|
return [os.path.join(gen_path, *target_file.split('/')) for target_file in target_files]
|
||||||
|
|
||||||
async def generate(self, *, pk: int) -> None:
|
async def generate(self, *, pk: int) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -155,32 +166,29 @@ class GenService:
|
|||||||
for tpl_path, code in tpl_code_map.items():
|
for tpl_path, code in tpl_code_map.items():
|
||||||
code_filepath = os.path.join(
|
code_filepath = os.path.join(
|
||||||
gen_path,
|
gen_path,
|
||||||
*gen_template.get_code_gen_path(tpl_path, business).split('/')[1:],
|
*gen_template.get_code_gen_path(tpl_path, business).split('/'),
|
||||||
)
|
)
|
||||||
code_folder = Path(str(code_filepath)).parent
|
|
||||||
code_folder.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
# 写入 init 文件
|
# 写入 init 文件
|
||||||
|
str_code_filepath = str(code_filepath)
|
||||||
|
code_folder = Path(str_code_filepath).parent
|
||||||
|
code_folder.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
init_filepath = code_folder.joinpath('__init__.py')
|
init_filepath = code_folder.joinpath('__init__.py')
|
||||||
if not init_filepath.exists():
|
async with aiofiles.open(init_filepath, 'w', encoding='utf-8') as f:
|
||||||
async with aiofiles.open(init_filepath, 'w', encoding='utf-8') as f:
|
await f.write(gen_template.init_content)
|
||||||
|
|
||||||
|
# api __init__.py
|
||||||
|
if 'api' in str_code_filepath:
|
||||||
|
api_init_filepath = code_folder.parent.joinpath('__init__.py')
|
||||||
|
async with aiofiles.open(api_init_filepath, 'w', encoding='utf-8') as f:
|
||||||
await f.write(gen_template.init_content)
|
await f.write(gen_template.init_content)
|
||||||
|
|
||||||
if 'api' in str(code_folder):
|
# app __init__.py
|
||||||
# api __init__.py
|
if 'service' in str_code_filepath:
|
||||||
api_init_filepath = code_folder.parent.joinpath('__init__.py')
|
app_init_filepath = code_folder.parent.joinpath('__init__.py')
|
||||||
if not api_init_filepath.exists():
|
async with aiofiles.open(app_init_filepath, 'w', encoding='utf-8') as f:
|
||||||
async with aiofiles.open(api_init_filepath, 'w', encoding='utf-8') as f:
|
await f.write(gen_template.init_content)
|
||||||
await f.write(gen_template.init_content)
|
|
||||||
# app __init__.py
|
|
||||||
app_init_filepath = api_init_filepath.parent.joinpath('__init__.py')
|
|
||||||
if not app_init_filepath.exists():
|
|
||||||
async with aiofiles.open(app_init_filepath, 'w', encoding='utf-8') as f:
|
|
||||||
await f.write(gen_template.init_content)
|
|
||||||
|
|
||||||
# 写入代码文件
|
|
||||||
async with aiofiles.open(code_filepath, 'w', encoding='utf-8') as f:
|
|
||||||
await f.write(code)
|
|
||||||
|
|
||||||
# model init 文件补充
|
# model init 文件补充
|
||||||
if code_folder.name == 'model':
|
if code_folder.name == 'model':
|
||||||
@@ -190,6 +198,10 @@ class GenService:
|
|||||||
f'import {to_pascal(business.table_name_en)}\n',
|
f'import {to_pascal(business.table_name_en)}\n',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 写入代码文件
|
||||||
|
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 def download(self, *, pk: int) -> io.BytesIO:
|
||||||
"""
|
"""
|
||||||
下载生成的代码
|
下载生成的代码
|
||||||
@@ -206,13 +218,12 @@ class GenService:
|
|||||||
with zipfile.ZipFile(bio, 'w') as zf:
|
with zipfile.ZipFile(bio, 'w') as zf:
|
||||||
tpl_code_map = await self.render_tpl_code(business=business)
|
tpl_code_map = await self.render_tpl_code(business=business)
|
||||||
for tpl_path, code in tpl_code_map.items():
|
for tpl_path, code in tpl_code_map.items():
|
||||||
# 写入代码文件
|
code_filepath = gen_template.get_code_gen_path(tpl_path, business)
|
||||||
new_code_path = gen_template.get_code_gen_path(tpl_path, business)
|
|
||||||
zf.writestr(new_code_path, code)
|
|
||||||
|
|
||||||
# 写入 init 文件
|
# 写入 init 文件
|
||||||
init_filepath = os.path.join(*new_code_path.split('/')[:-1], '__init__.py')
|
code_dir = os.path.dirname(code_filepath)
|
||||||
if 'model' not in new_code_path.split('/'):
|
init_filepath = os.path.join(code_dir, '__init__.py')
|
||||||
|
if 'model' not in code_filepath.split('/'):
|
||||||
zf.writestr(init_filepath, gen_template.init_content)
|
zf.writestr(init_filepath, gen_template.init_content)
|
||||||
else:
|
else:
|
||||||
zf.writestr(
|
zf.writestr(
|
||||||
@@ -222,11 +233,19 @@ class GenService:
|
|||||||
f'import {to_pascal(business.table_name_en)}\n',
|
f'import {to_pascal(business.table_name_en)}\n',
|
||||||
)
|
)
|
||||||
|
|
||||||
if 'api' in new_code_path:
|
# api __init__.py
|
||||||
# api __init__.py
|
if 'api' in code_dir:
|
||||||
api_init_filepath = os.path.join(*new_code_path.split('/')[:-2], '__init__.py')
|
api_init_filepath = os.path.join(os.path.dirname(code_dir), '__init__.py')
|
||||||
zf.writestr(api_init_filepath, gen_template.init_content)
|
zf.writestr(api_init_filepath, gen_template.init_content)
|
||||||
|
|
||||||
|
# app __init__.py
|
||||||
|
if 'service' in code_dir:
|
||||||
|
app_init_filepath = os.path.join(os.path.dirname(code_dir), '__init__.py')
|
||||||
|
zf.writestr(app_init_filepath, gen_template.init_content)
|
||||||
|
|
||||||
|
# 写入代码文件
|
||||||
|
zf.writestr(code_filepath, code)
|
||||||
|
|
||||||
bio.seek(0)
|
bio.seek(0)
|
||||||
return bio
|
return bio
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
+19
-20
@@ -5,10 +5,9 @@ from typing import Sequence
|
|||||||
from jinja2 import Environment, FileSystemLoader, Template, select_autoescape
|
from jinja2 import Environment, FileSystemLoader, Template, select_autoescape
|
||||||
from pydantic.alias_generators import to_pascal, to_snake
|
from pydantic.alias_generators import to_pascal, to_snake
|
||||||
|
|
||||||
from backend.app.generator.conf import generator_settings
|
|
||||||
from backend.app.generator.model import GenBusiness, GenModel
|
|
||||||
from backend.core.conf import settings
|
from backend.core.conf import settings
|
||||||
from backend.core.path_conf import JINJA2_TEMPLATE_DIR
|
from backend.plugin.code_generator.model import GenBusiness, GenColumn
|
||||||
|
from backend.plugin.code_generator.path_conf import JINJA2_TEMPLATE_DIR
|
||||||
|
|
||||||
|
|
||||||
class GenTemplate:
|
class GenTemplate:
|
||||||
@@ -34,19 +33,19 @@ class GenTemplate:
|
|||||||
return self.env.get_template(jinja_file)
|
return self.env.get_template(jinja_file)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_template_paths() -> list[str]:
|
def get_template_files() -> list[str]:
|
||||||
"""
|
"""
|
||||||
获取模板文件路径列表
|
获取模板文件列表
|
||||||
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return [
|
files = []
|
||||||
f'{generator_settings.TEMPLATE_BACKEND_DIR_NAME}/api.jinja',
|
|
||||||
f'{generator_settings.TEMPLATE_BACKEND_DIR_NAME}/crud.jinja',
|
# python
|
||||||
f'{generator_settings.TEMPLATE_BACKEND_DIR_NAME}/model.jinja',
|
python_template_path = JINJA2_TEMPLATE_DIR / 'python'
|
||||||
f'{generator_settings.TEMPLATE_BACKEND_DIR_NAME}/schema.jinja',
|
files.extend([f'python/{file.name}' for file in python_template_path.iterdir() if file.is_file()])
|
||||||
f'{generator_settings.TEMPLATE_BACKEND_DIR_NAME}/service.jinja',
|
|
||||||
]
|
return files
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_code_gen_paths(business: GenBusiness) -> list[str]:
|
def get_code_gen_paths(business: GenBusiness) -> list[str]:
|
||||||
@@ -57,13 +56,13 @@ class GenTemplate:
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
app_name = business.app_name
|
app_name = business.app_name
|
||||||
module_name = business.table_name_en
|
filename = business.filename
|
||||||
return [
|
return [
|
||||||
f'{generator_settings.TEMPLATE_BACKEND_DIR_NAME}/{app_name}/api/{business.api_version}/{module_name}.py',
|
f'{app_name}/api/{business.api_version}/{filename}.py',
|
||||||
f'{generator_settings.TEMPLATE_BACKEND_DIR_NAME}/{app_name}/crud/crud_{module_name}.py',
|
f'{app_name}/crud/crud_{filename}.py',
|
||||||
f'{generator_settings.TEMPLATE_BACKEND_DIR_NAME}/{app_name}/model/{module_name}.py',
|
f'{app_name}/model/{filename}.py',
|
||||||
f'{generator_settings.TEMPLATE_BACKEND_DIR_NAME}/{app_name}/schema/{module_name}.py',
|
f'{app_name}/schema/{filename}.py',
|
||||||
f'{generator_settings.TEMPLATE_BACKEND_DIR_NAME}/{app_name}/service/{module_name}_service.py',
|
f'{app_name}/service/{filename}_service.py',
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_code_gen_path(self, tpl_path: str, business: GenBusiness) -> str:
|
def get_code_gen_path(self, tpl_path: str, business: GenBusiness) -> str:
|
||||||
@@ -75,11 +74,11 @@ class GenTemplate:
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
target_files = self.get_code_gen_paths(business)
|
target_files = self.get_code_gen_paths(business)
|
||||||
code_gen_path_mapping = dict(zip(self.get_template_paths(), target_files))
|
code_gen_path_mapping = dict(zip(self.get_template_files(), target_files))
|
||||||
return code_gen_path_mapping[tpl_path]
|
return code_gen_path_mapping[tpl_path]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_vars(business: GenBusiness, models: Sequence[GenModel]) -> dict[str, str | Sequence[GenModel]]:
|
def get_vars(business: GenBusiness, models: Sequence[GenColumn]) -> dict[str, str | Sequence[GenColumn]]:
|
||||||
"""
|
"""
|
||||||
获取模板变量
|
获取模板变量
|
||||||
|
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from backend.common.enums import GenModelMySQLColumnType, GenModelPostgreSQLColumnType
|
|
||||||
from backend.core.conf import settings
|
from backend.core.conf import settings
|
||||||
|
from backend.plugin.code_generator.enums import GenModelMySQLColumnType, GenModelPostgreSQLColumnType
|
||||||
|
|
||||||
|
|
||||||
def sql_type_to_sqlalchemy(typing: str) -> str:
|
def sql_type_to_sqlalchemy(typing: str) -> str:
|
||||||
@@ -55,7 +55,7 @@ class CRUDConfig(CRUDPlus[Config]):
|
|||||||
"""
|
"""
|
||||||
return await self.select_model_by_column(db, key=key)
|
return await self.select_model_by_column(db, key=key)
|
||||||
|
|
||||||
async def get_list(self, name: str | None = None, type: str | None = None) -> Select:
|
async def get_list(self, name: str | None, type: str | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取参数配置列表
|
获取参数配置列表
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class ConfigService:
|
|||||||
return config
|
return config
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_select(*, name: str | None = None, type: str | None = None) -> Select:
|
async def get_select(*, name: str | None, type: str | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取参数配置列表查询条件
|
获取参数配置列表查询条件
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class CRUDDictData(CRUDPlus[DictData]):
|
|||||||
"""
|
"""
|
||||||
return await self.select_model(db, pk)
|
return await self.select_model(db, pk)
|
||||||
|
|
||||||
async def get_list(self, label: str | None = None, value: str | None = None, status: int | None = None) -> Select:
|
async def get_list(self, label: str | None, value: str | None, status: int | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取字典数据列表
|
获取字典数据列表
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from sqlalchemy import Select
|
from sqlalchemy import Select, and_, desc, select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy.orm import noload
|
||||||
from sqlalchemy_crud_plus import CRUDPlus
|
from sqlalchemy_crud_plus import CRUDPlus
|
||||||
|
|
||||||
from backend.plugin.dict.model import DictType
|
from backend.plugin.dict.model import DictType
|
||||||
@@ -21,7 +22,7 @@ class CRUDDictType(CRUDPlus[DictType]):
|
|||||||
"""
|
"""
|
||||||
return await self.select_model(db, pk)
|
return await self.select_model(db, pk)
|
||||||
|
|
||||||
async def get_list(self, *, name: str | None = None, code: str | None = None, status: int | None = None) -> Select:
|
async def get_list(self, *, name: str | None, code: str | None, status: int | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取字典类型列表
|
获取字典类型列表
|
||||||
|
|
||||||
@@ -30,14 +31,20 @@ class CRUDDictType(CRUDPlus[DictType]):
|
|||||||
:param status: 字典状态
|
:param status: 字典状态
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
filters = {}
|
stmt = select(self.model).options(noload(self.model.datas)).order_by(desc(self.model.created_time))
|
||||||
|
|
||||||
|
filters = []
|
||||||
if name is not None:
|
if name is not None:
|
||||||
filters.update(name__like=f'%{name}%')
|
filters.append(self.model.name.like(f'%{name}%'))
|
||||||
if code is not None:
|
if code is not None:
|
||||||
filters.update(code__like=f'%{code}%')
|
filters.append(self.model.code.like(f'%{code}%'))
|
||||||
if status is not None:
|
if status is not None:
|
||||||
filters.update(status=status)
|
filters.append(self.model.status == status)
|
||||||
return await self.select_order('created_time', 'desc', **filters)
|
|
||||||
|
if filters:
|
||||||
|
stmt = stmt.where(and_(*filters))
|
||||||
|
|
||||||
|
return stmt
|
||||||
|
|
||||||
async def get_by_code(self, db: AsyncSession, code: str) -> DictType | None:
|
async def get_by_code(self, db: AsyncSession, code: str) -> DictType | None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class DictData(Base):
|
|||||||
|
|
||||||
id: Mapped[id_key] = mapped_column(init=False)
|
id: Mapped[id_key] = mapped_column(init=False)
|
||||||
label: Mapped[str] = mapped_column(String(32), unique=True, comment='字典标签')
|
label: Mapped[str] = mapped_column(String(32), unique=True, comment='字典标签')
|
||||||
value: Mapped[str] = mapped_column(String(32), unique=True, comment='字典值')
|
value: Mapped[str] = mapped_column(String(32), comment='字典值')
|
||||||
sort: Mapped[int] = mapped_column(default=0, comment='排序')
|
sort: Mapped[int] = mapped_column(default=0, comment='排序')
|
||||||
status: Mapped[int] = mapped_column(default=1, comment='状态(0停用 1正常)')
|
status: Mapped[int] = mapped_column(default=1, comment='状态(0停用 1正常)')
|
||||||
remark: Mapped[str | None] = mapped_column(
|
remark: Mapped[str | None] = mapped_column(
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class DictType(Base):
|
|||||||
__tablename__ = 'sys_dict_type'
|
__tablename__ = 'sys_dict_type'
|
||||||
|
|
||||||
id: Mapped[id_key] = mapped_column(init=False)
|
id: Mapped[id_key] = mapped_column(init=False)
|
||||||
name: Mapped[str] = mapped_column(String(32), unique=True, comment='字典类型名称')
|
name: Mapped[str] = mapped_column(String(32), comment='字典类型名称')
|
||||||
code: Mapped[str] = mapped_column(String(32), unique=True, comment='字典类型编码')
|
code: Mapped[str] = mapped_column(String(32), unique=True, comment='字典类型编码')
|
||||||
status: Mapped[int] = mapped_column(default=1, comment='状态(0停用 1正常)')
|
status: Mapped[int] = mapped_column(default=1, comment='状态(0停用 1正常)')
|
||||||
remark: Mapped[str | None] = mapped_column(
|
remark: Mapped[str | None] = mapped_column(
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class DictDataService:
|
|||||||
return dict_data
|
return dict_data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_select(*, label: str | None = None, value: str | None = None, status: int | None = None) -> Select:
|
async def get_select(*, label: str | None, value: str | None, status: int | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取字典数据列表查询条件
|
获取字典数据列表查询条件
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class DictTypeService:
|
|||||||
"""字典类型服务类"""
|
"""字典类型服务类"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_select(*, name: str | None = None, code: str | None = None, status: int | None = None) -> Select:
|
async def get_select(*, name: str | None, code: str | None, status: int | None) -> Select:
|
||||||
"""
|
"""
|
||||||
获取字典类型列表查询条件
|
获取字典类型列表查询条件
|
||||||
|
|
||||||
|
|||||||
+20
-12
@@ -134,12 +134,13 @@ def inject_extra_router(plugin: str, data: dict[str, Any]) -> None:
|
|||||||
raise PluginInjectError(f'扩展级插件 {plugin} 路由注入失败:{str(e)}') from e
|
raise PluginInjectError(f'扩展级插件 {plugin} 路由注入失败:{str(e)}') from e
|
||||||
|
|
||||||
|
|
||||||
def inject_app_router(plugin: str, data: dict[str, Any]) -> None:
|
def inject_app_router(plugin: str, data: dict[str, Any], target_router: APIRouter) -> None:
|
||||||
"""
|
"""
|
||||||
应用级插件路由注入
|
应用级插件路由注入
|
||||||
|
|
||||||
:param plugin: 插件名称
|
:param plugin: 插件名称
|
||||||
:param data: 插件配置数据
|
:param data: 插件配置数据
|
||||||
|
:param target_router: FastAPI 路由器
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
module_path = f'backend.plugin.{plugin}.api.router'
|
module_path = f'backend.plugin.{plugin}.api.router'
|
||||||
@@ -149,10 +150,6 @@ def inject_app_router(plugin: str, data: dict[str, Any]) -> None:
|
|||||||
if not routers or not isinstance(routers, list):
|
if not routers or not isinstance(routers, list):
|
||||||
raise PluginInjectError(f'应用级插件 {plugin} 配置文件存在错误,请检查')
|
raise PluginInjectError(f'应用级插件 {plugin} 配置文件存在错误,请检查')
|
||||||
|
|
||||||
# 获取目标路由
|
|
||||||
target_module = import_module_cached('backend.app.router')
|
|
||||||
target_router = getattr(target_module, 'router')
|
|
||||||
|
|
||||||
for router in routers:
|
for router in routers:
|
||||||
plugin_router = getattr(module, router, None)
|
plugin_router = getattr(module, router, None)
|
||||||
if not plugin_router or not isinstance(plugin_router, APIRouter):
|
if not plugin_router or not isinstance(plugin_router, APIRouter):
|
||||||
@@ -166,15 +163,26 @@ def inject_app_router(plugin: str, data: dict[str, Any]) -> None:
|
|||||||
raise PluginInjectError(f'应用级插件 {plugin} 路由注入失败:{str(e)}') from e
|
raise PluginInjectError(f'应用级插件 {plugin} 路由注入失败:{str(e)}') from e
|
||||||
|
|
||||||
|
|
||||||
def plugin_router_inject() -> None:
|
def build_final_router() -> APIRouter:
|
||||||
"""插件路由注入"""
|
"""构建最终路由"""
|
||||||
|
|
||||||
|
extra_plugins = []
|
||||||
|
app_plugins = []
|
||||||
|
|
||||||
for plugin in get_plugins():
|
for plugin in get_plugins():
|
||||||
data = load_plugin_config(plugin)
|
data = load_plugin_config(plugin)
|
||||||
# 基于插件 plugin.toml 配置文件,判断插件类型
|
(extra_plugins if data.get('api') else app_plugins).append((plugin, data))
|
||||||
if data.get('api'):
|
|
||||||
inject_extra_router(plugin, data)
|
for plugin, data in extra_plugins:
|
||||||
else:
|
inject_extra_router(plugin, data)
|
||||||
inject_app_router(plugin, data)
|
|
||||||
|
# 主路由,必须在插件路由注入后导入
|
||||||
|
from backend.app.router import router as main_router
|
||||||
|
|
||||||
|
for plugin, data in app_plugins:
|
||||||
|
inject_app_router(plugin, data, main_router)
|
||||||
|
|
||||||
|
return main_router
|
||||||
|
|
||||||
|
|
||||||
def _install_plugin_requirements(plugin: str, requirements_file: str) -> None:
|
def _install_plugin_requirements(plugin: str, requirements_file: str) -> None:
|
||||||
|
|||||||
@@ -1,3 +1,54 @@
|
|||||||
|
create table gen_business
|
||||||
|
(
|
||||||
|
id int auto_increment comment '主键 ID'
|
||||||
|
primary key,
|
||||||
|
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) null comment '表描述',
|
||||||
|
schema_name varchar(255) null comment 'Schema 名称 (默认为英文表名称)',
|
||||||
|
filename varchar(20) null comment '基础文件名(默认为英文表名称)',
|
||||||
|
default_datetime_column tinyint(1) not null comment '是否存在默认时间列',
|
||||||
|
api_version varchar(20) not null comment '代码生成 api 版本,默认为 v1',
|
||||||
|
gen_path varchar(255) null comment '代码生成路径(默认为 app 根路径)',
|
||||||
|
remark longtext null comment '备注',
|
||||||
|
created_time datetime not null comment '创建时间',
|
||||||
|
updated_time datetime null comment '更新时间',
|
||||||
|
constraint table_name_en
|
||||||
|
unique (table_name_en)
|
||||||
|
)
|
||||||
|
comment '代码生成业务表';
|
||||||
|
|
||||||
|
create index ix_gen_business_id
|
||||||
|
on gen_business (id);
|
||||||
|
|
||||||
|
create table gen_column
|
||||||
|
(
|
||||||
|
id int auto_increment comment '主键 ID'
|
||||||
|
primary key,
|
||||||
|
name varchar(50) not null comment '列名称',
|
||||||
|
comment varchar(255) null comment '列描述',
|
||||||
|
type varchar(20) not null comment 'SQLA 模型列类型',
|
||||||
|
pd_type varchar(20) not null comment '列类型对应的 pydantic 类型',
|
||||||
|
`default` longtext null comment '列默认值',
|
||||||
|
sort int 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',
|
||||||
|
constraint gen_column_ibfk_1
|
||||||
|
foreign key (gen_business_id) references gen_business (id)
|
||||||
|
on delete cascade
|
||||||
|
)
|
||||||
|
comment '代码生成模型列表';
|
||||||
|
|
||||||
|
create index gen_business_id
|
||||||
|
on gen_column (gen_business_id);
|
||||||
|
|
||||||
|
create index ix_gen_column_id
|
||||||
|
on gen_column (id);
|
||||||
|
|
||||||
create table sys_api
|
create table sys_api
|
||||||
(
|
(
|
||||||
id int auto_increment comment '主键 ID'
|
id int auto_increment comment '主键 ID'
|
||||||
@@ -110,9 +161,7 @@ create table sys_dict_type
|
|||||||
created_time datetime not null comment '创建时间',
|
created_time datetime not null comment '创建时间',
|
||||||
updated_time datetime null comment '更新时间',
|
updated_time datetime null comment '更新时间',
|
||||||
constraint code
|
constraint code
|
||||||
unique (code),
|
unique (code)
|
||||||
constraint name
|
|
||||||
unique (name)
|
|
||||||
)
|
)
|
||||||
comment '字典类型表';
|
comment '字典类型表';
|
||||||
|
|
||||||
@@ -130,8 +179,6 @@ create table sys_dict_data
|
|||||||
updated_time datetime null comment '更新时间',
|
updated_time datetime null comment '更新时间',
|
||||||
constraint label
|
constraint label
|
||||||
unique (label),
|
unique (label),
|
||||||
constraint value
|
|
||||||
unique (value),
|
|
||||||
constraint sys_dict_data_ibfk_1
|
constraint sys_dict_data_ibfk_1
|
||||||
foreign key (type_id) references sys_dict_type (id)
|
foreign key (type_id) references sys_dict_type (id)
|
||||||
on delete cascade
|
on delete cascade
|
||||||
@@ -147,56 +194,6 @@ create index type_id
|
|||||||
create index ix_sys_dict_type_id
|
create index ix_sys_dict_type_id
|
||||||
on sys_dict_type (id);
|
on sys_dict_type (id);
|
||||||
|
|
||||||
create table sys_gen_business
|
|
||||||
(
|
|
||||||
id int auto_increment comment '主键 ID'
|
|
||||||
primary key,
|
|
||||||
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) null comment '表描述',
|
|
||||||
schema_name varchar(255) null comment 'Schema 名称 (默认为英文表名称)',
|
|
||||||
default_datetime_column tinyint(1) not null comment '是否存在默认时间列',
|
|
||||||
api_version varchar(20) not null comment '代码生成 api 版本,默认为 v1',
|
|
||||||
gen_path varchar(255) null comment '代码生成路径(默认为 app 根路径)',
|
|
||||||
remark longtext null comment '备注',
|
|
||||||
created_time datetime not null comment '创建时间',
|
|
||||||
updated_time datetime null comment '更新时间',
|
|
||||||
constraint table_name_en
|
|
||||||
unique (table_name_en)
|
|
||||||
)
|
|
||||||
comment '代码生成业务表';
|
|
||||||
|
|
||||||
create index ix_sys_gen_business_id
|
|
||||||
on sys_gen_business (id);
|
|
||||||
|
|
||||||
create table sys_gen_model
|
|
||||||
(
|
|
||||||
id int auto_increment comment '主键 ID'
|
|
||||||
primary key,
|
|
||||||
name varchar(50) not null comment '列名称',
|
|
||||||
comment varchar(255) null comment '列描述',
|
|
||||||
type varchar(20) not null comment 'SQLA 模型列类型',
|
|
||||||
pd_type varchar(20) not null comment '列类型对应的 pydantic 类型',
|
|
||||||
`default` longtext null comment '列默认值',
|
|
||||||
sort int 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',
|
|
||||||
constraint sys_gen_model_ibfk_1
|
|
||||||
foreign key (gen_business_id) references sys_gen_business (id)
|
|
||||||
on delete cascade
|
|
||||||
)
|
|
||||||
comment '代码生成模型表';
|
|
||||||
|
|
||||||
create index gen_business_id
|
|
||||||
on sys_gen_model (gen_business_id);
|
|
||||||
|
|
||||||
create index ix_sys_gen_model_id
|
|
||||||
on sys_gen_model (id);
|
|
||||||
|
|
||||||
create table sys_login_log
|
create table sys_login_log
|
||||||
(
|
(
|
||||||
id int auto_increment comment '主键 ID'
|
id int auto_increment comment '主键 ID'
|
||||||
|
|||||||
@@ -397,6 +397,62 @@ alter table sys_casbin_rule
|
|||||||
create index ix_sys_casbin_rule_id
|
create index ix_sys_casbin_rule_id
|
||||||
on sys_casbin_rule (id);
|
on sys_casbin_rule (id);
|
||||||
|
|
||||||
|
create table gen_business
|
||||||
|
(
|
||||||
|
id serial
|
||||||
|
primary key,
|
||||||
|
app_name varchar(50) not null,
|
||||||
|
table_name_en varchar(255) not null
|
||||||
|
unique,
|
||||||
|
table_name_zh varchar(255) not null,
|
||||||
|
table_simple_name_zh varchar(255) not null,
|
||||||
|
table_comment varchar(255),
|
||||||
|
schema_name varchar(255),
|
||||||
|
filename varchar(20),
|
||||||
|
default_datetime_column boolean not null,
|
||||||
|
api_version varchar(20) not null,
|
||||||
|
gen_path varchar(255),
|
||||||
|
remark text,
|
||||||
|
created_time timestamp with time zone not null,
|
||||||
|
updated_time timestamp with time zone
|
||||||
|
);
|
||||||
|
|
||||||
|
comment on table gen_business is '代码生成业务表';
|
||||||
|
|
||||||
|
comment on column gen_business.id is '主键 ID';
|
||||||
|
|
||||||
|
comment on column gen_business.app_name is '应用名称(英文)';
|
||||||
|
|
||||||
|
comment on column gen_business.table_name_en is '表名称(英文)';
|
||||||
|
|
||||||
|
comment on column gen_business.table_name_zh is '表名称(中文)';
|
||||||
|
|
||||||
|
comment on column gen_business.table_simple_name_zh is '表名称(中文简称)';
|
||||||
|
|
||||||
|
comment on column gen_business.table_comment is '表描述';
|
||||||
|
|
||||||
|
comment on column gen_business.schema_name is 'Schema 名称 (默认为英文表名称)';
|
||||||
|
|
||||||
|
comment on column gen_business.filename is '基础文件名(默认为英文表名称)';
|
||||||
|
|
||||||
|
comment on column gen_business.default_datetime_column is '是否存在默认时间列';
|
||||||
|
|
||||||
|
comment on column gen_business.api_version is '代码生成 api 版本,默认为 v1';
|
||||||
|
|
||||||
|
comment on column gen_business.gen_path is '代码生成路径(默认为 app 根路径)';
|
||||||
|
|
||||||
|
comment on column gen_business.remark is '备注';
|
||||||
|
|
||||||
|
comment on column gen_business.created_time is '创建时间';
|
||||||
|
|
||||||
|
comment on column gen_business.updated_time is '更新时间';
|
||||||
|
|
||||||
|
alter table gen_business
|
||||||
|
owner to postgres;
|
||||||
|
|
||||||
|
create index ix_gen_business_id
|
||||||
|
on gen_business (id);
|
||||||
|
|
||||||
create table sys_config
|
create table sys_config
|
||||||
(
|
(
|
||||||
id serial
|
id serial
|
||||||
@@ -438,6 +494,41 @@ alter table sys_config
|
|||||||
create index ix_sys_config_id
|
create index ix_sys_config_id
|
||||||
on sys_config (id);
|
on sys_config (id);
|
||||||
|
|
||||||
|
create table sys_dict_type
|
||||||
|
(
|
||||||
|
id serial
|
||||||
|
primary key,
|
||||||
|
name varchar(32) not null,
|
||||||
|
code varchar(32) not null
|
||||||
|
unique,
|
||||||
|
status integer not null,
|
||||||
|
remark text,
|
||||||
|
created_time timestamp with time zone not null,
|
||||||
|
updated_time timestamp with time zone
|
||||||
|
);
|
||||||
|
|
||||||
|
comment on table sys_dict_type is '字典类型表';
|
||||||
|
|
||||||
|
comment on column sys_dict_type.id is '主键 ID';
|
||||||
|
|
||||||
|
comment on column sys_dict_type.name is '字典类型名称';
|
||||||
|
|
||||||
|
comment on column sys_dict_type.code is '字典类型编码';
|
||||||
|
|
||||||
|
comment on column sys_dict_type.status is '状态(0停用 1正常)';
|
||||||
|
|
||||||
|
comment on column sys_dict_type.remark is '备注';
|
||||||
|
|
||||||
|
comment on column sys_dict_type.created_time is '创建时间';
|
||||||
|
|
||||||
|
comment on column sys_dict_type.updated_time is '更新时间';
|
||||||
|
|
||||||
|
alter table sys_dict_type
|
||||||
|
owner to postgres;
|
||||||
|
|
||||||
|
create index ix_sys_dict_type_id
|
||||||
|
on sys_dict_type (id);
|
||||||
|
|
||||||
create table sys_notice
|
create table sys_notice
|
||||||
(
|
(
|
||||||
id serial
|
id serial
|
||||||
@@ -478,95 +569,6 @@ alter table sys_notice
|
|||||||
create index ix_sys_notice_id
|
create index ix_sys_notice_id
|
||||||
on sys_notice (id);
|
on sys_notice (id);
|
||||||
|
|
||||||
create table sys_dict_type
|
|
||||||
(
|
|
||||||
id serial
|
|
||||||
primary key,
|
|
||||||
name varchar(32) not null
|
|
||||||
unique,
|
|
||||||
code varchar(32) not null
|
|
||||||
unique,
|
|
||||||
status integer not null,
|
|
||||||
remark text,
|
|
||||||
created_time timestamp with time zone not null,
|
|
||||||
updated_time timestamp with time zone
|
|
||||||
);
|
|
||||||
|
|
||||||
comment on table sys_dict_type is '字典类型表';
|
|
||||||
|
|
||||||
comment on column sys_dict_type.id is '主键 ID';
|
|
||||||
|
|
||||||
comment on column sys_dict_type.name is '字典类型名称';
|
|
||||||
|
|
||||||
comment on column sys_dict_type.code is '字典类型编码';
|
|
||||||
|
|
||||||
comment on column sys_dict_type.status is '状态(0停用 1正常)';
|
|
||||||
|
|
||||||
comment on column sys_dict_type.remark is '备注';
|
|
||||||
|
|
||||||
comment on column sys_dict_type.created_time is '创建时间';
|
|
||||||
|
|
||||||
comment on column sys_dict_type.updated_time is '更新时间';
|
|
||||||
|
|
||||||
alter table sys_dict_type
|
|
||||||
owner to postgres;
|
|
||||||
|
|
||||||
create index ix_sys_dict_type_id
|
|
||||||
on sys_dict_type (id);
|
|
||||||
|
|
||||||
create table sys_gen_business
|
|
||||||
(
|
|
||||||
id serial
|
|
||||||
primary key,
|
|
||||||
app_name varchar(50) not null,
|
|
||||||
table_name_en varchar(255) not null
|
|
||||||
unique,
|
|
||||||
table_name_zh varchar(255) not null,
|
|
||||||
table_simple_name_zh varchar(255) not null,
|
|
||||||
table_comment varchar(255),
|
|
||||||
schema_name varchar(255),
|
|
||||||
default_datetime_column boolean not null,
|
|
||||||
api_version varchar(20) not null,
|
|
||||||
gen_path varchar(255),
|
|
||||||
remark text,
|
|
||||||
created_time timestamp with time zone not null,
|
|
||||||
updated_time timestamp with time zone
|
|
||||||
);
|
|
||||||
|
|
||||||
comment on table sys_gen_business is '代码生成业务表';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.id is '主键 ID';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.app_name is '应用名称(英文)';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.table_name_en is '表名称(英文)';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.table_name_zh is '表名称(中文)';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.table_simple_name_zh is '表名称(中文简称)';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.table_comment is '表描述';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.schema_name is 'Schema 名称 (默认为英文表名称)';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.default_datetime_column is '是否存在默认时间列';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.api_version is '代码生成 api 版本,默认为 v1';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.gen_path is '代码生成路径(默认为 app 根路径)';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.remark is '备注';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.created_time is '创建时间';
|
|
||||||
|
|
||||||
comment on column sys_gen_business.updated_time is '更新时间';
|
|
||||||
|
|
||||||
alter table sys_gen_business
|
|
||||||
owner to postgres;
|
|
||||||
|
|
||||||
create index ix_sys_gen_business_id
|
|
||||||
on sys_gen_business (id);
|
|
||||||
|
|
||||||
create table sys_role_menu
|
create table sys_role_menu
|
||||||
(
|
(
|
||||||
id serial,
|
id serial,
|
||||||
@@ -681,17 +683,65 @@ comment on column sys_user.updated_time is '更新时间';
|
|||||||
alter table sys_user
|
alter table sys_user
|
||||||
owner to postgres;
|
owner to postgres;
|
||||||
|
|
||||||
|
create index ix_sys_user_id
|
||||||
|
on sys_user (id);
|
||||||
|
|
||||||
|
create unique index ix_sys_user_email
|
||||||
|
on sys_user (email);
|
||||||
|
|
||||||
create unique index ix_sys_user_username
|
create unique index ix_sys_user_username
|
||||||
on sys_user (username);
|
on sys_user (username);
|
||||||
|
|
||||||
create index ix_sys_user_status
|
create index ix_sys_user_status
|
||||||
on sys_user (status);
|
on sys_user (status);
|
||||||
|
|
||||||
create index ix_sys_user_id
|
create table gen_column
|
||||||
on sys_user (id);
|
(
|
||||||
|
id serial
|
||||||
|
primary key,
|
||||||
|
name varchar(50) not null,
|
||||||
|
comment varchar(255),
|
||||||
|
type varchar(20) not null,
|
||||||
|
pd_type varchar(20) not null,
|
||||||
|
"default" text,
|
||||||
|
sort integer,
|
||||||
|
length integer not null,
|
||||||
|
is_pk boolean not null,
|
||||||
|
is_nullable boolean not null,
|
||||||
|
gen_business_id integer not null
|
||||||
|
references gen_business
|
||||||
|
on delete cascade
|
||||||
|
);
|
||||||
|
|
||||||
create unique index ix_sys_user_email
|
comment on table gen_column is '代码生成模型列表';
|
||||||
on sys_user (email);
|
|
||||||
|
comment on column gen_column.id is '主键 ID';
|
||||||
|
|
||||||
|
comment on column gen_column.name is '列名称';
|
||||||
|
|
||||||
|
comment on column gen_column.comment is '列描述';
|
||||||
|
|
||||||
|
comment on column gen_column.type is 'SQLA 模型列类型';
|
||||||
|
|
||||||
|
comment on column gen_column.pd_type is '列类型对应的 pydantic 类型';
|
||||||
|
|
||||||
|
comment on column gen_column."default" is '列默认值';
|
||||||
|
|
||||||
|
comment on column gen_column.sort is '列排序';
|
||||||
|
|
||||||
|
comment on column gen_column.length is '列长度';
|
||||||
|
|
||||||
|
comment on column gen_column.is_pk is '是否主键';
|
||||||
|
|
||||||
|
comment on column gen_column.is_nullable is '是否可为空';
|
||||||
|
|
||||||
|
comment on column gen_column.gen_business_id is '代码生成业务ID';
|
||||||
|
|
||||||
|
alter table gen_column
|
||||||
|
owner to postgres;
|
||||||
|
|
||||||
|
create index ix_gen_column_id
|
||||||
|
on gen_column (id);
|
||||||
|
|
||||||
create table sys_dict_data
|
create table sys_dict_data
|
||||||
(
|
(
|
||||||
@@ -699,8 +749,7 @@ create table sys_dict_data
|
|||||||
primary key,
|
primary key,
|
||||||
label varchar(32) not null
|
label varchar(32) not null
|
||||||
unique,
|
unique,
|
||||||
value varchar(32) not null
|
value varchar(32) not null,
|
||||||
unique,
|
|
||||||
sort integer not null,
|
sort integer not null,
|
||||||
status integer not null,
|
status integer not null,
|
||||||
remark text,
|
remark text,
|
||||||
@@ -737,54 +786,6 @@ alter table sys_dict_data
|
|||||||
create index ix_sys_dict_data_id
|
create index ix_sys_dict_data_id
|
||||||
on sys_dict_data (id);
|
on sys_dict_data (id);
|
||||||
|
|
||||||
create table sys_gen_model
|
|
||||||
(
|
|
||||||
id serial
|
|
||||||
primary key,
|
|
||||||
name varchar(50) not null,
|
|
||||||
comment varchar(255),
|
|
||||||
type varchar(20) not null,
|
|
||||||
pd_type varchar(20) not null,
|
|
||||||
"default" text,
|
|
||||||
sort integer,
|
|
||||||
length integer not null,
|
|
||||||
is_pk boolean not null,
|
|
||||||
is_nullable boolean not null,
|
|
||||||
gen_business_id integer not null
|
|
||||||
references sys_gen_business
|
|
||||||
on delete cascade
|
|
||||||
);
|
|
||||||
|
|
||||||
comment on table sys_gen_model is '代码生成模型表';
|
|
||||||
|
|
||||||
comment on column sys_gen_model.id is '主键 ID';
|
|
||||||
|
|
||||||
comment on column sys_gen_model.name is '列名称';
|
|
||||||
|
|
||||||
comment on column sys_gen_model.comment is '列描述';
|
|
||||||
|
|
||||||
comment on column sys_gen_model.type is 'SQLA 模型列类型';
|
|
||||||
|
|
||||||
comment on column sys_gen_model.pd_type is '列类型对应的 pydantic 类型';
|
|
||||||
|
|
||||||
comment on column sys_gen_model."default" is '列默认值';
|
|
||||||
|
|
||||||
comment on column sys_gen_model.sort is '列排序';
|
|
||||||
|
|
||||||
comment on column sys_gen_model.length is '列长度';
|
|
||||||
|
|
||||||
comment on column sys_gen_model.is_pk is '是否主键';
|
|
||||||
|
|
||||||
comment on column sys_gen_model.is_nullable is '是否可为空';
|
|
||||||
|
|
||||||
comment on column sys_gen_model.gen_business_id is '代码生成业务ID';
|
|
||||||
|
|
||||||
alter table sys_gen_model
|
|
||||||
owner to postgres;
|
|
||||||
|
|
||||||
create index ix_sys_gen_model_id
|
|
||||||
on sys_gen_model (id);
|
|
||||||
|
|
||||||
create table sys_user_role
|
create table sys_user_role
|
||||||
(
|
(
|
||||||
id serial,
|
id serial,
|
||||||
|
|||||||
@@ -684,13 +684,13 @@ wheels = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "fastapi-oauth20"
|
name = "fastapi-oauth20"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "httpx" },
|
{ name = "httpx" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/8e/66/25d4ece161689184f47130bc7ea5bf9c73113c2e628cf586546aa574d8d1/fastapi_oauth20-0.0.1.tar.gz", hash = "sha256:22bf6564f3af42a94cb597f8e20af1366e89e19af49291d15784a4c14059d4c9", size = 6681 }
|
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/8e/66/25d4ece161689184f47130bc7ea5bf9c73113c2e628cf586546aa574d8d1/fastapi_oauth20-0.0.1.tar.gz", hash = "sha256:22bf6564f3af42a94cb597f8e20af1366e89e19af49291d15784a4c14059d4c9" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/cf/87/7e0423109ba2c0f8a0794eedff5bb1f79792e5d8d0941ba0b26f962e3e5c/fastapi_oauth20-0.0.1-py3-none-any.whl", hash = "sha256:921fe0e874cb4186998f74b1d951c360400aceb5ca3889a85c806d865a991f88", size = 11242 },
|
{ url = "https://mirrors.aliyun.com/pypi/packages/cf/87/7e0423109ba2c0f8a0794eedff5bb1f79792e5d8d0941ba0b26f962e3e5c/fastapi_oauth20-0.0.1-py3-none-any.whl", hash = "sha256:921fe0e874cb4186998f74b1d951c360400aceb5ca3889a85c806d865a991f88" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
Reference in New Issue
Block a user