feat: 添加IP归属地工具类并优化代码

refactor: 简化AuthPermission调用方式
style: 更新日志输出格式和内容
fix: 修复BaseSchema字段可选性
perf: 优化数据库连接日志显示
chore: 更新requirements.txt添加sqlglot依赖
docs: 更新配置注释说明
This commit is contained in:
zhangtao
2025-10-04 18:55:29 +08:00
parent 609324f6c7
commit f4b55334e2
40 changed files with 867 additions and 1161 deletions
@@ -21,7 +21,7 @@ MCPRouter = APIRouter(route_class=OperationLogRoute, prefix="/mcp", tags=["MCP
@MCPRouter.post("/chat", summary="智能对话", description="与MCP智能助手进行对话")
async def chat_controller(
query: ChatQuerySchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["ai:mcp:chat"]))
auth: AuthSchema = Depends(AuthPermission(["ai:mcp:chat"]))
) -> StreamingResponse:
"""智能对话接口"""
user_name = auth.user.name if auth.user else "未知用户"
@@ -43,7 +43,7 @@ async def chat_controller(
@MCPRouter.get("/detail/{id}", summary="获取 MCP 服务器详情", description="获取 MCP 服务器详情")
async def detail_controller(
id: int = Path(..., description="MCP ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["ai:mcp:query"]))
auth: AuthSchema = Depends(AuthPermission(["ai:mcp:query"]))
) -> JSONResponse:
result_dict = await McpService.detail_service(auth=auth, id=id)
logger.info(f"获取 MCP 服务器详情成功 {id}")
@@ -54,7 +54,7 @@ async def detail_controller(
async def list_controller(
page: PaginationQueryParam = Depends(),
search: McpQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["ai:mcp:query"]))
auth: AuthSchema = Depends(AuthPermission(["ai:mcp:query"]))
) -> JSONResponse:
result_dict_list = await McpService.list_service(auth=auth, search=search, order_by=page.order_by)
result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
@@ -65,7 +65,7 @@ async def list_controller(
@MCPRouter.post("/create", summary="创建 MCP 服务器", description="创建 MCP 服务器")
async def create_controller(
data: McpCreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["ai:mcp:create"]))
auth: AuthSchema = Depends(AuthPermission(["ai:mcp:create"]))
) -> JSONResponse:
result_dict = await McpService.create_service(auth=auth, data=data)
logger.info(f"创建 MCP 服务器成功: {result_dict}")
@@ -76,7 +76,7 @@ async def create_controller(
async def update_controller(
data: McpUpdateSchema,
id: int = Path(..., description="MCP ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["ai:mcp:update"]))
auth: AuthSchema = Depends(AuthPermission(["ai:mcp:update"]))
) -> JSONResponse:
result_dict = await McpService.update_service(auth=auth, id=id, data=data)
logger.info(f"修改 MCP 服务器成功: {result_dict}")
@@ -86,7 +86,7 @@ async def update_controller(
@MCPRouter.delete("/delete", summary="删除 MCP 服务器", description="删除 MCP 服务器")
async def delete_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["ai:mcp:delete"]))
auth: AuthSchema = Depends(AuthPermission(["ai:mcp:delete"]))
) -> JSONResponse:
await McpService.delete_service(auth=auth, ids=ids)
logger.info(f"删除 MCP 服务器成功: {ids}")
@@ -24,7 +24,7 @@ MyAppRouter = APIRouter(route_class=OperationLogRoute, prefix="/myapp", tags=["
@MyAppRouter.get("/detail/{id}", summary="获取应用详情", description="获取应用详情")
async def get_obj_detail_controller(
id: int = Path(..., description="应用ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["application:myapp:query"]))
auth: AuthSchema = Depends(AuthPermission(["application:myapp:query"]))
) -> JSONResponse:
result_dict = await ApplicationService.detail_service(id=id, auth=auth)
logger.info(f"获取应用详情成功 {id}")
@@ -34,7 +34,7 @@ async def get_obj_detail_controller(
async def get_obj_list_controller(
page: PaginationQueryParam = Depends(),
search: ApplicationQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["application:myapp:query"]))
auth: AuthSchema = Depends(AuthPermission(["application:myapp:query"]))
) -> JSONResponse:
result_dict_list = await ApplicationService.list_service(auth=auth, search=search, order_by=page.order_by)
result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
@@ -44,7 +44,7 @@ async def get_obj_list_controller(
@MyAppRouter.post("/create", summary="创建应用", description="创建应用")
async def create_obj_controller(
data: ApplicationCreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["application:myapp:create"]))
auth: AuthSchema = Depends(AuthPermission(["application:myapp:create"]))
) -> JSONResponse:
result_dict = await ApplicationService.create_service(auth=auth, data=data)
logger.info(f"创建应用成功: {result_dict}")
@@ -54,7 +54,7 @@ async def create_obj_controller(
async def update_obj_controller(
data: ApplicationUpdateSchema,
id: int = Path(..., description="应用ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["application:myapp:update"]))
auth: AuthSchema = Depends(AuthPermission(["application:myapp:update"]))
) -> JSONResponse:
result_dict = await ApplicationService.update_service(auth=auth, id=id, data=data)
logger.info(f"修改应用成功: {result_dict}")
@@ -63,7 +63,7 @@ async def update_obj_controller(
@MyAppRouter.delete("/delete", summary="删除应用", description="删除应用")
async def delete_obj_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["application:myapp:delete"]))
auth: AuthSchema = Depends(AuthPermission(["application:myapp:delete"]))
) -> JSONResponse:
await ApplicationService.delete_service(auth=auth, ids=ids)
logger.info(f"删除应用成功: {ids}")
@@ -72,7 +72,7 @@ async def delete_obj_controller(
@MyAppRouter.patch("/available/setting", summary="批量修改应用状态", description="批量修改应用状态")
async def batch_set_available_obj_controller(
data: BatchSetAvailable,
auth: AuthSchema = Depends(AuthPermission(permissions=["application:myapp:patch"]))
auth: AuthSchema = Depends(AuthPermission(["application:myapp:patch"]))
) -> JSONResponse:
await ApplicationService.set_available_service(auth=auth, data=data)
logger.info(f"批量修改应用状态成功: {data.ids}")
@@ -13,7 +13,7 @@ from .service import FileService
FileRouter = APIRouter(route_class=OperationLogRoute, prefix="/file", tags=["文件管理"])
@FileRouter.post("/upload", summary="上传文件", description="上传文件",dependencies=[Depends(AuthPermission(permissions=["common:file:upload"]))])
@FileRouter.post("/upload", summary="上传文件", description="上传文件",dependencies=[Depends(AuthPermission(["common:file:upload"]))])
async def upload_controller(
file: UploadFile,
request: Request,
@@ -22,7 +22,7 @@ async def upload_controller(
logger.info(f"上传文件成功 {result_dict}")
return SuccessResponse(data=result_dict, msg="上传文件成功")
@FileRouter.post("/download", summary="下载文件", description="下载文件", dependencies=[Depends(AuthPermission(permissions=["common:file:download"]))])
@FileRouter.post("/download", summary="下载文件", description="下载文件", dependencies=[Depends(AuthPermission(["common:file:download"]))])
async def download_controller(
background_tasks: BackgroundTasks,
file_path: str = Body(..., description="文件路径"),
@@ -26,7 +26,7 @@ DemoRouter = APIRouter(route_class=OperationLogRoute, prefix="/demo", tags=["示
@DemoRouter.get("/detail/{id}", summary="获取示例详情", description="获取示例详情")
async def get_obj_detail_controller(
id: int = Path(..., description="示例ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:query"]))
auth: AuthSchema = Depends(AuthPermission(["demo:example:query"]))
) -> JSONResponse:
result_dict = await DemoService.detail_service(id=id, auth=auth)
logger.info(f"获取示例详情成功 {id}")
@@ -36,7 +36,7 @@ async def get_obj_detail_controller(
async def get_obj_list_controller(
page: PaginationQueryParam = Depends(),
search: DemoQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:query"]))
auth: AuthSchema = Depends(AuthPermission(["demo:example:query"]))
) -> JSONResponse:
result_dict_list = await DemoService.list_service(auth=auth, search=search, order_by=page.order_by)
result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
@@ -46,7 +46,7 @@ async def get_obj_list_controller(
@DemoRouter.post("/create", summary="创建示例", description="创建示例")
async def create_obj_controller(
data: DemoCreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:create"]))
auth: AuthSchema = Depends(AuthPermission(["demo:example:create"]))
) -> JSONResponse:
result_dict = await DemoService.create_service(auth=auth, data=data)
logger.info(f"创建示例成功: {result_dict.get('name')}")
@@ -56,7 +56,7 @@ async def create_obj_controller(
async def update_obj_controller(
data: DemoUpdateSchema,
id: int = Path(..., description="示例ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:update"]))
auth: AuthSchema = Depends(AuthPermission(["demo:example:update"]))
) -> JSONResponse:
result_dict = await DemoService.update_service(auth=auth, id=id, data=data)
logger.info(f"修改示例成功: {result_dict.get('name')}")
@@ -65,7 +65,7 @@ async def update_obj_controller(
@DemoRouter.delete("/delete", summary="删除示例", description="删除示例")
async def delete_obj_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:delete"]))
auth: AuthSchema = Depends(AuthPermission(["demo:example:delete"]))
) -> JSONResponse:
await DemoService.delete_service(auth=auth, ids=ids)
logger.info(f"删除示例成功: {ids}")
@@ -74,7 +74,7 @@ async def delete_obj_controller(
@DemoRouter.patch("/available/setting", summary="批量修改示例状态", description="批量修改示例状态")
async def batch_set_available_obj_controller(
data: BatchSetAvailable,
auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:patch"]))
auth: AuthSchema = Depends(AuthPermission(["demo:example:patch"]))
) -> JSONResponse:
await DemoService.set_available_service(auth=auth, data=data)
logger.info(f"批量修改示例状态成功: {data.ids}")
@@ -83,7 +83,7 @@ async def batch_set_available_obj_controller(
@DemoRouter.post('/export', summary="导出示例", description="导出示例")
async def export_obj_list_controller(
search: DemoQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:export"]))
auth: AuthSchema = Depends(AuthPermission(["demo:example:export"]))
) -> StreamingResponse:
# 获取全量数据
result_dict_list = await DemoService.list_service(search=search, auth=auth)
@@ -101,13 +101,13 @@ async def export_obj_list_controller(
@DemoRouter.post('/import', summary="导入示例", description="导入示例")
async def import_obj_list_controller(
file: UploadFile,
auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:import"]))
auth: AuthSchema = Depends(AuthPermission(["demo:example:import"]))
) -> JSONResponse:
batch_import_result = await DemoService.batch_import_service(file=file, auth=auth, update_support=True)
logger.info(f"导入示例成功: {batch_import_result}")
return SuccessResponse(data=batch_import_result, msg="导入示例成功")
@DemoRouter.post('/download/template', summary="获取示例导入模板", description="获取示例导入模板", dependencies=[Depends(AuthPermission(permissions=["demo:example:download"]))])
@DemoRouter.post('/download/template', summary="获取示例导入模板", description="获取示例导入模板", dependencies=[Depends(AuthPermission(["demo:example:download"]))])
async def export_obj_template_controller() -> StreamingResponse:
example_import_template_result = await DemoService.import_template_download_service()
logger.info('获取示例导入模板成功')
@@ -1,9 +1,8 @@
# -*- coding:utf-8 -*-
from datetime import datetime
from typing import List
from fastapi import APIRouter, Depends, Query, Body, Path
from fastapi.responses import StreamingResponse, JSONResponse
from fastapi.responses import JSONResponse
from app.common.response import SuccessResponse, ErrorResponse, StreamResponse
from app.core.dependencies import AuthPermission
@@ -11,9 +10,8 @@ from app.core.router_class import OperationLogRoute
from app.core.base_params import PaginationQueryParam
from app.common.request import PaginationService
from app.api.v1.module_system.auth.schema import AuthSchema
from app.api.v1.module_system.user.schema import UserOutSchema
from .param import GenTableQueryParam
from .schema import GenTableDeleteSchema, GenTableUpdateSchema, GenTableOutSchema
from .schema import GenTableDeleteSchema, GenTableSchema, GenTableOutSchema
from .service import GenTableColumnService, GenTableService
from app.utils.common_util import bytes2file_response
from app.core.logger import logger
@@ -22,38 +20,59 @@ from app.core.logger import logger
GenRouter = APIRouter(route_class=OperationLogRoute, prefix='/gencode', tags=["代码生成模块"])
@GenRouter.get("/list", summary="查询代码生成业务表列表", description="查询代码生成业务表列表")
async def gen_table_list_controller(
page: PaginationQueryParam = Depends(),
search: GenTableQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(["generator:gencode:query"]))
) -> JSONResponse:
result_dict_list = await GenTableService.get_gen_table_list_service(auth=auth, search=search)
result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
logger.info('获取代码生成业务表列表成功')
return SuccessResponse(data=result_dict, msg="获取代码生成业务表列表成功")
@GenRouter.get("/db/list", summary="查询数据库表列表", description="查询数据库表列表")
async def get_gen_db_table_list_controller(
page: PaginationQueryParam = Depends(),
search: GenTableQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(["generator:dblist:query"]))
) -> JSONResponse:
result_dict_list = await GenTableService.get_gen_db_table_list_service(auth=auth, search=search, order_by=page.order_by)
result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
logger.info('获取数据库表列表成功')
return SuccessResponse(data=result_dict, msg="获取数据库表列表成功")
@GenRouter.post("/import", summary="导入表结构", description="导入表结构")
async def import_gen_table_controller(
table_names: List[str] = Query(..., description="表名列表", embed=True),
auth: AuthSchema = Depends(AuthPermission(["generator:gencode:import"])),
) -> JSONResponse:
add_gen_table_list = await GenTableService.get_gen_db_table_list_by_name_service(auth, table_names)
result = await GenTableService.import_gen_table_service(auth, add_gen_table_list)
logger.info('导入表结构成功')
return SuccessResponse(msg="导入表结构成功", data=result)
@GenRouter.get("/detail/{table_id}", summary="获取业务表详细信息", description="获取业务表详细信息")
async def get_gen_table_detail_controller(
async def gen_table_detail_controller(
table_id: int = Path(..., description="业务表ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:query"]))
auth: AuthSchema = Depends(AuthPermission(["generator:gencode:query"]))
) -> JSONResponse:
gen_table = await GenTableService.get_gen_table_by_id_service(auth, table_id)
gen_tables = await GenTableService.get_gen_table_all_service(auth)
gen_columns = await GenTableColumnService.get_gen_table_column_list_by_table_id_service(auth, table_id)
gen_table_detail_result = dict(info=gen_table, rows=gen_columns, tables=gen_tables)
gen_table_detail_result = dict(info=gen_table, rows=gen_table.columns, tables=gen_tables)
logger.info(f'获取table_id为{table_id}的信息成功')
return SuccessResponse(data=gen_table_detail_result, msg="获取业务表详细信息成功")
@GenRouter.get("/list", summary="查询代码生成业务表列表", description="查询代码生成业务表列表")
async def get_gen_table_list_controller(
page: PaginationQueryParam = Depends(),
search: GenTableQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:query"]))
) -> JSONResponse:
result_dict_list = await GenTableService.get_gen_table_list_service(auth=auth, query_object=search, is_page=False)
result_dict = await PaginationService.paginate(data_list=result_dict_list["items"], page_no=page.page_no, page_size=page.page_size)
logger.info('获取代码生成业务表列表成功')
return SuccessResponse(data=result_dict, msg="获取代码生成业务表列表成功")
@GenRouter.post("/create", summary="创建表结构", description="创建表结构")
async def create_table_controller(
sql: str = Query(..., description="SQL语句"),
auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:create"])),
current_user: UserOutSchema = Depends(lambda auth: auth.user)
auth: AuthSchema = Depends(AuthPermission(["generator:gencode:create"])),
) -> JSONResponse:
result = await GenTableService.create_table_service(auth, sql, current_user)
result = await GenTableService.create_table_service(auth, sql)
logger.info('创建表结构成功')
return SuccessResponse(msg="创建表结构成功", data=result)
@@ -61,54 +80,30 @@ async def create_table_controller(
@GenRouter.put("/update/{table_id}", summary="编辑业务表信息", description="编辑业务表信息")
async def update_gen_table_controller(
table_id: int = Path(..., description="业务表ID"),
data: GenTableUpdateSchema = Body(..., description="业务表信息"),
auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:update"])),
current_user: UserOutSchema = Depends(lambda auth: auth.user)
data: GenTableSchema = Body(..., description="业务表信息"),
auth: AuthSchema = Depends(AuthPermission(["generator:gencode:update"])),
) -> JSONResponse:
# 创建一个新的字典来包含所有数据,包括审计字段
update_data = data.model_dump()
update_data['update_by'] = current_user.username
update_data['updated_at'] = datetime.now()
# 创建一个新的GenTableUpdateSchema实例
updated_data = GenTableUpdateSchema(**update_data)
await GenTableService.validate_edit(updated_data)
edit_gen_result = await GenTableService.update_gen_table_service(auth, updated_data, table_id)
await GenTableService.validate_edit(data)
result_dict = await GenTableService.update_gen_table_service(auth, data, table_id)
logger.info('编辑业务表信息成功')
return SuccessResponse(data=edit_gen_result, msg="编辑业务表信息成功")
return SuccessResponse(data=result_dict, msg="编辑业务表信息成功")
@GenRouter.delete("/delete", summary="删除业务表信息", description="删除业务表信息")
async def delete_gen_table_controller(
table_ids: str = Body(..., description="ID列表,用逗号分隔"),
auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:delete"]))
data: GenTableDeleteSchema = Body(..., description="业务表ID列表"),
auth: AuthSchema = Depends(AuthPermission(["generator:gencode:delete"]))
) -> JSONResponse:
delete_gen_table = GenTableDeleteSchema(table_ids=table_ids)
result = await GenTableService.delete_gen_table_service(auth, delete_gen_table)
result = await GenTableService.delete_gen_table_service(auth, data)
logger.info('删除业务表信息成功')
return result
return SuccessResponse(msg="删除业务表信息成功", data=result)
@GenRouter.post("/import", summary="导入表结构", description="导入表结构")
async def import_gen_table_controller(
tables: List[str] = Body(..., description="表名列表", embed=True),
auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:import"])),
current_user: UserOutSchema = Depends(lambda auth: auth.user)
) -> JSONResponse:
table_names = tables if tables else []
add_gen_table_list = await GenTableService.get_gen_db_table_list_by_name_service(auth, table_names)
result = await GenTableService.import_gen_table_service(auth, add_gen_table_list, current_user)
logger.info('导入表结构成功')
return result
@GenRouter.patch("/batch/out", summary="批量生成代码", description="批量生成代码")
@GenRouter.patch("/batch/output", summary="批量生成代码", description="批量生成代码")
async def batch_gen_code_controller(
tables: str = Query(..., description="表名列表,用逗号分隔"),
auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:operate"]))
table_names: List[str] = Query(None, description="表名列表"),
auth: AuthSchema = Depends(AuthPermission(["generator:gencode:operate"]))
) -> StreamResponse:
table_names = tables.split(',') if tables else []
batch_gen_code_result = await GenTableService.batch_gen_code_service(auth, table_names)
logger.info('批量生成代码成功')
return StreamResponse(
@@ -118,10 +113,10 @@ async def batch_gen_code_controller(
)
@GenRouter.post("/out/path/{table_name}", summary="生成代码到指定路径", description="生成代码到指定路径")
@GenRouter.post("/output/{table_name}", summary="生成代码到指定路径", description="生成代码到指定路径")
async def gen_code_local_controller(
table_name: str = Path(..., description="表名"),
auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:code"]))
auth: AuthSchema = Depends(AuthPermission(["generator:gencode:code"]))
) -> JSONResponse:
from app.config.setting import settings
if not settings.allow_overwrite:
@@ -135,29 +130,17 @@ async def gen_code_local_controller(
@GenRouter.get("/preview/{table_id}", summary="预览代码", description="预览代码")
async def preview_code_controller(
table_id: int = Path(..., description="业务表ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:query"]))
auth: AuthSchema = Depends(AuthPermission(["generator:gencode:query"]))
) -> JSONResponse:
preview_code_result = await GenTableService.preview_code_service(auth, table_id)
logger.info('预览代码成功')
return SuccessResponse(data=preview_code_result, msg="预览代码成功")
@GenRouter.get("/db/list", summary="查询数据库表列表", description="查询数据库表列表")
async def get_gen_db_table_list_controller(
page: PaginationQueryParam = Depends(),
search: GenTableQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["generator:dblist:query"]))
) -> JSONResponse:
result_dict_list = await GenTableService.get_gen_db_table_list_service(auth=auth, search=search, order_by=page.order_by)
result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
logger.info('获取数据库表列表成功')
return SuccessResponse(data=result_dict, msg="获取数据库表列表成功")
@GenRouter.post("/sync/db/{table_name}", summary="同步数据库", description="同步数据库")
@GenRouter.post("/synch_db/{table_name}", summary="同步数据库", description="同步数据库")
async def sync_db_controller(
table_name: str = Path(..., description="表名"),
auth: AuthSchema = Depends(AuthPermission(permissions=["generator:db:sync"]))
auth: AuthSchema = Depends(AuthPermission(["generator:db:sync"]))
) -> JSONResponse:
result = await GenTableService.sync_db_service(auth, table_name)
logger.info('同步数据库成功')
@@ -3,23 +3,24 @@
from sqlalchemy.engine.row import Row
from sqlalchemy import and_, delete, select, text, update
from sqlalchemy.orm import selectinload
from sqlglot.expressions import Expression
from typing import List, Optional, Sequence, Dict
from app.core.logger import logger
from .model import GenTableModel, GenTableColumnModel
from app.config.setting import settings
from app.common.request import PaginationService
from app.core.base_crud import CRUDBase
from app.api.v1.module_system.auth.schema import AuthSchema
from .param import GenTableQueryParam, GenTableColumnQueryParam
from .model import GenTableModel, GenTableColumnModel
from .schema import (
GenTableSchema,
GenTableOutSchema,
GenTableDeleteSchema,
GenTableColumnSchema,
GenTableColumnOutSchema,
GenTableColumnDeleteSchema,
GenDBTableSchema,
)
from .param import GenTableQueryParam, GenTableColumnQueryParam
from app.core.base_crud import CRUDBase
from app.api.v1.module_system.auth.schema import AuthSchema
class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
@@ -70,12 +71,27 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
)
return gen_table
async def get_gen_table_all(self) -> Sequence[GenTableModel]:
"""
获取所有业务表信息
async def get_gen_table_list(self, search: Optional[GenTableQueryParam] = None):
:return: 所有业务表信息列表
"""
gen_table_all = (
await self.db.execute(
select(GenTableModel)
.options(selectinload(GenTableModel.columns))
)
).scalars().all()
return gen_table_all
async def get_gen_table_list(self, search: Optional[GenTableQueryParam] = None) -> Sequence[GenTableModel]:
"""
根据查询参数获取代码生成业务表列表信息
:param query_object: 查询参数对象
:param search: 查询参数对象
:return: 代码生成业务表列表信息对象
"""
# 构建查询条件
@@ -90,7 +106,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
# 获取所有数据
result = await self.db.execute(query)
gen_table_all = list(result.scalars().all())
gen_table_all = result.scalars().all()
return gen_table_all
@@ -105,7 +121,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
await self.db.flush()
return gen_table
async def edit_gen_table(self, table_id: int, edit_model: GenTableSchema):
async def edit_gen_table(self, table_id: int, edit_model: GenTableSchema) -> GenTableSchema:
"""
修改
"""
@@ -119,12 +135,12 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
await self.db.commit()
return edit_model
async def delete_gen_table(self, delete_model: GenTableDeleteSchema) -> None:
async def delete_gen_table(self, data: GenTableDeleteSchema) -> None:
"""
删除
"""
await self.db.execute(
delete(GenTableModel).where(GenTableModel.id.in_(delete_model.table_ids))
delete(GenTableModel).where(GenTableModel.id.in_(data.table_ids))
)
await self.db.flush()
@@ -212,7 +228,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
dict_data.append(dict_row)
return dict_data
async def get_db_table_list_by_names(self, table_names: List[str]) -> list[Dict]:
async def get_db_table_list_by_names(self, table_names: List[str]) -> list[GenDBTableSchema]:
"""
根据业务表名称组获取数据库列表信息
@@ -280,14 +296,14 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
# 检查row是否为Row对象
if isinstance(row, Row):
# 使用._mapping获取字典
dict_row = GenDBTableSchema(**dict(row._mapping)).model_dump()
dict_row = GenDBTableSchema(**dict(row._mapping))
dict_data.append(dict_row)
else:
dict_row = GenDBTableSchema(**dict(row)).model_dump()
dict_row = GenDBTableSchema(**dict(row))
dict_data.append(dict_row)
return dict_data
async def create_table_by_sql(self, sql: str) -> bool:
async def create_table_by_sql(self, sql_statements: List[Expression | None]) -> None:
"""
根据sql语句创建表结构
@@ -296,16 +312,16 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
:return:
"""
try:
await self.db.execute(text(sql))
# 提交事务
await self.db.commit()
await self.db.flush()
return True
for sql_statement in sql_statements:
# 检查sql_statement是否为空
if not sql_statement:
continue
sql = sql_statement.sql(dialect=settings.DATABASE_TYPE)
await self.db.execute(text(sql))
except Exception as e:
# 如果发生异常,回滚事务
await self.db.rollback()
logger.error(f"创建表时发生错误: {e}")
return False
class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, GenTableColumnSchema]):
@@ -315,35 +331,11 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
"""初始化CRUD"""
super().__init__(model=GenTableColumnModel, auth=auth)
async def get_by_id_crud(self, column_id: int) -> Optional[GenTableColumnModel]:
"""详情"""
return await self.get(id=column_id)
async def list_crud(
self,
search: Optional[Dict] = None,
order_by: Optional[List[Dict[str, str]]] = None,
) -> Sequence[GenTableColumnModel]:
"""列表查询"""
return await self.list(search=search, order_by=order_by)
async def create_crud(
self, data: GenTableColumnSchema
) -> Optional[GenTableColumnModel]:
"""创建"""
return await self.create(data=data)
async def update_crud(
self, id: int, data: GenTableColumnSchema
) -> Optional[GenTableColumnModel]:
"""更新"""
return await self.update(id=id, data=data)
async def delete_crud(self, data: GenTableColumnDeleteSchema) -> None:
"""批量删除"""
return await self.delete(ids=data.column_ids)
async def get_gen_db_table_columns_by_name(self, table_name: str) -> List[GenTableColumnSchema]:
async def get_gen_table_column_list_by_table_id(self, table_id: int) -> Optional[GenTableColumnModel]:
"""根据业务表ID获取业务表字段列表信息"""
return await self.get(table_id=table_id)
async def get_gen_db_table_columns_by_name(self, table_name: str | None) -> List[GenTableColumnOutSchema]:
"""
根据业务表名称获取业务表字段列表信息
@@ -412,7 +404,7 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
).fetchall()
return [
GenTableColumnSchema(
GenTableColumnOutSchema(
column_name=row[0],
is_required=row[1],
is_pk=row[2],
@@ -423,3 +415,23 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
)
for row in gen_db_table_columns_raw
]
async def list_gen_table_column_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[GenTableColumnModel]:
"""根据业务表ID查询业务表字段列表"""
return await self.list(search=search, order_by=order_by)
async def create_gen_table_column_crud(self, data: GenTableColumnSchema) -> Optional[GenTableColumnModel]:
"""创建业务表字段"""
return await self.create(data=data)
async def update_gen_table_column_crud(self, id: int, data: GenTableColumnSchema) -> Optional[GenTableColumnModel]:
"""更新业务表字段"""
return await self.update(id=id, data=data)
async def delete_gen_table_column_by_table_id_dao(self, data: GenTableDeleteSchema) -> None:
"""根据业务表ID批量删除"""
return await self.delete(ids=data.table_ids)
async def delete_gen_table_column_by_column_id_dao(self, data: GenTableColumnDeleteSchema) -> None:
"""根据业务表字段ID批量删除"""
return await self.delete(ids=data.column_ids)
@@ -35,16 +35,6 @@ class GenTableColumnQueryParam:
def __init__(
self,
column_name: Optional[str] = Query(None, description="列名称"),
creator: Optional[int] = Query(None, description="创建人"),
start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2025-01-01 00:00:00"),
end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2025-12-31 23:59:59"),
) -> None:
# 模糊查询字段
self.column_name = ("like", column_name)
# 精确查询字段
self.creator_id = creator
# 时间范围查询
if start_time and end_time:
self.created_at = ("between", (start_time, end_time))
@@ -1,10 +1,9 @@
# -*- coding:utf-8 -*-
from typing import Any, List, Literal, Optional
from typing import List, Literal, Optional
from pydantic import BaseModel, ConfigDict, Field, model_validator
from pydantic.alias_generators import to_camel
from app.utils.string_util import StringUtil
from app.common.constant import GenConstant
from app.core.base_schema import BaseSchema
@@ -18,6 +17,7 @@ class GenTableOptionModel(BaseModel):
tree_name: Optional[str] = Field(default=None, description='tree_name')
tree_parent_code: Optional[str] = Field(default=None, description='tree_parent_code')
class GenDBTableSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)
@@ -34,12 +34,13 @@ class GenTableBaseSchema(BaseModel):
"""
model_config = ConfigDict(from_attributes=True)
table_id: Optional[int] = Field(default=None, description='编号')
table_name: Optional[str] = Field(default=None, description='表名称')
table_comment: Optional[str] = Field(default=None, description='表描述')
sub_table_name: Optional[str] = Field(default=None, description='关联子表的表名')
sub_table_fk_name: Optional[str] = Field(default=None, description='子表关联的外键名')
class_name: Optional[str] = Field(default=None, description='实体类名称')
tpl_category: Optional[Literal['crud', 'tree']] = Field(default=None, description='使用的模板(crud单表操作 tree树表操作)')
tpl_category: Optional[str] = Field(default=None, description='使用的模板(crud单表操作 tree树表操作)')
tpl_web_type: Optional[str] = Field(default=None, description='前端模板类型(element-ui模版 element-plus模版)')
package_name: Optional[str] = Field(default=None, description='生成包路径')
module_name: Optional[str] = Field(default=None, description='生成模块名')
@@ -51,16 +52,17 @@ class GenTableBaseSchema(BaseModel):
options: Optional[str] = Field(default=None, description='其它生成选项')
description: Optional[str] = Field(default=None, description='功能描述')
params: Optional[Any] = Field(default=None, description='前端传递过来的表附加信息,转换成json字符串后放到options')
params: Optional[GenTableOptionModel] = Field(default=None, description='前端传递过来的表附加信息,转换成json字符串后放到options')
class GenTableSchema(GenTableBaseSchema):
"""
代码生成业务表更新模型
"""
pk_column: Optional['GenTableColumnSchema'] = Field(default=None, description='主键信息')
pk_column: Optional['GenTableColumnOutSchema'] = Field(default=None, description='主键信息')
sub_table: Optional['GenTableSchema'] = Field(default=None, description='子表信息')
columns: Optional[List['GenTableColumnSchema']] = Field(default=None, description='表列信息')
columns: Optional[List['GenTableColumnOutSchema']] = Field(default=None, description='表列信息')
tree_code: Optional[str] = Field(default=None, description='树编码字段tree_code')
tree_parent_code: Optional[str] = Field(default=None, description='树父编码字段')
tree_name: Optional[str] = Field(default=None, description='树名称字段ree_name')
@@ -78,13 +80,17 @@ class GenTableSchema(GenTableBaseSchema):
return self
class GenTableOutSchema(GenTableSchema, BaseSchema):
model_config = ConfigDict(from_attributes=True)
class GenTableDeleteSchema(BaseModel):
"""
删除代码生成业务表模型
"""
model_config = ConfigDict(alias_generator=to_camel)
table_ids: str = Field(..., description='需要删除的代码生成业务表ID')
table_ids: List[int] = Field(..., description='需要删除的代码生成业务表ID列表')
class GenTableColumnSchema(BaseModel):
@@ -114,6 +120,22 @@ class GenTableColumnSchema(BaseModel):
description: Optional[str] = Field(default=None, description='功能描述')
class GenTableColumnOutSchema(GenTableColumnSchema, BaseSchema):
model_config = ConfigDict(from_attributes=True)
cap_python_field: Optional[str] = Field(default=None, description='字段大写形式')
pk: Optional[bool] = Field(default=None, description='是否主键')
increment: Optional[bool] = Field(default=None, description='是否自增')
required: Optional[bool] = Field(default=None, description='是否必填')
unique: Optional[bool] = Field(default=None, description='是否唯一')
insert: Optional[bool] = Field(default=None, description='是否为插入字段')
edit: Optional[bool] = Field(default=None, description='是否编辑字段')
list: Optional[bool] = Field(default=None, description='是否列表字段')
query: Optional[bool] = Field(default=None, description='是否查询字段')
super_column: Optional[bool] = Field(default=None, description='是否为基类字段')
usable_column: Optional[bool] = Field(default=None, description='是否为基类字段白名单')
class GenTableColumnDeleteSchema(BaseModel):
"""
删除代码生成业务表字段模型
@@ -4,23 +4,21 @@ import io
import json
import os
import zipfile
from datetime import datetime
from typing import Any, List, Dict, Optional, Sequence
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Any, List, Dict, Literal, Optional
from sqlglot.expressions import Add, Alter, Create, Delete, Drop, Expression, Insert, Table, TruncateTable, Update
from sqlglot import parse as sqlglot_parse
from app.config.setting import settings
from app.core.base_model import CamelCaseUtil
from app.core.exceptions import CustomException
from app.utils.gen_util import GenUtils
from app.utils.template_util import TemplateInitializer, TemplateUtils
from app.common.constant import GenConstant
from app.common.response import SuccessResponse
from app.api.v1.module_system.user.schema import UserOutSchema
from app.api.v1.module_system.auth.schema import AuthSchema
from .schema import GenTableCreateSchema, GenTableUpdateSchema, GenTableOutSchema, GenTableDeleteSchema, GenTableColumnCreateSchema, GenTableColumnUpdateSchema, GenTableColumnOutSchema, GenTableColumnDeleteSchema
from app.utils.common_util import CamelCaseUtil
from app.utils.gen_util import GenUtils
from app.utils.jinja2_template_util import Jinja2TemplateInitializerUtil, Jinja2TemplateUtil
from .schema import GenTableSchema, GenTableOutSchema, GenTableOutSchema, GenTableDeleteSchema, GenTableColumnSchema, GenTableColumnOutSchema, GenTableColumnDeleteSchema
from .param import GenTableQueryParam
from .crud import GenTableColumnCRUD, GenTableCRUD
from .model import GenTableModel, GenTableColumnModel
# 定义默认的GenConfig值
@@ -40,197 +38,129 @@ class GenTableService:
@classmethod
async def get_gen_table_list_service(
cls, auth: AuthSchema, query_object: GenTableQueryParam, is_page: bool = False
) -> Dict:
cls, auth: AuthSchema, search: GenTableQueryParam
) -> List[Dict]:
"""获取代码生成业务表列表信息"""
if not auth.db:
raise CustomException(msg='数据库连接不存在')
# 确保db是AsyncSession类型
db = auth.db
if not isinstance(db, AsyncSession):
raise CustomException(msg='数据库连接类型不正确')
gen_table_dao = GenTableCRUD(auth=auth)
gen_table_list_result = await gen_table_dao.get_gen_table_list(query_object, is_page)
return gen_table_list_result
gen_table_list_result = await GenTableCRUD(auth=auth).get_gen_table_list(search)
return [GenTableOutSchema.model_validate(obj).model_dump() for obj in gen_table_list_result]
@classmethod
async def get_gen_db_table_list_service(cls, auth: AuthSchema, search: GenTableQueryParam, order_by: Optional[List[Dict[str, str]]] = None) -> list[Any]:
"""获取数据库列表信息"""
# 确保db是AsyncSession类型
gen_db_table_list_result = await GenTableCRUD(auth=auth).get_gen_db_table_list(search, order_by)
gen_db_table_list_result = await GenTableCRUD(auth=auth).get_db_table_list(search)
return gen_db_table_list_result
@classmethod
async def get_gen_db_table_list_by_name_service(cls, auth: AuthSchema, table_names: List[str]) -> List[GenTableOutSchema]:
"""根据表名称组获取数据库列表信息"""
gen_db_table_list_result = await GenTableCRUD(auth=auth).get_gen_db_table_list_by_names(table_names)
gen_db_table_list_result = await GenTableCRUD(auth=auth).get_db_table_list_by_names(table_names)
return [GenTableOutSchema(**gen_table) for gen_table in CamelCaseUtil.transform_result(gen_db_table_list_result)]
@classmethod
async def import_gen_table_service(
cls, auth: AuthSchema, gen_table_list: List[GenTableOutSchema], current_user: UserOutSchema
) -> SuccessResponse:
cls, auth: AuthSchema, gen_table_list: List[GenTableOutSchema]
) -> Literal[True] | None:
"""导入表结构"""
if not auth.db:
raise CustomException(msg='数据库连接不存在')
# 确保db是AsyncSession类型
db = auth.db
if not isinstance(db, AsyncSession):
raise CustomException(msg='数据库连接类型不正确')
try:
gen_table_dao = GenTableCRUD(auth=auth)
gen_table_column_dao = GenTableColumnCRUD(auth=auth)
for table in gen_table_list:
table_name = table.table_name
GenUtils.init_table(table, current_user.username)
add_gen_table = await gen_table_dao.create(data=table.model_dump())
GenUtils.init_table(table)
add_gen_table = await GenTableCRUD(auth).add_gen_table(table)
if add_gen_table:
# 使用id而不是table_id
table.id = add_gen_table.id
gen_table_columns = await gen_table_column_dao.get_gen_db_table_columns_by_name(db, table_name or "")
table.table_id = add_gen_table.id
gen_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(table_name)
for column in [
GenTableColumnOutSchema(**gen_table_column)
GenTableColumnSchema(**gen_table_column)
for gen_table_column in CamelCaseUtil.transform_result(gen_table_columns)
]:
GenUtils.init_column_field(column, table)
await gen_table_column_dao.create(data=column.model_dump())
if isinstance(db, AsyncSession):
await db.commit()
return SuccessResponse(msg='导入成功')
await GenTableColumnCRUD(auth).create_gen_table_column_crud(column)
return True
except Exception as e:
if isinstance(db, AsyncSession):
try:
await db.rollback()
except:
pass # 忽略回滚错误
raise CustomException(msg=f'导入失败, {str(e)}')
@classmethod
async def create_table_service(cls, auth: AuthSchema, sql: str, current_user: UserOutSchema) -> SuccessResponse:
async def create_table_service(cls, auth: AuthSchema, sql: str) -> Literal[True] | None:
"""创建表结构"""
if not auth.db:
raise CustomException(msg='数据库连接不存在')
# 确保db是AsyncSession类型
db = auth.db
if not isinstance(db, AsyncSession):
raise CustomException(msg='数据库连接类型不正确')
gen_table_dao = GenTableCRUD(auth=auth)
try:
sql_statements = sqlglot_parse(sql, dialect=settings.DATABASE_TYPE)
# 校验sql语句是否为合法的建表语句
if not cls.__is_valid_create_table(sql_statements):
raise CustomException(msg='sql语句不是合法的建表语句')
table_names = cls.__get_table_names(sql_statements)
# 执行SQL语句创建表
await gen_table_dao.create_table_by_sql_dao(db, [sql])
if isinstance(db, AsyncSession):
await db.commit()
return SuccessResponse(msg='创建表结构成功')
await GenTableCRUD(auth=auth).create_table_by_sql(sql_statements)
gen_table_list = await cls.get_gen_db_table_list_by_name_service(auth, table_names)
import_result = await cls.import_gen_table_service(auth, gen_table_list)
return import_result
except Exception as e:
if isinstance(db, AsyncSession):
try:
await db.rollback()
except:
pass # 忽略回滚错误
raise CustomException(msg=f'创建表结构失败: {str(e)}')
@classmethod
def __is_valid_create_table(cls, sql_statements: List[Expression | None]) -> bool:
"""
校验sql语句是否为合法的建表语句
:param sql_statements: sql语句的ast列表
:return: 校验结果
"""
validate_create = [isinstance(sql_statement, Create) for sql_statement in sql_statements]
validate_forbidden_keywords = [
isinstance(
sql_statement,
(Add, Alter, Delete, Drop, Insert, TruncateTable, Update),
)
for sql_statement in sql_statements
]
if not any(validate_create) or any(validate_forbidden_keywords):
return False
return True
@classmethod
def __get_table_names(cls, sql_statements: List[Expression | None]) -> List[str]:
"""
获取sql语句中所有的建表表名
:param sql_statements: sql语句的ast列表
:return: 建表表名列表
"""
table_names = []
for sql_statement in sql_statements:
if isinstance(sql_statement, Create):
table_names.append(sql_statement.find(Table).name)
return table_names
@classmethod
async def update_gen_table_service(cls, auth: AuthSchema, page_object: GenTableUpdateSchema, table_id: int) -> Dict[str, Any]:
"""编辑业务表信息"""
if not auth.db:
raise CustomException(msg='数据库连接不存在')
# 确保db是AsyncSession类型
db = auth.db
if not isinstance(db, AsyncSession):
raise CustomException(msg='数据库连接类型不正确')
gen_table_dao = GenTableCRUD(auth=auth)
gen_table_column_dao = GenTableColumnCRUD(auth=auth)
edit_gen_table = page_object.model_dump(exclude_unset=True, by_alias=True)
async def update_gen_table_service(cls, auth: AuthSchema, data: GenTableSchema, table_id: int) -> Dict[str, Any]:
"""编辑业务表信息"""
edit_gen_table = data.model_dump(exclude_unset=True, by_alias=True)
gen_table_info = await cls.get_gen_table_by_id_service(auth, table_id)
if gen_table_info.id:
try:
# 确保options字段存在且为有效JSON
if 'options' not in edit_gen_table or edit_gen_table['options'] is None:
edit_gen_table['options'] = '{}' # 默认空对象
else:
# 验证options是否为有效的JSON
try:
json.loads(edit_gen_table['options'])
except json.JSONDecodeError:
edit_gen_table['options'] = '{}'
await gen_table_dao.update(id=table_id, data=edit_gen_table)
if hasattr(page_object, 'columns') and page_object.columns:
for gen_table_column in page_object.columns:
# 为列添加更新信息
gen_table_column_dict = gen_table_column.model_dump()
gen_table_column_dict['update_by'] = getattr(page_object, 'update_by', '')
gen_table_column_dict['update_time'] = datetime.now()
# 检查是否有id属性
column_id = getattr(gen_table_column, 'id', None)
if column_id is not None:
await gen_table_column_dao.update(
id=column_id,
data=gen_table_column_dict
)
if isinstance(db, AsyncSession):
await db.commit()
return {"is_success": True, "message": "更新成功"}
edit_gen_table['options'] = json.dumps(edit_gen_table.get('params'))
result = await GenTableCRUD(auth).edit_gen_table(table_id, edit_gen_table)
for gen_table_column in data.columns:
await GenTableColumnCRUD(auth).update_gen_table_column_crud(table_id, gen_table_column)
return result.model_dump()
except Exception as e:
if isinstance(db, AsyncSession):
try:
await db.rollback()
except:
pass # 忽略回滚错误
raise CustomException(msg=f'更新失败: {str(e)}')
else:
raise CustomException(msg='业务表不存在')
@classmethod
async def delete_gen_table_service(cls, auth: AuthSchema, page_object: GenTableDeleteSchema) -> SuccessResponse:
async def delete_gen_table_service(cls, auth: AuthSchema, data: GenTableDeleteSchema) -> None:
"""删除业务表信息"""
if not auth.db:
raise CustomException(msg='数据库连接不存在')
# 确保db是AsyncSession类型
db = auth.db
if not isinstance(db, AsyncSession):
raise CustomException(msg='数据库连接类型不正确')
gen_table_dao = GenTableCRUD(auth=auth)
gen_table_column_dao = GenTableColumnCRUD(auth=auth)
if page_object.table_ids:
table_id_list = page_object.table_ids.split(',')
try:
for table_id in table_id_list:
await gen_table_dao.delete(ids=[int(table_id)])
# 删除相关的字段信息
# 这里需要先查询出所有相关的column_id,然后删除
columns = await gen_table_column_dao.get_gen_table_column_list_by_table_id(db, int(table_id))
if columns:
column_ids = [column.id for column in columns]
await gen_table_column_dao.delete(ids=column_ids)
if isinstance(db, AsyncSession):
await db.commit()
return SuccessResponse(msg='删除成功')
except Exception as e:
if isinstance(db, AsyncSession):
try:
await db.rollback()
except:
pass # 忽略回滚错误
raise CustomException(msg=f'删除失败: {str(e)}')
else:
raise CustomException(msg='传入业务表id为空')
try:
await GenTableCRUD(auth=auth).delete_gen_table(data)
await GenTableColumnCRUD(auth=auth).delete_gen_table_column_by_table_id_dao(data)
except Exception as e:
raise CustomException(msg=f'删除失败: {str(e)}')
@classmethod
async def get_gen_table_by_id_service(cls, auth: AuthSchema, table_id: int) -> GenTableOutSchema:
"""获取需要生成的业务表详细信息"""
if not auth.db:
raise CustomException(msg='数据库连接不存在')
# 确保db是AsyncSession类型
db = auth.db
if not isinstance(db, AsyncSession):
raise CustomException(msg='数据库连接类型不正确')
gen_table_dao = GenTableCRUD(auth=auth)
gen_table = await gen_table_dao.get_gen_table_by_id(db, table_id)
gen_table = await GenTableCRUD(auth=auth).get_gen_table_by_id(table_id)
if gen_table:
result = await cls.set_table_from_options(GenTableOutSchema(**CamelCaseUtil.transform_result(gen_table)))
return result
@@ -240,42 +170,38 @@ class GenTableService:
@classmethod
async def get_gen_table_all_service(cls, auth: AuthSchema) -> List[GenTableOutSchema]:
"""获取所有业务表信息"""
if not auth.db:
raise CustomException(msg='数据库连接不存在')
# 确保db是AsyncSession类型
db = auth.db
if not isinstance(db, AsyncSession):
raise CustomException(msg='数据库连接类型不正确')
gen_table_dao = GenTableCRUD(auth=auth)
gen_tables = await gen_table_dao.get_gen_table_all(db)
result = []
for table in gen_tables:
table_info = await cls.set_table_from_options(GenTableOutSchema(**CamelCaseUtil.transform_result(table)))
result.append(table_info)
gen_table_all = await GenTableCRUD(auth=auth).get_gen_table_all()
result = [GenTableOutSchema(**gen_table) for gen_table in CamelCaseUtil.transform_result(gen_table_all)]
return result
@classmethod
async def preview_code_service(cls, auth: AuthSchema, table_id: int) -> Dict[Any, Any]:
"""预览代码"""
gen_table = await cls.get_gen_table_by_id_service(auth, table_id)
await cls.set_sub_table(auth, gen_table)
await cls._set_pk_column(gen_table)
env = TemplateInitializer.init_jinja2()
context = TemplateUtils.prepare_context(gen_table)
template_list = TemplateUtils.get_template_list(
gen_table.tpl_category or "",
gen_table.tpl_web_type or ""
"""
预览代码service
:param auth: 认证对象
:param table_id: 业务表id
:return: 预览数据列表
"""
gen_table = GenTableOutSchema(
**CamelCaseUtil.transform_result(await GenTableCRUD(auth).get_gen_table_by_id(table_id))
)
await cls.set_sub_table(auth, gen_table)
await cls.set_pk_column(gen_table)
env = Jinja2TemplateInitializerUtil.init_jinja2()
context = Jinja2TemplateUtil.prepare_context(gen_table)
template_list = Jinja2TemplateUtil.get_template_list(gen_table.tpl_category, gen_table.tpl_web_type)
preview_code_result = {}
for template in template_list:
render_content = env.get_template(template).render(**context)
preview_code_result[template] = render_content
return preview_code_result
@classmethod
async def generate_code_service(cls, auth: AuthSchema, table_name: str) -> SuccessResponse:
"""生成代码至指定路径"""
env = TemplateInitializer.init_jinja2()
env = Jinja2TemplateInitializerUtil.init_jinja2()
render_info = await cls.__get_gen_render_info(auth, table_name)
for template in render_info[0]:
try:
@@ -292,11 +218,17 @@ class GenTableService:
@classmethod
async def batch_gen_code_service(cls, auth: AuthSchema, table_names: List[str]) -> bytes:
"""批量生成代码"""
"""
批量生成代码service
:param auth: 认证对象
:param table_names: 业务表名称组
:return: 下载代码结果
"""
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
for table_name in table_names:
env = TemplateInitializer.init_jinja2()
env = Jinja2TemplateInitializerUtil.init_jinja2()
render_info = await cls.__get_gen_render_info(auth, table_name)
for template_file, output_file in zip(render_info[0], render_info[1]):
render_content = env.get_template(template_file).render(**render_info[2])
@@ -307,93 +239,58 @@ class GenTableService:
return zip_data
@classmethod
async def sync_db_service(cls, auth: AuthSchema, table_name: str) -> SuccessResponse:
async def sync_db_service(cls, auth: AuthSchema, table_name: str) -> None:
"""同步数据库"""
if not auth.db:
raise CustomException(msg='数据库连接不存在')
# 确保db是AsyncSession类型
db = auth.db
if not isinstance(db, AsyncSession):
raise CustomException(msg='数据库连接类型不正确')
gen_table_dao = GenTableCRUD(auth=auth)
gen_table_column_dao = GenTableColumnCRUD(auth=auth)
gen_table = await gen_table_dao.get_gen_table_by_name(db, table_name)
if gen_table:
table = GenTableOutSchema(**CamelCaseUtil.transform_result(gen_table))
table_columns = table.columns or [] # 确保不为None
table_column_map = {column.column_name: column for column in table_columns}
query_db_table_columns = await gen_table_column_dao.get_gen_db_table_columns_by_name(db, table_name)
db_table_columns = [
GenTableColumnOutSchema(**column) for column in CamelCaseUtil.transform_result(query_db_table_columns)
]
if not db_table_columns:
raise CustomException('同步数据失败,原表结构不存在')
db_table_column_names = [column.column_name for column in db_table_columns]
try:
for column in db_table_columns:
GenUtils.init_column_field(column, table)
if column.column_name in table_column_map:
prev_column = table_column_map[column.column_name]
# 使用getattr安全访问id属性
column_id = getattr(prev_column, 'id', None)
if column_id is not None:
# 为column设置id属性
column.id = column_id
if getattr(column, 'list', False): # 使用getattr安全访问属性
column.dict_type = prev_column.dict_type
column.query_type = prev_column.query_type
if (
prev_column.is_required != ''
and not column.pk
and (column.insert or column.edit)
and (column.usable_column or column.super_column)
):
column.is_required = prev_column.is_required
column.html_type = prev_column.html_type
# 使用getattr安全访问id属性
column_id = getattr(column, 'id', None)
if column_id is not None:
await gen_table_column_dao.update(id=column_id, data=column.model_dump(by_alias=True))
else:
await gen_table_column_dao.create(data=column.model_dump(by_alias=True))
del_columns = [column for column in table_columns if column.column_name not in db_table_column_names]
if del_columns:
for column in del_columns:
# 使用getattr安全访问id属性
column_id = getattr(column, 'id', None)
if column_id is not None:
await gen_table_column_dao.delete(ids=[column_id])
if isinstance(db, AsyncSession):
await db.commit()
return SuccessResponse(msg='同步成功')
except Exception as e:
if isinstance(db, AsyncSession):
try:
await db.rollback()
except:
pass # 忽略回滚错误
raise CustomException(msg=f'同步失败: {str(e)}')
else:
raise CustomException('业务表不存在')
gen_table = await GenTableCRUD(auth).get_gen_table_by_name(table_name)
table = GenTableSchema(**CamelCaseUtil.transform_result(gen_table))
table_columns = table.columns
table_column_map = {column.column_name: column for column in table_columns}
query_db_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(table_name)
db_table_columns = [
GenTableColumnOutSchema(**column) for column in CamelCaseUtil.transform_result(query_db_table_columns)
]
if not db_table_columns:
raise CustomException(msg='同步数据失败,原表结构不存在')
db_table_column_names = [column.column_name for column in db_table_columns]
try:
for column in db_table_columns:
GenUtils.init_column_field(column, table)
if column.column_name in table_column_map:
prev_column = table_column_map[column.column_name]
column.id = prev_column.id
if column.list:
column.dict_type = prev_column.dict_type
column.query_type = prev_column.query_type
if (
prev_column.is_required != ''
and not column.pk
and (column.insert or column.edit)
and (column.usable_column or column.super_column)
):
column.is_required = prev_column.is_required
column.html_type = prev_column.html_type
await GenTableColumnCRUD(auth).update_gen_table_column_crud(column.id,column)
else:
await GenTableColumnCRUD(auth).create_gen_table_column_crud(column)
del_columns = [column for column in table_columns if column.column_name not in db_table_column_names]
if del_columns:
for column in del_columns:
await GenTableColumnCRUD(auth).delete_gen_table_column_by_column_id_dao(column.id)
except Exception as e:
raise e
@classmethod
async def set_sub_table(cls, auth: AuthSchema, gen_table: GenTableOutSchema) -> None:
"""设置主子表信息"""
if not auth.db:
raise CustomException(msg='数据库连接不存在')
# 确保db是AsyncSession类型
db = auth.db
if not isinstance(db, AsyncSession):
raise CustomException(msg='数据库连接类型不正确')
if gen_table.sub_table_name:
gen_table_dao = GenTableCRUD(auth=auth)
sub_table = await gen_table_dao.get_gen_table_by_name(db, gen_table.sub_table_name)
sub_table = await gen_table_dao.get_gen_table_by_name(gen_table.sub_table_name)
if sub_table:
gen_table.sub_table = GenTableOutSchema(**CamelCaseUtil.transform_result(sub_table))
@classmethod
async def _set_pk_column(cls, gen_table: GenTableOutSchema) -> None:
async def set_pk_column(cls, gen_table: GenTableOutSchema) -> None:
"""设置主键列信息"""
if gen_table.columns:
for column in gen_table.columns:
@@ -425,7 +322,7 @@ class GenTableService:
return gen_table
@classmethod
async def validate_edit(cls, edit_gen_table: GenTableUpdateSchema) -> None:
async def validate_edit(cls, edit_gen_table: GenTableSchema) -> None:
"""编辑保存参数校验"""
if edit_gen_table.tpl_category == GenConstant.TPL_TREE:
# 从options字段获取参数,而不是params
@@ -448,29 +345,26 @@ class GenTableService:
@classmethod
async def __get_gen_render_info(cls, auth: AuthSchema, table_name: str) -> List[Any]:
"""获取生成代码渲染模板相关信息"""
if not auth.db:
raise CustomException(msg='数据库连接不存在')
# 确保db是AsyncSession类型
db = auth.db
if not isinstance(db, AsyncSession):
raise CustomException(msg='数据库连接类型不正确')
gen_table_dao = GenTableCRUD(auth=auth)
gen_table = await gen_table_dao.get_gen_table_by_name(db, table_name)
if gen_table:
gen_table_schema = GenTableOutSchema(**CamelCaseUtil.transform_result(gen_table))
await cls.set_sub_table(auth, gen_table_schema)
await cls._set_pk_column(gen_table_schema)
context = TemplateUtils.prepare_context(gen_table_schema)
template_list = TemplateUtils.get_template_list(
gen_table_schema.tpl_category or "",
gen_table_schema.tpl_web_type or ""
)
output_files = [TemplateUtils.get_file_name([template], gen_table_schema)[0] for template in template_list]
"""
获取生成代码渲染模板相关信息
return [template_list, output_files, context, gen_table_schema]
else:
raise CustomException(msg=f'业务表 {table_name} 不存在')
:param auth: 认证对象
:param table_name: 业务表名称
:return: 生成代码渲染模板相关信息
"""
gen_table = await GenTableCRUD(auth=auth).get_gen_table_by_name(table_name)
gen_table_schema = GenTableOutSchema(**CamelCaseUtil.transform_result(gen_table))
await cls.set_sub_table(auth, gen_table_schema)
await cls.set_pk_column(gen_table_schema)
context = Jinja2TemplateUtil.prepare_context(gen_table_schema)
template_list = Jinja2TemplateUtil.get_template_list(
gen_table_schema.tpl_category or "",
gen_table_schema.tpl_web_type or ""
)
output_files = [Jinja2TemplateUtil.get_file_name([template], gen_table_schema)[0] for template in template_list]
return [template_list, output_files, context, gen_table_schema]
@classmethod
def __get_gen_path(cls, gen_table: GenTableOutSchema, template: str) -> Optional[str]:
@@ -478,10 +372,10 @@ class GenTableService:
try:
gen_path = gen_table.gen_path or ""
if gen_path == '/':
file_name = TemplateUtils.get_file_name([template], gen_table)[0]
file_name = Jinja2TemplateUtil.get_file_name([template], gen_table)[0]
return os.path.join(os.getcwd(), GEN_PATH, file_name)
else:
file_name = TemplateUtils.get_file_name([template], gen_table)[0]
file_name = Jinja2TemplateUtil.get_file_name([template], gen_table)[0]
return os.path.join(gen_path, file_name)
except Exception:
return None
@@ -493,14 +387,7 @@ class GenTableColumnService:
@classmethod
async def get_gen_table_column_list_by_table_id_service(cls, auth: AuthSchema, table_id: int) -> List[GenTableColumnOutSchema]:
"""获取业务表字段列表信息"""
if not auth.db:
raise CustomException(msg='数据库连接不存在')
# 确保db是AsyncSession类型
db = auth.db
if not isinstance(db, AsyncSession):
raise CustomException(msg='数据库连接类型不正确')
gen_table_column_dao = GenTableColumnCRUD(auth=auth)
gen_table_column_list_result = await gen_table_column_dao.get_gen_table_column_list_by_table_id(db, table_id)
gen_table_column_list_result = await GenTableColumnCRUD(auth).list_gen_table_column_crud({"table_id": table_id})
return [
GenTableColumnOutSchema(**gen_table_column)
for gen_table_column in CamelCaseUtil.transform_result(gen_table_column_list_result)
+7 -7
View File
@@ -17,7 +17,7 @@ CacheRouter = APIRouter(route_class=OperationLogRoute, prefix="/cache", tags=["
@CacheRouter.get(
'/info',
dependencies=[Depends(AuthPermission(permissions=['monitor:cache:query']))],
dependencies=[Depends(AuthPermission(['monitor:cache:query']))],
summary="获取缓存监控信息",
description="获取缓存监控信息"
)
@@ -32,7 +32,7 @@ async def get_monitor_cache_info_controller(
@CacheRouter.get(
'/get/names',
dependencies=[Depends(AuthPermission(permissions=['monitor:cache:query']))],
dependencies=[Depends(AuthPermission(['monitor:cache:query']))],
summary="获取缓存名称列表",
description="获取缓存名称列表"
)
@@ -45,7 +45,7 @@ async def get_monitor_cache_name_controller() -> JSONResponse:
@CacheRouter.get(
'/get/keys/{cache_name}',
dependencies=[Depends(AuthPermission(permissions=['monitor:cache:query']))],
dependencies=[Depends(AuthPermission(['monitor:cache:query']))],
summary="获取缓存键名列表",
description="获取缓存键名列表"
)
@@ -61,7 +61,7 @@ async def get_monitor_cache_key_controller(
@CacheRouter.get(
'/get/value/{cache_name}/{cache_key}',
dependencies=[Depends(AuthPermission(permissions=['monitor:cache:query']))],
dependencies=[Depends(AuthPermission(['monitor:cache:query']))],
summary="获取缓存值",
description="获取缓存值"
)
@@ -78,7 +78,7 @@ async def get_monitor_cache_value_controller(
@CacheRouter.delete(
'/delete/name/{cache_name}',
dependencies=[Depends(AuthPermission(permissions=['monitor:cache:delete']))],
dependencies=[Depends(AuthPermission(['monitor:cache:delete']))],
summary="清除指定缓存名称的所有缓存",
description="清除指定缓存名称的所有缓存"
)
@@ -96,7 +96,7 @@ async def clear_monitor_cache_name_controller(
@CacheRouter.delete(
'/delete/key/{cache_key}',
dependencies=[Depends(AuthPermission(permissions=['monitor:cache:delete']))],
dependencies=[Depends(AuthPermission(['monitor:cache:delete']))],
summary="清除指定缓存键",
description="清除指定缓存键"
)
@@ -114,7 +114,7 @@ async def clear_monitor_cache_key_controller(
@CacheRouter.delete(
'/delete/all',
dependencies=[Depends(AuthPermission(permissions=['monitor:cache:delete']))],
dependencies=[Depends(AuthPermission(['monitor:cache:delete']))],
summary="清除所有缓存",
description="清除所有缓存"
)
@@ -25,7 +25,7 @@ JobRouter = APIRouter(route_class=OperationLogRoute, prefix="/job", tags=["定
@JobRouter.get("/detail/{id}", summary="获取定时任务详情", description="获取定时任务详情")
async def get_obj_detail_controller(
id: int = Path(..., description="定时任务ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:query"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:query"]))
) -> JSONResponse:
result_dict = await JobService.get_job_detail_service(id=id, auth=auth)
logger.info(f"获取定时任务详情成功 {id}")
@@ -35,7 +35,7 @@ async def get_obj_detail_controller(
async def get_obj_list_controller(
page: PaginationQueryParam = Depends(),
search: JobQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:query"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:query"]))
) -> JSONResponse:
result_dict_list = await JobService.get_job_list_service(auth=auth, search=search, order_by=page.order_by)
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
@@ -45,7 +45,7 @@ async def get_obj_list_controller(
@JobRouter.post("/create", summary="创建定时任务", description="创建定时任务")
async def create_obj_controller(
data: JobCreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:create"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:create"]))
) -> JSONResponse:
result_dict = await JobService.create_job_service(auth=auth, data=data)
logger.info(f"创建定时任务成功: {result_dict}")
@@ -55,7 +55,7 @@ async def create_obj_controller(
async def update_obj_controller(
data: JobUpdateSchema,
id: int = Path(..., description="定时任务ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:update"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:update"]))
) -> JSONResponse:
result_dict = await JobService.update_job_service(auth=auth, id=id, data=data)
logger.info(f"修改定时任务成功: {result_dict}")
@@ -64,7 +64,7 @@ async def update_obj_controller(
@JobRouter.delete("/delete", summary="删除定时任务", description="删除定时任务")
async def delete_obj_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:delete"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:delete"]))
) -> JSONResponse:
await JobService.delete_job_service(auth=auth, ids=ids)
logger.info(f"删除定时任务成功: {ids}")
@@ -73,7 +73,7 @@ async def delete_obj_controller(
@JobRouter.post('/export', summary="导出定时任务", description="导出定时任务")
async def export_obj_list_controller(
search: JobQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:export"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:export"]))
) -> StreamingResponse:
# 获取全量数据
result_dict_list = await JobService.get_job_list_service(search=search, auth=auth)
@@ -90,7 +90,7 @@ async def export_obj_list_controller(
@JobRouter.delete("/clear", summary="清空定时任务日志", description="清空定时任务日志")
async def clear_obj_log_controller(
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:delete"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:delete"]))
) -> JSONResponse:
await JobService.clear_job_service(auth=auth)
logger.info(f"清空定时任务成功")
@@ -100,13 +100,13 @@ async def clear_obj_log_controller(
async def option_obj_controller(
id: int = Body(..., description="定时任务ID"),
option: int = Body(..., description="操作类型 1: 暂停 2: 恢复 3: 重启"),
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:update"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:update"]))
) -> JSONResponse:
await JobService.option_job_service(auth=auth, id=id, option=option)
logger.info(f"操作定时任务成功: {id}")
return SuccessResponse(msg="操作定时任务成功")
@JobRouter.get("/log", summary="获取定时任务日志", description="获取定时任务日志", dependencies=[Depends(AuthPermission(permissions=["monitor:job:query"]))])
@JobRouter.get("/log", summary="获取定时任务日志", description="获取定时任务日志", dependencies=[Depends(AuthPermission(["monitor:job:query"]))])
async def get_job_log_controller():
data = [
{
@@ -134,7 +134,7 @@ async def get_job_log_controller():
@JobRouter.get("/log/detail/{id}", summary="获取定时任务日志详情", description="获取定时任务日志详情")
async def get_job_log_detail_controller(
id: int = Path(..., description="定时任务日志ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:query"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:query"]))
) -> JSONResponse:
result_dict = await JobLogService.get_job_log_detail_service(id=id, auth=auth)
logger.info(f"获取定时任务日志详情成功 {id}")
@@ -145,7 +145,7 @@ async def get_job_log_detail_controller(
async def get_job_log_list_controller(
page: PaginationQueryParam = Depends(),
search: JobLogQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:query"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:query"]))
) -> JSONResponse:
order_by = [{"create_time": "desc"}]
result_dict_list = await JobLogService.get_job_log_list_service(auth=auth, search=search, order_by=order_by)
@@ -157,7 +157,7 @@ async def get_job_log_list_controller(
@JobRouter.delete("/log/delete", summary="删除定时任务日志", description="删除定时任务日志")
async def delete_job_log_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:delete"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:delete"]))
) -> JSONResponse:
await JobLogService.delete_job_log_service(auth=auth, ids=ids)
logger.info(f"删除定时任务日志成功: {ids}")
@@ -166,7 +166,7 @@ async def delete_job_log_controller(
@JobRouter.delete("/log/clear", summary="清空定时任务日志", description="清空定时任务日志")
async def clear_job_log_controller(
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:delete"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:delete"]))
) -> JSONResponse:
await JobLogService.clear_job_log_service(auth=auth)
logger.info(f"清空定时任务日志成功")
@@ -176,7 +176,7 @@ async def clear_job_log_controller(
@JobRouter.post('/log/export', summary="导出定时任务日志", description="导出定时任务日志")
async def export_job_log_list_controller(
search: JobLogQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:export"]))
auth: AuthSchema = Depends(AuthPermission(["monitor:job:export"]))
) -> StreamingResponse:
# 获取全量数据
result_dict_list = await JobLogService.get_job_log_list_service(search=search, auth=auth)
@@ -19,7 +19,7 @@ OnlineRouter = APIRouter(route_class=OperationLogRoute, prefix="/online", tags=[
@OnlineRouter.get(
'/list',
dependencies=[Depends(AuthPermission(permissions=['monitor:online:query']))],
dependencies=[Depends(AuthPermission(['monitor:online:query']))],
summary="获取在线用户列表",
description="获取在线用户列表"
)
@@ -38,7 +38,7 @@ async def get_online_list_controller(
@OnlineRouter.delete(
'/delete',
dependencies=[Depends(AuthPermission(permissions=['monitor:online:delete']))],
dependencies=[Depends(AuthPermission(['monitor:online:delete']))],
summary="强制下线",
description="强制下线"
)
@@ -56,7 +56,7 @@ async def delete_online_controller(
@OnlineRouter.delete(
'/clear',
dependencies=[Depends(AuthPermission(permissions=['monitor:online:delete']))],
dependencies=[Depends(AuthPermission(['monitor:online:delete']))],
summary="清除所有在线用户",
description="清除所有在线用户"
)
@@ -28,7 +28,7 @@ ResourceRouter = APIRouter(route_class=OperationLogRoute, prefix="/resource", ta
"/list",
summary="获取目录列表",
description="获取指定目录下的文件和子目录列表",
dependencies=[Depends(AuthPermission(permissions=["monitor:resource:query"]))]
dependencies=[Depends(AuthPermission(["monitor:resource:query"]))]
)
async def get_directory_list_controller(
request: Request,
@@ -56,7 +56,7 @@ async def get_directory_list_controller(
"/upload",
summary="上传文件",
description="上传文件到指定目录",
dependencies=[Depends(AuthPermission(permissions=["monitor:resource:upload"]))])
dependencies=[Depends(AuthPermission(["monitor:resource:upload"]))])
async def upload_file_controller(
file: UploadFile,
request: Request,
@@ -76,7 +76,7 @@ async def upload_file_controller(
"/download",
summary="下载文件",
description="下载指定文件",
dependencies=[Depends(AuthPermission(permissions=["monitor:resource:download"]))]
dependencies=[Depends(AuthPermission(["monitor:resource:download"]))]
)
async def download_file_controller(
request: Request,
@@ -104,7 +104,7 @@ async def download_file_controller(
"/delete",
summary="删除文件",
description="删除指定文件或目录",
dependencies=[Depends(AuthPermission(permissions=["monitor:resource:delete"]))]
dependencies=[Depends(AuthPermission(["monitor:resource:delete"]))]
)
async def delete_files_controller(
paths: List[str] = Body(..., description="文件路径列表")
@@ -119,7 +119,7 @@ async def delete_files_controller(
"/move",
summary="移动文件",
description="移动文件或目录",
dependencies=[Depends(AuthPermission(permissions=["monitor:resource:move"]))]
dependencies=[Depends(AuthPermission(["monitor:resource:move"]))]
)
async def move_file_controller(
data: ResourceMoveSchema
@@ -134,7 +134,7 @@ async def move_file_controller(
"/copy",
summary="复制文件",
description="复制文件或目录",
dependencies=[Depends(AuthPermission(permissions=["monitor:resource:copy"]))]
dependencies=[Depends(AuthPermission(["monitor:resource:copy"]))]
)
async def copy_file_controller(
data: ResourceCopySchema
@@ -149,7 +149,7 @@ async def copy_file_controller(
"/rename",
summary="重命名文件",
description="重命名文件或目录",
dependencies=[Depends(AuthPermission(permissions=["monitor:resource:rename"]))]
dependencies=[Depends(AuthPermission(["monitor:resource:rename"]))]
)
async def rename_file_controller(
data: ResourceRenameSchema
@@ -164,7 +164,7 @@ async def rename_file_controller(
"/create-dir",
summary="创建目录",
description="在指定路径创建新目录",
dependencies=[Depends(AuthPermission(permissions=["monitor:resource:create_dir"]))]
dependencies=[Depends(AuthPermission(["monitor:resource:create_dir"]))]
)
async def create_directory_controller(
data: ResourceCreateDirSchema
@@ -179,7 +179,7 @@ async def create_directory_controller(
"/export",
summary="导出资源列表",
description="导出资源列表",
dependencies=[Depends(AuthPermission(permissions=["monitor:resource:export"]))]
dependencies=[Depends(AuthPermission(["monitor:resource:export"]))]
)
async def export_resource_list_controller(
request: Request,
@@ -17,7 +17,7 @@ ServerRouter = APIRouter(route_class=OperationLogRoute, prefix="/server", tags=[
'/info',
summary="查询服务器监控信息",
description="查询服务器监控信息",
dependencies=[Depends(AuthPermission(permissions=["monitor:server:query"]))]
dependencies=[Depends(AuthPermission(["monitor:server:query"]))]
)
async def get_monitor_server_info_controller() -> JSONResponse:
# 获取全量数据
@@ -23,7 +23,7 @@ DeptRouter = APIRouter(route_class=OperationLogRoute, prefix="/dept", tags=["部
@DeptRouter.get("/tree", summary="查询部门树", description="查询部门树")
async def get_dept_tree_controller(
search: DeptQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dept:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:dept:query"]))
) -> JSONResponse:
order_by = [{"order": "asc"}]
result_dict_list = await DeptService.get_dept_tree_service(search=search, auth=auth, order_by=order_by)
@@ -34,7 +34,7 @@ async def get_dept_tree_controller(
@DeptRouter.get("/detail/{id}", summary="查询部门详情", description="查询部门详情")
async def get_obj_detail_controller(
id: int = Path(..., description="部门ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dept:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:dept:query"]))
) -> JSONResponse:
result_dict = await DeptService.get_dept_detail_service(id=id, auth=auth)
logger.info(f"查询部门详情成功 {id}")
@@ -44,7 +44,7 @@ async def get_obj_detail_controller(
@DeptRouter.post("/create", summary="创建部门", description="创建部门")
async def create_obj_controller(
data: DeptCreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dept:create"]))
auth: AuthSchema = Depends(AuthPermission(["system:dept:create"]))
) -> JSONResponse:
result_dict = await DeptService.create_dept_service(data=data, auth=auth)
logger.info(f"创建部门成功: {result_dict}")
@@ -55,7 +55,7 @@ async def create_obj_controller(
async def update_obj_controller(
data: DeptUpdateSchema,
id: int = Path(..., description="部门ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dept:update"]))
auth: AuthSchema = Depends(AuthPermission(["system:dept:update"]))
) -> JSONResponse:
result_dict = await DeptService.update_dept_service(auth=auth, id=id, data=data)
logger.info(f"修改部门成功: {result_dict}")
@@ -65,7 +65,7 @@ async def update_obj_controller(
@DeptRouter.delete("/delete", summary="删除部门", description="删除部门")
async def delete_obj_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dept:delete"]))
auth: AuthSchema = Depends(AuthPermission(["system:dept:delete"]))
) -> JSONResponse:
await DeptService.delete_dept_service(ids=ids, auth=auth)
logger.info(f"删除部门成功: {ids}")
@@ -75,7 +75,7 @@ async def delete_obj_controller(
@DeptRouter.patch("/available/setting", summary="批量修改部门状态", description="批量修改部门状态")
async def batch_set_available_obj_controller(
data: BatchSetAvailable,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dept:patch"]))
auth: AuthSchema = Depends(AuthPermission(["system:dept:patch"]))
) -> JSONResponse:
await DeptService.batch_set_available_service(data=data, auth=auth)
logger.info(f"批量修改部门状态成功: {data.ids}")
@@ -29,7 +29,7 @@ DictRouter = APIRouter(route_class=OperationLogRoute, prefix="/dict", tags=["字
@DictRouter.get("/type/detail/{id}", summary="获取字典类型详情", description="获取字典类型详情")
async def get_type_detail_controller(
id: int = Path(..., description="字典类型ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_type:query"]))
) -> JSONResponse:
result_dict = await DictTypeService.get_obj_detail_service(id=id, auth=auth)
logger.info(f"获取字典类型详情成功 {id}")
@@ -39,7 +39,7 @@ async def get_type_detail_controller(
async def get_type_list_controller(
page: PaginationQueryParam = Depends(),
search: DictTypeQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_type:query"]))
) -> JSONResponse:
result_dict_list = await DictTypeService.get_obj_list_service(auth=auth, search=search, order_by=page.order_by)
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
@@ -48,7 +48,7 @@ async def get_type_list_controller(
@DictRouter.get("/type/optionselect", summary="获取全部字典类型", description="获取全部字典类型")
async def get_type_loptionselect_controller(
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_type:query"]))
) -> JSONResponse:
result_dict_list = await DictTypeService.get_obj_list_service(auth=auth)
logger.info(f"获取字典类型列表成功")
@@ -58,7 +58,7 @@ async def get_type_loptionselect_controller(
async def create_type_controller(
data: DictTypeCreateSchema,
redis: Redis = Depends(redis_getter),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:create"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_type:create"]))
) -> JSONResponse:
result_dict = await DictTypeService.create_obj_service(auth=auth, redis=redis, data=data)
logger.info(f"创建字典类型成功: {result_dict}")
@@ -69,7 +69,7 @@ async def update_type_controller(
data: DictTypeUpdateSchema,
redis: Redis = Depends(redis_getter),
id: int = Path(..., description="字典类型ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:update"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_type:update"]))
) -> JSONResponse:
result_dict = await DictTypeService.update_obj_service(auth=auth, redis=redis, id=id, data=data)
logger.info(f"修改字典类型成功: {result_dict}")
@@ -79,7 +79,7 @@ async def update_type_controller(
async def delete_type_controller(
redis: Redis = Depends(redis_getter),
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:delete"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_type:delete"]))
) -> JSONResponse:
await DictTypeService.delete_obj_service(auth=auth, redis=redis, ids=ids)
logger.info(f"删除字典类型成功: {ids}")
@@ -88,7 +88,7 @@ async def delete_type_controller(
@DictRouter.patch("/type/available/setting", summary="批量修改字典类型状态", description="批量修改字典类型状态")
async def batch_set_available_dict_type_controller(
data: BatchSetAvailable,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:patch"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_type:patch"]))
) -> JSONResponse:
await DictTypeService.set_obj_available_service(auth=auth, data=data)
logger.info(f"批量修改字典类型状态成功: {data.ids}")
@@ -97,7 +97,7 @@ async def batch_set_available_dict_type_controller(
@DictRouter.post('/type/export', summary="导出字典类型", description="导出字典类型")
async def export_type_list_controller(
search: DictTypeQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:export"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_type:export"]))
) -> StreamingResponse:
# 获取全量数据
result_dict_list = await DictTypeService.get_obj_list_service(search=search, auth=auth)
@@ -115,7 +115,7 @@ async def export_type_list_controller(
@DictRouter.get("/data/detail/{id}", summary="获取字典数据详情", description="获取字典数据详情")
async def get_data_detail_controller(
id: int = Path(..., description="字典数据ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_data:query"]))
) -> JSONResponse:
result_dict = await DictDataService.get_obj_detail_service(id=id, auth=auth)
logger.info(f"获取字典数据详情成功 {id}")
@@ -125,7 +125,7 @@ async def get_data_detail_controller(
async def get_data_list_controller(
page: PaginationQueryParam = Depends(),
search: DictDataQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_data:query"]))
) -> JSONResponse:
result_dict_list = await DictDataService.get_obj_list_service(auth=auth, search=search, order_by=page.order_by)
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
@@ -136,7 +136,7 @@ async def get_data_list_controller(
async def create_data_controller(
data: DictDataCreateSchema,
redis: Redis = Depends(redis_getter),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:create"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_data:create"]))
) -> JSONResponse:
result_dict = await DictDataService.create_obj_service(auth=auth, redis=redis, data=data)
logger.info(f"创建字典数据成功: {result_dict}")
@@ -147,7 +147,7 @@ async def update_data_controller(
data: DictDataUpdateSchema,
redis: Redis = Depends(redis_getter),
id: int = Path(..., description="字典数据ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:update"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_data:update"]))
) -> JSONResponse:
result_dict = await DictDataService.update_obj_service(auth=auth, redis=redis, id=id, data=data)
logger.info(f"修改字典数据成功: {result_dict}")
@@ -157,7 +157,7 @@ async def update_data_controller(
async def delete_data_controller(
redis: Redis = Depends(redis_getter),
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:delete"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_data:delete"]))
) -> JSONResponse:
await DictDataService.delete_obj_service(auth=auth, redis=redis, ids=ids)
logger.info(f"删除字典数据成功: {ids}")
@@ -166,7 +166,7 @@ async def delete_data_controller(
@DictRouter.patch("/data/available/setting", summary="批量修改字典数据状态", description="批量修改字典数据状态")
async def batch_set_available_dict_data_controller(
data: BatchSetAvailable,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:patch"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_data:patch"]))
) -> JSONResponse:
await DictDataService.set_obj_available_service(auth=auth, data=data)
logger.info(f"批量修改字典数据状态成功: {data.ids}")
@@ -176,7 +176,7 @@ async def batch_set_available_dict_data_controller(
async def export_data_list_controller(
search: DictDataQueryParam = Depends(),
page: PaginationQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:export"]))
auth: AuthSchema = Depends(AuthPermission(["system:dict_data:export"]))
) -> StreamingResponse:
# 获取全量数据
result_dict_list = await DictDataService.get_obj_list_service(auth=auth, search=search, order_by=page.order_by)
@@ -183,14 +183,14 @@ class DictDataService:
auth = AuthSchema(db=session)
obj_list = await DictTypeCRUD(auth).get_obj_list_crud()
if not obj_list:
logger.warning("未找到任何字典类型数据")
logger.warning("❗️ 未找到任何字典类型数据")
return
for obj in obj_list:
dict_type = obj.dict_type
dict_data_list = await DictDataCRUD(auth).get_obj_list_crud(search={'dict_type': dict_type})
if not dict_data_list:
logger.warning(f"字典类型 {dict_type} 未找到对应的字典数据")
logger.warning(f"❗️ 字典类型 {dict_type} 未找到对应的字典数据")
continue
dict_data = [DictDataOutSchema.model_validate(row).model_dump() for row in dict_data_list if row]
@@ -204,7 +204,7 @@ class DictDataService:
value=value,
)
except Exception as e:
logger.error(f"初始化字典数据失败: {e}")
logger.error(f"❌️ 初始化字典数据失败: {e}")
raise CustomException(msg=f"初始化字典数据失败 {e}")
@classmethod
@@ -22,7 +22,7 @@ LogRouter = APIRouter(route_class=OperationLogRoute, prefix="/log", tags=["日
async def get_obj_list_controller(
page: PaginationQueryParam = Depends(),
search: OperationLogQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:log:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:log:query"]))
) -> JSONResponse:
""" 查询日志 """
order_by = [{"created_at": "desc"}]
@@ -37,7 +37,7 @@ async def get_obj_list_controller(
@LogRouter.get("/detail/{id}", summary="日志详情", description="日志详情")
async def get_obj_detail_controller(
id: int = Path(..., description="操作日志ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:log:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:log:query"]))
) -> JSONResponse:
""" 详情日志 """
result_dict = await OperationLogService.get_log_detail_service(id=id, auth=auth)
@@ -48,7 +48,7 @@ async def get_obj_detail_controller(
@LogRouter.delete("/delete", summary="删除日志", description="删除日志")
async def delete_obj_log_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:log:delete"]))
auth: AuthSchema = Depends(AuthPermission(["system:log:delete"]))
) -> JSONResponse:
""" 删除日志 """
await OperationLogService.delete_log_service(ids=ids, auth=auth)
@@ -59,7 +59,7 @@ async def delete_obj_log_controller(
@LogRouter.post("/export", summary="导出日志", description="导出日志")
async def export_obj_list_controller(
search: OperationLogQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:log:export"]))
auth: AuthSchema = Depends(AuthPermission(["system:log:export"]))
) -> StreamingResponse:
""" 导出日志 """
operation_log_list = await OperationLogService.get_log_list_service(search=search, auth=auth)
@@ -22,7 +22,7 @@ MenuRouter = APIRouter(route_class=OperationLogRoute, prefix="/menu", tags=["菜
@MenuRouter.get("/tree", summary="查询菜单树", description="查询菜单树")
async def get_menu_tree_controller(
search: MenuQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:menu:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:menu:query"]))
) -> JSONResponse:
order_by = [{"order": "asc"}]
result_dict_list = await MenuService.get_menu_tree_service(search=search, auth=auth, order_by=order_by)
@@ -33,7 +33,7 @@ async def get_menu_tree_controller(
@MenuRouter.get("/detail/{id}", summary="查询菜单详情", description="查询菜单详情")
async def get_obj_detail_controller(
id: int = Path(..., description="菜单ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:menu:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:menu:query"]))
) -> JSONResponse:
result_dict = await MenuService.get_menu_detail_service(id=id, auth=auth)
logger.info(f"查询菜单情成功 {id}")
@@ -43,7 +43,7 @@ async def get_obj_detail_controller(
@MenuRouter.post("/create", summary="创建菜单", description="创建菜单")
async def create_obj_controller(
data: MenuCreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:menu:create"]))
auth: AuthSchema = Depends(AuthPermission(["system:menu:create"]))
) -> JSONResponse:
result_dict = await MenuService.create_menu_service(data=data, auth=auth)
logger.info(f"创建菜单成功: {result_dict}")
@@ -54,7 +54,7 @@ async def create_obj_controller(
async def update_obj_controller(
data: MenuUpdateSchema,
id: int = Path(..., description="菜单ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:menu:update"]))
auth: AuthSchema = Depends(AuthPermission(["system:menu:update"]))
) -> JSONResponse:
result_dict = await MenuService.update_menu_service(id=id, data=data, auth=auth)
logger.info(f"修改菜单成功: {result_dict}")
@@ -64,7 +64,7 @@ async def update_obj_controller(
@MenuRouter.delete("/delete", summary="删除菜单", description="删除菜单")
async def delete_obj_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:menu:delete"]))
auth: AuthSchema = Depends(AuthPermission(["system:menu:delete"]))
) -> JSONResponse:
await MenuService.delete_menu_service(ids=ids, auth=auth)
logger.info(f"删除菜单成功: {ids}")
@@ -74,7 +74,7 @@ async def delete_obj_controller(
@MenuRouter.patch("/available/setting", summary="批量修改菜单状态", description="批量修改菜单状态")
async def batch_set_available_obj_controller(
data: BatchSetAvailable,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:menu:patch"]))
auth: AuthSchema = Depends(AuthPermission(["system:menu:patch"]))
) -> JSONResponse:
await MenuService.set_menu_available_service(data=data, auth=auth)
logger.info(f"批量修改菜单状态成功: {data.ids}")
@@ -25,7 +25,7 @@ NoticeRouter = APIRouter(route_class=OperationLogRoute, prefix="/notice", tags=[
@NoticeRouter.get("/detail/{id}", summary="获取公告详情", description="获取公告详情")
async def get_obj_detail_controller(
id: int = Path(..., description="公告ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:notice:query"]))
) -> JSONResponse:
result_dict = await NoticeService.get_notice_detail_service(id=id, auth=auth)
logger.info(f"获取公告详情成功 {id}")
@@ -35,7 +35,7 @@ async def get_obj_detail_controller(
async def get_obj_list_controller(
page: PaginationQueryParam = Depends(),
search: NoticeQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:notice:query"]))
) -> JSONResponse:
result_dict_list = await NoticeService.get_notice_list_service(auth=auth, search=search, order_by=page.order_by)
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
@@ -45,7 +45,7 @@ async def get_obj_list_controller(
@NoticeRouter.post("/create", summary="创建公告", description="创建公告")
async def create_obj_controller(
data: NoticeCreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:create"]))
auth: AuthSchema = Depends(AuthPermission(["system:notice:create"]))
) -> JSONResponse:
result_dict = await NoticeService.create_notice_service(auth=auth, data=data)
logger.info(f"创建公告成功: {result_dict}")
@@ -55,7 +55,7 @@ async def create_obj_controller(
async def update_obj_controller(
data: NoticeUpdateSchema,
id: int = Path(..., description="公告ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:update"]))
auth: AuthSchema = Depends(AuthPermission(["system:notice:update"]))
) -> JSONResponse:
result_dict = await NoticeService.update_notice_service(auth=auth, id=id, data=data)
logger.info(f"修改公告成功: {result_dict}")
@@ -64,7 +64,7 @@ async def update_obj_controller(
@NoticeRouter.delete("/delete", summary="删除公告", description="删除公告")
async def delete_obj_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:delete"]))
auth: AuthSchema = Depends(AuthPermission(["system:notice:delete"]))
) -> JSONResponse:
await NoticeService.delete_notice_service(auth=auth, ids=ids)
logger.info(f"删除公告成功: {ids}")
@@ -73,7 +73,7 @@ async def delete_obj_controller(
@NoticeRouter.patch("/available/setting", summary="批量修改公告状态", description="批量修改公告状态")
async def batch_set_available_obj_controller(
data: BatchSetAvailable,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:patch"]))
auth: AuthSchema = Depends(AuthPermission(["system:notice:patch"]))
) -> JSONResponse:
await NoticeService.set_notice_available_service(auth=auth, data=data)
logger.info(f"批量修改公告状态成功: {data.ids}")
@@ -82,7 +82,7 @@ async def batch_set_available_obj_controller(
@NoticeRouter.post('/export', summary="导出公告", description="导出公告")
async def export_obj_list_controller(
search: NoticeQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:export"]))
auth: AuthSchema = Depends(AuthPermission(["system:notice:export"]))
) -> StreamingResponse:
# 获取全量数据
result_dict_list = await NoticeService.get_notice_list_service(search=search, auth=auth)
@@ -23,7 +23,7 @@ ParamsRouter = APIRouter(route_class=OperationLogRoute, prefix="/param", tags=["
@ParamsRouter.get("/detail/{id}", summary="获取参数详情", description="获取参数详情")
async def get_type_detail_controller(
id: int = Path(..., description="参数ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:param:query"]))
) -> JSONResponse:
result_dict = await ParamsService.get_obj_detail_service(id=id, auth=auth)
logger.info(f"获取参数详情成功 {id}")
@@ -33,7 +33,7 @@ async def get_type_detail_controller(
@ParamsRouter.get("/key/{config_key}", summary="根据配置键获取参数详情", description="根据配置键获取参数详情")
async def get_obj_by_key_controller(
config_key: str = Path(..., description="配置键"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:param:query"]))
) -> JSONResponse:
result_dict = await ParamsService.get_obj_by_key_service(config_key=config_key, auth=auth)
logger.info(f"根据配置键获取参数详情成功 {config_key}")
@@ -43,7 +43,7 @@ async def get_obj_by_key_controller(
@ParamsRouter.get("/value/{config_key}", summary="根据配置键获取参数值", description="根据配置键获取参数值")
async def get_config_value_by_key_controller(
config_key: str = Path(..., description="配置键"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:query"]))
auth: AuthSchema = Depends(AuthPermission(["system:param:query"]))
) -> JSONResponse:
result_value = await ParamsService.get_config_value_by_key_service(config_key=config_key, auth=auth)
logger.info(f"根据配置键获取参数值成功 {config_key}")
@@ -52,7 +52,7 @@ async def get_config_value_by_key_controller(
@ParamsRouter.get("/list", summary="获取参数列表", description="获取参数列表")
async def get_obj_list_controller(
auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:query"])),
auth: AuthSchema = Depends(AuthPermission(["system:param:query"])),
page: PaginationQueryParam = Depends(),
search: ParamsQueryParam = Depends(),
) -> JSONResponse:
@@ -66,7 +66,7 @@ async def get_obj_list_controller(
async def create_obj_controller(
data: ParamsCreateSchema,
redis: Redis = Depends(redis_getter),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:create"]))
auth: AuthSchema = Depends(AuthPermission(["system:param:create"]))
) -> JSONResponse:
result_dict = await ParamsService.create_obj_service(auth=auth, redis=redis, data=data)
logger.info(f"创建参数成功: {result_dict}")
@@ -78,7 +78,7 @@ async def update_objs_controller(
data: ParamsUpdateSchema,
id: int = Path(..., description="参数ID"),
redis: Redis = Depends(redis_getter),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:update"]))
auth: AuthSchema = Depends(AuthPermission(["system:param:update"]))
) -> JSONResponse:
result_dict = await ParamsService.update_obj_service(auth=auth, redis=redis, id=id, data=data)
logger.info(f"更新参数成功 {result_dict}")
@@ -89,7 +89,7 @@ async def update_objs_controller(
async def delete_obj_controller(
redis: Redis = Depends(redis_getter),
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:delete"]))
auth: AuthSchema = Depends(AuthPermission(["system:param:delete"]))
) -> JSONResponse:
await ParamsService.delete_obj_service(auth=auth, redis=redis, ids=ids)
logger.info(f"删除参数成功: {ids}")
@@ -99,7 +99,7 @@ async def delete_obj_controller(
@ParamsRouter.post('/export', summary="导出参数", description="导出参数")
async def export_obj_list_controller(
search: ParamsQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:export"]))
auth: AuthSchema = Depends(AuthPermission(["system:param:export"]))
) -> StreamingResponse:
# 获取全量数据
result_dict_list = await ParamsService.get_obj_list_service(search=search, auth=auth)
@@ -115,7 +115,7 @@ async def export_obj_list_controller(
)
@ParamsRouter.post("/upload", summary="上传文件", dependencies=[Depends(AuthPermission(permissions=["system:param:upload"]))])
@ParamsRouter.post("/upload", summary="上传文件", dependencies=[Depends(AuthPermission(["system:param:upload"]))])
async def upload_file_controller(
file: UploadFile,
request: Request
@@ -194,10 +194,10 @@ class ParamsService:
value=value,
)
if not result:
logger.error(f"初始化系统配置失败: {config_obj_dict}")
logger.error(f"❌️ 初始化系统配置失败: {config_obj_dict}")
raise CustomException(msg="初始化系统配置失败")
except Exception as e:
logger.error(f"初始化系统配置失败: {e}")
logger.error(f"❌️ 初始化系统配置失败: {e}")
raise CustomException(msg="初始化系统配置失败")
@classmethod
@@ -27,7 +27,7 @@ PositionRouter = APIRouter(route_class=OperationLogRoute, prefix="/position", ta
async def get_obj_list_controller(
page: PaginationQueryParam = Depends(),
search: PositionQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:query"])),
auth: AuthSchema = Depends(AuthPermission(["system:position:query"])),
) -> JSONResponse:
order_by = [{"order": "asc"}]
if page.order_by:
@@ -41,7 +41,7 @@ async def get_obj_list_controller(
@PositionRouter.get("/detail/{id}", summary="查询岗位详情", description="查询岗位详情")
async def get_obj_detail_controller(
id: int = Path(..., description="岗位ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:query"])),
auth: AuthSchema = Depends(AuthPermission(["system:position:query"])),
) -> JSONResponse:
result_dict = await PositionService.get_position_detail_service(id=id, auth=auth)
logger.info(f"查询岗位详情成功 {id}")
@@ -51,7 +51,7 @@ async def get_obj_detail_controller(
@PositionRouter.post("/create", summary="创建岗位", description="创建岗位")
async def create_obj_controller(
data: PositionCreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:create"])),
auth: AuthSchema = Depends(AuthPermission(["system:position:create"])),
) -> JSONResponse:
result_dict = await PositionService.create_position_service(data=data, auth=auth)
logger.info(f"创建岗位成功: {result_dict}")
@@ -62,7 +62,7 @@ async def create_obj_controller(
async def update_obj_controller(
data: PositionUpdateSchema,
id: int = Path(..., description="岗位ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:update"])),
auth: AuthSchema = Depends(AuthPermission(["system:position:update"])),
) -> JSONResponse:
result_dict = await PositionService.update_position_service(id=id, data=data, auth=auth)
logger.info(f"修改岗位成功: {result_dict}")
@@ -72,7 +72,7 @@ async def update_obj_controller(
@PositionRouter.delete("/delete", summary="删除岗位", description="删除岗位")
async def delete_obj_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:delete"])),
auth: AuthSchema = Depends(AuthPermission(["system:position:delete"])),
) -> JSONResponse:
await PositionService.delete_position_service(ids=ids, auth=auth)
logger.info(f"删除岗位成功: {ids}")
@@ -82,7 +82,7 @@ async def delete_obj_controller(
@PositionRouter.patch("/available/setting", summary="批量修改岗位状态", description="批量修改岗位状态")
async def batch_set_available_obj_controller(
data: BatchSetAvailable,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:patch"])),
auth: AuthSchema = Depends(AuthPermission(["system:position:patch"])),
) -> JSONResponse:
await PositionService.set_position_available_service(data=data, auth=auth)
logger.info(f"批量修改岗位状态成功: {data.ids}")
@@ -92,7 +92,7 @@ async def batch_set_available_obj_controller(
@PositionRouter.post('/export', summary="导出岗位", description="导出岗位")
async def export_obj_list_controller(
search: PositionQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:export"])),
auth: AuthSchema = Depends(AuthPermission(["system:position:export"])),
) -> StreamingResponse:
# 获取全量数据
position_query_result = await PositionService.get_position_list_service(search=search, auth=auth)
@@ -28,7 +28,7 @@ RoleRouter = APIRouter(route_class=OperationLogRoute, prefix="/role", tags=["角
async def get_obj_list_controller(
page: PaginationQueryParam = Depends(),
search: RoleQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:query"])),
auth: AuthSchema = Depends(AuthPermission(["system:role:query"])),
) -> JSONResponse:
order_by = [{"order": "asc"}]
if page.order_by:
@@ -42,7 +42,7 @@ async def get_obj_list_controller(
@RoleRouter.get("/detail/{id}", summary="查询角色详情", description="查询角色详情")
async def get_obj_detail_controller(
id: int = Path(..., description="角色ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:query"])),
auth: AuthSchema = Depends(AuthPermission(["system:role:query"])),
) -> JSONResponse:
result_dict = await RoleService.get_role_detail_service(id=id, auth=auth)
logger.info(f"获取角色详情成功 {id}")
@@ -52,7 +52,7 @@ async def get_obj_detail_controller(
@RoleRouter.post("/create", summary="创建角色", description="创建角色")
async def create_obj_controller(
data: RoleCreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:create"])),
auth: AuthSchema = Depends(AuthPermission(["system:role:create"])),
) -> JSONResponse:
result_dict = await RoleService.create_role_service(data=data, auth=auth)
logger.info(f"创建角色成功: {result_dict}")
@@ -63,7 +63,7 @@ async def create_obj_controller(
async def update_obj_controller(
data: RoleUpdateSchema,
id: int = Path(..., description="角色ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:update"])),
auth: AuthSchema = Depends(AuthPermission(["system:role:update"])),
) -> JSONResponse:
result_dict = await RoleService.update_role_service(id=id, data=data, auth=auth)
logger.info(f"修改角色成功: {result_dict}")
@@ -73,7 +73,7 @@ async def update_obj_controller(
@RoleRouter.delete("/delete", summary="删除角色", description="删除角色")
async def delete_obj_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:delete"])),
auth: AuthSchema = Depends(AuthPermission(["system:role:delete"])),
) -> JSONResponse:
await RoleService.delete_role_service(ids=ids, auth=auth)
logger.info(f"删除角色成功: {ids}")
@@ -83,7 +83,7 @@ async def delete_obj_controller(
@RoleRouter.patch("/available/setting", summary="批量修改角色状态", description="批量修改角色状态")
async def batch_set_available_obj_controller(
data: BatchSetAvailable,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:patch"])),
auth: AuthSchema = Depends(AuthPermission(["system:role:patch"])),
) -> JSONResponse:
await RoleService.set_role_available_service(data=data, auth=auth)
logger.info(f"批量修改角色状态成功: {data.ids}")
@@ -93,7 +93,7 @@ async def batch_set_available_obj_controller(
@RoleRouter.patch("/permission/setting", summary="角色授权", description="角色授权")
async def set_role_permission_controller(
data: RolePermissionSettingSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:permission"])),
auth: AuthSchema = Depends(AuthPermission(["system:role:permission"])),
) -> JSONResponse:
await RoleService.set_role_permission_service(data=data, auth=auth)
logger.info(f"设置角色权限成功: {data}")
@@ -103,7 +103,7 @@ async def set_role_permission_controller(
@RoleRouter.post('/export', summary="导出角色", description="导出角色")
async def export_obj_list_controller(
search: RoleQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:export"])),
auth: AuthSchema = Depends(AuthPermission(["system:role:export"])),
) -> StreamingResponse:
# 获取全量数据
role_query_result = await RoleService.get_role_list_service(search=search, auth=auth)
@@ -103,7 +103,7 @@ async def forget_password_controller(
async def get_obj_list_controller(
page: PaginationQueryParam = Depends(),
search: UserQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:query"])),
auth: AuthSchema = Depends(AuthPermission(["system:user:query"])),
) -> JSONResponse:
result_dict_list = await UserService.get_user_list_service(search=search, auth=auth, order_by=page.order_by)
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
@@ -114,7 +114,7 @@ async def get_obj_list_controller(
@UserRouter.get("/detail/{id}", summary="查询用户详情", description="查询用户详情")
async def get_obj_detail_controller(
id: int = Path(..., description="用户ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:query"])),
auth: AuthSchema = Depends(AuthPermission(["system:user:query"])),
) -> JSONResponse:
result_dict = await UserService.get_detail_by_id_service(id=id, auth=auth)
logger.info(f"获取用户详情成功 {id}")
@@ -124,7 +124,7 @@ async def get_obj_detail_controller(
@UserRouter.post("/create", summary="创建用户", description="创建用户")
async def create_obj_controller(
data: UserCreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:create"])),
auth: AuthSchema = Depends(AuthPermission(["system:user:create"])),
) -> JSONResponse:
result_dict = await UserService.create_user_service(data=data, auth=auth)
logger.info(f"创建用户成功: {result_dict}")
@@ -135,7 +135,7 @@ async def create_obj_controller(
async def update_obj_controller(
data: UserUpdateSchema,
id: int = Path(..., description="用户ID"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:update"])),
auth: AuthSchema = Depends(AuthPermission(["system:user:update"])),
) -> JSONResponse:
result_dict = await UserService.update_user_service(id=id, data=data, auth=auth)
logger.info(f"修改用户成功: {result_dict}")
@@ -145,7 +145,7 @@ async def update_obj_controller(
@UserRouter.delete("/delete", summary="删除用户", description="删除用户")
async def delete_obj_controller(
ids: list[int] = Body(..., description="ID列表"),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:delete"])),
auth: AuthSchema = Depends(AuthPermission(["system:user:delete"])),
) -> JSONResponse:
await UserService.delete_user_service(ids=ids, auth=auth)
logger.info(f"删除用户成功: {ids}")
@@ -155,14 +155,14 @@ async def delete_obj_controller(
@UserRouter.patch("/available/setting", summary="批量修改用户状态", description="批量修改用户状态")
async def batch_set_available_obj_controller(
data: BatchSetAvailable,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:patch"])),
auth: AuthSchema = Depends(AuthPermission(["system:user:patch"])),
) -> JSONResponse:
await UserService.set_user_available_service(data=data, auth=auth)
logger.info(f"批量修改用户状态成功: {data.ids}")
return SuccessResponse(msg="批量修改用户状态成功")
@UserRouter.post('/import/template', summary="获取用户导入模板", description="获取用户导入模板", dependencies=[Depends(AuthPermission(permissions=["system:user:import"]))])
@UserRouter.post('/import/template', summary="获取用户导入模板", description="获取用户导入模板", dependencies=[Depends(AuthPermission(["system:user:import"]))])
async def export_obj_template_controller()-> StreamingResponse:
user_import_template_result = await UserService.get_import_template_user_service()
logger.info('获取用户导入模板成功')
@@ -181,7 +181,7 @@ async def export_obj_template_controller()-> StreamingResponse:
async def export_obj_list_controller(
page: PaginationQueryParam = Depends(),
search: UserQueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:export"])),
auth: AuthSchema = Depends(AuthPermission(["system:user:export"])),
) -> StreamingResponse:
# 获取全量数据
user_list = await UserService.get_user_list_service(auth=auth, search=search, order_by=page.order_by)
@@ -200,7 +200,7 @@ async def export_obj_list_controller(
@UserRouter.post('/import/data', summary="导入用户", description="导入用户")
async def import_obj_list_controller(
file: UploadFile,
auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:import"]))
auth: AuthSchema = Depends(AuthPermission(["system:user:import"]))
) -> JSONResponse:
batch_import_result = await UserService.batch_import_user_service(file=file, auth=auth, update_support=True)
logger.info(f"导入用户成功: {batch_import_result}")
@@ -79,7 +79,7 @@ class UserCRUD(CRUDBase[UserModel, UserCreateSchema, UserUpdateSchema]):
Returns:
Optional[UserModel]: 更新后的用户信息
"""
return await self.update(id=id, data=UserUpdateSchema(last_login=datetime.now()))
return await self.update(id=id, data={"last_login": datetime.now()})
async def set_available_crud(self, ids: List[int], status: bool) -> None:
"""
+111 -256
View File
@@ -3,235 +3,6 @@
from enum import Enum
from app.config.setting import settings
class GenConfig:
def __init__(self):
pass
# 作者
author: str = 'Richard'
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
packageName: str = ''
# 自动去除表前缀,默认是True
autoRemovePre: bool = True
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
tablePrefix: str = 'system_'
# 是否允许生成文件覆盖到本地(自定义路径),默认不允许
allowOverwrite: bool = False
class GenConstants:
"""代码生成通用常量"""
# 单表(增删改查)
TPL_CRUD = "crud"
# 树表(增删改查)
TPL_TREE = "tree"
# 主子表(增删改查)
TPL_SUB = "sub"
# 树编码字段
TREE_CODE = "treeCode"
# 树父编码字段
TREE_PARENT_CODE = "treeParentCode"
# 树名称字段
TREE_NAME = "treeName"
# 上级菜单ID字段
PARENT_MENU_ID = "parentMenuId"
# 上级菜单名称字段
PARENT_MENU_NAME = "parentMenuName"
# 数据库字符串类型
TYPE_STRING = ["char", "varchar", "nvarchar", "varchar2"]
# 数据库文本类型
TYPE_TEXT = ["tinytext", "text", "mediumtext", "longtext"]
# 数据库时间类型
TYPE_DATE_TIME = ["datetime", "time", "date", "timestamp" ]
# 数据库数字类型
TYPE_NUMBER = ["tinyint", "smallint", "mediumint", "int", "number", "integer", "bigint", "float", "float", "double", "decimal"]
# 页面不需要编辑字段
COLUMN_NAME_NOT_EDIT = ["id", "create_by", "dept_id", "create_time", "del_flag", "update_time"]
# 页面不需要显示的列表字段
COLUMN_NAME_NOT_LIST = ["id", "create_by", "dept_id", "create_time", "del_flag", "update_by"]
# 页面不需要查询字段
COLUMN_NAME_NOT_QUERY = ["id", "create_by", "dept_id", "create_time", "del_flag", "update_by", "update_time", "remark"]
# Dao基类字段
DAO_COLUMN_NOT_EDIT = ["create_by", "dept_id", "create_time", "del_flag", "update_time"]
# Entity基类字段
BASE_ENTITY = ['id', 'create_time', 'update_time', "create_by", "dept_id", 'del_flag']
# Tree基类字段
TREE_ENTITY = ["parentName", "parentId", "orderNum", "ancestors"]
# 文本框
HTML_INPUT = "input"
# 文本域
HTML_TEXTAREA = "textarea"
# 下拉框
HTML_SELECT = "select"
# 单选框
HTML_RADIO = "radio"
# 复选框
HTML_CHECKBOX = "checkbox"
# 日期控件
HTML_DATETIME = "datetime"
# 图片上传控件
HTML_IMAGE_UPLOAD = "imageUpload"
# 文件上传控件
HTML_FILE_UPLOAD = "fileUpload"
# 富文本控件
HTML_EDITOR = "editor"
# 模糊查询
QUERY_LIKE = "LIKE"
# 相等查询
QUERY_EQ = "EQ"
# 需要
REQUIRE = "1"
# MySQL -> SQLAlchemy 类型映射
MYSQL_TO_SQLALCHEMY = {
# Numeric Types
"TINYINT": "SmallInteger",
"SMALLINT": "SmallInteger",
"MEDIUMINT": "Integer",
"INT": "Integer",
"INTEGER": "Integer",
"BIGINT": "BigInteger",
"FLOAT": "Float",
"DOUBLE": "Float",
"DECIMAL": "Numeric",
"NUMERIC": "Numeric",
# String Types
"CHAR": "String",
"VARCHAR": "String",
"TEXT": "Text",
"TINYTEXT": "Text",
"MEDIUMTEXT": "Text",
"LONGTEXT": "Text",
"BLOB": "LargeBinary",
"TINYBLOB": "LargeBinary",
"MEDIUMBLOB": "LargeBinary",
"LONGBLOB": "LargeBinary",
# Date and Time Types
"DATE": "Date",
"DATETIME": "DateTime",
"TIMESTAMP": "DateTime",
"TIME": "Time",
"YEAR": "Integer", # MySQL YEAR type is commonly represented as Integer in SQLAlchemy
# Binary Types
"BINARY": "Binary",
"VARBINARY": "Binary",
# Enum and Set Types
"ENUM": "Enum",
"SET": "Enum", # Set can be represented using Enum type in SQLAlchemy
# JSON Types
"JSON": "JSON", # SQLAlchemy supports JSON type from 1.3.0 version
# Spatial Types (less common in typical usage)
"GEOMETRY": "String", # Can be represented as String or Binary
"POINT": "String", # Represented as String in SQLAlchemy
"LINESTRING": "String", # Represented as String in SQLAlchemy
"POLYGON": "String", # Represented as String in SQLAlchemy
# Other Types
"BIT": "Boolean",
"BOOL": "Boolean",
"UUID": "String", # UUIDs in SQLAlchemy can be represented as String
"BINARY": "Binary", # MySQL BINARY type corresponds to SQLAlchemy's Binary
}
MYSQL_TO_PYTHON = {
# 字符串类型
"VARCHAR": "str",
"CHAR": "str",
"TEXT": "str",
"TINYTEXT": "str",
"MEDIUMTEXT": "str",
"LONGTEXT": "str",
# 数值类型
"INT": "int",
"TINYINT": "int",
"SMALLINT": "int",
"MEDIUMINT": "int",
"BIGINT": "int",
"FLOAT": "float",
"DOUBLE": "float",
"DECIMAL": "float",
"NUMERIC": "float",
"BIT": "bool", # 位字段,0 或 1
# 日期和时间类型
"DATETIME": "datetime",
"TIMESTAMP": "datetime",
"DATE": "datetime.date",
"TIME": "datetime.time",
"YEAR": "int", # 存储年份
"TINYINT UNSIGNED": "int", # 无符号小整数类型
# 布尔类型
"BOOLEAN": "bool",
"BOOL": "bool", # 布尔类型,通常与 BOOLEAN 相同
# JSON 数据类型
"JSON": "dict", # JSON 数据存储为字典
# 二进制类型
"BLOB": "bytes",
"TINYBLOB": "bytes",
"MEDIUMBLOB": "bytes",
"LONGBLOB": "bytes",
# 枚举和集合类型
"ENUM": "str", # 枚举类型作为字符串
"SET": "list", # 集合类型作为列表
# 时间单位类型
"DATE": "datetime.date", # 仅日期
"TIME": "datetime.time", # 仅时间
# 大文本类型
"LONGTEXT": "str",
"MEDIUMTEXT": "str",
"TINYTEXT": "str",
# UUID
"UUID": "str", # UUID 一般作为字符串
# 用于二进制数据
"BINARY": "bytes", # 固定长度的二进制数据
"VARBINARY": "bytes", # 可变长度的二进制数据
# 其他数据类型
"GEOMETRY": "bytes", # 空间数据类型,通常存储为字节流
"POINT": "bytes", # 点数据类型
"LINESTRING": "bytes", # 线数据类型
"POLYGON": "bytes", # 多边形数据类型
"MULTIPOINT": "bytes", # 多点数据类型
"MULTILINESTRING": "bytes", # 多线数据类型
"MULTIPOLYGON": "bytes", # 多多边形数据类型
"GEOMETRYCOLLECTION": "bytes", # 几何集合类型
}
class RET(Enum):
"""
系统返回码枚举
@@ -587,22 +358,43 @@ class GenConstant:
DB_TO_PYTHON_TYPE_MAPPING: 数据库类型与python类型映射
"""
TPL_CRUD = 'crud'
TPL_TREE = 'tree'
TPL_SUB = 'sub'
TREE_CODE = 'treeCode'
TREE_PARENT_CODE = 'treeParentCode'
TREE_NAME = 'treeName'
PARENT_MENU_ID = 'parentMenuId'
PARENT_MENU_NAME = 'parentMenuName'
# 单表(增删改查)
TPL_CRUD = "crud"
# 树表(增删改查)
TPL_TREE = "tree"
# 主子表(增删改查)
TPL_SUB = "sub"
# 树编码字段
TREE_CODE = "treeCode"
# 树父编码字段
TREE_PARENT_CODE = "treeParentCode"
# 树名称字段
TREE_NAME = "treeName"
# 上级菜单ID字段
PARENT_MENU_ID = "parentMenuId"
# 上级菜单名称字段
PARENT_MENU_NAME = "parentMenuName"
# 数据库字符串类型
COLUMNTYPE_STR = (
['character varying', 'varchar', 'character', 'char']
if settings.DATABASE_TYPE == 'postgresql'
else ['char', 'varchar', 'nvarchar', 'varchar2']
)
# 数据库文本类型
COLUMNTYPE_TEXT = (
['text', 'citext'] if settings.DATABASE_TYPE == 'postgresql' else ['tinytext', 'text', 'mediumtext', 'longtext']
)
# 数据库时间类型
COLUMNTYPE_TIME = (
[
'date',
@@ -617,6 +409,8 @@ class GenConstant:
if settings.DATABASE_TYPE == 'postgresql'
else ['datetime', 'time', 'date', 'timestamp']
)
# 数据库字空间类型
COLUMNTYPE_GEOMETRY = (
['point', 'line', 'lseg', 'box', 'path', 'polygon', 'circle']
if settings.DATABASE_TYPE == 'postgresql'
@@ -631,6 +425,8 @@ class GenConstant:
'geometrycollection',
]
)
# 数据库数字类型
COLUMNTYPE_NUMBER = [
'tinyint',
'smallint',
@@ -644,28 +440,74 @@ class GenConstant:
'double',
'decimal',
]
# 页面不需要显示的添加字段
COLUMNNAME_NOT_ADD_SHOW = ['create_by', 'create_time']
# 页面不需要显示的编辑字段
COLUMNNAME_NOT_EDIT_SHOW = ['updated_at']
COLUMNNAME_NOT_EDIT = ['id', 'created_at', 'updated_at', 'del_flag']
COLUMNNAME_NOT_LIST = ['id', 'created_at', 'updated_at', 'del_flag']
COLUMNNAME_NOT_QUERY = ['id', 'created_at', 'updated_at', 'del_flag', 'remark']
BASE_ENTITY = ['createdAt', 'updatedAt', 'remark']
# 页面不需要编辑字段
COLUMNNAME_NOT_EDIT = ["id", "create_by", "dept_id", "create_time", "del_flag", "update_time"]
# 页面不需要显示的列表字段
COLUMNNAME_NOT_LIST = ["id", "create_by", "dept_id", "create_time", "del_flag", "update_time"]
# 页面不需要查询字段
COLUMNNAME_NOT_QUERY = ["id", "create_by", "dept_id", "create_time", "del_flag", "update_by", "update_time", "remark"]
# Crud基类字段
CRUD_COLUMN_NOT_EDIT = ["create_by", "dept_id", "create_time", "del_flag", "update_time"]
# 实体基类字段
BASE_ENTITY = ['id', 'create_time', 'update_time', "create_by", "dept_id", 'del_flag']
# Tree基类字段
TREE_ENTITY = ['parentName', 'parentId', 'orderNum', 'ancestors', 'children']
# 文本框
HTML_INPUT = 'input'
# 文本域
HTML_TEXTAREA = 'textarea'
# 下拉框
HTML_SELECT = 'select'
# 单选框
HTML_RADIO = 'radio'
# 复选框
HTML_CHECKBOX = 'checkbox'
# 日期控件
HTML_DATETIME = 'datetime'
# 图片上传控件
HTML_IMAGE_UPLOAD = 'imageUpload'
# 文件上传控件
HTML_FILE_UPLOAD = 'fileUpload'
# 富文本控件
HTML_EDITOR = 'editor'
# 高精度计算类型
TYPE_DECIMAL = 'Decimal'
# 时间类型
TYPE_DATE = ['date', 'time', 'datetime']
# 模糊查询
QUERY_LIKE = 'LIKE'
# 相等查询
QUERY_EQ = 'EQ'
# 需要
REQUIRE = '1'
DB_TO_SQLALCHEMY_TYPE_MAPPING = (
# 数据库类型与sqlalchemy类型映射
DB_TO_SQLALCHEMY = (
{
'boolean': 'Boolean',
'smallint': 'SmallInteger',
@@ -734,6 +576,7 @@ class GenConstant:
'DOUBLE': 'Float',
'DECIMAL': 'DECIMAL',
'BIT': 'Integer',
"NUMERIC": "Numeric",
# 日期和时间类型
'DATE': 'Date',
'TIME': 'Time',
@@ -767,9 +610,14 @@ class GenConstant:
'MULTILINESTRING': 'Geometry',
'MULTIPOLYGON': 'Geometry',
'GEOMETRYCOLLECTION': 'Geometry',
# 其他类型
"BOOL": "Boolean",
"UUID": "String",
}
)
DB_TO_PYTHON_TYPE_MAPPING = (
# 数据库类型与python类型映射
DB_TO_PYTHON = (
{
'boolean': 'bool',
'smallint': 'int',
@@ -836,14 +684,21 @@ class GenConstant:
'BIGINT': 'int',
'FLOAT': 'float',
'DOUBLE': 'float',
"NUMERIC": "float",
'DECIMAL': 'Decimal',
'BIT': 'int',
# 日期和时间类型
'DATE': 'date',
'TIME': 'time',
'DATETIME': 'datetime',
'TIMESTAMP': 'datetime',
'DATE': 'datetime.date',
'TIME': 'datetime.time',
'DATETIME': 'datetime.datetime',
'TIMESTAMP': 'datetime.datetime',
'YEAR': 'int',
"TINYINT UNSIGNED": "int", # 无符号小整数类型
# 布尔类型
"BOOLEAN": "bool",
"BOOL": "bool", # 布尔类型,通常与 BOOLEAN 相同
# UUID
"UUID": "str", # UUID 一般作为字符串
# 字符串类型
'CHAR': 'str',
'VARCHAR': 'str',
@@ -859,18 +714,18 @@ class GenConstant:
'LONGBLOB': 'bytes',
# 枚举和集合类型
'ENUM': 'str',
'SET': 'str',
'SET': 'list',
# JSON 类型
'JSON': 'dict',
# 空间数据类型(通常需要特殊处理)
'GEOMETRY': 'bytes',
'POINT': 'bytes',
'LINESTRING': 'bytes',
'POLYGON': 'bytes',
'MULTIPOINT': 'bytes',
'MULTILINESTRING': 'bytes',
'MULTIPOLYGON': 'bytes',
'GEOMETRYCOLLECTION': 'bytes',
"GEOMETRY": "bytes", # 空间数据类型,通常存储为字节流
"POINT": "bytes", # 点数据类型
"LINESTRING": "bytes", # 线数据类型
"POLYGON": "bytes", # 多边形数据类型
"MULTIPOINT": "bytes", # 多点数据类型
"MULTILINESTRING": "bytes", # 多线数据类型
"MULTIPOLYGON": "bytes", # 多多边形数据类型
"GEOMETRYCOLLECTION": "bytes", # 几何集合类型
}
)
+5 -5
View File
@@ -243,11 +243,11 @@ class Settings(BaseSettings):
# ================================================= #
# ******************* 代码生成配置 ****************** #
# ================================================= #
author: str = 'insistence'
package_name: str = 'module_admin.system'
auto_remove_pre: bool = False
table_prefix: str = 'gen_'
allow_overwrite: bool = False
author: str = 'insistence' # 作者
package_name: str = 'module_generator.gencode' # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
auto_remove_pre: bool = False # 自动去除表前缀,默认是True
table_prefix: str = 'gen_' # 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
allow_overwrite: bool = False # 是否允许生成文件覆盖到本地(自定义路径),默认不允许
GEN_PATH: Path = BASE_DIR.joinpath('app/api/v1/module_generator/gen_backend_code')
+2 -124
View File
@@ -4,15 +4,11 @@
提供跨数据库兼容的基础模型类和类型装饰器
"""
import re
from datetime import datetime
from typing import Literal, Optional, Dict, Any, Union
from typing import Optional
from sqlalchemy import Integer, DateTime, Text
from sqlalchemy.ext.asyncio import AsyncAttrs
from sqlalchemy.orm import relationship, DeclarativeBase, Mapped, declared_attr, mapped_column
from sqlalchemy.engine.row import Row
from sqlalchemy.orm.collections import InstrumentedList
class MappedBase(AsyncAttrs, DeclarativeBase):
@@ -51,7 +47,7 @@ class ModelMixin(MappedBase):
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True, default=None, comment="备注/描述")
created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, default=datetime.now, comment='创建时间')
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, default=datetime.now, onupdate=datetime.now, comment='更新时间')
class CreatorMixin(ModelMixin):
"""
@@ -72,121 +68,3 @@ class CreatorMixin(ModelMixin):
viewonly=True,
uselist=False # 明确指定返回单个对象
)
class SqlalchemyUtil:
"""
sqlalchemy工具类
"""
@classmethod
def base_to_dict(
cls, obj: Union[MappedBase, Dict], transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case'
) -> dict[str, Any] | dict[Any, Any]:
"""
将sqlalchemy模型对象转换为字典
:param obj: sqlalchemy模型对象或普通字典
:param transform_case: 转换得到的结果形式可选的有'no_case'(不转换)'snake_to_camel'(下划线转小驼峰)'camel_to_snake'(小驼峰转下划线)默认为'no_case'
:return: 字典结果
"""
if isinstance(obj, MappedBase):
base_dict = obj.__dict__.copy()
base_dict.pop('_sa_instance_state', None)
for name, value in base_dict.items():
if isinstance(value, InstrumentedList):
base_dict[name] = cls.serialize_result(value, 'snake_to_camel')
elif isinstance(obj, dict):
base_dict = obj.copy()
if transform_case == 'snake_to_camel':
return {CamelCaseUtil.snake_to_camel(k): v for k, v in base_dict.items()}
elif transform_case == 'camel_to_snake':
return {SnakeCaseUtil.camel_to_snake(k): v for k, v in base_dict.items()}
return base_dict
@classmethod
def serialize_result(
cls, result: Any, transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case'
) -> Any:
"""
将sqlalchemy查询结果序列化
:param result: sqlalchemy查询结果
:param transform_case: 转换得到的结果形式可选的有'no_case'(不转换)'snake_to_camel'(下划线转小驼峰)'camel_to_snake'(小驼峰转下划线)默认为'no_case'
:return: 序列化结果
"""
if isinstance(result, (MappedBase, dict)):
return cls.base_to_dict(result, transform_case)
elif isinstance(result, list):
return [cls.serialize_result(row, transform_case) for row in result]
elif isinstance(result, Row):
if all([isinstance(row, MappedBase) for row in result]):
return [cls.base_to_dict(row, transform_case) for row in result]
elif any([isinstance(row, MappedBase) for row in result]):
return [cls.serialize_result(row, transform_case) for row in result]
else:
result_dict = result._asdict()
if transform_case == 'snake_to_camel':
return {CamelCaseUtil.snake_to_camel(k): v for k, v in result_dict.items()}
elif transform_case == 'camel_to_snake':
return {SnakeCaseUtil.camel_to_snake(k): v for k, v in result_dict.items()}
return result_dict
return result
class CamelCaseUtil:
"""
下划线形式(snake_case)转小驼峰形式(camelCase)工具方法
"""
@classmethod
def snake_to_camel(cls, snake_str: str) -> str:
"""
下划线形式字符串(snake_case)转换为小驼峰形式字符串(camelCase)
:param snake_str: 下划线形式字符串
:return: 小驼峰形式字符串
"""
# 分割字符串
words = snake_str.split('_')
# 小驼峰命名,第一个词首字母小写,其余词首字母大写
return words[0] + ''.join(word.capitalize() for word in words[1:])
@classmethod
def transform_result(cls, result: Any) -> Any:
"""
针对不同类型将下划线形式(snake_case)批量转换为小驼峰形式(camelCase)方法
:param result: 输入数据
:return: 小驼峰形式结果
"""
return SqlalchemyUtil.serialize_result(result=result, transform_case='snake_to_camel')
class SnakeCaseUtil:
"""
小驼峰形式(camelCase)转下划线形式(snake_case)工具方法
"""
@classmethod
def camel_to_snake(cls, camel_str: str) -> str:
"""
小驼峰形式字符串(camelCase)转换为下划线形式字符串(snake_case)
:param camel_str: 小驼峰形式字符串
:return: 下划线形式字符串
"""
# 在大写字母前添加一个下划线,然后将整个字符串转为小写
words = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', camel_str)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', words).lower()
@classmethod
def transform_result(cls, result: Any) -> Any:
"""
针对不同类型将下划线形式(snake_case)批量转换为小驼峰形式(camelCase)方法
:param result: 输入数据
:return: 小驼峰形式结果
"""
return SqlalchemyUtil.serialize_result(result=result, transform_case='camel_to_snake')
+3 -3
View File
@@ -26,10 +26,10 @@ class BaseSchema(BaseModel):
"""通用输出模型,包含基础字段和审计字段"""
model_config = ConfigDict(from_attributes=True)
id: int = Field(description="主键ID")
id: Optional[int] = Field(default=None, description="主键ID")
description: Optional[str] = Field(default=None, description="描述")
created_at: DateTimeStr = Field(description="创建时间")
updated_at: DateTimeStr = Field(description="更新时间")
created_at: Optional[DateTimeStr] = Field(default=None, description="创建时间")
updated_at: Optional[DateTimeStr] = Field(default=None, description="更新时间")
creator_id: Optional[int] = Field(default=None, description="创建人ID")
creator: Optional[UserInfoSchema] = Field(default=None, description="创建人信息")
+2 -2
View File
@@ -77,7 +77,7 @@ async def redis_connect(app: FastAPI, status: bool) -> Redis | None:
)
app.state.redis = rd
if await rd.ping():
logger.info("Redis连接成功...")
logger.info("✅️ Redis连接成功...")
return rd
raise CustomException(msg="Redis连接失败")
except exceptions.AuthenticationError as e:
@@ -113,4 +113,4 @@ async def mongodb_connect(app: FastAPI, status: bool) -> AsyncIOMotorClient | No
raise ValueError(f"MongoDB连接失败: {e}")
else:
app.state.mongo_client.close()
logger.info("MongoDB连接已关闭")
logger.info("❌️ MongoDB连接已关闭")
+6 -6
View File
@@ -30,17 +30,17 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[Any, Any]:
logger.info(settings.BANNER + '\n' + f'{settings.TITLE} 服务开始启动...')
await InitializeData().init_db()
logger.info(f"初始化 {settings.DATABASE_TYPE} 数据库初始化完成...")
logger.info(f"✅️ 初始化 {settings.DATABASE_TYPE} 数据库初始化完成...")
await import_modules_async(modules=settings.EVENT_LIST, desc="全局事件", app=app, status=True)
logger.info("初始化全局事件完成...")
logger.info("✅️ 初始化全局事件完成...")
await ParamsService().init_config_service(redis=app.state.redis)
logger.info("初始化Redis系统配置完成...")
logger.info("✅️ 初始化Redis系统配置完成...")
await DictDataService().init_dict_service(redis=app.state.redis)
logger.info('初始化Redis数据字典完成...')
logger.info('✅️ 初始化Redis数据字典完成...')
await SchedulerUtil.init_system_scheduler()
logger.info('初始化定时任务完成...')
logger.info('✅️ 初始化定时任务完成...')
logger.info(f'{settings.TITLE} 服务成功启动...')
logger.info(f'✅️ {settings.TITLE} 服务成功启动...')
yield
+8 -8
View File
@@ -58,9 +58,9 @@ class InitializeData:
# 使用引擎创建所有表
async with async_engine.begin() as conn:
await conn.run_sync(MappedBase.metadata.create_all)
logger.info("数据库表结构初始化完成")
logger.info("✅️ 数据库表结构初始化完成")
except Exception as e:
logger.error(f"数据库表结构初始化失败: {str(e)}")
logger.error(f"❌️ 数据库表结构初始化失败: {str(e)}")
raise
async def __init_data(self, db: AsyncSession) -> None:
@@ -72,12 +72,12 @@ class InitializeData:
count_result = await db.execute(select(func.count()).select_from(model))
existing_count = count_result.scalar()
if existing_count and existing_count > 0:
logger.warning(f"跳过 {table_name} 表数据初始化(表已存在 {existing_count} 条记录)")
logger.warning(f"⚠️ 跳过 {table_name} 表数据初始化(表已存在 {existing_count} 条记录)")
continue
data = await self.__get_data(table_name)
if not data:
logger.warning(f"跳过 {table_name} 表,无初始化数据")
logger.warning(f"⚠️ 跳过 {table_name} 表,无初始化数据")
continue
try:
@@ -91,10 +91,10 @@ class InitializeData:
objs = [model(**item) for item in data]
db.add_all(objs)
await db.flush()
logger.info(f"已向 {table_name} 表写入初始化数据")
logger.info(f"✅️ 已向 {table_name} 表写入初始化数据")
except Exception as e:
logger.error(f"初始化 {table_name} 表数据失败: {str(e)}")
logger.error(f"❌️ 初始化 {table_name} 表数据失败: {str(e)}")
raise
def __create_objects_with_children(self, data: List[Dict], model_class) -> List:
@@ -129,10 +129,10 @@ class InitializeData:
with open(json_path, 'r', encoding='utf-8') as f:
return json.loads(f.read())
except json.JSONDecodeError as e:
logger.error(f"解析 {json_path} 失败: {str(e)}")
logger.error(f"❌️ 解析 {json_path} 失败: {str(e)}")
raise
except Exception as e:
logger.error(f"读取 {json_path} 失败: {str(e)}")
logger.error(f"❌️ 读取 {json_path} 失败: {str(e)}")
raise
async def init_db(self) -> None:
+132 -17
View File
@@ -1,16 +1,20 @@
# -*- coding: utf-8 -*-
from pathlib import Path
import importlib
from types import ModuleType
import re
import uuid
from typing import Any, Generator, List, Dict, Sequence, Optional
from pathlib import Path
from typing import Any, Generator, List, Dict, Literal, Sequence, Optional, Union
from sqlalchemy.engine.row import Row
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm.collections import InstrumentedList
from app.config import setting
from app.core.base_model import MappedBase
from app.core.logger import logger
from app.core.exceptions import CustomException
def worship():
print("""
______ _ _
@@ -23,6 +27,7 @@ def worship():
|_|
""")
def import_module(module: str, desc: str) -> Any:
"""
动态导入模块
@@ -35,10 +40,10 @@ def import_module(module: str, desc: str) -> Any:
module = importlib.import_module(module_path)
return getattr(module, module_class)
except ModuleNotFoundError:
logger.error(f"导入{desc}失败,未找到模块:{module}")
logger.error(f"❗️ 导入{desc}失败,未找到模块:{module}")
raise ModuleNotFoundError(f"导入{desc}失败,未找到模块:{module}")
except AttributeError:
logger.error(f"导入{desc}失败,未找到模块方法:{module}")
logger.error(f"导入{desc}失败,未找到模块方法:{module}")
raise AttributeError(f"导入{desc}失败,未找到模块方法:{module}")
@@ -58,10 +63,10 @@ async def import_modules_async(modules: list, desc: str, **kwargs):
module_obj = importlib.import_module(module_path)
await getattr(module_obj, module_name)(**kwargs)
except ModuleNotFoundError:
logger.error(f"导入{desc}失败,未找到模块:{module}")
logger.error(f"❌️ 导入{desc}失败,未找到模块:{module}")
raise ModuleNotFoundError(f"导入{desc}失败,未找到模块:{module}")
except AttributeError:
logger.error(f"导入{desc}失败,未找到模块方法:{module}")
logger.error(f"❌️ 导入{desc}失败,未找到模块方法:{module}")
raise AttributeError(f"导入{desc}失败,未找到模块方法:{module}")
@@ -79,11 +84,7 @@ def get_parent_id_map(model_list: Sequence[DeclarativeBase]) -> Dict[int, int]:
return {item.id: item.parent_id for item in model_list}
def get_parent_recursion(
id: int,
id_map: Dict[int, int],
ids: Optional[List[int]] = None
) -> List[int]:
def get_parent_recursion(id: int, id_map: Dict[int, int], ids: Optional[List[int]] = None) -> List[int]:
"""
递归获取所有父级ID
:param id: 当前ID
@@ -115,11 +116,7 @@ def get_child_id_map(model_list: Sequence[DeclarativeBase]) -> Dict[int, List[in
return data_map
def get_child_recursion(
id: int,
id_map: Dict[int, List[int]],
ids: Optional[List[int]] = None
) -> List[int]:
def get_child_recursion(id: int, id_map: Dict[int, List[int]], ids: Optional[List[int]] = None) -> List[int]:
"""
递归获取所有子级ID
:param id: 当前ID
@@ -224,3 +221,121 @@ def get_filepath_from_url(url: str) -> Path:
filepath = setting.settings.STATIC_ROOT.joinpath(task_path, task_id, file_name)
return filepath
class SqlalchemyUtil:
"""
sqlalchemy工具类
"""
@classmethod
def base_to_dict(
cls, obj: Union[MappedBase, Dict], transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case'
) -> dict[str, Any] | dict[Any, Any]:
"""
将sqlalchemy模型对象转换为字典
:param obj: sqlalchemy模型对象或普通字典
:param transform_case: 转换得到的结果形式可选的有'no_case'(不转换)'snake_to_camel'(下划线转小驼峰)'camel_to_snake'(小驼峰转下划线)默认为'no_case'
:return: 字典结果
"""
if isinstance(obj, MappedBase):
base_dict = obj.__dict__.copy()
base_dict.pop('_sa_instance_state', None)
for name, value in base_dict.items():
if isinstance(value, InstrumentedList):
base_dict[name] = cls.serialize_result(value, 'snake_to_camel')
elif isinstance(obj, dict):
base_dict = obj.copy()
if transform_case == 'snake_to_camel':
return {CamelCaseUtil.snake_to_camel(k): v for k, v in base_dict.items()}
elif transform_case == 'camel_to_snake':
return {SnakeCaseUtil.camel_to_snake(k): v for k, v in base_dict.items()}
return base_dict
@classmethod
def serialize_result(
cls, result: Any, transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case'
) -> Any:
"""
将sqlalchemy查询结果序列化
:param result: sqlalchemy查询结果
:param transform_case: 转换得到的结果形式可选的有'no_case'(不转换)'snake_to_camel'(下划线转小驼峰)'camel_to_snake'(小驼峰转下划线)默认为'no_case'
:return: 序列化结果
"""
if isinstance(result, (MappedBase, dict)):
return cls.base_to_dict(result, transform_case)
elif isinstance(result, list):
return [cls.serialize_result(row, transform_case) for row in result]
elif isinstance(result, Row):
if all([isinstance(row, MappedBase) for row in result]):
return [cls.base_to_dict(row, transform_case) for row in result]
elif any([isinstance(row, MappedBase) for row in result]):
return [cls.serialize_result(row, transform_case) for row in result]
else:
result_dict = result._asdict()
if transform_case == 'snake_to_camel':
return {CamelCaseUtil.snake_to_camel(k): v for k, v in result_dict.items()}
elif transform_case == 'camel_to_snake':
return {SnakeCaseUtil.camel_to_snake(k): v for k, v in result_dict.items()}
return result_dict
return result
class CamelCaseUtil:
"""
下划线形式(snake_case)转小驼峰形式(camelCase)工具方法
"""
@classmethod
def snake_to_camel(cls, snake_str: str) -> str:
"""
下划线形式字符串(snake_case)转换为小驼峰形式字符串(camelCase)
:param snake_str: 下划线形式字符串
:return: 小驼峰形式字符串
"""
# 分割字符串
words = snake_str.split('_')
# 小驼峰命名,第一个词首字母小写,其余词首字母大写
return words[0] + ''.join(word.capitalize() for word in words[1:])
@classmethod
def transform_result(cls, result: Any) -> Any:
"""
针对不同类型将下划线形式(snake_case)批量转换为小驼峰形式(camelCase)方法
:param result: 输入数据
:return: 小驼峰形式结果
"""
return SqlalchemyUtil.serialize_result(result=result, transform_case='snake_to_camel')
class SnakeCaseUtil:
"""
小驼峰形式(camelCase)转下划线形式(snake_case)工具方法
"""
@classmethod
def camel_to_snake(cls, camel_str: str) -> str:
"""
小驼峰形式字符串(camelCase)转换为下划线形式字符串(snake_case)
:param camel_str: 小驼峰形式字符串
:return: 下划线形式字符串
"""
# 在大写字母前添加一个下划线,然后将整个字符串转为小写
words = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', camel_str)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', words).lower()
@classmethod
def transform_result(cls, result: Any) -> Any:
"""
针对不同类型将下划线形式(snake_case)批量转换为小驼峰形式(camelCase)方法
:param result: 输入数据
:return: 小驼峰形式结果
"""
return SqlalchemyUtil.serialize_result(result=result, transform_case='camel_to_snake')
@@ -1,11 +1,22 @@
# -*- coding: utf-8 -*-
import re
from datetime import datetime
from typing import List
from app.common.constant import GenConstant
from app.config.setting import settings
from app.api.v1.module_generator.gencode.schema import GenTableColumnOutSchema as GenTableColumnSchema, GenTableOutSchema as GenTableSchema
from .string_util import StringUtil
from app.utils.string_util import StringUtil
from app.api.v1.module_generator.gencode.schema import (
GenTableOptionModel,
GenDBTableSchema,
GenTableBaseSchema,
GenTableSchema,
GenTableOutSchema,
GenTableDeleteSchema,
GenTableColumnSchema,
GenTableColumnOutSchema,
GenTableColumnDeleteSchema
)
class GenUtils:
@@ -20,14 +31,13 @@ class GenUtils:
param oper_name: 操作人
:return:
"""
gen_table.class_name = cls.convert_class_name(gen_table.table_name)
gen_table.package_name = settings.package_name
gen_table.module_name = cls.get_module_name(settings.package_name)
gen_table.business_name = cls.get_business_name(gen_table.table_name)
gen_table.function_name = cls.replace_text(gen_table.table_comment)
gen_table.class_name = cls.convert_class_name(gen_table.table_name or "")
gen_table.package_name = cls.get_package_name(gen_table.table_name or "")
gen_table.module_name = cls.get_module_name(settings.package_name or "")
gen_table.business_name = cls.get_business_name(gen_table.table_name or "")
gen_table.function_name = cls.replace_text(gen_table.table_comment or "")
gen_table.function_author = settings.author
gen_table.created_at = datetime.now()
gen_table.updated_at = datetime.now()
@classmethod
def init_column_field(cls, column: GenTableColumnSchema, table: GenTableSchema) -> None:
@@ -38,22 +48,20 @@ class GenUtils:
param table: 业务表对象
:return:
"""
data_type = cls.get_db_type(column.column_type)
column_name = column.column_name
column.table_id = table.id
data_type = cls.get_db_type(column.column_type or "")
column_name = column.column_name or ""
column.table_id = table.table_id
# 设置Python字段名
column.python_field = cls.to_camel_case(column_name)
column.python_field = column_name
# 设置默认类型
column.python_type = StringUtil.get_mapping_value_by_key_ignore_case(
GenConstant.DB_TO_PYTHON_TYPE_MAPPING, data_type
)
column.python_type = GenConstant.DB_TO_PYTHON.get(data_type.upper(), "Any")
column.query_type = GenConstant.QUERY_EQ
if cls.arrays_contains(GenConstant.COLUMNTYPE_STR, data_type) or cls.arrays_contains(
GenConstant.COLUMNTYPE_TEXT, data_type
):
# 字符串长度超过500设置为文本域
column_length = cls.get_column_length(column.column_type)
column_length = cls.get_column_length(column.column_type or "")
html_type = (
GenConstant.HTML_TEXTAREA
if column_length >= 500 or cls.arrays_contains(GenConstant.COLUMNTYPE_TEXT, data_type)
@@ -69,13 +77,13 @@ class GenUtils:
column.is_insert = GenConstant.REQUIRE
# 编辑字段
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_EDIT, column_name) and not column.pk:
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_EDIT, column_name) and not column.is_pk == '1':
column.is_edit = GenConstant.REQUIRE
# 列表字段
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_LIST, column_name) and not column.pk:
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_LIST, column_name) and not column.is_pk == '1':
column.is_list = GenConstant.REQUIRE
# 查询字段
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_QUERY, column_name) and not column.pk:
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_QUERY, column_name) and not column.is_pk == '1':
column.is_query = GenConstant.REQUIRE
# 查询字段类型
@@ -96,11 +104,8 @@ class GenUtils:
# 内容字段设置富文本控件
elif column_name.lower().endswith('content'):
column.html_type = GenConstant.HTML_EDITOR
column.create_by = table.create_by
column.create_time = datetime.now()
column.update_by = table.update_by
column.update_time = datetime.now()
else:
column.html_type = GenConstant.HTML_INPUT
@classmethod
def arrays_contains(cls, arr: List[str], target_value: str) -> bool:
@@ -123,6 +128,11 @@ class GenUtils:
"""
return package_name.split('.')[-1]
@classmethod
def get_package_name(cls, table_name: str) -> str:
"""获取包名"""
return settings.package_name # 可配置的包名
@classmethod
def get_business_name(cls, table_name: str) -> str:
"""
@@ -170,7 +180,7 @@ class GenUtils:
param text: 需要被替换的字符串
:return: 替换后的字符串
"""
return re.sub(r'(?:表|若依)', '', text)
return re.sub(r'(?:表|测试)', '', text)
@classmethod
def get_db_type(cls, column_type: str) -> str:
@@ -1,20 +1,20 @@
# -*- coding:utf-8 -*-
import json
import os
from datetime import datetime
from jinja2 import Environment, FileSystemLoader
from typing import Dict, List, Set
from jinja2.environment import Environment
from jinja2 import Environment, FileSystemLoader, select_autoescape, Template
from typing import Dict, List, Any, Set
from app.common.constant import GenConstant
from app.config.setting import settings
from app.api.v1.module_generator.gencode.schema import GenTableOutSchema as GenTableSchema, GenTableColumnOutSchema as GenTableColumnSchema
from app.core.base_model import CamelCaseUtil, SnakeCaseUtil
from app.core.exceptions import CustomException
from .string_util import StringUtil
from . import jinja2_util
from app.utils.common_util import CamelCaseUtil, SnakeCaseUtil
from app.api.v1.module_generator.gencode.schema import GenTableOutSchema, GenTableColumnOutSchema
from app.utils.string_util import StringUtil
class TemplateInitializer:
class Jinja2TemplateInitializerUtil:
"""
模板引擎初始化类
"""
@@ -29,9 +29,9 @@ class TemplateInitializer:
try:
# 修复模板路径,使用正确的相对路径
template_dir = settings.TEMPLATE_DIR
env = Environment(
loader=FileSystemLoader(template_dir),
loader=FileSystemLoader(settings.TEMPLATE_DIR),
autoescape=select_autoescape(['html', 'xml']),
keep_trailing_newline=True,
trim_blocks=True,
lstrip_blocks=True,
@@ -40,10 +40,7 @@ class TemplateInitializer:
{
'camel_to_snake': SnakeCaseUtil.camel_to_snake,
'snake_to_camel': CamelCaseUtil.snake_to_camel,
'snake_to_pascal_case': jinja2_util.snake_to_pascal_case,
'is_base_column': jinja2_util.is_base_column,
'get_column_options': jinja2_util.get_column_options,
'get_sqlalchemy_type': jinja2_util.get_sqlalchemy_type,
'get_sqlalchemy_type': Jinja2TemplateUtil.get_sqlalchemy_type,
}
)
return env
@@ -51,18 +48,34 @@ class TemplateInitializer:
raise RuntimeError(f'初始化Jinja2模板引擎失败: {e}')
class TemplateUtils:
class Jinja2TemplateUtil:
"""
模板工具类
模板处理工具类
"""
# 项目路径
FRONTEND_PROJECT_PATH = 'frontend'
BACKEND_PROJECT_PATH = 'backend'
DEFAULT_PARENT_MENU_ID = '3'
# 默认上级菜单,系统工具
DEFAULT_PARENT_MENU_ID = "3"
# 环境对象
_env = None
@classmethod
def prepare_context(cls, gen_table: GenTableSchema):
def get_env(cls) -> Environment:
"""获取模板环境对象"""
if cls._env is None:
cls._env = Jinja2TemplateInitializerUtil.init_jinja2()
return cls._env
@classmethod
def get_template(cls, template_path: str) -> Template:
"""获取模板"""
return cls.get_env().get_template(template_path)
@classmethod
def prepare_context(cls, gen_table: GenTableOutSchema) -> dict[str, Any]:
"""
准备模板变量
@@ -77,7 +90,7 @@ class TemplateUtils:
package_name = gen_table.package_name
tpl_category = gen_table.tpl_category
function_name = gen_table.function_name
context = {
'tplCategory': tpl_category,
'tableName': gen_table.table_name,
@@ -111,9 +124,9 @@ class TemplateUtils:
cls.set_sub_context(context, gen_table)
return context
@classmethod
def set_menu_context(cls, context: Dict, gen_table: GenTableSchema):
def set_menu_context(cls, context: Dict, gen_table: GenTableOutSchema):
"""
设置菜单上下文
@@ -122,12 +135,11 @@ class TemplateUtils:
:return: 新的模板上下文字典
"""
options = gen_table.options
if options:
params_obj = json.loads(options)
context['parentMenuId'] = cls.get_parent_menu_id(params_obj)
params_obj = json.loads(options)
context['parentMenuId'] = cls.get_parent_menu_id(params_obj)
@classmethod
def set_tree_context(cls, context: Dict, gen_table: GenTableSchema):
def set_tree_context(cls, context: Dict, gen_table: GenTableOutSchema):
"""
设置树形结构上下文
@@ -136,15 +148,14 @@ class TemplateUtils:
:return: 新的模板上下文字典
"""
options = gen_table.options
if options:
params_obj = json.loads(options)
context['treeCode'] = cls.get_tree_code(params_obj)
context['treeParentCode'] = cls.get_tree_parent_code(params_obj)
context['treeName'] = cls.get_tree_name(params_obj)
context['expandColumn'] = cls.get_expand_column(gen_table)
params_obj = json.loads(options)
context['treeCode'] = cls.get_tree_code(params_obj)
context['treeParentCode'] = cls.get_tree_parent_code(params_obj)
context['treeName'] = cls.get_tree_name(params_obj)
context['expandColumn'] = cls.get_expand_column(gen_table)
@classmethod
def set_sub_context(cls, context: Dict, gen_table: GenTableSchema):
def set_sub_context(cls, context: Dict, gen_table: GenTableOutSchema):
"""
设置子表上下文
@@ -155,12 +166,8 @@ class TemplateUtils:
sub_table = gen_table.sub_table
sub_table_name = gen_table.sub_table_name
sub_table_fk_name = gen_table.sub_table_fk_name
# 修复类型检查问题,确保sub_table存在
if sub_table:
sub_class_name = sub_table.class_name or ""
else:
sub_class_name = ""
sub_table_fk_class_name = StringUtil.convert_to_camel_case(sub_table_fk_name or "")
sub_class_name = sub_table.class_name
sub_table_fk_class_name = StringUtil.convert_to_camel_case(sub_table_fk_name)
context['subTable'] = sub_table
context['subTableName'] = sub_table_name
context['subTableFkName'] = sub_table_fk_name
@@ -182,13 +189,17 @@ class TemplateUtils:
if tpl_web_type == 'element-plus':
use_web_type = 'vue/v3'
templates = [
# Python相关模板
'python/controller.py.j2',
'python/crud.py.j2',
'python/model.py.j2',
'python/schema.py.j2',
'python/service.py.j2',
'python/crud.py.j2',
'python/schema.py.j2',
'python/param.py.j2',
'python/model.py.j2',
# Vue相关模板
'ts/api.ts.j2',
# SQL脚本模板
'sql/sql.j2',
'vue/api.js.j2',
]
if tpl_category == GenConstant.TPL_CRUD:
templates.append(f'{use_web_type}/index.vue.j2')
@@ -198,9 +209,9 @@ class TemplateUtils:
templates.append(f'{use_web_type}/index.vue.j2')
# templates.append('python/sub-domain.python.jinja2')
return templates
@classmethod
def get_file_name(cls, template: List[str], gen_table: GenTableSchema):
def get_file_name(cls, template: List[str], gen_table: GenTableOutSchema):
"""
根据模板生成文件名
@@ -216,25 +227,25 @@ class TemplateUtils:
python_path = f'{cls.BACKEND_PROJECT_PATH}/{package_name.replace(".", "/")}'
if 'controller.py.j2' in template:
return f'{python_path}/{business_name}_controller.py'
return f'{python_path}/controller/{business_name}_controller.py'
elif 'crud.py.j2' in template:
return f'{python_path}/{business_name}_crud.py'
return f'{python_path}/crud/{business_name}_crud.py'
elif 'model.py.j2' in template:
return f'{python_path}/{business_name}_model.py'
return f'{python_path}/entity/model/{business_name}_model.py'
elif 'service.py.j2' in template:
return f'{python_path}/{business_name}_service.py'
return f'{python_path}/service/{business_name}_service.py'
elif 'schema.py.j2' in template:
return f'{python_path}/{business_name}_schema.py'
return f'{python_path}/entity/schema/{business_name}_schema.py'
elif 'sql.j2' in template:
return f'{cls.BACKEND_PROJECT_PATH}/sql/{business_name}_menu.sql'
elif 'api.js.j2' in template:
return f'{vue_path}/api/{module_name}/{business_name}.js'
elif 'api.ts.j2' in template:
return f'{vue_path}/api/{module_name}/{business_name}.ts'
elif 'index.vue.j2' in template or 'index-tree.vue.j2' in template:
return f'{vue_path}/views/{module_name}/{business_name}/index.vue'
return ''
@classmethod
def get_package_prefix(cls, package_name: str):
def get_package_prefix(cls, package_name: str) -> str:
"""
获取包前缀
@@ -244,7 +255,7 @@ class TemplateUtils:
return package_name[: package_name.rfind('.')]
@classmethod
def get_vo_import_list(cls, gen_table: GenTableSchema):
def get_vo_import_list(cls, gen_table: GenTableOutSchema):
"""
获取vo模板导入包列表
@@ -258,8 +269,7 @@ class TemplateUtils:
import_list.add(f'from datetime import {column.python_type}')
elif column.python_type == GenConstant.TYPE_DECIMAL:
import_list.add('from decimal import Decimal')
# 修复类型检查问题,确保sub_table存在且有columns属性
if gen_table.sub and gen_table.sub_table:
if gen_table.sub:
sub_columns = gen_table.sub_table.columns or []
for sub_column in sub_columns:
if sub_column.python_type in GenConstant.TYPE_DATE:
@@ -267,9 +277,9 @@ class TemplateUtils:
elif sub_column.python_type == GenConstant.TYPE_DECIMAL:
import_list.add('from decimal import Decimal')
return cls.merge_same_imports(list(import_list), 'from datetime import')
@classmethod
def get_do_import_list(cls, gen_table: GenTableSchema):
def get_do_import_list(cls, gen_table: GenTableOutSchema) -> List[str]:
"""
获取do模板导入包列表
@@ -284,16 +294,15 @@ class TemplateUtils:
if data_type in GenConstant.COLUMNTYPE_GEOMETRY:
import_list.add('from geoalchemy2 import Geometry')
import_list.add(
f'from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY_TYPE_MAPPING, data_type)}'
f'from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, data_type)}'
)
# 修复类型检查问题,确保sub_table存在且有columns属性
if gen_table.sub and gen_table.sub_table:
if gen_table.sub:
import_list.add('from sqlalchemy import ForeignKey')
sub_columns = gen_table.sub_table.columns or []
for sub_column in sub_columns:
data_type = cls.get_db_type(sub_column.column_type)
import_list.add(
f'from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY_TYPE_MAPPING, data_type)}'
f'from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, data_type)}'
)
return cls.merge_same_imports(list(import_list), 'from sqlalchemy import')
@@ -308,7 +317,7 @@ class TemplateUtils:
if '(' in column_type:
return column_type.split('(')[0]
return column_type
@classmethod
def merge_same_imports(cls, imports: List[str], import_start: str) -> List[str]:
"""
@@ -332,9 +341,9 @@ class TemplateUtils:
merged_imports.append(merged_datetime_import)
return merged_imports
@classmethod
def get_dicts(cls, gen_table: GenTableSchema):
def get_dicts(cls, gen_table: GenTableOutSchema):
"""
获取字典列表
@@ -345,12 +354,11 @@ class TemplateUtils:
dicts = set()
cls.add_dicts(dicts, columns)
if gen_table.sub_table is not None:
sub_columns = gen_table.sub_table.columns or []
cls.add_dicts(dicts, sub_columns)
cls.add_dicts(dicts, gen_table.sub_table.columns)
return ', '.join(dicts)
@classmethod
def add_dicts(cls, dicts: Set[str], columns: List):
def add_dicts(cls, dicts: Set[str], columns: List[GenTableColumnOutSchema]):
"""
添加字典列表
@@ -369,7 +377,7 @@ class TemplateUtils:
dicts.add(f"'{column.dict_type}'")
@classmethod
def get_permission_prefix(cls, module_name: str, business_name: str):
def get_permission_prefix(cls, module_name: str | None, business_name: str | None) -> str:
"""
获取权限前缀
@@ -378,7 +386,7 @@ class TemplateUtils:
:return: 权限前缀
"""
return f'{module_name}:{business_name}'
@classmethod
def get_parent_menu_id(cls, params_obj: Dict):
"""
@@ -390,7 +398,7 @@ class TemplateUtils:
if params_obj and params_obj.get(GenConstant.PARENT_MENU_ID):
return params_obj.get(GenConstant.PARENT_MENU_ID)
return cls.DEFAULT_PARENT_MENU_ID
@classmethod
def get_tree_code(cls, params_obj: Dict):
"""
@@ -400,9 +408,9 @@ class TemplateUtils:
:return: 树编码
"""
if GenConstant.TREE_CODE in params_obj:
return cls.to_camel_case(params_obj.get(GenConstant.TREE_CODE, 'treeCode'))
return cls.to_camel_case(params_obj.get(GenConstant.TREE_CODE))
return ''
@classmethod
def get_tree_parent_code(cls, params_obj: Dict):
"""
@@ -412,9 +420,9 @@ class TemplateUtils:
:return: 树父编码
"""
if GenConstant.TREE_PARENT_CODE in params_obj:
return cls.to_camel_case(params_obj.get(GenConstant.TREE_PARENT_CODE, 'treeParentCode'))
return cls.to_camel_case(params_obj.get(GenConstant.TREE_PARENT_CODE))
return ''
@classmethod
def get_tree_name(cls, params_obj: Dict):
"""
@@ -424,11 +432,11 @@ class TemplateUtils:
:return: 树名称
"""
if GenConstant.TREE_NAME in params_obj:
return cls.to_camel_case(params_obj.get(GenConstant.TREE_NAME, 'treeName'))
return cls.to_camel_case(params_obj.get(GenConstant.TREE_NAME))
return ''
@classmethod
def get_expand_column(cls, gen_table: GenTableSchema):
def get_expand_column(cls, gen_table: GenTableOutSchema):
"""
获取展开列
@@ -436,18 +444,16 @@ class TemplateUtils:
:return: 展开列
"""
options = gen_table.options
if not options:
return 0
params_obj = json.loads(options)
tree_name = params_obj.get(GenConstant.TREE_NAME)
num = 0
for column in gen_table.columns or []:
for column in gen_table.columns:
if column.list:
num += 1
if column.column_name == tree_name:
break
return num
@classmethod
def to_camel_case(cls, text: str) -> str:
"""
@@ -458,7 +464,7 @@ class TemplateUtils:
"""
parts = text.split('_')
return parts[0] + ''.join(word.capitalize() for word in parts[1:])
@classmethod
def get_sqlalchemy_type(cls, column_type: str):
"""
@@ -472,18 +478,18 @@ class TemplateUtils:
if column_type_list[0] in GenConstant.COLUMNTYPE_STR:
sqlalchemy_type = (
StringUtil.get_mapping_value_by_key_ignore_case(
GenConstant.DB_TO_SQLALCHEMY_TYPE_MAPPING, column_type_list[0]
GenConstant.DB_TO_SQLALCHEMY, column_type_list[0]
)
+ '('
+ column_type_list[1]
)
else:
sqlalchemy_type = StringUtil.get_mapping_value_by_key_ignore_case(
GenConstant.DB_TO_SQLALCHEMY_TYPE_MAPPING, column_type_list[0]
GenConstant.DB_TO_SQLALCHEMY, column_type_list[0]
)
else:
sqlalchemy_type = StringUtil.get_mapping_value_by_key_ignore_case(
GenConstant.DB_TO_SQLALCHEMY_TYPE_MAPPING, column_type
GenConstant.DB_TO_SQLALCHEMY, column_type
)
return sqlalchemy_type
-53
View File
@@ -1,53 +0,0 @@
import re
from app.common.constant import GenConstants
def snake_to_pascal_case(value):
"""将下划线命名 (snake_case) 转换大驼峰"""
return ''.join(word.capitalize() for word in value.split('_'))
def snake_to_camel(snake_str):
"""将下划线命名 (snake_case) 转换小驼峰"""
components = snake_str.split('_')
return components[0] + ''.join(x.title() for x in components[1:])
def snake_2_colon(snake_str: str) -> str:
"""将下划线命名 (snake_case) 转换冒号分隔"""
return snake_str.replace('_', ':')
def is_base_column(column_name: str) -> bool:
"""判断是否是基础字段"""
return column_name in GenConstants.BASE_ENTITY
def get_sqlalchemy_type(mysql_field_type: str) -> str:
"""mysql_field_type 转sqlalchemy类型"""
if mysql_field_type:
base_type = mysql_field_type.split("(", 1)[0]
if base_type.upper() in GenConstants.MYSQL_TO_SQLALCHEMY.keys():
sqlalchemy_type = GenConstants.MYSQL_TO_SQLALCHEMY[base_type.upper()]
if sqlalchemy_type == 'String' :
match = re.search(r'\((.*?)\)', mysql_field_type)
if match:
return f'{sqlalchemy_type}({match.group(1)})'
else:
return f'{sqlalchemy_type}'
else:
return f'{sqlalchemy_type}'
return "String"
def get_column_options(col) -> str:
options = []
# 主键
if col['isPk'] == "1":
options.append("primary_key=True")
# 是否允许为空
if col['isRequired'] == "1":
options.append("nullable=False")
# 自增
if col["isIncrement"] == "1":
options.append("autoincrement=True")
# 注释
options.append(f"comment='{col['columnComment']}'")
return ", ".join(options)
+2 -1
View File
@@ -35,4 +35,5 @@ PyMySQL==1.1.2 # mysql 异步操作数据库基于 pymysqlaiomys
cryptography==45.0.2 # mysql8 密码加密
openai==1.55.2 # ai 大模型
oss2==2.18.4 # 阿里云对象存储
rich==13.9.4
rich==13.9.4
sqlglot[rs]==27.8.0 # sql 解析
+8 -8
View File
@@ -22,7 +22,7 @@ from {{ packageName }}.param import {{ tableName|snake_to_pascal_case }}QueryPar
async def get_{{ tableName }}_list(
page: PaginationQueryParam = Depends(),
search: {{ tableName|snake_to_pascal_case }}QueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:list"]))
auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:list"]))
):
{{ tableName }}_result_list = await {{ tableName|snake_to_pascal_case }}Service.get_{{ tableName }}_list_service(auth, search, page.order_by)
{{ tableName }}_result = await PaginationService.paginate(
@@ -36,7 +36,7 @@ async def get_{{ tableName }}_list(
@{{ tableName|snake_to_camel }}Controller.get('/{id}', summary="获取{{ functionName }}详细信息", description="获取{{ functionName }}详细信息")
async def get_{{ tableName }}_by_id(
id: int,
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:query"]))
auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:query"]))
):
{{ tableName }} = await {{ tableName|snake_to_pascal_case }}Service.get_{{ tableName }}_by_id_service(auth, id)
return SuccessResponse(data={{ tableName }})
@@ -45,7 +45,7 @@ async def get_{{ tableName }}_by_id(
@{{ tableName|snake_to_camel }}Controller.post('', summary="新增{{ functionName }}", description="新增{{ functionName }}")
async def add_{{ tableName }} (
add_model: {{ tableName|snake_to_pascal_case }}CreateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:add"])),
auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:add"])),
current_user: UserOutSchema = Depends(lambda auth: auth.user)
):
add_result = await {{ tableName|snake_to_pascal_case }}Service.add_{{ tableName }}_service(auth, add_model)
@@ -56,7 +56,7 @@ async def add_{{ tableName }} (
async def update_{{ tableName }}(
id: int,
edit_model: {{ tableName|snake_to_pascal_case }}UpdateSchema,
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:edit"])),
auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:edit"])),
current_user: UserOutSchema = Depends(lambda auth: auth.user)
):
update_result = await {{ tableName|snake_to_pascal_case }}Service.update_{{ tableName }}_service(auth, id, edit_model)
@@ -66,7 +66,7 @@ async def update_{{ tableName }}(
@{{ tableName|snake_to_camel }}Controller.delete('/{ids}', summary="删除{{ functionName }}", description="删除{{ functionName }}")
async def del_{{ tableName }}(
ids: str,
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:remove"]))
auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:remove"]))
):
id_list = [int(id) for id in ids.split(',')]
del_result = await {{ tableName|snake_to_pascal_case }}Service.del_{{ tableName }}_service(auth, id_list)
@@ -76,7 +76,7 @@ async def del_{{ tableName }}(
@{{ tableName|snake_to_camel }}Controller.post('/export', summary="导出{{ functionName }}", description="导出{{ functionName }}")
async def export_{{ tableName }}(
search: {{ tableName|snake_to_pascal_case }}QueryParam = Depends(),
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:export"]))
auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:export"]))
) -> StreamingResponse:
# 获取全量数据
{{ tableName }}_list = await {{ tableName|snake_to_pascal_case }}Service.get_{{ tableName }}_list_service(auth=auth, search=search)
@@ -93,7 +93,7 @@ async def export_{{ tableName }}(
@{{ tableName|snake_to_camel }}Controller.post('/import', summary="导入{{ functionName }}", description="导入{{ functionName }}")
async def import_{{ tableName }}(
file: UploadFile,
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:import"]))
auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:import"]))
) -> JSONResponse:
batch_import_result = await {{ tableName|snake_to_pascal_case }}Service.import_{{ tableName }}_service(file=file, auth=auth, update_support=True)
return SuccessResponse(data=batch_import_result, msg="导入成功")
@@ -101,7 +101,7 @@ async def import_{{ tableName }}(
@{{ tableName|snake_to_camel }}Controller.post('/download/template', summary="获取{{ functionName }}导入模板", description="获取{{ functionName }}导入模板")
async def export_{{ tableName }}_template(
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:import"]))
auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:import"]))
) -> StreamingResponse:
{{ tableName }}_import_template_result = await {{ tableName|snake_to_pascal_case }}Service.get_import_template_{{ tableName }}_service()
return StreamResponse(