Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
a22b541
Add code generator app
wu-clan Apr 30, 2024
14e6c32
Define the generator interfaces
wu-clan Apr 30, 2024
8c726af
Update the name of the template files
wu-clan Apr 30, 2024
9f7e46d
Merge branch 'refs/heads/master' into add-generator-app
wu-clan May 4, 2024
93197a5
update lock hash
wu-clan May 4, 2024
8ce9577
update route and schemas
wu-clan May 6, 2024
55d67f5
update gen code structure
wu-clan May 9, 2024
42d4d7d
add some gen codes
wu-clan May 12, 2024
a24b9ca
upgrade SQLAlchemy to release
wu-clan May 17, 2024
76b6d05
update template util
wu-clan May 17, 2024
82f1447
Fix lint error
wu-clan May 21, 2024
b4e9415
Add template rendering code
wu-clan May 26, 2024
b95303d
Add gen model code
wu-clan May 27, 2024
2b33441
Fix gen model table
wu-clan May 27, 2024
7525926
Update business model column naming
wu-clan May 27, 2024
70f0fd9
Update any route
wu-clan May 27, 2024
bca4405
Fix api ninja args
wu-clan May 27, 2024
c5efb5f
Update gen business model
wu-clan May 28, 2024
2449632
Fix interface services logic
wu-clan May 28, 2024
e9672bb
Fix template render
wu-clan May 28, 2024
1248a93
Add more code template
wu-clan May 29, 2024
41dab93
Merge branch 'refs/heads/master' into add-generator-app
wu-clan Jun 3, 2024
95a756c
Add model auto gen template
wu-clan Jun 23, 2024
c03d6ce
Fix model type map style
wu-clan Jun 23, 2024
27dee1c
Update schema and model templates
wu-clan Jun 26, 2024
1ca84c5
Update gen_model schema
wu-clan Jun 26, 2024
30f5bda
Fix model template if
wu-clan Jun 26, 2024
0cc216e
Update have_datetime_column field from gen_model to gen_business table
wu-clan Jun 26, 2024
1320ff9
Update the name of the zip
wu-clan Jun 26, 2024
edcc782
Update model template vars
wu-clan Jun 26, 2024
840b47c
Add download generate code
wu-clan Jun 30, 2024
cdd5ba4
Merge branch 'refs/heads/master' into add-generator-app
wu-clan Jul 1, 2024
88d5139
Add preview code encoding
wu-clan Jul 1, 2024
87bcd67
Update service code template
wu-clan Jul 1, 2024
4271b4f
fix lint
wu-clan Jul 1, 2024
2413bcf
Merge branch 'refs/heads/master' into add-generator-app
wu-clan Jul 2, 2024
f45faf3
Update github auth tag
wu-clan Jul 2, 2024
c16b364
Update requirements
wu-clan Jul 2, 2024
9764c38
Add code generation write
wu-clan Jul 2, 2024
799224c
Update generate interface and zip conf
wu-clan Jul 2, 2024
fccc2f7
Optimize templates details
wu-clan Jul 2, 2024
e6554f1
Update model relation column conf
wu-clan Jul 3, 2024
1bb39d6
Add import tables module
wu-clan Jul 3, 2024
63406a1
Add get all databases interface
wu-clan Jul 3, 2024
cfa9378
Fix get tables interface
wu-clan Jul 3, 2024
3182ff6
Bump pydantic to 2.8.1
wu-clan Jul 4, 2024
88dfe4a
Merge branch 'refs/heads/master' into add-generator-app
wu-clan Jul 8, 2024
7d6e83b
Upgrading lint dependencies
wu-clan Jul 8, 2024
1f13449
Merge branch 'refs/heads/master' into add-generator-app
wu-clan Jul 9, 2024
7c68c3b
Add import business and model implement
wu-clan Jul 10, 2024
56e2ee8
Split import database execution code
wu-clan Jul 10, 2024
f39d383
Update text SQL execution to prevent SQL injection
wu-clan Jul 10, 2024
1b4b20e
Optimize schema name generation
wu-clan Jul 10, 2024
b237a94
clean code
wu-clan Jul 10, 2024
fb4acdb
Update create tables sql scripts
wu-clan Jul 10, 2024
9764d65
Update import sql query conditions
wu-clan Jul 10, 2024
77d153c
Add todo comments
wu-clan Jul 10, 2024
ada250d
Mark business creation interface
wu-clan Jul 10, 2024
9f8c7d1
Update features in README
wu-clan Jul 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.6.0
hooks:
- id: check-added-large-files
- id: end-of-file-fixer
- id: check-yaml
- id: check-toml

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.4.2
rev: v0.5.1
hooks:
- id: ruff
args:
Expand All @@ -20,7 +20,7 @@ repos:
- id: ruff-format

- repo: https://github.com/pdm-project/pdm
rev: 2.12.4
rev: 2.16.1
hooks:
- id: pdm-export
args:
Expand Down
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,18 @@ Luckily, we now have a demo site: [FBA UI](https://fba.xwboy.top/)

## Built-in features

1. [x] User management: system user role management, permission assignment
2. [x] Department Management: Configure the system organization (company, department, group...)
3. [x] Menu Management: Configuration of system menus, user menus, button privilege identification
4. [x] Role Management: Assign role menu privileges, assign role routing privileges
5. [x] Dictionary Management: Maintain common fixed data or parameters within the system.
6. [x] Operation Logs: logging and querying of normal and abnormal system operations.
7. [x] Login Authentication: graphical authentication code background authentication login
8. [x] Login Logs: Logging and querying of normal and abnormal user logins
9. [x] Service Monitoring: server hardware device information and status
10. [x] Scheduled tasks: automated tasks, asynchronous tasks, and function invocation are supported
11. [x] Interface Documentation: Automatically generate online interactive API interface documentation.
1. [x] User management: management of system user roles, assignment of permissions
2. [x] Departmental management: Configuration of the system organization (company, department, group, ...)
3. [x] Menu management: Configuration of system menus, user menus, button permission labels
4. [x] Role management: assignment of role menu privileges, assignment of role routing privileges
5. [x] Dictionary management: maintenance of commonly used fixed data or parameters within the system
6. [x] Code generation: back-end code is automatically generated, supporting preview, write and download.
7. [x] Operation log: logging and querying of normal and abnormal system operations.
8. [x] Login authentication: graphical captcha backend authentication login
9. [x] Logging: logging and querying of normal and abnormal user logins
10. [x] Service monitoring: server hardware device information and status
11. [x] Timed tasks: automated tasks, asynchronous tasks, support for function calls
12. [x] Interface Documentation: Automatically generate online interactive API interface documentation.

## Local development

Expand Down
13 changes: 7 additions & 6 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,13 @@ mvc 架构作为常规设计模式,在 python web 中也很常见,但是三
3. [x] 菜单管理:配置系统菜单,用户菜单,按钮权限标识
4. [x] 角色管理:角色菜单权限分配,角色路由权限分配
5. [x] 字典管理:维护系统内部常用固定数据或参数
6. [x] 操作日志:系统正常操作和异常操作日志记录和查询
7. [x] 登录认证:图形验证码后台认证登录
8. [x] 登录日志:用户正常登录和异常登录的日志记录与查询
9. [x] 服务监控:服务器硬件设备信息与状态
10. [x] 定时任务:自动化任务,异步任务,支持函数调用
11. [x] 接口文档:自动生成在线交互式 API 接口文档
6. [x] 代码生成:后端代码自动生成,支持预览,写入及下载
7. [x] 操作日志:系统正常和异常操作的日志记录与查询
8. [x] 登录认证:图形验证码后台认证登录
9. [x] 登录日志:用户正常和异常登录的日志记录与查询
10. [x] 服务监控:服务器硬件设备信息与状态
11. [x] 定时任务:自动化任务,异步任务,支持函数调用
12. [x] 接口文档:自动生成在线交互式 API 接口文档

## 本地开发

Expand Down
1 change: 0 additions & 1 deletion backend/.ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ select = [
"UP007"
]
preview = true
ignore-init-module-imports = true
ignore = ["FURB101"]

[lint.flake8-pytest-style]
Expand Down
2 changes: 2 additions & 0 deletions backend/alembic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
# for 'autogenerate' support
# https://alembic.sqlalchemy.org/en/latest/autogenerate.html#autogenerating-multiple-metadata-collections
from backend.app.admin.model import MappedBase as AdminBase
from backend.app.generator.model import MappedBase as GeneratorBase

target_metadata = [
AdminBase.metadata,
GeneratorBase.metadata,
]

# other values from the config, defined by the needs of env.py,
Expand Down
1 change: 0 additions & 1 deletion backend/app/admin/api/v1/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@

router = APIRouter(prefix='/auth')


router.include_router(auth_router, tags=['授权'])
router.include_router(captcha_router, prefix='/captcha', tags=['验证码'])
2 changes: 1 addition & 1 deletion backend/app/admin/model/sys_dict_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ class DictData(Base):
status: Mapped[int] = mapped_column(default=1, comment='状态(0停用 1正常)')
remark: Mapped[str | None] = mapped_column(LONGTEXT, default=None, comment='备注')
# 字典类型一对多
type_id: Mapped[int] = mapped_column(ForeignKey('sys_dict_type.id'), default=None, comment='字典类型关联ID')
type_id: Mapped[int] = mapped_column(ForeignKey('sys_dict_type.id'), default=0, comment='字典类型关联ID')
type: Mapped['DictType'] = relationship(init=False, back_populates='datas') # noqa: F821
2 changes: 2 additions & 0 deletions backend/app/generator/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
2 changes: 2 additions & 0 deletions backend/app/generator/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
9 changes: 9 additions & 0 deletions backend/app/generator/api/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from fastapi import APIRouter

from backend.app.generator.api.v1.gen import router as gen_router

v1 = APIRouter()

v1.include_router(gen_router, prefix='/gen', tags=['代码生成'])
2 changes: 2 additions & 0 deletions backend/app/generator/api/v1/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
114 changes: 114 additions & 0 deletions backend/app/generator/api/v1/gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Annotated

from fastapi import APIRouter, Path, Query
from fastapi.responses import StreamingResponse

from backend.app.generator.conf import generator_settings
from backend.app.generator.schema.gen_business import CreateGenBusinessParam, UpdateGenBusinessParam
from backend.app.generator.schema.gen_model import CreateGenModelParam, UpdateGenModelParam
from backend.app.generator.service.gen_business_service import gen_business_service
from backend.app.generator.service.gen_model_service import gen_model_service
from backend.app.generator.service.gen_service import gen_service
from backend.common.response.response_schema import ResponseModel, response_base
from backend.common.security.jwt import DependsJwtAuth
from backend.common.security.rbac import DependsRBAC
from backend.utils.serializers import select_list_serialize

router = APIRouter()


@router.get('/all', summary='获取所有代码生成业务', dependencies=[DependsJwtAuth])
async def get_all_businesses() -> ResponseModel:
businesses = await gen_business_service.get_all()
data = await select_list_serialize(businesses)
return await response_base.success(data=data)


@router.get('/businesses/{pk}', summary='获取代码生成业务详情', dependencies=[DependsJwtAuth])
async def get_business(pk: Annotated[int, Path(...)]) -> ResponseModel:
data = await gen_service.get_business_and_model(pk=pk)
return await response_base.success(data=data)


@router.post('/businesses', summary='创建代码生成业务', deprecated=True, dependencies=[DependsRBAC])
async def create_business(obj: CreateGenBusinessParam) -> ResponseModel:
await gen_business_service.create(obj=obj)
return await response_base.success()


@router.put('/businesses/{pk}', summary='更新代码生成业务', dependencies=[DependsRBAC])
async def update_business(pk: Annotated[int, Path(...)], obj: UpdateGenBusinessParam) -> ResponseModel:
count = await gen_business_service.update(pk=pk, obj=obj)
if count > 0:
return await response_base.success()
return await response_base.fail()


@router.delete('/businesses', summary='删除代码生成业务', dependencies=[DependsRBAC])
async def delete_business(pk: Annotated[int, Query(...)]) -> ResponseModel:
count = await gen_business_service.delete(pk=pk)
if count > 0:
return await response_base.success()
return await response_base.fail()


@router.post('/models', summary='创建代码生成模型', dependencies=[DependsRBAC])
async def create_model(obj: CreateGenModelParam) -> ResponseModel:
await gen_model_service.create(obj=obj)
return await response_base.success()


@router.put('/models/{pk}', summary='更新代码生成模型', dependencies=[DependsRBAC])
async def update_model(pk: Annotated[int, Path(...)], obj: UpdateGenModelParam) -> ResponseModel:
count = await gen_model_service.update(pk=pk, obj=obj)
if count > 0:
return await response_base.success()
return await response_base.fail()


@router.delete('/models/{pk}', summary='删除代码生成模型', dependencies=[DependsRBAC])
async def delete_model(pk: Annotated[int, Path(...)]) -> ResponseModel:
count = await gen_model_service.delete(pk=pk)
if count > 0:
return await response_base.success()
return await response_base.fail()


@router.get('/tables', summary='获取数据库表', dependencies=[DependsRBAC])
async def get_all_tables(table_schema: Annotated[str, Query(..., description='数据库名')] = 'fba') -> ResponseModel:
data = await gen_service.get_tables(table_schema=table_schema)
return await response_base.success(data=data)


@router.post('/import', summary='导入代码生成业务和模型列', dependencies=[DependsRBAC])
async def import_table(
app: Annotated[str, Query(..., description='应用名称,用于代码生成到指定 app')],
table_name: Annotated[str, Query(..., description='数据库表名')],
table_schema: Annotated[str, Query(..., description='数据库名')] = 'fba',
) -> ResponseModel:
await gen_service.import_business_and_model(app=app, table_schema=table_schema, table_name=table_name)
return await response_base.success()


@router.get('/preview/{pk}', summary='生成代码预览', dependencies=[DependsJwtAuth])
async def preview_code(pk: Annotated[int, Path(..., description='业务ID')]) -> ResponseModel:
data = await gen_service.preview(pk=pk)
return await response_base.success(data=data)


@router.post('/generate/{pk}', summary='生成代码', description='文件磁盘写入,请谨慎操作', dependencies=[DependsRBAC])
async def generate_code(pk: Annotated[int, Path(..., description='业务ID')]) -> ResponseModel:
await gen_service.generate(pk=pk)
return await response_base.success()


@router.post('/download/{pk}', summary='下载代码', dependencies=[DependsRBAC])
async def download_code(pk: Annotated[int, Path(..., description='业务ID')]):
bio = await gen_service.download(pk=pk)
return StreamingResponse(
bio,
media_type='application/x-zip-compressed',
headers={'Content-Disposition': f'attachment; filename={generator_settings.ZIP_FILENAME}.zip'},
)
25 changes: 25 additions & 0 deletions backend/app/generator/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from functools import lru_cache

from pydantic_settings import BaseSettings, SettingsConfigDict

from backend.core.path_conf import BasePath


class GeneratorSettings(BaseSettings):
"""Admin Settings"""

model_config = SettingsConfigDict(env_file=f'{BasePath}/.env', env_file_encoding='utf-8', extra='ignore')

# 代码下载
ZIP_FILENAME: str = 'fba_generator'


@lru_cache
def get_generator_settings() -> GeneratorSettings:
"""获取 generator 配置"""
return GeneratorSettings()


generator_settings = get_generator_settings()
2 changes: 2 additions & 0 deletions backend/app/generator/crud/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
53 changes: 53 additions & 0 deletions backend/app/generator/crud/crud_gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Sequence

from sqlalchemy import Row, text
from sqlalchemy.ext.asyncio import AsyncSession


class CRUDGen:
@staticmethod
async def get_all_tables(db: AsyncSession, table_schema: str) -> Sequence[str]:
t = text(
'select table_name as table_name '
'from information_schema.tables '
'where table_name not like "sys_gen_%" '
'and table_schema = :table_schema;'
).bindparams(table_schema=table_schema)
stmt = await db.execute(t)
return stmt.scalars().all()

@staticmethod
async def get_table(db: AsyncSession, table_name: str) -> Row[tuple]:
t = text(
'select table_name as table_name, table_comment as table_comment '
'from information_schema.tables '
'where table_name not like "sys_gen_%" '
'and table_name = :table_name;'
).bindparams(table_name=table_name)
stmt = await db.execute(t)
return stmt.fetchone()

@staticmethod
async def get_all_columns(db: AsyncSession, table_schema: str, table_name: str) -> Sequence[Row[tuple]]:
t = text(
'select column_name AS column_name, '
'case when column_key = "PRI" then 1 else 0 end as is_pk, '
'case when is_nullable = "NO" or column_key = "PRI" then 0 else 1 end as is_nullable, '
'ordinal_position as sort, '
'column_comment as column_comment, '
'column_type as column_type '
'from information_schema.columns '
'where table_schema = :table_schema '
'and table_name = :table_name '
'and column_name != "id" '
'and column_name != "created_time" '
'and column_name != "updated_time" '
'order by sort;'
).bindparams(table_schema=table_schema, table_name=table_name)
stmt = await db.execute(t)
return stmt.fetchall()


gen_dao = CRUDGen()
Loading