mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-21 12:52:26 +00:00
refactor(module): 重构模块结构和代码生成模板
- 删除废弃的ticket和version模块相关代码 - 将resource模块从system迁移到monitor - 移除前端资源管理相关配置 - 重构代码生成模板路径和配置 - 修复CRUD基类初始化参数问题 - 优化模板工具类路径处理 - 更新基础模型字段和配置
This commit is contained in:
@@ -11,9 +11,6 @@ from .module_monitor import MonitorRouter
|
||||
# 通用模块
|
||||
from .module_common import CommonRouter
|
||||
|
||||
# 资源管理模块
|
||||
from .module_resource import ResourceRouter
|
||||
|
||||
# 示例模块
|
||||
from .module_example import ExampleRouter
|
||||
|
||||
@@ -34,7 +31,6 @@ router = APIRouter()
|
||||
router.include_router(SystemRouter)
|
||||
router.include_router(MonitorRouter)
|
||||
router.include_router(CommonRouter)
|
||||
router.include_router(ResourceRouter)
|
||||
router.include_router(ExampleRouter)
|
||||
router.include_router(ApplicationRouter)
|
||||
router.include_router(AIRouter)
|
||||
|
||||
@@ -14,7 +14,7 @@ class McpCRUD(CRUDBase[McpModel, McpCreateSchema, McpUpdateSchema]):
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
"""初始化CRUD"""
|
||||
self.auth = auth
|
||||
super().__init__(model=McpModel(), auth=auth)
|
||||
super().__init__(model=McpModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[McpModel]:
|
||||
"""详情"""
|
||||
|
||||
@@ -14,7 +14,7 @@ class DemoCRUD(CRUDBase[DemoModel, DemoCreateSchema, DemoUpdateSchema]):
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
"""初始化CRUD"""
|
||||
self.auth = auth
|
||||
super().__init__(model=DemoModel, auth=auth)
|
||||
super().__init__(model=DemoModel(), auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[DemoModel]:
|
||||
"""详情"""
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from fastapi import APIRouter
|
||||
from .gencode.controller import genController
|
||||
from .gencode.controller import GenRouter
|
||||
|
||||
# 创建代码生成模块路由
|
||||
GeneratorRouter = APIRouter(prefix="/generator")
|
||||
|
||||
# 包含代码生成路由
|
||||
GeneratorRouter.include_router(genController)
|
||||
GeneratorRouter.include_router(GenRouter)
|
||||
@@ -1,107 +1,107 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, Query, Request, Body
|
||||
from fastapi.responses import StreamingResponse
|
||||
from typing import List
|
||||
from fastapi import APIRouter, Depends, Query, Body
|
||||
from fastapi.responses import StreamingResponse, JSONResponse
|
||||
from pydantic_validation_decorator import ValidateFields
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.common.enums import BusinessType
|
||||
from app.common.response import SuccessResponse, ErrorResponse, StreamResponse
|
||||
from app.core.dependencies import AuthPermission
|
||||
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 DeleteGenTableSchema, EditGenTableSchema, GenTableSchema
|
||||
from .schema import GenTableDeleteSchema, GenTableUpdateSchema, GenTableOutSchema
|
||||
from .service import GenTableColumnService, GenTableService
|
||||
from app.utils.common_util import bytes2file_response
|
||||
from app.core.logger import logger
|
||||
|
||||
|
||||
genController = APIRouter(route_class=OperationLogRoute, prefix='/tool/gen', tags=["代码生成模块"])
|
||||
GenRouter = APIRouter(route_class=OperationLogRoute, prefix='/tool/gen', tags=["代码生成模块"])
|
||||
|
||||
|
||||
@genController.get('/list', summary="查询代码生成业务表列表", description="查询代码生成业务表列表")
|
||||
async def get_gen_table_list(
|
||||
@GenRouter.get('/list', summary="查询代码生成业务表列表", description="查询代码生成业务表列表")
|
||||
async def get_gen_table_list_controller(
|
||||
page: PaginationQueryParam = Depends(),
|
||||
search: GenTableQueryParam = Depends(),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["tool:gen:list"]))
|
||||
):
|
||||
# 获取分页数据
|
||||
gen_page_query_result = await GenTableService.get_gen_table_list_services(auth, search, is_page=True)
|
||||
) -> JSONResponse:
|
||||
result_dict_list = await GenTableService.get_gen_table_list_services(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=gen_page_query_result, msg="获取代码生成业务表列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="获取代码生成业务表列表成功")
|
||||
|
||||
|
||||
@genController.get('/db/list', summary="查询数据库表列表", description="查询数据库表列表")
|
||||
async def get_gen_db_table_list(
|
||||
@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=["tool:gen:list"]))
|
||||
):
|
||||
# 获取分页数据
|
||||
gen_page_query_result = await GenTableService.get_gen_db_table_list_services(auth, search, is_page=True)
|
||||
) -> JSONResponse:
|
||||
result_dict_list = await GenTableService.get_gen_db_table_list_services(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=gen_page_query_result, msg="获取数据库表列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="获取数据库表列表成功")
|
||||
|
||||
|
||||
@genController.post('/importTable', summary="导入表结构", description="导入表结构")
|
||||
@GenRouter.post('/importTable', summary="导入表结构", description="导入表结构")
|
||||
@ValidateFields(validate_model='edit_gen_table')
|
||||
async def import_gen_table(
|
||||
tables: str = Query(..., description="表名列表"),
|
||||
async def import_gen_table_controller(
|
||||
tables: List[str] = Body(..., description="表名列表", embed=True),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["tool:gen:import"])),
|
||||
current_user: UserOutSchema = Depends(lambda auth: auth.user)
|
||||
):
|
||||
table_names = tables.split(',') if tables else []
|
||||
) -> JSONResponse:
|
||||
table_names = tables if tables else []
|
||||
add_gen_table_list = await GenTableService.get_gen_db_table_list_by_name_services(auth, table_names)
|
||||
result = await GenTableService.import_gen_table_services(auth, add_gen_table_list, current_user)
|
||||
logger.info('导入表结构成功')
|
||||
return result
|
||||
|
||||
|
||||
@genController.put('', summary="编辑业务表信息", description="编辑业务表信息")
|
||||
@GenRouter.put('', summary="编辑业务表信息", description="编辑业务表信息")
|
||||
@ValidateFields(validate_model='edit_gen_table')
|
||||
async def edit_gen_table(
|
||||
data: EditGenTableSchema,
|
||||
async def edit_gen_table_controller(
|
||||
data: GenTableUpdateSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["tool:gen:edit"])),
|
||||
current_user: UserOutSchema = Depends(lambda auth: auth.user)
|
||||
):
|
||||
) -> JSONResponse:
|
||||
data.update_by = current_user.username
|
||||
data.update_time = datetime.now()
|
||||
data.updated_at = datetime.now()
|
||||
await GenTableService.validate_edit(data)
|
||||
edit_gen_result = await GenTableService.edit_gen_table_services(auth, data)
|
||||
logger.info('编辑业务表信息成功')
|
||||
return SuccessResponse(data=edit_gen_result, msg="编辑业务表信息成功")
|
||||
|
||||
|
||||
@genController.delete('/{table_ids}', summary="删除业务表信息", description="删除业务表信息")
|
||||
async def delete_gen_table(
|
||||
@GenRouter.delete('/{table_ids}', summary="删除业务表信息", description="删除业务表信息")
|
||||
async def delete_gen_table_controller(
|
||||
table_ids: str,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["tool:gen:remove"]))
|
||||
):
|
||||
delete_gen_table = DeleteGenTableSchema(table_ids=table_ids)
|
||||
) -> JSONResponse:
|
||||
delete_gen_table = GenTableDeleteSchema(table_ids=table_ids)
|
||||
result = await GenTableService.delete_gen_table_services(auth, delete_gen_table)
|
||||
logger.info('删除业务表信息成功')
|
||||
return result
|
||||
|
||||
|
||||
@genController.post('/createTable', summary="创建表结构", description="创建表结构")
|
||||
async def create_table(
|
||||
@GenRouter.post('/createTable', summary="创建表结构", description="创建表结构")
|
||||
async def create_table_controller(
|
||||
sql: str = Query(..., description="SQL语句"),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["tool:gen:create"])),
|
||||
current_user: UserOutSchema = Depends(lambda auth: auth.user)
|
||||
):
|
||||
) -> JSONResponse:
|
||||
result = await GenTableService.create_table_services(auth, sql, current_user)
|
||||
logger.info('创建表结构成功')
|
||||
return result
|
||||
return SuccessResponse(msg="创建表结构成功", data=result)
|
||||
|
||||
|
||||
@genController.get('/batchGenCode', summary="批量生成代码", description="批量生成代码")
|
||||
async def batch_gen_code(
|
||||
@GenRouter.get('/batchGenCode', summary="批量生成代码", description="批量生成代码")
|
||||
async def batch_gen_code_controller(
|
||||
tables: str = Query(..., description="表名列表"),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["tool:gen:code"]))
|
||||
):
|
||||
) -> StreamResponse:
|
||||
table_names = tables.split(',') if tables else []
|
||||
batch_gen_code_result = await GenTableService.batch_gen_code_services(auth, table_names)
|
||||
logger.info('批量生成代码成功')
|
||||
@@ -112,11 +112,11 @@ async def batch_gen_code(
|
||||
)
|
||||
|
||||
|
||||
@genController.get('/genCode/{table_name}', summary="生成代码到指定路径", description="生成代码到指定路径")
|
||||
async def gen_code_local(
|
||||
@GenRouter.get('/genCode/{table_name}', summary="生成代码到指定路径", description="生成代码到指定路径")
|
||||
async def gen_code_local_controller(
|
||||
table_name: str,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["tool:gen:code"]))
|
||||
):
|
||||
) -> JSONResponse:
|
||||
from app.config.setting import settings
|
||||
if not settings.allow_overwrite:
|
||||
logger.error('【系统预设】不允许生成文件覆盖到本地')
|
||||
@@ -126,11 +126,11 @@ async def gen_code_local(
|
||||
return result
|
||||
|
||||
|
||||
@genController.get('/{table_id}', summary="获取业务表详细信息", description="获取业务表详细信息")
|
||||
async def query_detail_gen_table(
|
||||
@GenRouter.get('/{table_id}', summary="获取业务表详细信息", description="获取业务表详细信息")
|
||||
async def query_detail_gen_table_controller(
|
||||
table_id: int,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["tool:gen:query"]))
|
||||
):
|
||||
) -> JSONResponse:
|
||||
gen_table = await GenTableService.get_gen_table_by_id_services(auth, table_id)
|
||||
gen_tables = await GenTableService.get_gen_table_all_services(auth)
|
||||
gen_columns = await GenTableColumnService.get_gen_table_column_list_by_table_id_services(auth, table_id)
|
||||
@@ -139,21 +139,21 @@ async def query_detail_gen_table(
|
||||
return SuccessResponse(data=gen_table_detail_result, msg="获取业务表详细信息成功")
|
||||
|
||||
|
||||
@genController.get('/preview/{table_id}', summary="预览代码", description="预览代码")
|
||||
async def preview_code(
|
||||
@GenRouter.get('/preview/{table_id}', summary="预览代码", description="预览代码")
|
||||
async def preview_code_controller(
|
||||
table_id: int,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["tool:gen:preview"]))
|
||||
):
|
||||
) -> JSONResponse:
|
||||
preview_code_result = await GenTableService.preview_code_services(auth, table_id)
|
||||
logger.info('预览代码成功')
|
||||
return SuccessResponse(data=preview_code_result, msg="预览代码成功")
|
||||
|
||||
|
||||
@genController.get('/synchDb/{table_name}', summary="同步数据库", description="同步数据库")
|
||||
async def sync_db(
|
||||
@GenRouter.get('/synchDb/{table_name}', summary="同步数据库", description="同步数据库")
|
||||
async def sync_db_controller(
|
||||
table_name: str,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["tool:gen:edit"]))
|
||||
):
|
||||
) -> JSONResponse:
|
||||
result = await GenTableService.sync_db_services(auth, table_name)
|
||||
logger.info('同步数据库成功')
|
||||
return result
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from datetime import datetime, time
|
||||
from sqlalchemy.engine.row import Row
|
||||
from sqlalchemy import delete, func, select, text, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
@@ -9,25 +10,20 @@ from typing import List, Optional, Sequence, Any, Dict
|
||||
from .model import GenTableModel, GenTableColumnModel
|
||||
from app.config.setting import settings
|
||||
from app.common.request import PaginationService
|
||||
from .schema import (
|
||||
GenTableBaseSchema,
|
||||
GenTableColumnBaseSchema,
|
||||
GenTableColumnSchema,
|
||||
GenTableSchema,
|
||||
)
|
||||
from .schema import GenTableCreateSchema, GenTableUpdateSchema, GenTableOutSchema, GenTableDeleteSchema, GenTableColumnCreateSchema, GenTableColumnUpdateSchema, GenTableColumnOutSchema, GenTableColumnDeleteSchema
|
||||
from .param import GenTableQueryParam
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
|
||||
|
||||
class GenTableDao(CRUDBase[GenTableModel, GenTableBaseSchema, GenTableBaseSchema]):
|
||||
class GenTableCRUD(CRUDBase[GenTableModel, GenTableCreateSchema, GenTableUpdateSchema]):
|
||||
"""
|
||||
代码生成业务表模块数据库操作层
|
||||
"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
"""初始化CRUD"""
|
||||
super().__init__(model=GenTableModel(), auth=auth)
|
||||
super().__init__(model=GenTableModel, auth=auth)
|
||||
|
||||
async def get_gen_table_by_id(self, db: AsyncSession, table_id: int) -> Optional[GenTableModel]:
|
||||
"""
|
||||
@@ -104,23 +100,24 @@ class GenTableDao(CRUDBase[GenTableModel, GenTableBaseSchema, GenTableBaseSchema
|
||||
# 构建查询条件
|
||||
conditions = []
|
||||
|
||||
# 访问name属性而不是table_name
|
||||
if query_object.name:
|
||||
conditions.append(func.lower(GenTableModel.table_name).like(f'%{str(query_object.name).lower()}%'))
|
||||
# 访问table_name属性
|
||||
if getattr(query_object, 'table_name', None) and query_object.table_name[1]:
|
||||
conditions.append(func.lower(GenTableModel.table_name).like(f'%{str(query_object.table_name[1]).lower()}%'))
|
||||
|
||||
# 访问table_comment属性
|
||||
if query_object.table_comment:
|
||||
if getattr(query_object, 'table_comment', None):
|
||||
conditions.append(func.lower(GenTableModel.table_comment).like(f'%{str(query_object.table_comment).lower()}%'))
|
||||
|
||||
# 访问created_at属性而不是start_time和end_time
|
||||
if hasattr(query_object, 'created_at') and query_object.created_at:
|
||||
if isinstance(query_object.created_at, tuple) and query_object.created_at[0] == "between":
|
||||
conditions.append(GenTableModel.create_time.between(*query_object.created_at[1]))
|
||||
conditions.append(GenTableModel.created_at.between(*query_object.created_at[1]))
|
||||
|
||||
query = (
|
||||
select(GenTableModel)
|
||||
.options(selectinload(GenTableModel.columns))
|
||||
.where(*conditions)
|
||||
.order_by(GenTableModel.created_at.desc())
|
||||
.distinct()
|
||||
)
|
||||
|
||||
@@ -154,47 +151,43 @@ class GenTableDao(CRUDBase[GenTableModel, GenTableBaseSchema, GenTableBaseSchema
|
||||
:param is_page: 是否开启分页
|
||||
:return: 数据库列表信息对象
|
||||
"""
|
||||
if settings.DATABASE_TYPE == 'postgresql':
|
||||
query_sql = """
|
||||
SELECT table_name as table_name,
|
||||
table_comment as table_comment,
|
||||
create_time as create_time,
|
||||
update_time as update_time
|
||||
from
|
||||
list_table
|
||||
where
|
||||
table_name not like 'apscheduler_%'
|
||||
and table_name not like 'gen_%'
|
||||
and table_name not in (select table_name from gen_table)
|
||||
"""
|
||||
else:
|
||||
query_sql = """
|
||||
SELECT table_name as table_name,
|
||||
table_comment as table_comment,
|
||||
create_time as create_time,
|
||||
update_time as update_time
|
||||
from
|
||||
information_schema.tables
|
||||
where
|
||||
table_schema = (select database())
|
||||
and table_name not like 'apscheduler\_%'
|
||||
and table_name not like 'gen\_%'
|
||||
and table_name not in (select table_name from gen_table)
|
||||
"""
|
||||
if query_object.name:
|
||||
query_sql = """
|
||||
SELECT table_name as table_name,
|
||||
table_comment as table_comment,
|
||||
create_time as create_time,
|
||||
update_time as update_time
|
||||
from
|
||||
information_schema.tables
|
||||
where
|
||||
table_schema = (select database())
|
||||
and table_name not like 'apscheduler\_%'
|
||||
and table_name not like 'gen\_%'
|
||||
and table_name not in (select table_name from gen_table)
|
||||
"""
|
||||
# 根据param.py中的定义,table_name是元组形式("like", value)
|
||||
if getattr(query_object, 'table_name', None) and query_object.table_name[1]:
|
||||
query_sql += """and lower(table_name) like lower(concat('%', :table_name, '%'))"""
|
||||
if query_object.table_comment:
|
||||
|
||||
# 处理table_comment字段(如果有)
|
||||
if getattr(query_object, 'table_comment', None):
|
||||
query_sql += """and lower(table_comment) like lower(concat('%', :table_comment, '%'))"""
|
||||
# 修复查询参数处理
|
||||
query_params = {
|
||||
k: v for k, v in query_object.model_dump(exclude_none=True, exclude={'page_no', 'page_size'}).items()
|
||||
}
|
||||
|
||||
# 构建查询参数
|
||||
query_params = {}
|
||||
|
||||
# 添加table_name查询参数
|
||||
if getattr(query_object, 'table_name', None) and query_object.table_name[1]:
|
||||
query_params['table_name'] = query_object.table_name[1]
|
||||
|
||||
# 添加table_comment查询参数
|
||||
if getattr(query_object, 'table_comment', None):
|
||||
query_params['table_comment'] = query_object.table_comment
|
||||
|
||||
query_sql += """order by create_time desc"""
|
||||
query = text(query_sql).bindparams(**query_params)
|
||||
|
||||
# 执行查询
|
||||
result = await db.execute(select(query))
|
||||
result = await db.execute(query)
|
||||
all_data = list(result.fetchall())
|
||||
|
||||
# 使用PaginationService.paginate进行分页
|
||||
@@ -222,49 +215,32 @@ class GenTableDao(CRUDBase[GenTableModel, GenTableBaseSchema, GenTableBaseSchema
|
||||
:param table_names: 业务表名称组
|
||||
:return: 数据库列表信息对象
|
||||
"""
|
||||
if settings.DATABASE_TYPE == 'postgresql':
|
||||
query_sql = """
|
||||
select
|
||||
table_name as table_name,
|
||||
table_comment as table_comment,
|
||||
create_time as create_time,
|
||||
update_time as update_time
|
||||
from
|
||||
list_table
|
||||
where
|
||||
table_name not like 'qrtz_%'
|
||||
and table_name not like 'gen_%'
|
||||
and table_name = any(:table_names)
|
||||
"""
|
||||
else:
|
||||
query_sql = """
|
||||
select
|
||||
table_name as table_name,
|
||||
table_comment as table_comment,
|
||||
create_time as create_time,
|
||||
update_time as update_time
|
||||
from
|
||||
information_schema.tables
|
||||
where
|
||||
table_name not like 'qrtz\_%'
|
||||
and table_name not like 'gen\_%'
|
||||
and table_schema = (select database())
|
||||
and table_name in :table_names
|
||||
"""
|
||||
query_sql = """
|
||||
select
|
||||
table_name as table_name,
|
||||
table_comment as table_comment,
|
||||
create_time as create_time,
|
||||
update_time as update_time
|
||||
from
|
||||
information_schema.tables
|
||||
where
|
||||
table_schema = (select database())
|
||||
and table_name in :table_names
|
||||
"""
|
||||
query = text(query_sql).bindparams(table_names=tuple(table_names))
|
||||
gen_db_table_list = (await db.execute(query)).fetchall()
|
||||
|
||||
return gen_db_table_list
|
||||
|
||||
|
||||
class GenTableColumnDao(CRUDBase[GenTableColumnModel, GenTableColumnBaseSchema, GenTableColumnBaseSchema]):
|
||||
class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnCreateSchema, GenTableColumnUpdateSchema]):
|
||||
"""
|
||||
代码生成业务表字段模块数据库操作层
|
||||
"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
"""初始化CRUD"""
|
||||
super().__init__(model=GenTableColumnModel(), auth=auth)
|
||||
super().__init__(model=GenTableColumnModel, auth=auth)
|
||||
|
||||
async def get_gen_table_column_list_by_table_id(self, db: AsyncSession, table_id: int) -> Sequence[GenTableColumnModel]:
|
||||
"""
|
||||
@@ -286,7 +262,7 @@ class GenTableColumnDao(CRUDBase[GenTableColumnModel, GenTableColumnBaseSchema,
|
||||
|
||||
return gen_table_column_list
|
||||
|
||||
async def get_gen_db_table_columns_by_name(self, db: AsyncSession, table_name: str):
|
||||
async def get_gen_db_table_columns_by_name(self, db: AsyncSession, table_name: str) -> Sequence[Row[Any]]:
|
||||
"""
|
||||
根据业务表名称获取业务表字段列表信息
|
||||
|
||||
@@ -294,42 +270,32 @@ class GenTableColumnDao(CRUDBase[GenTableColumnModel, GenTableColumnBaseSchema,
|
||||
:param table_name: 业务表名称
|
||||
:return: 业务表字段列表信息对象
|
||||
"""
|
||||
if settings.DATABASE_TYPE == 'postgresql':
|
||||
query_sql = """
|
||||
select
|
||||
column_name, is_required, is_pk, sort, column_comment, is_increment, column_type
|
||||
from
|
||||
list_column
|
||||
where
|
||||
table_name = :table_name
|
||||
"""
|
||||
else:
|
||||
query_sql = """
|
||||
select
|
||||
column_name as column_name,
|
||||
case
|
||||
when is_nullable = 'no' and column_key != 'PRI' then '1'
|
||||
else '0'
|
||||
end as is_required,
|
||||
case
|
||||
when column_key = 'PRI' then '1'
|
||||
else '0'
|
||||
end as is_pk,
|
||||
ordinal_position as sort,
|
||||
column_comment as column_comment,
|
||||
case
|
||||
when extra = 'auto_increment' then '1'
|
||||
else '0'
|
||||
end as is_increment,
|
||||
column_type as column_type
|
||||
from
|
||||
information_schema.columns
|
||||
where
|
||||
table_schema = (select database())
|
||||
and table_name = :table_name
|
||||
order by
|
||||
ordinal_position
|
||||
"""
|
||||
query_sql = """
|
||||
select
|
||||
column_name as column_name,
|
||||
case
|
||||
when is_nullable = 'no' and column_key != 'PRI' then '1'
|
||||
else '0'
|
||||
end as is_required,
|
||||
case
|
||||
when column_key = 'PRI' then '1'
|
||||
else '0'
|
||||
end as is_pk,
|
||||
ordinal_position as sort,
|
||||
column_comment as column_comment,
|
||||
case
|
||||
when extra = 'auto_increment' then '1'
|
||||
else '0'
|
||||
end as is_increment,
|
||||
column_type as column_type
|
||||
from
|
||||
information_schema.columns
|
||||
where
|
||||
table_schema = (select database())
|
||||
and table_name = :table_name
|
||||
order by
|
||||
ordinal_position
|
||||
"""
|
||||
query = text(query_sql).bindparams(table_name=table_name)
|
||||
gen_db_table_columns = (await db.execute(query)).fetchall()
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional, List
|
||||
from sqlalchemy import String, Integer, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
@@ -5,25 +5,21 @@ from typing import Optional
|
||||
from fastapi import Query
|
||||
|
||||
from app.core.validator import DateTimeStr
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from .schema import GenTableBaseSchema, GenTableColumnBaseSchema
|
||||
|
||||
|
||||
class GenTableQueryParam(PaginationQueryParam, GenTableBaseSchema):
|
||||
class GenTableQueryParam:
|
||||
"""数据库表查询参数"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: Optional[str] = Query(None, description="名称"),
|
||||
table_name: Optional[str] = Query(None, description="表名称"),
|
||||
status: Optional[bool] = Query(None, description="是否启用"),
|
||||
creator: Optional[int] = Query(None, description="创建人"),
|
||||
start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2023-01-01 00:00:00"),
|
||||
end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2023-12-31 23:59:59"),
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
# 模糊查询字段
|
||||
self.name = ("like", name)
|
||||
) -> None:
|
||||
# 存储查询条件,不直接赋值给父类属性
|
||||
self.table_name = ("like", table_name)
|
||||
|
||||
# 精确查询字段
|
||||
self.creator_id = creator
|
||||
@@ -36,21 +32,19 @@ class GenTableQueryParam(PaginationQueryParam, GenTableBaseSchema):
|
||||
self.created_at = ("between", (start_datetime, end_datetime))
|
||||
|
||||
|
||||
class GenTableColumnQueryParam(PaginationQueryParam, GenTableColumnBaseSchema):
|
||||
class GenTableColumnQueryParam:
|
||||
"""数据库表字段查询参数"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: Optional[str] = Query(None, description="名称"),
|
||||
column_name: Optional[str] = Query(None, description="列名称"),
|
||||
status: Optional[bool] = Query(None, description="是否启用"),
|
||||
creator: Optional[int] = Query(None, description="创建人"),
|
||||
start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2023-01-01 00:00:00"),
|
||||
end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2023-12-31 23:59:59"),
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
# 模糊查询字段
|
||||
self.name = ("like", name)
|
||||
) -> None:
|
||||
# 存储查询条件,不直接赋值给父类属性
|
||||
self.column_name = ("like", column_name)
|
||||
|
||||
# 精确查询字段
|
||||
self.creator_id = creator
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from datetime import datetime
|
||||
from typing import List, Literal, Optional, Union
|
||||
from typing import List, Literal, Optional
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
from pydantic.alias_generators import to_camel
|
||||
from pydantic_validation_decorator import NotBlank
|
||||
|
||||
from app.utils.string_util import StringUtil
|
||||
from app.common.constant import GenConstant
|
||||
from app.core.base_schema import BaseSchema
|
||||
|
||||
|
||||
class GenTableBaseSchema(BaseModel):
|
||||
class GenTableCreateSchema(BaseModel):
|
||||
"""
|
||||
代码生成业务表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
table_id: Optional[int] = Field(default=None, description='编号')
|
||||
table_name: str = Field(default=..., description='表名称')
|
||||
table_comment: str = Field(default=..., description='表描述')
|
||||
sub_table_name: Optional[str] = Field(default=None, description='关联子表的表名')
|
||||
@@ -33,12 +32,6 @@ class GenTableBaseSchema(BaseModel):
|
||||
gen_type: Optional[Literal['0', '1']] = Field(default=None, description='生成代码方式(0zip压缩包 1自定义路径)')
|
||||
gen_path: Optional[str] = Field(default=None, description='生成路径(不填默认项目路径)')
|
||||
options: Optional[str] = Field(default=None, description='其它生成选项')
|
||||
|
||||
create_by: Optional[str] = Field(default=None, description='创建者')
|
||||
create_time: Optional[datetime] = Field(default=None, description='创建时间')
|
||||
update_by: Optional[str] = Field(default=None, description='更新者')
|
||||
update_time: Optional[datetime] = Field(default=None, description='更新时间')
|
||||
remark: Optional[str] = Field(default=None, description='备注')
|
||||
|
||||
@NotBlank(field_name='table_name', message='表名称不能为空')
|
||||
def get_table_name(self):
|
||||
@@ -83,14 +76,14 @@ class GenTableBaseSchema(BaseModel):
|
||||
self.get_function_author()
|
||||
|
||||
|
||||
class GenTableSchema(GenTableBaseSchema):
|
||||
class GenTableUpdateSchema(GenTableCreateSchema):
|
||||
"""
|
||||
代码生成业务表模型
|
||||
"""
|
||||
|
||||
pk_column: Optional['GenTableColumnSchema'] = Field(default=None, description='主键信息')
|
||||
sub_table: Optional['GenTableSchema'] = Field(default=None, description='子表信息')
|
||||
columns: List['GenTableColumnSchema'] = Field(default=..., description='表列信息')
|
||||
pk_column: Optional['GenTableColumnUpdateSchema'] = Field(default=None, description='主键信息')
|
||||
sub_table: Optional['GenTableUpdateSchema'] = Field(default=None, description='子表信息')
|
||||
columns: List['GenTableColumnUpdateSchema'] = Field(default=..., description='表列信息')
|
||||
tree_code: Optional[str] = Field(default=None, description='树编码字段')
|
||||
tree_parent_code: Optional[str] = Field(default=None, description='树父编码字段')
|
||||
tree_name: Optional[str] = Field(default=None, description='树名称字段')
|
||||
@@ -101,35 +94,19 @@ class GenTableSchema(GenTableBaseSchema):
|
||||
crud: Optional[bool] = Field(default=None, description='是否为单表')
|
||||
|
||||
@model_validator(mode='after')
|
||||
def check_some_is(self) -> 'GenTableSchema':
|
||||
def check_some_is(self) -> 'GenTableUpdateSchema':
|
||||
self.sub = True if self.tpl_category and self.tpl_category == GenConstant.TPL_SUB else False
|
||||
self.tree = True if self.tpl_category and self.tpl_category == GenConstant.TPL_TREE else False
|
||||
self.crud = True if self.tpl_category and self.tpl_category == GenConstant.TPL_CRUD else False
|
||||
return self
|
||||
|
||||
|
||||
class EditGenTableSchema(GenTableSchema):
|
||||
"""
|
||||
修改代码生成业务表模型
|
||||
"""
|
||||
|
||||
params: Optional['GenTableParamsSchema'] = Field(default=None, description='业务表参数')
|
||||
class GenTableOutSchema(GenTableUpdateSchema, BaseSchema):
|
||||
"""响应模型"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class GenTableParamsSchema(BaseModel):
|
||||
"""
|
||||
代码生成业务表参数模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
tree_code: Optional[str] = Field(default=None, description='树编码字段')
|
||||
tree_parent_code: Optional[str] = Field(default=None, description='树父编码字段')
|
||||
tree_name: Optional[str] = Field(default=None, description='树名称字段')
|
||||
parent_menu_id: Optional[int] = Field(default=None, description='上级菜单ID字段')
|
||||
|
||||
|
||||
class DeleteGenTableSchema(BaseModel):
|
||||
class GenTableDeleteSchema(BaseModel):
|
||||
"""
|
||||
删除代码生成业务表模型
|
||||
"""
|
||||
@@ -139,15 +116,15 @@ class DeleteGenTableSchema(BaseModel):
|
||||
table_ids: str = Field(description='需要删除的代码生成业务表ID')
|
||||
|
||||
|
||||
class GenTableColumnBaseSchema(BaseModel):
|
||||
class GenTableColumnCreateSchema(BaseModel):
|
||||
"""
|
||||
代码生成业务表字段对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
column_id: Optional[int] = Field(default=None, description='编号')
|
||||
table_id: Optional[int] = Field(default=None, description='归属表编号')
|
||||
|
||||
column_name: str = Field(default=..., description='列名称')
|
||||
column_comment: Optional[str] = Field(default=None, description='列描述')
|
||||
column_type: str = Field(default=..., description='列类型')
|
||||
@@ -166,11 +143,6 @@ class GenTableColumnBaseSchema(BaseModel):
|
||||
dict_type: str = Field(default=..., description='字典类型')
|
||||
sort: Optional[int] = Field(default=None, description='排序')
|
||||
|
||||
create_by: Optional[str] = Field(default=None, description='创建者')
|
||||
create_time: Optional[datetime] = Field(default=None, description='创建时间')
|
||||
update_by: Optional[str] = Field(default=None, description='更新者')
|
||||
update_time: Optional[datetime] = Field(default=None, description='更新时间')
|
||||
|
||||
@NotBlank(field_name='python_field', message='Python属性不能为空')
|
||||
def get_python_field(self):
|
||||
return self.python_field
|
||||
@@ -179,7 +151,7 @@ class GenTableColumnBaseSchema(BaseModel):
|
||||
self.get_python_field()
|
||||
|
||||
|
||||
class GenTableColumnSchema(GenTableColumnBaseSchema):
|
||||
class GenTableColumnUpdateSchema(GenTableColumnCreateSchema):
|
||||
"""
|
||||
代码生成业务表字段模型
|
||||
"""
|
||||
@@ -197,7 +169,7 @@ class GenTableColumnSchema(GenTableColumnBaseSchema):
|
||||
usable_column: Optional[bool] = Field(default=None, description='是否为基类字段白名单')
|
||||
|
||||
@model_validator(mode='after')
|
||||
def check_some_is(self) -> 'GenTableColumnSchema':
|
||||
def check_some_is(self) -> 'GenTableColumnUpdateSchema':
|
||||
self.cap_python_field = self.python_field[0].upper() + self.python_field[1:] if self.python_field else None
|
||||
self.pk = True if self.is_pk and self.is_pk == '1' else False
|
||||
self.increment = True if self.is_increment and self.is_increment == '1' else False
|
||||
@@ -218,11 +190,16 @@ class GenTableColumnSchema(GenTableColumnBaseSchema):
|
||||
return self
|
||||
|
||||
|
||||
class DeleteGenTableColumnSchema(BaseModel):
|
||||
class GenTableColumnOutSchema(GenTableColumnUpdateSchema, BaseSchema):
|
||||
"""响应模型"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class GenTableColumnDeleteSchema(BaseModel):
|
||||
"""
|
||||
删除代码生成业务表字段模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
column_ids: str = Field(description='需要删除的代码生成业务表字段ID')
|
||||
|
||||
@@ -17,9 +17,9 @@ 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 DeleteGenTableSchema, EditGenTableSchema, GenTableColumnSchema, GenTableSchema
|
||||
from .schema import GenTableCreateSchema, GenTableUpdateSchema, GenTableOutSchema, GenTableDeleteSchema, GenTableColumnCreateSchema, GenTableColumnUpdateSchema, GenTableColumnOutSchema, GenTableColumnDeleteSchema
|
||||
from .param import GenTableQueryParam
|
||||
from .crud import GenTableColumnDao, GenTableDao
|
||||
from .crud import GenTableColumnCRUD, GenTableCRUD
|
||||
from .model import GenTableModel, GenTableColumnModel
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class GenTableService:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_gen_table_list_services(
|
||||
async def get_gen_table_list_service(
|
||||
cls, auth: AuthSchema, query_object: GenTableQueryParam, is_page: bool = False
|
||||
):
|
||||
"""
|
||||
@@ -44,17 +44,13 @@ class GenTableService:
|
||||
:param is_page: 是否开启分页
|
||||
:return: 代码生成业务列表信息对象
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
gen_table_dao = GenTableDao(auth=auth)
|
||||
gen_table_dao = GenTableCRUD(auth=auth)
|
||||
gen_table_list_result = await gen_table_dao.get_gen_table_list(auth.db, query_object, is_page)
|
||||
|
||||
return gen_table_list_result
|
||||
|
||||
@classmethod
|
||||
async def get_gen_db_table_list_services(
|
||||
async def get_gen_db_table_list_service(
|
||||
cls, auth: AuthSchema, query_object: GenTableQueryParam, is_page: bool = False
|
||||
):
|
||||
"""
|
||||
@@ -65,17 +61,13 @@ class GenTableService:
|
||||
:param is_page: 是否开启分页
|
||||
:return: 数据库列表信息对象
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
gen_table_dao = GenTableDao(auth=auth)
|
||||
gen_table_dao = GenTableCRUD(auth=auth)
|
||||
gen_db_table_list_result = await gen_table_dao.get_gen_db_table_list(auth.db, query_object, is_page)
|
||||
|
||||
return gen_db_table_list_result
|
||||
|
||||
@classmethod
|
||||
async def get_gen_db_table_list_by_name_services(cls, auth: AuthSchema, table_names: List[str]) -> list[GenTableSchema]:
|
||||
async def get_gen_db_table_list_by_name_service(cls, auth: AuthSchema, table_names: List[str]) -> list[GenTableOutSchema]:
|
||||
"""
|
||||
根据表名称组获取数据库列表信息service
|
||||
|
||||
@@ -83,18 +75,14 @@ class GenTableService:
|
||||
:param table_names: 表名称组
|
||||
:return: 数据库列表信息对象
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
gen_table_dao = GenTableDao(auth=auth)
|
||||
gen_table_dao = GenTableCRUD(auth=auth)
|
||||
gen_db_table_list_result = await gen_table_dao.get_gen_db_table_list_by_names(auth.db, table_names)
|
||||
|
||||
return [GenTableSchema(**gen_table) for gen_table in CamelCaseUtil.transform_result(gen_db_table_list_result)]
|
||||
return [GenTableOutSchema(**gen_table) for gen_table in CamelCaseUtil.transform_result(gen_db_table_list_result)]
|
||||
|
||||
@classmethod
|
||||
async def import_gen_table_services(
|
||||
cls, auth: AuthSchema, gen_table_list: List[GenTableSchema], current_user: UserOutSchema
|
||||
async def import_gen_table_service(
|
||||
cls, auth: AuthSchema, gen_table_list: List[GenTableOutSchema], current_user: UserOutSchema
|
||||
):
|
||||
"""
|
||||
导入表结构service
|
||||
@@ -105,12 +93,8 @@ class GenTableService:
|
||||
:return: 导入结果
|
||||
"""
|
||||
try:
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
gen_table_dao = GenTableDao(auth=auth)
|
||||
gen_table_column_dao = GenTableColumnDao(auth=auth)
|
||||
gen_table_dao = GenTableCRUD(auth=auth)
|
||||
gen_table_column_dao = GenTableColumnCRUD(auth=auth)
|
||||
|
||||
for table in gen_table_list:
|
||||
table_name = table.table_name
|
||||
@@ -120,24 +104,22 @@ class GenTableService:
|
||||
table.table_id = add_gen_table.id
|
||||
gen_table_columns = await gen_table_column_dao.get_gen_db_table_columns_by_name(auth.db, table_name or "")
|
||||
for column in [
|
||||
GenTableColumnSchema(**gen_table_column)
|
||||
GenTableColumnOutSchema(**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(auth.db, AsyncSession):
|
||||
await auth.db.commit()
|
||||
await auth.db.commit()
|
||||
return SuccessResponse(msg='导入成功')
|
||||
except Exception as e:
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
raise CustomException(msg=f'导入失败, {str(e)}')
|
||||
|
||||
@classmethod
|
||||
async def edit_gen_table_services(cls, auth: AuthSchema, page_object: EditGenTableSchema) -> Dict[str, Any]:
|
||||
async def edit_gen_table_service(cls, auth: AuthSchema, page_object: GenTableUpdateSchema) -> Dict[str, Any]:
|
||||
"""
|
||||
编辑业务表信息service
|
||||
|
||||
@@ -145,30 +127,26 @@ class GenTableService:
|
||||
:param page_object: 编辑业务表对象
|
||||
:return: 编辑业务表校验结果
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
gen_table_dao = GenTableDao(auth=auth)
|
||||
gen_table_column_dao = GenTableColumnDao(auth=auth)
|
||||
gen_table_dao = GenTableCRUD(auth=auth)
|
||||
gen_table_column_dao = GenTableColumnCRUD(auth=auth)
|
||||
|
||||
# 检查必要字段是否存在
|
||||
if page_object.table_id is None:
|
||||
if getattr(page_object, 'table_id', None) is None:
|
||||
raise CustomException(msg='业务表ID不能为空')
|
||||
|
||||
edit_gen_table = page_object.model_dump(exclude_unset=True, by_alias=True)
|
||||
gen_table_info = await cls.get_gen_table_by_id_services(auth, page_object.table_id)
|
||||
gen_table_info = await cls.get_gen_table_by_id_service(auth, page_object.table_id)
|
||||
if gen_table_info.table_id:
|
||||
try:
|
||||
# 处理params字段,确保不为None
|
||||
params = edit_gen_table.get('params')
|
||||
if params is not None:
|
||||
edit_gen_table['options'] = json.dumps(params)
|
||||
else:
|
||||
# 确保options字段存在且为有效JSON
|
||||
if 'options' not in edit_gen_table or edit_gen_table['options'] is None:
|
||||
edit_gen_table['options'] = '{}' # 默认空对象
|
||||
|
||||
# 移除params字段,因为options字段已经包含了序列化的params
|
||||
edit_gen_table.pop('params', None)
|
||||
else:
|
||||
# 验证options是否为有效的JSON
|
||||
try:
|
||||
json.loads(edit_gen_table['options'])
|
||||
except json.JSONDecodeError:
|
||||
edit_gen_table['options'] = '{}'
|
||||
|
||||
await gen_table_dao.update(id=page_object.table_id, data=edit_gen_table)
|
||||
if page_object.columns:
|
||||
@@ -180,21 +158,19 @@ class GenTableService:
|
||||
id=gen_table_column.column_id,
|
||||
data=gen_table_column.model_dump(by_alias=True)
|
||||
)
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
await auth.db.commit()
|
||||
await auth.db.commit()
|
||||
return {"is_success": True, "message": "更新成功"}
|
||||
except Exception as e:
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
raise CustomException(msg=f'更新失败: {str(e)}')
|
||||
else:
|
||||
raise CustomException(msg='业务表不存在')
|
||||
|
||||
@classmethod
|
||||
async def delete_gen_table_services(cls, auth: AuthSchema, page_object: DeleteGenTableSchema) -> SuccessResponse:
|
||||
async def delete_gen_table_service(cls, auth: AuthSchema, page_object: GenTableDeleteSchema) -> SuccessResponse:
|
||||
"""
|
||||
删除业务表信息service
|
||||
|
||||
@@ -202,12 +178,8 @@ class GenTableService:
|
||||
:param page_object: 删除业务表对象
|
||||
:return: 删除业务表校验结果
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
gen_table_dao = GenTableDao(auth=auth)
|
||||
gen_table_column_dao = GenTableColumnDao(auth=auth)
|
||||
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(',')
|
||||
@@ -220,21 +192,19 @@ class GenTableService:
|
||||
if columns:
|
||||
column_ids = [column.id for column in columns]
|
||||
await gen_table_column_dao.delete(ids=column_ids)
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
await auth.db.commit()
|
||||
await auth.db.commit()
|
||||
return SuccessResponse(msg='删除成功')
|
||||
except Exception as e:
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
raise CustomException(msg=f'删除失败: {str(e)}')
|
||||
else:
|
||||
raise CustomException(msg='传入业务表id为空')
|
||||
|
||||
@classmethod
|
||||
async def get_gen_table_by_id_services(cls, auth: AuthSchema, table_id: int) -> GenTableSchema:
|
||||
async def get_gen_table_by_id_service(cls, auth: AuthSchema, table_id: int) -> GenTableOutSchema:
|
||||
"""
|
||||
获取需要生成的业务表详细信息service
|
||||
|
||||
@@ -242,38 +212,32 @@ class GenTableService:
|
||||
:param table_id: 需要生成的业务表id
|
||||
:return: 需要生成的业务表id对应的信息
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
gen_table_dao = GenTableDao(auth=auth)
|
||||
gen_table_dao = GenTableCRUD(auth=auth)
|
||||
gen_table = await gen_table_dao.get_gen_table_by_id(auth.db, table_id)
|
||||
if gen_table:
|
||||
result = await cls.set_table_from_options(GenTableSchema(**CamelCaseUtil.transform_result(gen_table)))
|
||||
result = await cls.set_table_from_options(GenTableOutSchema(**CamelCaseUtil.transform_result(gen_table)))
|
||||
return result
|
||||
else:
|
||||
raise CustomException(msg='业务表不存在')
|
||||
|
||||
@classmethod
|
||||
async def get_gen_table_all_services(cls, auth: AuthSchema) -> list[GenTableSchema]:
|
||||
async def get_gen_table_all_service(cls, auth: AuthSchema) -> list[GenTableOutSchema]:
|
||||
"""
|
||||
获取所有业务表信息service
|
||||
|
||||
:param auth: 认证信息
|
||||
:return: 所有业务表信息
|
||||
:return: 所有业务表信息列表
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
gen_table_dao = GenTableDao(auth=auth)
|
||||
gen_table_all = await gen_table_dao.get_gen_table_all(auth.db)
|
||||
result = [GenTableSchema(**gen_table) for gen_table in CamelCaseUtil.transform_result(gen_table_all)]
|
||||
|
||||
gen_table_dao = GenTableCRUD(auth=auth)
|
||||
gen_tables = await gen_table_dao.get_gen_table_all(auth.db)
|
||||
result = []
|
||||
for table in gen_tables:
|
||||
table_info = await cls.set_table_from_options(GenTableOutSchema(**CamelCaseUtil.transform_result(table)))
|
||||
result.append(table_info)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def create_table_services(cls, auth: AuthSchema, sql: str, current_user: UserOutSchema) -> SuccessResponse:
|
||||
async def create_table_service(cls, auth: AuthSchema, sql: str, current_user: UserOutSchema) -> SuccessResponse:
|
||||
"""
|
||||
创建表结构service
|
||||
|
||||
@@ -282,28 +246,22 @@ class GenTableService:
|
||||
:param current_user: 当前用户信息对象
|
||||
:return: 创建表结构结果
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
gen_table_dao = GenTableDao(auth=auth)
|
||||
gen_table_dao = GenTableCRUD(auth=auth)
|
||||
|
||||
try:
|
||||
# 执行SQL语句创建表
|
||||
await gen_table_dao.create_table_by_sql_dao(auth.db, [sql])
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
await auth.db.commit()
|
||||
await auth.db.commit()
|
||||
return SuccessResponse(msg='创建表结构成功')
|
||||
except Exception as e:
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
raise CustomException(msg=f'创建表结构失败: {str(e)}')
|
||||
|
||||
@classmethod
|
||||
async def preview_code_services(cls, auth: AuthSchema, table_id: int) -> dict[Any, Any]:
|
||||
async def preview_code_service(cls, auth: AuthSchema, table_id: int) -> dict[Any, Any]:
|
||||
"""
|
||||
预览代码service
|
||||
|
||||
@@ -311,7 +269,7 @@ class GenTableService:
|
||||
:param table_id: 业务表id
|
||||
:return: 预览数据列表
|
||||
"""
|
||||
gen_table = await cls.get_gen_table_by_id_services(auth, table_id)
|
||||
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()
|
||||
@@ -327,7 +285,7 @@ class GenTableService:
|
||||
return preview_code_result
|
||||
|
||||
@classmethod
|
||||
async def generate_code_services(cls, auth: AuthSchema, table_name: str) -> SuccessResponse:
|
||||
async def generate_code_service(cls, auth: AuthSchema, table_name: str) -> SuccessResponse:
|
||||
"""
|
||||
生成代码至指定路径service
|
||||
|
||||
@@ -351,7 +309,7 @@ class GenTableService:
|
||||
return SuccessResponse(msg='生成代码成功')
|
||||
|
||||
@classmethod
|
||||
async def batch_gen_code_services(cls, auth: AuthSchema, table_names: List[str]) -> bytes:
|
||||
async def batch_gen_code_service(cls, auth: AuthSchema, table_names: List[str]) -> bytes:
|
||||
"""
|
||||
批量生成代码service
|
||||
|
||||
@@ -381,14 +339,11 @@ class GenTableService:
|
||||
:param table_name: 业务表名称
|
||||
:return: 生成代码渲染模板相关信息
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
gen_table_dao = GenTableDao(auth=auth)
|
||||
gen_table_dao = GenTableCRUD(auth=auth)
|
||||
gen_table = await gen_table_dao.get_gen_table_by_name(auth.db, table_name)
|
||||
if gen_table:
|
||||
gen_table_schema = GenTableSchema(**CamelCaseUtil.transform_result(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)
|
||||
@@ -403,7 +358,7 @@ class GenTableService:
|
||||
raise CustomException(msg=f'业务表 {table_name} 不存在')
|
||||
|
||||
@classmethod
|
||||
def __get_gen_path(cls, gen_table: GenTableSchema, template: str) -> Optional[str]:
|
||||
def __get_gen_path(cls, gen_table: GenTableOutSchema, template: str) -> Optional[str]:
|
||||
"""
|
||||
根据GenTableModel对象和模板名称生成路径
|
||||
|
||||
@@ -423,7 +378,7 @@ class GenTableService:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def sync_db_services(cls, auth: AuthSchema, table_name: str) -> SuccessResponse:
|
||||
async def sync_db_service(cls, auth: AuthSchema, table_name: str) -> SuccessResponse:
|
||||
"""
|
||||
同步数据库service
|
||||
|
||||
@@ -431,21 +386,17 @@ class GenTableService:
|
||||
:param table_name: 业务表名称
|
||||
:return: 同步数据库结果
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
gen_table_dao = GenTableDao(auth=auth)
|
||||
gen_table_column_dao = GenTableColumnDao(auth=auth)
|
||||
gen_table_dao = GenTableCRUD(auth=auth)
|
||||
gen_table_column_dao = GenTableColumnCRUD(auth=auth)
|
||||
|
||||
gen_table = await gen_table_dao.get_gen_table_by_name(auth.db, table_name)
|
||||
if gen_table:
|
||||
table = GenTableSchema(**CamelCaseUtil.transform_result(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(auth.db, table_name)
|
||||
db_table_columns = [
|
||||
GenTableColumnSchema(**column) for column in CamelCaseUtil.transform_result(query_db_table_columns)
|
||||
GenTableColumnOutSchema(**column) for column in CamelCaseUtil.transform_result(query_db_table_columns)
|
||||
]
|
||||
if not db_table_columns:
|
||||
raise CustomException('同步数据失败,原表结构不存在')
|
||||
@@ -476,21 +427,19 @@ class GenTableService:
|
||||
for column in del_columns:
|
||||
if column.column_id is not None:
|
||||
await gen_table_column_dao.delete(ids=[column.column_id])
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
await auth.db.commit()
|
||||
await auth.db.commit()
|
||||
return SuccessResponse(msg='同步成功')
|
||||
except Exception as e:
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
raise CustomException(msg=f'同步失败: {str(e)}')
|
||||
else:
|
||||
raise CustomException('业务表不存在')
|
||||
|
||||
@classmethod
|
||||
async def set_sub_table(cls, auth: AuthSchema, gen_table: GenTableSchema) -> None:
|
||||
async def set_sub_table(cls, auth: AuthSchema, gen_table: GenTableOutSchema) -> None:
|
||||
"""
|
||||
设置主子表信息
|
||||
|
||||
@@ -498,18 +447,15 @@ class GenTableService:
|
||||
:param gen_table: 业务表信息
|
||||
:return:
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
if gen_table.sub_table_name:
|
||||
gen_table_dao = GenTableDao(auth=auth)
|
||||
gen_table_dao = GenTableCRUD(auth=auth)
|
||||
sub_table = await gen_table_dao.get_gen_table_by_name(auth.db, gen_table.sub_table_name)
|
||||
if sub_table:
|
||||
gen_table.sub_table = GenTableSchema(**CamelCaseUtil.transform_result(sub_table))
|
||||
gen_table.sub_table = GenTableOutSchema(**CamelCaseUtil.transform_result(sub_table))
|
||||
|
||||
@classmethod
|
||||
async def _set_pk_column(cls, gen_table: GenTableSchema) -> None:
|
||||
async def _set_pk_column(cls, gen_table: GenTableOutSchema) -> None:
|
||||
"""
|
||||
设置主键列信息
|
||||
|
||||
@@ -533,7 +479,7 @@ class GenTableService:
|
||||
gen_table.sub_table.pk_column = gen_table.sub_table.columns[0]
|
||||
|
||||
@classmethod
|
||||
async def set_table_from_options(cls, gen_table: GenTableSchema) -> GenTableSchema:
|
||||
async def set_table_from_options(cls, gen_table: GenTableOutSchema) -> GenTableOutSchema:
|
||||
"""
|
||||
设置代码生成其他选项值
|
||||
|
||||
@@ -551,18 +497,18 @@ class GenTableService:
|
||||
return gen_table
|
||||
|
||||
@classmethod
|
||||
async def validate_edit(cls, edit_gen_table: EditGenTableSchema):
|
||||
async def validate_edit(cls, edit_gen_table: GenTableUpdateSchema):
|
||||
"""
|
||||
编辑保存参数校验
|
||||
|
||||
:param edit_gen_table: 编辑业务表对象
|
||||
"""
|
||||
if edit_gen_table.tpl_category == GenConstant.TPL_TREE:
|
||||
# 检查params是否为None
|
||||
if edit_gen_table.params is None:
|
||||
# 从options字段获取参数,而不是params
|
||||
if not edit_gen_table.options:
|
||||
raise CustomException(msg='树表参数不能为空')
|
||||
|
||||
params_obj = edit_gen_table.params.model_dump(by_alias=True)
|
||||
params_obj = json.loads(edit_gen_table.options)
|
||||
|
||||
if GenConstant.TREE_CODE not in params_obj:
|
||||
raise CustomException(msg='树编码字段不能为空')
|
||||
@@ -583,7 +529,7 @@ class GenTableColumnService:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_gen_table_column_list_by_table_id_services(cls, auth: AuthSchema, table_id: int):
|
||||
async def get_gen_table_column_list_by_table_id_service(cls, auth: AuthSchema, table_id: int) -> List[GenTableColumnOutSchema]:
|
||||
"""
|
||||
获取业务表字段列表信息service
|
||||
|
||||
@@ -591,14 +537,10 @@ class GenTableColumnService:
|
||||
:param table_id: 业务表格id
|
||||
:return: 业务表字段列表信息对象
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
gen_table_column_dao = GenTableColumnDao(auth=auth)
|
||||
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(auth.db, table_id)
|
||||
|
||||
return [
|
||||
GenTableColumnSchema(**gen_table_column)
|
||||
GenTableColumnOutSchema(**gen_table_column)
|
||||
for gen_table_column in CamelCaseUtil.transform_result(gen_table_column_list_result)
|
||||
]
|
||||
@@ -1,94 +0,0 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from fastapi import APIRouter, Depends, Form
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from starlette.requests import Request
|
||||
from typing import List
|
||||
from app.common.enums import BusinessType
|
||||
from app.core.dependencies import get_db
|
||||
from app.common.response import SuccessResponse
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.api.v1.module_system.user.schema import UserOutSchema
|
||||
from app.utils.common_util import bytes2file_response
|
||||
|
||||
from {{ packageName }}.entity.vo.{{ tableName }}_vo import {{ tableName|snake_to_pascal_case }}PageModel, {{ tableName|snake_to_pascal_case }}Model
|
||||
from {{ packageName }}.service.{{ tableName }}_service import {{ tableName|snake_to_pascal_case }}Service
|
||||
|
||||
{{ tableName|snake_to_camel }}Controller = APIRouter(prefix='/{{ moduleName }}/{{ businessName }}', tags=["{{ functionName }}模块"])
|
||||
|
||||
|
||||
@{{ tableName|snake_to_camel }}Controller.get('/list', summary="查询{{ functionName }}列表", description="查询{{ functionName }}列表")
|
||||
async def get_{{ tableName }}_list(
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:list"])),
|
||||
page_query: {{ tableName|snake_to_pascal_case }}PageModel = Depends({{ tableName|snake_to_pascal_case }}PageModel.as_query)
|
||||
):
|
||||
{{ tableName }}_result = await {{ tableName|snake_to_pascal_case }}Service.get_{{ tableName }}_list_services(auth, page_query)
|
||||
|
||||
return SuccessResponse(data={{ tableName }}_result)
|
||||
|
||||
|
||||
@{{ 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"]))
|
||||
):
|
||||
{{ tableName }} = await {{ tableName|snake_to_pascal_case }}Service.get_{{ tableName }}_by_id_services(auth, id)
|
||||
return SuccessResponse(data={{ tableName }})
|
||||
|
||||
|
||||
@{{ tableName|snake_to_camel }}Controller.post('', summary="新增{{ functionName }}", description="新增{{ functionName }}")
|
||||
async def add_{{ tableName }} (
|
||||
add_model: {{ tableName|snake_to_pascal_case }}Model,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:add"])),
|
||||
current_user: UserOutSchema = Depends(lambda auth: auth.user)
|
||||
):
|
||||
add_model.create_by = current_user.username
|
||||
add_result = await {{ tableName|snake_to_pascal_case }}Service.add_{{ tableName }}_services(auth, add_model)
|
||||
return SuccessResponse(msg="新增成功")
|
||||
|
||||
|
||||
@{{ tableName|snake_to_camel }}Controller.put('', summary="修改{{ functionName }}", description="修改{{ functionName }}")
|
||||
async def update_{{ tableName }}(
|
||||
edit_model: {{ tableName|snake_to_pascal_case }}Model,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:edit"])),
|
||||
current_user: UserOutSchema = Depends(lambda auth: auth.user)
|
||||
):
|
||||
edit_model.update_by = current_user.username
|
||||
update_result = await {{ tableName|snake_to_pascal_case }}Service.update_{{ tableName }}_services(auth, edit_model)
|
||||
return SuccessResponse(msg="修改成功")
|
||||
|
||||
|
||||
@{{ tableName|snake_to_camel }}Controller.delete('/{ids}', summary="删除{{ functionName }}", description="删除{{ functionName }}")
|
||||
async def del_{{ tableName }}(
|
||||
ids: str,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:remove"]))
|
||||
):
|
||||
id_list = ids.split(',')
|
||||
del_result = await {{ tableName|snake_to_pascal_case }}Service.del_{{ tableName }}_services(auth, id_list)
|
||||
return SuccessResponse(msg="删除成功")
|
||||
|
||||
|
||||
@{{ tableName|snake_to_camel }}Controller.post('/export', summary="导出{{ functionName }}", description="导出{{ functionName }}")
|
||||
async def export_{{ tableName }}(
|
||||
{{ tableName }}_form: {{ tableName|snake_to_pascal_case }}PageModel = Form(),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:export"]))
|
||||
):
|
||||
# 获取全量数据
|
||||
export_result = await {{ tableName|snake_to_pascal_case }}Service.export_{{ tableName }}_list_services(
|
||||
auth, {{ tableName }}_form
|
||||
)
|
||||
return bytes2file_response(export_result)
|
||||
|
||||
|
||||
@{{ tableName|snake_to_camel }}Controller.post('/import', dependencies=[Depends(CheckUserInterfaceAuth('{{ permissionPrefix }}:import'))])
|
||||
async def import_{{ tableName }}(request: Request,
|
||||
import_model: ImportModel,
|
||||
query_db: AsyncSession = Depends(get_db),
|
||||
current_user: CurrentUserModel = Depends(LoginService.get_current_user)
|
||||
):
|
||||
"""
|
||||
导入数据
|
||||
"""
|
||||
await ImportService.import_data(query_db, import_model, current_user)
|
||||
return ResponseUtil.success()
|
||||
@@ -1,185 +0,0 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from typing import List, Optional
|
||||
from sqlalchemy import delete, func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
from {{ packageName }}.entity.do.{{ tableName }}_do import {{ tableName|snake_to_pascal_case }}Model
|
||||
from {{ packageName }}.entity.vo.{{ tableName }}_vo import {{ tableName|snake_to_pascal_case }}PageModel, {{ tableName|snake_to_pascal_case }}Model
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from typing import List
|
||||
from datetime import datetime, time
|
||||
from module_admin.entity.do.role_do import SysRoleDept
|
||||
from sqlalchemy import and_, delete, desc, func, or_, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from module_gen.constants.gen_constants import GenConstants
|
||||
{% if subTable %}
|
||||
from sqlalchemy.orm import selectinload
|
||||
{% endif %}
|
||||
from {{ packageName }}.entity.do.{{ tableName }}_do import {{ tableName|snake_to_pascal_case }}
|
||||
from {{ packageName }}.entity.vo.{{ tableName }}_vo import {{ tableName|snake_to_pascal_case }}PageModel, {{ tableName|snake_to_pascal_case }}Model
|
||||
from utils.page_util import PageUtil, PageResponseModel
|
||||
from utils.common_util import CamelCaseUtil
|
||||
|
||||
|
||||
class {{ tableName|snake_to_pascal_case }}CRUD:
|
||||
|
||||
@classmethod
|
||||
async def get_by_id(cls, db: AsyncSession, {{ tableName }}_id: int) -> {{ tableName|snake_to_pascal_case }}:
|
||||
"""根据主键获取单条记录"""
|
||||
{{ tableName }} = (((await db.execute(
|
||||
select({{ tableName|snake_to_pascal_case }})
|
||||
.where({{ tableName|snake_to_pascal_case }}.id == {{ tableName }}_id)))
|
||||
.scalars())
|
||||
.first())
|
||||
return {{ tableName }}
|
||||
|
||||
"""
|
||||
查询
|
||||
"""
|
||||
@classmethod
|
||||
async def get_{{ tableName }}_list(cls, db: AsyncSession,
|
||||
query_object: {{ tableName|snake_to_pascal_case }}PageModel,
|
||||
data_scope_sql: str = None,
|
||||
is_page: bool = False) -> [list | PageResponseModel]:
|
||||
|
||||
query = (
|
||||
select({{ tableName|snake_to_pascal_case }})
|
||||
{% if subTable %}
|
||||
.options(selectinload({{ tableName|snake_to_pascal_case }}.{{ subTable.table_name }}_list))
|
||||
{% endif %}
|
||||
.where(
|
||||
{% for column in columns %}
|
||||
{% if column.isQuery == "1" %}
|
||||
{% if column.queryType == "LIKE" %}
|
||||
{{ tableName|snake_to_pascal_case }}.{{ column.columnName }}.like(f"%{query_object.{{ column.columnName }}}%") if query_object.{{ column.columnName }} else True,
|
||||
{% elif column.queryType == "EQ" %}
|
||||
{{ tableName|snake_to_pascal_case }}.{{ column.columnName }} == query_object.{{ column.columnName }} if query_object.{{ column.columnName }} else True,
|
||||
{% elif column.queryType == "GT" %}
|
||||
{{ tableName|snake_to_pascal_case }}.{{ column.columnName }} > query_object.{{ column.columnName }} if query_object.{{ column.columnName }} else True,
|
||||
{% elif column.queryType == "GTE" %}
|
||||
{{ tableName|snake_to_pascal_case }}.{{ column.columnName }} >= query_object.{{ column.columnName }} if query_object.{{ column.columnName }} else True,
|
||||
{% elif column.queryType == "NE" %}
|
||||
{{ tableName|snake_to_pascal_case }}.{{ column.columnName }} != query_object.{{ column.columnName }} if query_object.{{ column.columnName }} else True,
|
||||
{% elif column.queryType == "LT" %}
|
||||
{{ tableName|snake_to_pascal_case }}.{{ column.columnName }} < query_object.{{ column.columnName }} if query_object.{{ column.columnName }} else True,
|
||||
{% elif column.queryType == "LTE" %}
|
||||
{{ tableName|snake_to_pascal_case }}.{{ column.columnName }} <= query_object.{{ column.columnName }} if query_object.{{ column.columnName }} else True,
|
||||
{% elif column.queryType == "BETWEEN" %}
|
||||
{{ tableName|snake_to_pascal_case }}.{{ column.columnName }}.between(query_object.begin_{{ column.columnName }}, query_object.end_{{ column.columnName }}) if query_object.{{ column.columnName }} else True,
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{{ tableName|snake_to_pascal_case }}.del_flag == '0',
|
||||
eval(data_scope_sql) if data_scope_sql else True,
|
||||
)
|
||||
.order_by(desc({{ tableName|snake_to_pascal_case }}.create_time))
|
||||
.distinct()
|
||||
)
|
||||
{{ tableName }}_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
|
||||
return {{ tableName }}_list
|
||||
|
||||
|
||||
@classmethod
|
||||
async def add_{{ tableName }}(cls, db: AsyncSession, add_model: {{ tableName|snake_to_pascal_case }}Model, auto_commit: bool = True) -> {{ tableName|snake_to_pascal_case }}Model:
|
||||
"""
|
||||
增加
|
||||
"""
|
||||
{{ tableName }} = {{ tableName|snake_to_pascal_case }}(**add_model.model_dump(exclude_unset=True, {% if subTable %}exclude={'{{ subTable.table_name }}_list',}{% endif %}))
|
||||
db.add({{ tableName }})
|
||||
await db.flush()
|
||||
{{ tableName }}_model = {{ tableName|snake_to_pascal_case }}Model(**CamelCaseUtil.transform_result({{ tableName }}))
|
||||
if auto_commit:
|
||||
await db.commit()
|
||||
return {{ tableName }}_model
|
||||
|
||||
@classmethod
|
||||
async def edit_{{ tableName }}(cls, db: AsyncSession, edit_model: {{ tableName|snake_to_pascal_case }}Model, auto_commit: bool = True) -> {{ tableName|snake_to_pascal_case }}:
|
||||
"""
|
||||
修改
|
||||
"""
|
||||
edit_dict_data = edit_model.model_dump(exclude_unset=True, exclude={ {% if subTable %}'{{ subTable.table_name }}_list', {% endif %}*GenConstants.DAO_COLUMN_NOT_EDIT })
|
||||
await db.execute(update({{ tableName|snake_to_pascal_case }}), [edit_dict_data])
|
||||
await db.flush()
|
||||
if auto_commit:
|
||||
await db.commit()
|
||||
return await cls.get_by_id(db, edit_model.{{ pkColumn.pythonField }})
|
||||
|
||||
@classmethod
|
||||
async def del_{{ tableName }}(cls, db: AsyncSession, {{ tableName }}_ids: List[str], soft_del: bool = True, auto_commit: bool = True):
|
||||
"""
|
||||
删除
|
||||
"""
|
||||
if soft_del:
|
||||
await db.execute(update({{ tableName|snake_to_pascal_case }}).where({{ tableName|snake_to_pascal_case }}.id.in_({{ tableName }}_ids)).values(del_flag='2'))
|
||||
else:
|
||||
await db.execute(delete({{ tableName|snake_to_pascal_case }}).where({{ tableName|snake_to_pascal_case }}.id.in_({{ tableName }}_ids)))
|
||||
await db.flush()
|
||||
if auto_commit:
|
||||
await db.commit()
|
||||
|
||||
|
||||
class {{ tableName|snake_to_pascal_case }}Dao(CRUDBase[{{ tableName|snake_to_pascal_case }}Model, {{ tableName|snake_to_pascal_case }}Model, {{ tableName|snake_to_pascal_case }}Model]):
|
||||
"""
|
||||
{{ functionName }}模块数据库操作层
|
||||
"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
"""初始化CRUD"""
|
||||
super().__init__(model={{ tableName|snake_to_pascal_case }}Model(), auth=auth)
|
||||
|
||||
async def get_{{ tableName }}_by_id(self, db: AsyncSession, {{ tableName }}_id: int) -> Optional[{{ tableName|snake_to_pascal_case }}Model]:
|
||||
"""
|
||||
根据{{ tableName }}id获取{{ functionName }}信息
|
||||
|
||||
:param db: orm对象
|
||||
:param {{ tableName }}_id: {{ tableName }}id
|
||||
:return: {{ functionName }}信息对象
|
||||
"""
|
||||
{{ tableName }}_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select({{ tableName|snake_to_pascal_case }}Model)
|
||||
.where({{ tableName|snake_to_pascal_case }}Model.id == {{ tableName }}_id)
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return {{ tableName }}_info
|
||||
|
||||
async def get_{{ tableName }}_list(self, db: AsyncSession, query_object: {{ tableName|snake_to_pascal_case }}PageModel, is_page: bool = False):
|
||||
"""
|
||||
根据查询参数获取{{ functionName }}列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: {{ functionName }}列表信息对象
|
||||
"""
|
||||
query = select({{ tableName|snake_to_pascal_case }}Model)
|
||||
|
||||
# 执行查询
|
||||
result = await db.execute(query)
|
||||
all_data = list(result.scalars().all())
|
||||
|
||||
# 使用PaginationService.paginate进行分页
|
||||
if is_page:
|
||||
paginated_result = await PaginationService.paginate(
|
||||
data_list=all_data,
|
||||
page_no=query_object.page_no,
|
||||
page_size=query_object.page_size
|
||||
)
|
||||
return paginated_result
|
||||
else:
|
||||
return {
|
||||
"items": all_data,
|
||||
"total": len(all_data),
|
||||
"page_no": None,
|
||||
"page_size": None,
|
||||
"has_next": False
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from sqlalchemy import String, Integer, Text, DateTime
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base_model import Base
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, {{ importList }}
|
||||
from config.database import BaseMixin, Base
|
||||
{% if subTable %}
|
||||
from sqlalchemy.orm import relationship
|
||||
{% endif %}
|
||||
|
||||
class {{ tableName|snake_to_pascal_case }}Model(Base, BaseMixin):
|
||||
"""
|
||||
{{ functionName }}表
|
||||
"""
|
||||
__tablename__ = "{{ tableName }}"
|
||||
|
||||
{% for column in columns %}
|
||||
{% if not column.columnName | is_base_column %}
|
||||
{{ column.columnName }} = Column({{ column.columnType|get_sqlalchemy_type }}, {{ column | get_column_options }})
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if subTable %}
|
||||
{{ subTable.table_name }}_list = relationship('{{ subClassName }}', back_populates='{{ tableName }}')
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if subTable %}
|
||||
class {{ subClassName }}(Base, BaseMixin):
|
||||
"""
|
||||
{{ functionName }}表
|
||||
"""
|
||||
__tablename__ = '{{ subTableName }}'
|
||||
{% for column in subTable.columns %}
|
||||
{% if not column.column_name | is_base_column %}
|
||||
{{ column.column_name }} = Column({{ column.column_type | get_sqlalchemy_type }}, {% if column.column_name == subTableFkName %}ForeignKey('{{ tableName }}.id'), {% endif %}{% if column.pk %}primary_key=True, {% endif %}{% if column.increment %}autoincrement=True, {% endif %}{% if column.required %}nullable=True{% else %}nullable=False{% endif %}, comment='{{ column.column_comment }}')
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if subTable %}
|
||||
{{ tableName }} = relationship('{{ ClassName }}', back_populates='{{ subTable.table_name }}_list')
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
|
||||
class {{ tableName|snake_to_pascal_case }}Model(Base):
|
||||
"""
|
||||
{{ functionName }}
|
||||
"""
|
||||
|
||||
__tablename__ = '{{ tableName }}'
|
||||
|
||||
__table_args__ = {'comment': '{{ functionName }}'}
|
||||
|
||||
{% for column in columns %}
|
||||
{{ column.columnName }}: Mapped[Optional[{{ column.pythonType }}]] = mapped_column({{ column.columnType|get_sqlalchemy_type }}, nullable=True, comment='{{ column.columnComment }}')
|
||||
{% endfor %}
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{{ tableName|snake_to_pascal_case }}Model(id={self.id})>"
|
||||
@@ -1,35 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from fastapi import Query
|
||||
|
||||
from app.core.validator import DateTimeStr
|
||||
|
||||
class {{ tableName|snake_to_pascal_case }}QueryParam:
|
||||
"""示例查询参数"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: Optional[str] = Query(None, description="名称"),
|
||||
status: Optional[bool] = Query(None, description="是否启用"),
|
||||
creator: Optional[int] = Query(None, description="创建人"),
|
||||
start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2023-01-01 00:00:00"),
|
||||
end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2023-12-31 23:59:59"),
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
# 模糊查询字段
|
||||
self.name = ("like", name)
|
||||
|
||||
# 精确查询字段
|
||||
self.creator_id = creator
|
||||
self.status = status
|
||||
|
||||
# 时间范围查询
|
||||
if start_time and end_time:
|
||||
start_datetime = datetime.strptime(str(start_time), '%Y-%m-%d %H:%M:%S')
|
||||
end_datetime = datetime.strptime(str(end_time), '%Y-%m-%d %H:%M:%S')
|
||||
self.created_at = ("between", (start_datetime, end_datetime))
|
||||
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
|
||||
from app.core.base_params import PageBaseParam
|
||||
from app.core.base_schema import PageBaseSchema
|
||||
# -*- coding:utf-8 -*-
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
from typing import List, Literal, Optional, Union
|
||||
from module_admin.annotation.pydantic_annotation import as_query
|
||||
|
||||
|
||||
class {{ tableName|snake_to_pascal_case }}{% if subTable %}Base{% endif %}Model(BaseModel):
|
||||
"""
|
||||
表对应pydantic模型
|
||||
"""
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
{% for column in columns %}
|
||||
{{ column.columnName }}: Optional[{{ column.pythonType }}] = Field(default=None, description='{{ column.columnComment }}')
|
||||
{% if column.queryType == 'BETWEEN' %}
|
||||
begin_{{ column.columnName }}: Optional[{{ column.pythonType }}] = Field(default=None, description='{{ column.columnComment }}最小值')
|
||||
{% endif %}
|
||||
{% if column.queryType == 'BETWEEN' %}
|
||||
end_{{ column.columnName }}: Optional[{{ column.pythonType }}] = Field(default=None, description='{{ column.columnComment }}最大值')
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% if subTable %}
|
||||
class {{ tableName|snake_to_pascal_case }}Model({{ tableName|snake_to_pascal_case }}BaseModel):
|
||||
{{ subTableName }}_list: Optional[List['{{ subTable.table_name | snake_to_pascal_case }}Model']] = Field(default=None, description='子表列信息')
|
||||
{% endif %}
|
||||
|
||||
@as_query
|
||||
class {{ tableName|snake_to_pascal_case }}PageModel({{ tableName|snake_to_pascal_case }}{% if subTable %}Base{% endif %}Model):
|
||||
"""
|
||||
分页查询模型
|
||||
"""
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
{% if subTable %}
|
||||
class {{ subTable.table_name | snake_to_pascal_case }}Schema(BaseModel):
|
||||
"""
|
||||
{{ subTable.function_name }}表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
{% for sub_column in subTable.columns %}
|
||||
{{ sub_column.column_name }}: Optional[{{ sub_column.python_type }}] = Field(default=None, description='{{ sub_column.column_comment}}')
|
||||
{% endfor %}
|
||||
|
||||
{% for sub_column in subTable.columns %}
|
||||
{% if sub_column.required %}
|
||||
{% set parentheseIndex = sub_column.column_comment.find("(") %}
|
||||
{% set comment = sub_column.column_comment[:parentheseIndex] if parentheseIndex != -1 else sub_column.column_comment %}
|
||||
@NotBlank(field_name='{{ sub_column.column_name }}', message='{{ comment }}不能为空')
|
||||
def get_{{ sub_column.column_name }}(self):
|
||||
return self.{{ sub_column.column_name }}
|
||||
{% if not loop.last %}{{ "\n" }}{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
class {{ tableName|snake_to_pascal_case }}BaseModel(BaseModel):
|
||||
"""
|
||||
{{ functionName }}对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
{% for column in columns %}
|
||||
{{ column.columnName }}: Optional[{{ column.pythonType }}] = Field(default=None, description='{{ column.columnComment }}')
|
||||
{% endfor %}
|
||||
|
||||
|
||||
class {{ tableName|snake_to_pascal_case }}Model({{ tableName|snake_to_pascal_case }}BaseModel):
|
||||
"""
|
||||
{{ functionName }}表模型
|
||||
"""
|
||||
|
||||
id: Optional[int] = Field(default=None, description='编号')
|
||||
create_by: Optional[str] = Field(default=None, description='创建者')
|
||||
create_time: Optional[datetime] = Field(default=None, description='创建时间')
|
||||
update_by: Optional[str] = Field(default=None, description='更新者')
|
||||
update_time: Optional[datetime] = Field(default=None, description='更新时间')
|
||||
remark: Optional[str] = Field(default=None, description='备注')
|
||||
|
||||
|
||||
class {{ tableName|snake_to_pascal_case }}PageModel(PageBaseParam, {{ tableName|snake_to_pascal_case }}BaseModel):
|
||||
"""
|
||||
{{ functionName }}分页查询模型
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class {{ tableName|snake_to_pascal_case }}PageObject(PageBaseSchema, {{ tableName|snake_to_pascal_case }}Model):
|
||||
"""
|
||||
{{ functionName }}分页查询结果模型
|
||||
"""
|
||||
pass
|
||||
@@ -1,244 +0,0 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import Any, List, Dict, Optional
|
||||
|
||||
from app.core.exceptions import CustomException
|
||||
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 {{ packageName }}.entity.vo.{{ tableName }}_vo import {{ tableName|snake_to_pascal_case }}PageModel, {{ tableName|snake_to_pascal_case }}Model
|
||||
from {{ packageName }}.dao.{{ tableName }}_dao import {{ tableName|snake_to_pascal_case }}Dao
|
||||
|
||||
|
||||
class {{ tableName|snake_to_pascal_case }}Service:
|
||||
"""
|
||||
{{ functionName }}服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_{{ tableName }}_list_services(
|
||||
cls, auth: AuthSchema, query_object: {{ tableName|snake_to_pascal_case }}PageModel
|
||||
):
|
||||
"""
|
||||
获取{{ functionName }}列表信息service
|
||||
|
||||
:param auth: 认证信息
|
||||
:param query_object: 查询参数对象
|
||||
:return: {{ functionName }}列表信息对象
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
{{ tableName }}_dao = {{ tableName|snake_to_pascal_case }}Dao(auth=auth)
|
||||
{{ tableName }}_list_result = await {{ tableName }}_dao.get_{{ tableName }}_list(auth.db, query_object, is_page=True)
|
||||
|
||||
return {{ tableName }}_list_result
|
||||
|
||||
@classmethod
|
||||
async def get_{{ tableName }}_by_id_services(cls, auth: AuthSchema, {{ tableName }}_id: int) -> {{ tableName|snake_to_pascal_case }}Model:
|
||||
"""
|
||||
根据{{ tableName }}id获取{{ functionName }}信息service
|
||||
|
||||
:param auth: 认证信息
|
||||
:param {{ tableName }}_id: {{ tableName }}id
|
||||
:return: {{ functionName }}信息对象
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
{{ tableName }}_dao = {{ tableName|snake_to_pascal_case }}Dao(auth=auth)
|
||||
{{ tableName }} = await {{ tableName }}_dao.get_{{ tableName }}_by_id(auth.db, {{ tableName }}_id)
|
||||
if {{ tableName }}:
|
||||
return {{ tableName }}
|
||||
else:
|
||||
raise CustomException(msg='{{ functionName }}不存在')
|
||||
|
||||
@classmethod
|
||||
async def add_{{ tableName }}_services(
|
||||
cls, auth: AuthSchema, page_object: {{ tableName|snake_to_pascal_case }}Model
|
||||
) -> SuccessResponse:
|
||||
"""
|
||||
新增{{ functionName }}service
|
||||
|
||||
:param auth: 认证信息
|
||||
:param page_object: 新增{{ functionName }}对象
|
||||
:return: 新增{{ functionName }}结果
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
{{ tableName }}_dao = {{ tableName|snake_to_pascal_case }}Dao(auth=auth)
|
||||
|
||||
try:
|
||||
page_object.create_time = datetime.now()
|
||||
await {{ tableName }}_dao.create(data=page_object.model_dump())
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
await auth.db.commit()
|
||||
return SuccessResponse(msg='新增成功')
|
||||
except Exception as e:
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
raise CustomException(msg=f'新增失败: {str(e)}')
|
||||
|
||||
@classmethod
|
||||
async def update_{{ tableName }}_services(cls, auth: AuthSchema, page_object: {{ tableName|snake_to_pascal_case }}Model) -> SuccessResponse:
|
||||
"""
|
||||
编辑{{ functionName }}service
|
||||
|
||||
:param auth: 认证信息
|
||||
:param page_object: 编辑{{ functionName }}对象
|
||||
:return: 编辑{{ functionName }}结果
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
{{ tableName }}_dao = {{ tableName|snake_to_pascal_case }}Dao(auth=auth)
|
||||
|
||||
# 检查必要字段是否存在
|
||||
if page_object.id is None:
|
||||
raise CustomException(msg='{{ functionName }}ID不能为空')
|
||||
|
||||
edit_{{ tableName }} = page_object.model_dump(exclude_unset=True)
|
||||
{{ tableName }}_info = await cls.get_{{ tableName }}_by_id_services(auth, page_object.id)
|
||||
if {{ tableName }}_info:
|
||||
try:
|
||||
edit_{{ tableName }}['update_time'] = datetime.now()
|
||||
await {{ tableName }}_dao.update(id=page_object.id, data=edit_{{ tableName }})
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
await auth.db.commit()
|
||||
return SuccessResponse(msg='更新成功')
|
||||
except Exception as e:
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
raise CustomException(msg=f'更新失败: {str(e)}')
|
||||
else:
|
||||
raise CustomException(msg='{{ functionName }}不存在')
|
||||
|
||||
@classmethod
|
||||
async def del_{{ tableName }}_services(cls, auth: AuthSchema, ids: List[str]) -> SuccessResponse:
|
||||
"""
|
||||
删除{{ functionName }}service
|
||||
|
||||
:param auth: 认证信息
|
||||
:param ids: {{ functionName }}id列表
|
||||
:return: 删除{{ functionName }}结果
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
{{ tableName }}_dao = {{ tableName|snake_to_pascal_case }}Dao(auth=auth)
|
||||
|
||||
try:
|
||||
id_list = [int(id) for id in ids]
|
||||
await {{ tableName }}_dao.delete(ids=id_list)
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
await auth.db.commit()
|
||||
return SuccessResponse(msg='删除成功')
|
||||
except Exception as e:
|
||||
if isinstance(auth.db, AsyncSession):
|
||||
try:
|
||||
await auth.db.rollback()
|
||||
except:
|
||||
pass # 忽略回滚错误
|
||||
raise CustomException(msg=f'删除失败: {str(e)}')
|
||||
|
||||
@classmethod
|
||||
async def export_{{ tableName }}_list_services(cls, auth: AuthSchema, query_object: {{ tableName|snake_to_pascal_case }}PageModel) -> bytes:
|
||||
"""
|
||||
导出{{ functionName }}service
|
||||
|
||||
:param auth: 认证信息
|
||||
:param query_object: 查询参数对象
|
||||
:return: 导出{{ functionName }}结果
|
||||
"""
|
||||
# 确保db是AsyncSession类型
|
||||
if not isinstance(auth.db, AsyncSession):
|
||||
raise CustomException(msg='数据库会话类型不正确')
|
||||
|
||||
{{ tableName }}_dao = {{ tableName|snake_to_pascal_case }}Dao(auth=auth)
|
||||
{{ tableName }}_list_result = await {{ tableName }}_dao.get_{{ tableName }}_list(auth.db, query_object, is_page=False)
|
||||
|
||||
# 这里应该实现导出逻辑,例如生成Excel文件
|
||||
# 为简化起见,我们返回一个简单的文本文件
|
||||
export_data = "ID,名称\n"
|
||||
for item in {{ tableName }}_list_result.get("items", []):
|
||||
export_data += f"{item.id},{getattr(item, 'name', '')}\n"
|
||||
|
||||
return export_data.encode('utf-8')
|
||||
|
||||
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from typing import List
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from utils.common_util import CamelCaseUtil, export_list2excel
|
||||
from module_admin.entity.vo.sys_table_vo import SysTablePageModel
|
||||
from module_admin.service.sys_table_service import SysTableService
|
||||
from utils.page_util import PageResponseModel
|
||||
from {{ packageName }}.dao.{{ tableName }}_dao import {{ tableName|snake_to_pascal_case }}Dao
|
||||
from {{ packageName }}.entity.do.{{ tableName }}_do import {{ tableName|snake_to_pascal_case }}
|
||||
from {{ packageName }}.entity.vo.{{ tableName }}_vo import {{ tableName|snake_to_pascal_case }}PageModel, {{ tableName|snake_to_pascal_case }}Model
|
||||
|
||||
|
||||
class {{ tableName|snake_to_pascal_case }}Service:
|
||||
"""
|
||||
{{ tableName|snake_to_pascal_case }}管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_{{ tableName }}_list(cls, query_db: AsyncSession, query_object: {{ tableName|snake_to_pascal_case }}PageModel, data_scope_sql: str) -> [list | PageResponseModel]:
|
||||
{{ tableName }}_list = await {{ tableName|snake_to_pascal_case }}Dao.get_{{ tableName }}_list(query_db, query_object, data_scope_sql, is_page=True)
|
||||
return {{ tableName }}_list
|
||||
|
||||
@classmethod
|
||||
async def get_{{ tableName }}_by_id(cls, query_db: AsyncSession, {{ tableName }}_id: int) -> {{ tableName|snake_to_pascal_case }}Model:
|
||||
{{ tableName }} = await {{ tableName|snake_to_pascal_case }}Dao.get_by_id(query_db, {{ tableName }}_id)
|
||||
{{ tableName }}_model = {{ tableName|snake_to_pascal_case }}Model(**CamelCaseUtil.transform_result({{ tableName }}))
|
||||
return {{ tableName }}_model
|
||||
|
||||
|
||||
@classmethod
|
||||
async def add_{{ tableName }}(cls, query_db: AsyncSession, query_object: {{ tableName|snake_to_pascal_case }}Model) -> {{ tableName|snake_to_pascal_case }}Model:
|
||||
{{ tableName }}_model = await {{ tableName|snake_to_pascal_case }}Dao.add_{{ tableName }}(query_db, query_object)
|
||||
return {{ tableName }}_model
|
||||
|
||||
|
||||
@classmethod
|
||||
async def update_{{ tableName }}(cls, query_db: AsyncSession, query_object: {{ tableName|snake_to_pascal_case }}Model) -> {{ tableName|snake_to_pascal_case }}Model:
|
||||
{{ tableName }} = await {{ tableName|snake_to_pascal_case }}Dao.edit_{{ tableName }}(query_db, query_object)
|
||||
{{ tableName }}_model = {{ tableName|snake_to_pascal_case }}Model(**CamelCaseUtil.transform_result({{ tableName }}))
|
||||
return {{ tableName }}_model
|
||||
|
||||
|
||||
@classmethod
|
||||
async def del_{{ tableName }}(cls, query_db: AsyncSession, {{ tableName }}_ids: List[str]):
|
||||
await {{ tableName|snake_to_pascal_case }}Dao.del_{{ tableName }}(query_db, {{ tableName }}_ids)
|
||||
|
||||
|
||||
@classmethod
|
||||
async def export_{{ tableName }}_list(cls, query_db: AsyncSession, query_object: {{ tableName|snake_to_pascal_case }}PageModel, data_scope_sql) -> bytes:
|
||||
{{ tableName }}_list = await {{ tableName|snake_to_pascal_case }}Dao.get_{{ tableName }}_list(query_db, query_object, data_scope_sql, is_page=False)
|
||||
filed_list = await SysTableService.get_sys_table_list(query_db, SysTablePageModel(tableName='{{ tableName }}'), is_page=False)
|
||||
filtered_filed = sorted(filter(lambda x: x["show"] == '1', filed_list), key=lambda x: x["sequence"])
|
||||
new_data = []
|
||||
for item in {{ tableName }}_list:
|
||||
mapping_dict = {}
|
||||
for fild in filtered_filed:
|
||||
if fild["prop"] in item:
|
||||
mapping_dict[fild["label"]] = item[fild["prop"]]
|
||||
new_data.append(mapping_dict)
|
||||
binary_data = export_list2excel(new_data)
|
||||
return binary_data
|
||||
@@ -1,36 +0,0 @@
|
||||
-- 菜单 SQL
|
||||
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
values('{{functionName}}', '{{parentMenuId}}', '1', '{{businessName}}', '{{moduleName}}/{{businessName}}/index', 1, 0, 'C', '0', '0', '{{permissionPrefix}}:list', '#', 'admin', sysdate(), '', null, '{{functionName}}菜单');
|
||||
|
||||
-- 按钮父菜单ID
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
|
||||
-- 按钮 SQL
|
||||
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
values('{{functionName}}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '{{permissionPrefix}}:query', '#', 'admin', sysdate(), '', null, '');
|
||||
|
||||
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
values('{{functionName}}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '{{permissionPrefix}}:add', '#', 'admin', sysdate(), '', null, '');
|
||||
|
||||
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
values('{{functionName}}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '{{permissionPrefix}}:edit', '#', 'admin', sysdate(), '', null, '');
|
||||
|
||||
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
values('{{functionName}}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '{{permissionPrefix}}:remove', '#', 'admin', sysdate(), '', null, '');
|
||||
|
||||
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
values('{{functionName}}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '{{permissionPrefix}}:export', '#', 'admin', sysdate(), '', null, '');
|
||||
|
||||
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
values('{{functionName}}导入', @parentId, '6', '#', '', 1, 0, 'F', '0', '0', '{{permissionPrefix}}:import', '#', 'admin', sysdate(), '', null, '');
|
||||
|
||||
|
||||
{% for column in columns %}
|
||||
{% set pythonField = column.pythonField | snake_to_camel %}
|
||||
{% set parentheseIndex = column.columnComment.find("(") %}
|
||||
{% set comment = column.columnComment[:parentheseIndex] if parentheseIndex != -1 else column.columnComment %}
|
||||
{% if column.isList %}
|
||||
INSERT INTO `sys_table` (`table_name`, `field_name`, `prop`, `label`, `sequence`) VALUES ('{{ tableName }}', '{{ column.pythonField }}', '{{ pythonField }}', '{{ comment }}', {{ loop.index0 }});
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
INSERT INTO `sys_table` (`table_name`, `field_name`, `prop`, `label`, `sequence`, `fixed`) VALUES ('{{ tableName }}', 'operate', 'operate', '操作', {{ columns|length }}, '2');
|
||||
@@ -1,53 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询{{functionName}}列表
|
||||
export function list{{BusinessName}}(query) {
|
||||
return request({
|
||||
url: '/{{moduleName}}/{{businessName}}/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询{{functionName}}详细
|
||||
export function get{{BusinessName}}(id) {
|
||||
return request({
|
||||
url: '/{{moduleName}}/{{businessName}}/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增{{functionName}}
|
||||
export function add{{BusinessName}}(data) {
|
||||
return request({
|
||||
url: '/{{moduleName}}/{{businessName}}/create',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改{{functionName}}
|
||||
export function update{{BusinessName}}(data) {
|
||||
return request({
|
||||
url: '/{{moduleName}}/{{businessName}}/update',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除{{functionName}}
|
||||
export function del{{BusinessName}}(ids) {
|
||||
return request({
|
||||
url: '/{{moduleName}}/{{businessName}}/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导入{{functionName}}
|
||||
export function import{{BusinessName}}(data) {
|
||||
return request({
|
||||
url: '/{{moduleName}}/{{businessName}}/import',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
@@ -1,628 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||
{% for column in columns %}
|
||||
{% if column.isQuery == "1" %}
|
||||
{% set dictType = column.dictType %}
|
||||
{% set parentheseIndex = column.columnComment.find("(") %}
|
||||
{% set comment = column.columnComment[:parentheseIndex] if parentheseIndex != -1 else column.columnComment %}
|
||||
|
||||
{% if column.htmlType == "input" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.pythonField | snake_to_camel }}">
|
||||
<el-input
|
||||
v-model="queryParams.{{ column.pythonField | snake_to_camel }}"
|
||||
placeholder="请输入{{ comment }}"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
{% elif (column.htmlType == "select" or column.htmlType == "radio") and dictType != "" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.pythonField | snake_to_camel }}">
|
||||
<el-select
|
||||
v-model="queryParams.{{ column.pythonField | snake_to_camel }}"
|
||||
placeholder="请选择{{ comment }}"
|
||||
style="width: 180px"
|
||||
clearable>
|
||||
<el-option
|
||||
v-for="dict in {{ dictType }}"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
{% elif (column.htmlType == "select" or column.htmlType == "radio") and dictType %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.pythonField | snake_to_camel }}">
|
||||
<el-select v-model="queryParams.{{ column.pythonField | snake_to_camel }}" placeholder="请选择{{ comment }}" clearable>
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "datetime" and column.queryType != "BETWEEN" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.pythonField | snake_to_camel }}">
|
||||
<el-date-picker clearable
|
||||
v-model="queryParams.{{ column.pythonField | snake_to_camel }}"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择{{ comment }}">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "datetime" and column.queryType == "BETWEEN" %}
|
||||
<el-form-item label="{{ comment }}" style="width: 308px">
|
||||
<el-date-picker
|
||||
v-model="daterange{{ column.pythonField | snake_to_pascal_case }}"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-card class="base-table" ref="fullTable">
|
||||
<TableSetup
|
||||
ref="tSetup"
|
||||
@onStripe="onStripe"
|
||||
@onRefresh="onRefresh"
|
||||
@onChange="onChange"
|
||||
@onfullTable="onfullTable"
|
||||
@onSearchChange="onSearchChange"
|
||||
:columns="columns"
|
||||
:isTable="isTable"
|
||||
>
|
||||
<template v-slot:operate>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['{{ moduleName }}:{{ businessName }}:add']"
|
||||
>新增</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['{{ moduleName }}:{{ businessName }}:edit']"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['{{ moduleName }}:{{ businessName }}:remove']"
|
||||
>删除</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Upload"
|
||||
@click="handleImport"
|
||||
v-hasPermi="['{{ moduleName }}:{{ businessName }}:import']"
|
||||
>导入</el-button
|
||||
>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['{{ moduleName }}:{{ businessName }}:export']"
|
||||
>导出</el-button>
|
||||
</template>
|
||||
</TableSetup>
|
||||
<auto-table
|
||||
ref="multipleTable"
|
||||
class="mytable"
|
||||
:tableData="{{ businessName }}List"
|
||||
:columns="columns"
|
||||
:loading="loading"
|
||||
:stripe="stripe"
|
||||
:tableHeight="tableHeight"
|
||||
@onColumnWidthChange="onColumnWidthChange"
|
||||
@onSelectionChange="handleSelectionChange"
|
||||
>
|
||||
{% for column in columns %}
|
||||
{% set pythonField = column.pythonField | snake_to_camel %}
|
||||
{% set parentheseIndex = column.columnComment.find("(") %}
|
||||
{% set comment = column.columnComment[:parentheseIndex] if parentheseIndex != -1 else column.columnComment %}
|
||||
|
||||
{% if column.isList and column.htmlType == "datetime" %}
|
||||
<template #{{ pythonField }}="{ row }">
|
||||
<span>{% raw %}{{{% endraw %} parseTime(row.{{ pythonField }}, '{y}-{m}-{d}') {% raw %}}}{% endraw %}</span>
|
||||
</template>
|
||||
{% elif column.isList == "1" and column.htmlType == "imageUpload" %}
|
||||
<template #{{ pythonField }}="{ row }">
|
||||
<image-preview :src="fullUrl(row.{{ pythonField }})" v-if="row.{{ pythonField }}" :width="50" :height="50"/>
|
||||
</template>
|
||||
{% elif column.isList == "1" and column.dictType != "" %}
|
||||
<template #{{ pythonField }}="{ row }">
|
||||
{% if column.htmlType == "checkbox" %}
|
||||
<dict-tag :options="{{ column.dictType }}" :value="row.{{ pythonField }} ? row.{{ pythonField }}.split(',') : []"/>
|
||||
{% else %}
|
||||
<dict-tag :options="{{ column.dictType }}" :value="row.{{ pythonField }}"/>
|
||||
{% endif %}
|
||||
</template>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<template #operate="{ row }">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(row)" v-hasPermi="['{{ moduleName }}:{{ businessName }}:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(row)" v-hasPermi="['{{ moduleName }}:{{ businessName }}:remove']">删除</el-button>
|
||||
</template>
|
||||
</auto-table>
|
||||
<div class="table-pagination">
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 添加或修改{{ functionName }}对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="800px" append-to-body>
|
||||
<el-form ref="{{ businessName }}Ref" :model="form" :rules="rules" label-width="80px">
|
||||
{% for column in columns %}
|
||||
{% set field = column.pythonField | snake_to_camel %}
|
||||
{% if column.isInsert == "1" and not column.isPk == "1" %}
|
||||
{% if column.usableColumn or not column.superColumn %}
|
||||
{% set parentheseIndex = column.columnComment.find("(") %}
|
||||
{% set comment = column.columnComment[:parentheseIndex] if parentheseIndex != -1 else column.columnComment %}
|
||||
{% set dictType = column.dictType %}
|
||||
|
||||
{% if column.htmlType == "input" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ field }}">
|
||||
<el-input v-model="form.{{ field }}" placeholder="请输入{{ comment }}" />
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "imageUpload" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ field }}">
|
||||
<image-upload v-model="form.{{ field }}"/>
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "fileUpload" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ field }}">
|
||||
<file-upload v-model="form.{{ field }}"/>
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "editor" %}
|
||||
<el-form-item label="{{ comment }}">
|
||||
<editor v-model="form.{{ field }}" :min-height="192"/>
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "select" and dictType != "" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ field }}">
|
||||
<el-select v-model="form.{{ field }}" placeholder="请选择{{ comment }}">
|
||||
<el-option
|
||||
v-for="dict in {{ dictType }}"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
{% if column.pythonType == "int" %}
|
||||
:value="parseInt(dict.value)"
|
||||
{% else %}
|
||||
:value="dict.value"
|
||||
{% endif %}
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "select" and dictType %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ field }}">
|
||||
<el-select v-model="form.{{ field }}" placeholder="请选择{{ comment }}">
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "checkbox" and dictType != "" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ field }}">
|
||||
<el-checkbox-group v-model="form.{{ field }}">
|
||||
<el-checkbox
|
||||
v-for="dict in {{ dictType }}"
|
||||
:key="dict.value"
|
||||
:label="dict.value">
|
||||
{{ dict.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "checkbox" and dictType %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ field }}">
|
||||
<el-checkbox-group v-model="form.{{ field }}">
|
||||
<el-checkbox>请选择字典生成</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "radio" and dictType != "" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ field }}">
|
||||
<el-radio-group v-model="form.{{ field }}">
|
||||
<el-radio
|
||||
v-for="dict in {{ dictType }}"
|
||||
:key="dict.value"
|
||||
{% if column.pythonType == "int" %}
|
||||
:label="parseInt(dict.value)"
|
||||
{% else %}
|
||||
:label="dict.value"
|
||||
{% endif %}
|
||||
>{{ dict.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "radio" and dictType %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ field }}">
|
||||
<el-radio-group v-model="form.{{ field }}">
|
||||
<el-radio label="1">请选择字典生成</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "datetime" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ field }}">
|
||||
<el-date-picker clearable
|
||||
v-model="form.{{ field }}"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择{{ comment }}">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
{% elif column.htmlType == "textarea" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ field }}">
|
||||
<el-input v-model="form.{{ field }}" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 导入数据对话框 -->
|
||||
<ImportData
|
||||
v-if="openImport"
|
||||
v-model="openImport"
|
||||
tableName="{{ tableName }}"
|
||||
@success="handleImportSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="{{ tableName|snake_to_pascal_case }}">
|
||||
import { list{{ BusinessName }}, get{{ BusinessName }}, del{{ BusinessName }}, add{{ BusinessName }}, update{{ BusinessName }}, import{{ BusinessName }} } from "@/api/{{ moduleName }}/{{ businessName }}";
|
||||
import { listAllTable } from '@/api/system/table'
|
||||
import TableSetup from '@/components/TableSetup'
|
||||
import AutoTable from '@/components/AutoTable'
|
||||
import ImportData from '@/components/ImportData'
|
||||
const { proxy } = getCurrentInstance();
|
||||
{% if dicts != '' %}
|
||||
{% set dictsNoSymbol = dicts.replace("'", "") %}
|
||||
const { {{ dictsNoSymbol }} } = proxy.useDict({{ dicts }});
|
||||
{% endif %}
|
||||
|
||||
const {{ businessName }}List = ref([]);
|
||||
{#{% if table.sub %}#}
|
||||
{# const {{ subclassName }}List = ref([]);#}
|
||||
{#{% endif %}#}
|
||||
const open = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
{#{% if table.sub %}#}
|
||||
{# const checked{{ subClassName }} = ref([]);#}
|
||||
{#{% endif %}#}
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const title = ref("");
|
||||
{% for column in columns %}
|
||||
{% if column.htmlType == "datetime" and column.queryType == "BETWEEN" %}
|
||||
const daterange{{ column.pythonField | snake_to_pascal_case }} = ref([]);
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
const columns = ref([])
|
||||
const stripe = ref(true)
|
||||
const isTable = ref(true)
|
||||
const tableHeight = ref(500)
|
||||
const fullScreen = ref(false)
|
||||
const openImport = ref(false)
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
{% for column in columns %}
|
||||
{% if column.isQuery == "1" %}
|
||||
{{ column.pythonField | snake_to_camel }}: null{% if not loop.last %},{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
},
|
||||
rules: {
|
||||
{% for column in columns %}
|
||||
{% if column.isRequired == "1" %}
|
||||
{% set parentheseIndex = column.columnComment.find("(") %}
|
||||
{% set comment = column.columnComment[:parentheseIndex] if parentheseIndex != -1 else column.columnComment %}
|
||||
{{ column.pythonField | snake_to_camel }}: [
|
||||
{ required: true, message: "{{ comment }}不能为空", trigger: "{% if column.htmlType == "select" or column.htmlType == "radio" %}change{% else %}blur{% endif %}" }
|
||||
]{% if not loop.last %},{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询{{ functionName }}列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
{% for column in columns %}
|
||||
{% if column.htmlType == "datetime" and column.queryType == "BETWEEN" %}
|
||||
queryParams.value.params = {};
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for column in columns %}
|
||||
{% if column.htmlType == "datetime" and column.queryType == "BETWEEN" %}
|
||||
if (null != daterange{{ column.pythonField | snake_to_pascal_case }} && '' != daterange{{ column.pythonField | snake_to_pascal_case }}) {
|
||||
queryParams.value.params["begin{{ column.pythonField | snake_to_pascal_case }}"] = daterange{{ column.pythonField | snake_to_pascal_case }}.value[0];
|
||||
queryParams.value.params["end{{ column.pythonField | snake_to_pascal_case }}"] = daterange{{ column.pythonField | snake_to_pascal_case }}.value[1];
|
||||
}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
list{{ BusinessName }}(queryParams.value).then(response => {
|
||||
{{ businessName }}List.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function getColumns() {
|
||||
listAllTable({ tableName: '{{ tableName }}' })
|
||||
.then((response) => {
|
||||
columns.value = response.data
|
||||
})
|
||||
.then(() => {
|
||||
getList()
|
||||
})
|
||||
}
|
||||
|
||||
// 取消按钮
|
||||
function cancel() {
|
||||
open.value = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
// 表单重置
|
||||
function reset() {
|
||||
form.value = {
|
||||
{% for column in columns %}
|
||||
{% if column.htmlType == "checkbox" %}
|
||||
{{ column.pythonField | snake_to_camel }}: []{% if not loop.last %},{% endif %}
|
||||
{% else %}
|
||||
{{ column.pythonField | snake_to_camel }}: null{% if not loop.last %},{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
};
|
||||
{# {% if table.sub %}#}
|
||||
{# {{ subclassName }}List.value = [];#}
|
||||
{# {% endif %}#}
|
||||
proxy.resetForm("{{ businessName }}Ref");
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
{% for column in columns %}
|
||||
{% if column.htmlType == "datetime" and column.queryType == "BETWEEN" %}
|
||||
daterange{{ column.pythonField | snake_to_pascal_case }}.value = [];
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
proxy.resetForm("queryRef");
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
// 多选框选中数据
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map(item => item.{{ pkColumn.pythonField }});
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
|
||||
/** 新增按钮操作 */
|
||||
function handleAdd() {
|
||||
reset();
|
||||
open.value = true;
|
||||
title.value = "添加{{ functionName }}";
|
||||
}
|
||||
|
||||
/** 新增按钮操作 */
|
||||
function handleImport() {
|
||||
openImport.value = true
|
||||
}
|
||||
|
||||
/** 修改按钮操作 */
|
||||
function handleUpdate(row) {
|
||||
reset();
|
||||
const {{ tableName | snake_to_camel }}{{ pkColumn.pythonField | snake_to_pascal_case }} = row.{{ pkColumn.pythonField | snake_to_camel }} || ids.value
|
||||
get{{ BusinessName }}({{ tableName | snake_to_camel }}{{ pkColumn.pythonField | snake_to_pascal_case }}).then(response => {
|
||||
form.value = response.data;
|
||||
{% for column in columns %}
|
||||
{% if column.htmlType == "checkbox" %}
|
||||
form.value.{{ column.pythonField | snake_to_camel }} = form.value.{{ column.pythonField | snake_to_camel }}.split(",");
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{# {% if table.sub %}#}
|
||||
{# {{ subclassName }}List.value = response.data.{{ subclassName }}List;#}
|
||||
{# {% endif %}#}
|
||||
open.value = true;
|
||||
title.value = "修改{{ functionName }}";
|
||||
});
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs["{{ businessName }}Ref"].validate(valid => {
|
||||
if (valid) {
|
||||
{% for column in columns %}
|
||||
{% if column.htmlType == "checkbox" %}
|
||||
form.value.{{ column.pythonField | snake_to_camel }} = form.value.{{ column.pythonField | snake_to_camel }}.join(",");
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{# {% if table.sub %}#}
|
||||
{# form.value.{{ subclassName }}List = {{ subclassName }}List.value;#}
|
||||
{# {% endif %}#}
|
||||
if (form.value.{{ pkColumn.pythonField }} != null) {
|
||||
update{{ BusinessName }}(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
} else {
|
||||
add{{ BusinessName }}(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("新增成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
const _{{ pkColumn.pythonField }}s = row.{{ pkColumn.pythonField }} || ids.value;
|
||||
proxy.$modal.confirm('是否确认删除{{ functionName }}编号为"' + _{{ pkColumn.pythonField }}s + '"的数据项?').then(function() {
|
||||
return del{{ BusinessName }}(_{{ pkColumn.pythonField }}s);
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
{#{% if table.sub %}#}
|
||||
{# /** {{ subTable.functionName }}序号 */#}
|
||||
{# function row{{ subClassName }}Index({ row, rowIndex }) {#}
|
||||
{# row.index = rowIndex + 1;#}
|
||||
{# }#}
|
||||
{##}
|
||||
{# /** {{ subTable.functionName }}添加按钮操作 */#}
|
||||
{# function handleAdd{{ subClassName }}() {#}
|
||||
{# let obj = {};#}
|
||||
{# {% for column in subTable.columns %}#}
|
||||
{# {% if column.pk or column.pythonField == subTableFkclassName %}#}
|
||||
{# {% elif column.list and pythonField != "" %}#}
|
||||
{# obj.{{ column.pythonField }} = "";#}
|
||||
{# {% endif %}#}
|
||||
{# {% endfor %}#}
|
||||
{# {{ subclassName }}List.value.push(obj);#}
|
||||
{# }#}
|
||||
{##}
|
||||
{# /** {{ subTable.functionName }}删除按钮操作 */#}
|
||||
{# function handleDelete{{ subClassName }}() {#}
|
||||
{# if (checked{{ subClassName }}.value.length == 0) {#}
|
||||
{# proxy.$modal.msgError("请先选择要删除的{{ subTable.functionName }}数据");#}
|
||||
{# } else {#}
|
||||
{# const {{ subclassName }}s = {{ subclassName }}List.value;#}
|
||||
{# const checked{{ subClassName }}s = checked{{ subClassName }}.value;#}
|
||||
{# {{ subclassName }}List.value = {{ subclassName }}s.filter(function(item) {#}
|
||||
{# return checked{{ subClassName }}s.indexOf(item.index) == -1#}
|
||||
{# });#}
|
||||
{# }#}
|
||||
{# }#}
|
||||
{##}
|
||||
{# /** 复选框选中数据 */#}
|
||||
{# function handle{{ subClassName }}SelectionChange(selection) {#}
|
||||
{# checked{{ subClassName }}.value = selection.map(item => item.index)#}
|
||||
{# }#}
|
||||
{#{% endif %}#}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download('{{ moduleName }}/{{ businessName }}/export', {
|
||||
...queryParams.value
|
||||
}, `{{ businessName }}_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
//表格全屏
|
||||
function onfullTable() {
|
||||
proxy.$refs.tSetup.onFull(proxy.$refs.fullTable.$el)
|
||||
fullScreen.value = !fullScreen.value
|
||||
updateTableHeight()
|
||||
}
|
||||
//表格刷新
|
||||
function onRefresh() {
|
||||
getList()
|
||||
}
|
||||
//搜索框显示隐藏
|
||||
function onSearchChange() {
|
||||
showSearch.value = !showSearch.value
|
||||
}
|
||||
|
||||
function onStripe(val) {
|
||||
stripe.value = val
|
||||
}
|
||||
//改变表头数据
|
||||
function onChange(val) {
|
||||
columns.value = val
|
||||
}
|
||||
|
||||
//改变表格宽度
|
||||
function onColumnWidthChange(column) {
|
||||
proxy.$refs.tSetup.tableWidth(column)
|
||||
}
|
||||
|
||||
//更新表格高度
|
||||
function updateTableHeight() {
|
||||
if (
|
||||
proxy.$refs.tSetup &&
|
||||
proxy.$refs.queryRef &&
|
||||
document.querySelector('.table-pagination')
|
||||
) {
|
||||
if (fullScreen.value) {
|
||||
tableHeight.value = window.innerHeight - 145
|
||||
} else {
|
||||
tableHeight.value =
|
||||
window.innerHeight -
|
||||
proxy.$refs.tSetup.$el.clientHeight -
|
||||
proxy.$refs.queryRef.$el.clientHeight -
|
||||
document.querySelector('.table-pagination').clientHeight -
|
||||
220
|
||||
}
|
||||
}
|
||||
}
|
||||
//导入成功
|
||||
function handleImportSuccess(sheetName, filedInfo, fileName) {
|
||||
let data = {
|
||||
tableName: '{{ tableName }}',
|
||||
filedInfo: filedInfo,
|
||||
fileName: fileName,
|
||||
sheetName: sheetName
|
||||
}
|
||||
import{{ BusinessName }}(data).then(() => {
|
||||
proxy.$modal.msgSuccess('导入成功')
|
||||
openImport.value = false
|
||||
getList()
|
||||
})
|
||||
getList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
updateTableHeight() // 初始化计算高度
|
||||
window.addEventListener('resize', updateTableHeight) // 监听窗口大小变化
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateTableHeight) // 销毁监听
|
||||
})
|
||||
|
||||
getColumns()
|
||||
|
||||
</script>
|
||||
@@ -6,6 +6,7 @@ from .cache.controller import CacheRouter
|
||||
from .job.controller import JobRouter
|
||||
from .online.controller import OnlineRouter
|
||||
from .server.controller import ServerRouter
|
||||
from .resource.controller import ResourceRouter
|
||||
|
||||
|
||||
MonitorRouter = APIRouter(prefix="/monitor")
|
||||
@@ -14,4 +15,5 @@ MonitorRouter = APIRouter(prefix="/monitor")
|
||||
MonitorRouter.include_router(CacheRouter)
|
||||
MonitorRouter.include_router(JobRouter)
|
||||
MonitorRouter.include_router(OnlineRouter)
|
||||
MonitorRouter.include_router(ServerRouter)
|
||||
MonitorRouter.include_router(ServerRouter)
|
||||
MonitorRouter.include_router(ResourceRouter)
|
||||
+23
-25
@@ -4,10 +4,8 @@ from fastapi import APIRouter, Body, Depends, Path, Query, Request, UploadFile,
|
||||
from fastapi.responses import JSONResponse, StreamingResponse, FileResponse
|
||||
from typing import List, Optional
|
||||
|
||||
from app.common.request import PaginationService
|
||||
from app.common.response import StreamResponse, SuccessResponse
|
||||
from app.utils.common_util import bytes2file_response
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.logger import logger
|
||||
@@ -23,15 +21,15 @@ from .schema import (
|
||||
from .service import ResourceService
|
||||
|
||||
|
||||
ResourceFileRouter = APIRouter(route_class=OperationLogRoute, prefix="/resource", tags=["资源管理"])
|
||||
ResourceRouter = APIRouter(route_class=OperationLogRoute, prefix="/resource", tags=["资源管理"])
|
||||
|
||||
|
||||
@ResourceFileRouter.get("/list", summary="获取目录列表", description="获取指定目录下的文件和子目录列表")
|
||||
@ResourceRouter.get("/list", summary="获取目录列表", description="获取指定目录下的文件和子目录列表")
|
||||
async def get_directory_list_controller(
|
||||
request: Request,
|
||||
path: Optional[str] = Query(None, description="目录路径"),
|
||||
include_hidden: bool = Query(False, description="是否包含隐藏文件"),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["resource:file:query"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:resource:query"]))
|
||||
) -> JSONResponse:
|
||||
"""获取目录列表"""
|
||||
result_dict = await ResourceService.get_directory_list_service(
|
||||
@@ -44,11 +42,11 @@ async def get_directory_list_controller(
|
||||
return SuccessResponse(data=result_dict, msg="获取目录列表成功")
|
||||
|
||||
|
||||
@ResourceFileRouter.post("/search", summary="搜索资源", description="根据条件搜索资源")
|
||||
@ResourceRouter.post("/search", summary="搜索资源", description="根据条件搜索资源")
|
||||
async def search_resources_controller(
|
||||
request: Request,
|
||||
search: ResourceSearchSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["resource:file:search"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:resource:search"]))
|
||||
) -> JSONResponse:
|
||||
"""搜索资源"""
|
||||
result_list = await ResourceService.search_resources_service(
|
||||
@@ -60,12 +58,12 @@ async def search_resources_controller(
|
||||
return SuccessResponse(data=result_list, msg=f"搜索成功,找到 {len(result_list)} 个结果")
|
||||
|
||||
|
||||
@ResourceFileRouter.post("/upload", summary="上传文件", description="上传文件到指定目录")
|
||||
@ResourceRouter.post("/upload", summary="上传文件", description="上传文件到指定目录")
|
||||
async def upload_file_controller(
|
||||
file: UploadFile,
|
||||
request: Request,
|
||||
target_path: Optional[str] = Form(None, description="目标目录路径"),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["resource:file:upload"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:resource:upload"]))
|
||||
) -> JSONResponse:
|
||||
"""上传文件"""
|
||||
result_dict = await ResourceService.upload_file_service(
|
||||
@@ -78,11 +76,11 @@ async def upload_file_controller(
|
||||
return SuccessResponse(data=result_dict, msg="上传文件成功")
|
||||
|
||||
|
||||
@ResourceFileRouter.get("/download", summary="下载文件", description="下载指定文件")
|
||||
@ResourceRouter.get("/download", summary="下载文件", description="下载指定文件")
|
||||
async def download_file_controller(
|
||||
request: Request,
|
||||
path: str = Query(..., description="文件路径"),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["resource:file:download"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:resource:download"]))
|
||||
) -> FileResponse:
|
||||
"""下载文件"""
|
||||
file_path = await ResourceService.download_file_service(
|
||||
@@ -103,10 +101,10 @@ async def download_file_controller(
|
||||
)
|
||||
|
||||
|
||||
@ResourceFileRouter.delete("/delete", summary="删除文件", description="删除指定文件或目录")
|
||||
@ResourceRouter.delete("/delete", summary="删除文件", description="删除指定文件或目录")
|
||||
async def delete_files_controller(
|
||||
paths: List[str] = Body(..., description="文件路径列表"),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["resource:file:delete"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:resource:delete"]))
|
||||
) -> JSONResponse:
|
||||
"""删除文件"""
|
||||
await ResourceService.delete_file_service(auth=auth, paths=paths)
|
||||
@@ -114,10 +112,10 @@ async def delete_files_controller(
|
||||
return SuccessResponse(msg="删除文件成功")
|
||||
|
||||
|
||||
@ResourceFileRouter.post("/move", summary="移动文件", description="移动文件或目录")
|
||||
@ResourceRouter.post("/move", summary="移动文件", description="移动文件或目录")
|
||||
async def move_file_controller(
|
||||
data: ResourceMoveSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["resource:file:move"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:resource:move"]))
|
||||
) -> JSONResponse:
|
||||
"""移动文件"""
|
||||
await ResourceService.move_file_service(auth=auth, data=data)
|
||||
@@ -125,10 +123,10 @@ async def move_file_controller(
|
||||
return SuccessResponse(msg="移动文件成功")
|
||||
|
||||
|
||||
@ResourceFileRouter.post("/copy", summary="复制文件", description="复制文件或目录")
|
||||
@ResourceRouter.post("/copy", summary="复制文件", description="复制文件或目录")
|
||||
async def copy_file_controller(
|
||||
data: ResourceCopySchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["resource:file:copy"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:resource:copy"]))
|
||||
) -> JSONResponse:
|
||||
"""复制文件"""
|
||||
await ResourceService.copy_file_service(auth=auth, data=data)
|
||||
@@ -136,10 +134,10 @@ async def copy_file_controller(
|
||||
return SuccessResponse(msg="复制文件成功")
|
||||
|
||||
|
||||
@ResourceFileRouter.post("/rename", summary="重命名文件", description="重命名文件或目录")
|
||||
@ResourceRouter.post("/rename", summary="重命名文件", description="重命名文件或目录")
|
||||
async def rename_file_controller(
|
||||
data: ResourceRenameSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["resource:file:rename"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["rmonitor:resource:rename"]))
|
||||
) -> JSONResponse:
|
||||
"""重命名文件"""
|
||||
await ResourceService.rename_file_service(auth=auth, data=data)
|
||||
@@ -147,10 +145,10 @@ async def rename_file_controller(
|
||||
return SuccessResponse(msg="重命名文件成功")
|
||||
|
||||
|
||||
@ResourceFileRouter.post("/create-dir", summary="创建目录", description="在指定路径创建新目录")
|
||||
@ResourceRouter.post("/create-dir", summary="创建目录", description="在指定路径创建新目录")
|
||||
async def create_directory_controller(
|
||||
data: ResourceCreateDirSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["resource:file:create_dir"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:resource:create_dir"]))
|
||||
) -> JSONResponse:
|
||||
"""创建目录"""
|
||||
await ResourceService.create_directory_service(auth=auth, data=data)
|
||||
@@ -158,10 +156,10 @@ async def create_directory_controller(
|
||||
return SuccessResponse(msg="创建目录成功")
|
||||
|
||||
|
||||
@ResourceFileRouter.get("/stats", summary="获取资源统计", description="获取资源统计信息")
|
||||
@ResourceRouter.get("/stats", summary="获取资源统计", description="获取资源统计信息")
|
||||
async def get_resource_stats_controller(
|
||||
request: Request,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["resource:stats:query"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:resource:query"]))
|
||||
) -> JSONResponse:
|
||||
"""获取资源统计"""
|
||||
result_dict = await ResourceService.get_stats_service(
|
||||
@@ -172,11 +170,11 @@ async def get_resource_stats_controller(
|
||||
return SuccessResponse(data=result_dict, msg="获取资源统计成功")
|
||||
|
||||
|
||||
@ResourceFileRouter.post("/export", summary="导出资源列表", description="导出资源列表")
|
||||
@ResourceRouter.post("/export", summary="导出资源列表", description="导出资源列表")
|
||||
async def export_resource_list_controller(
|
||||
request: Request,
|
||||
search: ResourceSearchSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["resource:file:export"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:resource:export"]))
|
||||
) -> StreamingResponse:
|
||||
"""导出资源列表"""
|
||||
# 获取搜索结果
|
||||
@@ -1,11 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .resource.controller import ResourceFileRouter
|
||||
|
||||
|
||||
ResourceRouter = APIRouter(prefix="/resource")
|
||||
|
||||
# 包含所有子路由
|
||||
ResourceRouter.include_router(ResourceFileRouter)
|
||||
@@ -11,13 +11,9 @@ from .dict.controller import DictRouter
|
||||
from .params.controller import ParamsRouter
|
||||
from .notice.controller import NoticeRouter
|
||||
from .log.controller import LogRouter
|
||||
from .version.controller import VersionRouter
|
||||
from .ticket.controller import TicketRouter
|
||||
|
||||
|
||||
SystemRouter = APIRouter(prefix="/system")
|
||||
|
||||
# 包含所有子路由
|
||||
SystemRouter.include_router(AuthRouter)
|
||||
SystemRouter.include_router(UserRouter)
|
||||
SystemRouter.include_router(RoleRouter)
|
||||
@@ -27,6 +23,4 @@ SystemRouter.include_router(PositionRouter)
|
||||
SystemRouter.include_router(DictRouter)
|
||||
SystemRouter.include_router(ParamsRouter)
|
||||
SystemRouter.include_router(NoticeRouter)
|
||||
SystemRouter.include_router(LogRouter)
|
||||
SystemRouter.include_router(VersionRouter)
|
||||
SystemRouter.include_router(TicketRouter)
|
||||
SystemRouter.include_router(LogRouter)
|
||||
@@ -161,8 +161,6 @@ class ParamsService:
|
||||
@classmethod
|
||||
async def upload_service(cls, base_url: str, file: UploadFile) -> Dict:
|
||||
"""上传文件"""
|
||||
if not file:
|
||||
raise CustomException(msg="请选择要上传的文件")
|
||||
filename, filepath, file_url = await UploadUtil.upload_file(file=file, base_url=base_url)
|
||||
|
||||
return UploadResponseSchema(
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, Path
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from app.common.response import SuccessResponse
|
||||
from app.common.request import PaginationService
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.logger import logger
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .param import TicketQueryParam
|
||||
from .service import TicketService
|
||||
from .schema import TicketCreateSchema, TicketUpdateSchema
|
||||
|
||||
|
||||
TicketRouter = APIRouter(route_class=OperationLogRoute, prefix="/ticket", tags=["工单管理"])
|
||||
|
||||
|
||||
@TicketRouter.get("/detail/{id}", summary="获取工单详情", description="获取工单详情")
|
||||
async def get_ticket_detail_controller(
|
||||
id: int = Path(..., description="工单ID"),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:ticket:query"]))
|
||||
) -> JSONResponse:
|
||||
result_dict = await TicketService.get_ticket_detail_service(auth=auth, id=id)
|
||||
logger.info(f"获取工单详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取工单详情成功")
|
||||
|
||||
|
||||
@TicketRouter.get("/list", summary="查询工单列表", description="查询工单列表")
|
||||
async def get_ticket_list_controller(
|
||||
page: PaginationQueryParam = Depends(),
|
||||
search: TicketQueryParam = Depends(),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:ticket:query"]))
|
||||
) -> JSONResponse:
|
||||
result_dict_list = await TicketService.get_ticket_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="查询工单列表成功")
|
||||
|
||||
|
||||
@TicketRouter.post("/create", summary="创建工单", description="创建工单")
|
||||
async def create_ticket_controller(
|
||||
data: TicketCreateSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:ticket:create"]))
|
||||
) -> JSONResponse:
|
||||
result_dict = await TicketService.create_ticket_service(auth=auth, data=data)
|
||||
logger.info(f"创建工单成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建工单成功")
|
||||
|
||||
|
||||
@TicketRouter.put("/update/{id}", summary="修改工单", description="修改工单")
|
||||
async def update_ticket_controller(
|
||||
data: TicketUpdateSchema,
|
||||
id: int = Path(..., description="工单ID"),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:ticket:update"]))
|
||||
) -> JSONResponse:
|
||||
result_dict = await TicketService.update_ticket_service(auth=auth, id=id, data=data)
|
||||
logger.info(f"修改工单成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改工单成功")
|
||||
|
||||
|
||||
@TicketRouter.delete("/delete", summary="删除工单", description="删除工单")
|
||||
async def delete_ticket_controller(
|
||||
ids: list[int] = Body(..., description="ID列表"),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:ticket:delete"]))
|
||||
) -> JSONResponse:
|
||||
await TicketService.delete_ticket_service(auth=auth, ids=ids)
|
||||
logger.info(f"删除工单成功: {ids}")
|
||||
return SuccessResponse(msg="删除工单成功")
|
||||
@@ -1,56 +0,0 @@
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .model import TicketModel
|
||||
from .schema import TicketCreateSchema, TicketUpdateSchema
|
||||
|
||||
|
||||
class TicketCRUD(CRUDBase[TicketModel, TicketCreateSchema, TicketUpdateSchema]):
|
||||
"""工单 CRUD 操作"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
"""初始化工单CRUD"""
|
||||
self.auth = auth
|
||||
super().__init__(model=TicketModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[TicketModel]:
|
||||
"""根据id获取工单信息
|
||||
|
||||
:param id: 工单ID
|
||||
:return: 工单信息
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[TicketModel]:
|
||||
"""获取工单列表
|
||||
|
||||
:param search: 搜索条件
|
||||
:param order_by: 排序字段
|
||||
:return: 工单列表
|
||||
"""
|
||||
return await self.list(search=search or {}, order_by=order_by or [])
|
||||
|
||||
async def create_crud(self, data: TicketCreateSchema) -> Optional[TicketModel]:
|
||||
"""创建工单
|
||||
|
||||
:param data: 工单创建数据
|
||||
:return: 创建的工单
|
||||
"""
|
||||
return await self.create(data=data)
|
||||
|
||||
async def update_crud(self, id: int, data: TicketUpdateSchema) -> Optional[TicketModel]:
|
||||
"""更新工单
|
||||
|
||||
:param id: 工单ID
|
||||
:param data: 工单更新数据
|
||||
:return: 更新的工单
|
||||
"""
|
||||
return await self.update(id=id, data=data)
|
||||
|
||||
async def delete_crud(self, ids: List[int]) -> None:
|
||||
"""批量删除工单
|
||||
|
||||
:param ids: 工单ID列表
|
||||
"""
|
||||
return await self.delete(ids=ids)
|
||||
@@ -1,30 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Optional
|
||||
from sqlalchemy import String, Text, DateTime, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from datetime import datetime
|
||||
|
||||
from app.core.base_model import CreatorMixin
|
||||
|
||||
|
||||
class TicketModel(CreatorMixin):
|
||||
"""
|
||||
工单模型 - SQLAlchemy 2.0 语法
|
||||
兼容 MySQL 和 PostgreSQL
|
||||
"""
|
||||
__tablename__ = 'system_ticket'
|
||||
__table_args__ = ({'comment': '工单表'})
|
||||
|
||||
title: Mapped[str] = mapped_column(String(255), nullable=False, comment='工单标题')
|
||||
status: Mapped[str] = mapped_column(String(50), default='pending', comment='工单状态(pending:待处理 progress:处理中 resolved:已解决 closed:已关闭)')
|
||||
priority: Mapped[str] = mapped_column(String(50), default='medium', comment='优先级(low:低, medium:中, high:高, urgent:紧急)')
|
||||
type: Mapped[Optional[str]] = mapped_column(String(50), comment='工单类型(bug:缺陷, feature:功能, task:任务)')
|
||||
assignee_id: Mapped[Optional[int]] = mapped_column(ForeignKey('system_users.id'), comment='指派给用户ID')
|
||||
reporter_id: Mapped[Optional[int]] = mapped_column(ForeignKey('system_users.id'), comment='报告人ID')
|
||||
project: Mapped[Optional[str]] = mapped_column(String(100), comment='所属项目或模块')
|
||||
version: Mapped[Optional[str]] = mapped_column(String(50), comment='版本号')
|
||||
|
||||
# 关系
|
||||
assignee = relationship('UserModel', foreign_keys=[assignee_id])
|
||||
reporter = relationship('UserModel', foreign_keys=[reporter_id])
|
||||
@@ -1,36 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from fastapi import Query
|
||||
|
||||
from app.core.validator import DateTimeStr
|
||||
|
||||
|
||||
class TicketQueryParam:
|
||||
"""工单管理查询参数"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
title: Optional[str] = Query(None, description="工单标题"),
|
||||
status: Optional[bool] = Query(None, description="工单状态"),
|
||||
priority: Optional[str] = Query(None, description="优先级"),
|
||||
creator: Optional[int] = Query(None, description="创建人"),
|
||||
start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2023-01-01 00:00:00"),
|
||||
end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2023-12-31 23:59:59"),
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
# 模糊查询字段
|
||||
self.title = ("like", title)
|
||||
|
||||
# 精确查询字段
|
||||
self.creator_id = creator
|
||||
self.status = status
|
||||
self.priority = priority
|
||||
|
||||
# 时间范围查询
|
||||
if start_time and end_time:
|
||||
start_datetime = datetime.strptime(str(start_time), '%Y-%m-%d %H:%M:%S')
|
||||
end_datetime = datetime.strptime(str(end_time), '%Y-%m-%d %H:%M:%S')
|
||||
self.created_at = ("between", (start_datetime, end_datetime))
|
||||
@@ -1,31 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from datetime import datetime
|
||||
|
||||
from app.core.base_schema import BaseSchema
|
||||
|
||||
|
||||
class TicketCreateSchema(BaseModel):
|
||||
"""创建工单"""
|
||||
title: str = Field(..., max_length=255, description='工单标题')
|
||||
description: Optional[str] = Field(default=None, description='工单描述')
|
||||
status: Optional[str] = Field(default='pending', description='工单处理状态(pending:待处理 progress:处理中 resolved:已解决 closed:已关闭)')
|
||||
priority: Optional[str] = Field(default='medium', description='优先级(low, medium, high, urgent)')
|
||||
type: Optional[str] = Field(default='bug', description='工单类型(bug, feature, task)')
|
||||
assignee_id: Optional[int] = Field(default=None, description='指派给用户ID')
|
||||
reporter_id: int = Field(..., description='报告人ID')
|
||||
project: Optional[str] = Field(default=None, max_length=100, description='所属项目')
|
||||
version: Optional[str] = Field(default=None, max_length=50, description='版本号')
|
||||
|
||||
|
||||
class TicketUpdateSchema(TicketCreateSchema):
|
||||
"""更新工单"""
|
||||
...
|
||||
|
||||
|
||||
class TicketOutSchema(TicketCreateSchema, BaseSchema):
|
||||
"""工单输出"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
...
|
||||
@@ -1,60 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Union
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.core.exceptions import CustomException
|
||||
from .crud import TicketCRUD
|
||||
from .schema import TicketCreateSchema, TicketUpdateSchema, TicketOutSchema
|
||||
from .param import TicketQueryParam
|
||||
|
||||
|
||||
class TicketService:
|
||||
"""工单模块服务层"""
|
||||
|
||||
@classmethod
|
||||
async def get_ticket_detail_service(cls, auth: AuthSchema, id: int) -> Dict:
|
||||
"""获取工单详情"""
|
||||
obj = await TicketCRUD(auth).get_by_id_crud(id=id)
|
||||
return TicketOutSchema.model_validate(obj).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def get_ticket_list_service(cls, auth: AuthSchema, search: Optional[TicketQueryParam] = None, order_by: Optional[Union[str, List[Dict[str, str]]]] = None) -> List[Dict]:
|
||||
"""获取工单列表"""
|
||||
# 处理排序参数
|
||||
processed_order_by = None
|
||||
if order_by:
|
||||
if isinstance(order_by, str):
|
||||
processed_order_by = eval(order_by)
|
||||
else:
|
||||
processed_order_by = order_by
|
||||
|
||||
obj_list = await TicketCRUD(auth).get_list_crud(search=search.__dict__ if search else None, order_by=processed_order_by)
|
||||
return [TicketOutSchema.model_validate(obj).model_dump() for obj in obj_list]
|
||||
|
||||
@classmethod
|
||||
async def create_ticket_service(cls, auth: AuthSchema, data: TicketCreateSchema) -> Dict:
|
||||
"""创建工单"""
|
||||
obj = await TicketCRUD(auth).create_crud(data=data)
|
||||
return TicketOutSchema.model_validate(obj).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def update_ticket_service(cls, auth: AuthSchema, id: int, data: TicketUpdateSchema) -> Dict:
|
||||
"""更新工单"""
|
||||
# 检查工单是否存在
|
||||
obj = await TicketCRUD(auth).get_by_id_crud(id=id)
|
||||
if not obj:
|
||||
raise CustomException(msg='更新失败,该工单不存在')
|
||||
|
||||
obj = await TicketCRUD(auth).update_crud(id=id, data=data)
|
||||
return TicketOutSchema.model_validate(obj).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def delete_ticket_service(cls, auth: AuthSchema, ids: List[int]) -> None:
|
||||
"""删除工单"""
|
||||
if len(ids) < 1:
|
||||
raise CustomException(msg='删除失败,删除对象不能为空')
|
||||
for id in ids:
|
||||
obj = await TicketCRUD(auth).get_by_id_crud(id=id)
|
||||
if not obj:
|
||||
raise CustomException(msg='删除失败,该工单不存在')
|
||||
await TicketCRUD(auth).delete_crud(ids=ids)
|
||||
@@ -1,71 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, Path
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from app.common.response import SuccessResponse
|
||||
from app.common.request import PaginationService
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.logger import logger
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .param import VersionQueryParam
|
||||
from .service import VersionService
|
||||
from .schema import VersionCreateSchema, VersionUpdateSchema
|
||||
|
||||
|
||||
VersionRouter = APIRouter(route_class=OperationLogRoute, prefix="/version", tags=["版本管理"])
|
||||
|
||||
|
||||
@VersionRouter.get("/detail/{id}", summary="获取版本详情", description="获取版本详情")
|
||||
async def get_version_detail_controller(
|
||||
id: int = Path(..., description="版本ID"),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:version:query"]))
|
||||
) -> JSONResponse:
|
||||
result_dict = await VersionService.get_version_detail_service(auth=auth, id=id)
|
||||
logger.info(f"获取版本详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取版本详情成功")
|
||||
|
||||
|
||||
@VersionRouter.get("/list", summary="查询版本列表", description="查询版本列表")
|
||||
async def get_version_list_controller(
|
||||
page: PaginationQueryParam = Depends(),
|
||||
search: VersionQueryParam = Depends(),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:version:query"]))
|
||||
) -> JSONResponse:
|
||||
result_dict_list = await VersionService.get_version_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="查询版本列表成功")
|
||||
|
||||
|
||||
@VersionRouter.post("/create", summary="创建版本", description="创建版本")
|
||||
async def create_version_controller(
|
||||
data: VersionCreateSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:version:create"]))
|
||||
) -> JSONResponse:
|
||||
result_dict = await VersionService.create_version_service(auth=auth, data=data)
|
||||
logger.info(f"创建版本成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建版本成功")
|
||||
|
||||
|
||||
@VersionRouter.put("/update/{id}", summary="修改版本", description="修改版本")
|
||||
async def update_version_controller(
|
||||
data: VersionUpdateSchema,
|
||||
id: int = Path(..., description="版本ID"),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:version:update"]))
|
||||
) -> JSONResponse:
|
||||
result_dict = await VersionService.update_version_service(auth=auth, id=id, data=data)
|
||||
logger.info(f"修改版本成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改版本成功")
|
||||
|
||||
|
||||
@VersionRouter.delete("/delete", summary="删除版本", description="删除版本")
|
||||
async def delete_version_controller(
|
||||
ids: list[int] = Body(..., description="ID列表"),
|
||||
auth: AuthSchema = Depends(AuthPermission(permissions=["system:version:delete"]))
|
||||
) -> JSONResponse:
|
||||
await VersionService.delete_version_service(auth=auth, ids=ids)
|
||||
logger.info(f"删除版本成功: {ids}")
|
||||
return SuccessResponse(msg="删除版本成功")
|
||||
@@ -1,96 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from datetime import datetime
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .model import VersionModel
|
||||
from .schema import VersionCreateSchema, VersionUpdateSchema
|
||||
|
||||
|
||||
class VersionCRUD(CRUDBase[VersionModel, VersionCreateSchema, VersionUpdateSchema]):
|
||||
"""版本模块数据层"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
"""初始化版本CRUD"""
|
||||
self.auth = auth
|
||||
super().__init__(model=VersionModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[VersionModel]:
|
||||
"""根据id获取版本信息
|
||||
|
||||
:param id: 版本ID
|
||||
:return: 版本信息
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[VersionModel]:
|
||||
"""获取版本列表
|
||||
|
||||
:param search: 搜索条件
|
||||
:param order_by: 排序字段
|
||||
:return: 版本列表
|
||||
"""
|
||||
return await self.list(search=search or {}, order_by=order_by or [])
|
||||
|
||||
async def create_crud(self, data: VersionCreateSchema) -> Optional[VersionModel]:
|
||||
"""创建版本
|
||||
|
||||
:param data: 版本创建数据
|
||||
:return: 创建的版本
|
||||
"""
|
||||
# 处理 released_at 字段,确保字符串转换为 datetime 对象
|
||||
data_dict = data.model_dump()
|
||||
if data_dict.get('released_at') and isinstance(data_dict['released_at'], str):
|
||||
try:
|
||||
data_dict['released_at'] = datetime.strptime(data_dict['released_at'], '%Y-%m-%d %H:%M:%S')
|
||||
except ValueError:
|
||||
# 如果格式不正确,让数据库层处理错误
|
||||
pass
|
||||
|
||||
return await self.create(data=data_dict)
|
||||
|
||||
async def update_crud(self, id: int, data: VersionUpdateSchema) -> Optional[VersionModel]:
|
||||
"""更新版本
|
||||
|
||||
:param id: 版本ID
|
||||
:param data: 版本更新数据
|
||||
:return: 更新的版本
|
||||
"""
|
||||
# 处理 released_at 字段,确保字符串转换为 datetime 对象
|
||||
data_dict = data.model_dump(exclude_unset=True, exclude={"id"})
|
||||
if data_dict.get('released_at') and isinstance(data_dict['released_at'], str):
|
||||
try:
|
||||
data_dict['released_at'] = datetime.strptime(data_dict['released_at'], '%Y-%m-%d %H:%M:%S')
|
||||
except ValueError:
|
||||
# 如果格式不正确,让数据库层处理错误
|
||||
pass
|
||||
|
||||
return await self.update(id=id, data=data_dict)
|
||||
|
||||
async def delete_crud(self, ids: List[int]) -> None:
|
||||
"""批量删除版本
|
||||
|
||||
:param ids: 版本ID列表
|
||||
"""
|
||||
return await self.delete(ids=ids)
|
||||
|
||||
async def get_by_number_crud(self, version_number: str) -> Optional[VersionModel]:
|
||||
"""根据版本号获取版本
|
||||
|
||||
:param version_number: 版本号
|
||||
:return: 版本信息
|
||||
"""
|
||||
return await self.get(version_number=version_number)
|
||||
|
||||
async def get_by_status_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[VersionModel]:
|
||||
"""根据状态获取版本列表
|
||||
|
||||
:param search: 搜索条件
|
||||
:param order_by: 排序字段
|
||||
:return: 版本列表
|
||||
"""
|
||||
if search is None:
|
||||
search = {}
|
||||
return await self.list(search=search, order_by=order_by or [])
|
||||
@@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Optional
|
||||
from sqlalchemy import String, Text, DateTime
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from datetime import datetime
|
||||
|
||||
from app.core.base_model import CreatorMixin
|
||||
|
||||
|
||||
class VersionModel(CreatorMixin):
|
||||
"""
|
||||
版本模型 - SQLAlchemy 2.0 语法
|
||||
兼容 MySQL 和 PostgreSQL
|
||||
"""
|
||||
__tablename__ = 'system_version'
|
||||
__table_args__ = ({'comment': '版本表'})
|
||||
|
||||
version_number: Mapped[str] = mapped_column(String(50), nullable=False, comment='版本号')
|
||||
title: Mapped[str] = mapped_column(String(255), nullable=False, comment='版本标题')
|
||||
release_notes: Mapped[Optional[str]] = mapped_column(Text, comment='发布说明')
|
||||
status: Mapped[str] = mapped_column(String(50), default='draft', comment='版本状态(draft: 草稿, released: 已发布, archived: 已归档)')
|
||||
project: Mapped[Optional[str]] = mapped_column(String(100), comment='所属项目')
|
||||
released_at: Mapped[Optional[datetime]] = mapped_column(DateTime, comment='发布时间')
|
||||
@@ -1,34 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from fastapi import Query
|
||||
|
||||
from app.core.validator import DateTimeStr
|
||||
|
||||
|
||||
class VersionQueryParam:
|
||||
"""版本管理查询参数"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
title: Optional[str] = Query(None, description="版本标题"),
|
||||
status: Optional[str] = Query(None, description="版本状态"),
|
||||
creator: Optional[int] = Query(None, description="创建人"),
|
||||
start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2023-01-01 00:00:00"),
|
||||
end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2023-12-31 23:59:59"),
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
# 模糊查询字段
|
||||
self.title = ("like", title)
|
||||
|
||||
# 精确查询字段
|
||||
self.creator_id = creator
|
||||
self.status = status
|
||||
|
||||
# 时间范围查询
|
||||
if start_time and end_time:
|
||||
start_datetime = datetime.strptime(str(start_time), '%Y-%m-%d %H:%M:%S')
|
||||
end_datetime = datetime.strptime(str(end_time), '%Y-%m-%d %H:%M:%S')
|
||||
self.created_at = ("between", (start_datetime, end_datetime))
|
||||
@@ -1,30 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from datetime import datetime
|
||||
|
||||
from app.core.base_schema import BaseSchema
|
||||
from app.core.validator import DateTimeStr
|
||||
|
||||
|
||||
class VersionCreateSchema(BaseModel):
|
||||
"""创建版本"""
|
||||
version_number: str = Field(..., max_length=50, description='版本号')
|
||||
title: str = Field(..., max_length=255, description='版本标题')
|
||||
release_notes: Optional[str] = Field(default=None, description='发布说明')
|
||||
description: Optional[str] = Field(default=None, description='工单描述')
|
||||
status: Optional[str] = Field(default='draft', description='版本状态(draft: 草稿, released: 已发布, archived: 已归档)')
|
||||
project: Optional[str] = Field(default=None, max_length=100, description='所属项目')
|
||||
released_at: Optional[DateTimeStr] = Field(default=None, description='发布时间')
|
||||
|
||||
|
||||
class VersionUpdateSchema(VersionCreateSchema):
|
||||
"""更新版本"""
|
||||
...
|
||||
|
||||
|
||||
class VersionOutSchema(VersionCreateSchema, BaseSchema):
|
||||
"""版本输出"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
...
|
||||
@@ -1,87 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Union
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.core.exceptions import CustomException
|
||||
from .crud import VersionCRUD
|
||||
from .schema import VersionCreateSchema, VersionUpdateSchema, VersionOutSchema
|
||||
from .param import VersionQueryParam
|
||||
|
||||
|
||||
class VersionService:
|
||||
"""版本模块服务层"""
|
||||
|
||||
@classmethod
|
||||
async def get_version_detail_service(cls, auth: AuthSchema, id: int) -> Dict:
|
||||
"""获取版本详情"""
|
||||
obj = await VersionCRUD(auth).get_by_id_crud(id=id)
|
||||
return VersionOutSchema.model_validate(obj).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def get_version_list_service(cls, auth: AuthSchema, search: Optional[VersionQueryParam] = None, order_by: Optional[Union[str, List[Dict[str, str]]]] = None) -> List[Dict]:
|
||||
"""获取版本列表"""
|
||||
# 处理排序参数
|
||||
processed_order_by = None
|
||||
if order_by:
|
||||
if isinstance(order_by, str):
|
||||
processed_order_by = eval(order_by)
|
||||
else:
|
||||
processed_order_by = order_by
|
||||
|
||||
obj_list = await VersionCRUD(auth).get_list_crud(search=search.__dict__ if search else None, order_by=processed_order_by)
|
||||
return [VersionOutSchema.model_validate(obj).model_dump() for obj in obj_list]
|
||||
|
||||
@classmethod
|
||||
async def create_version_service(cls, auth: AuthSchema, data: VersionCreateSchema) -> Dict:
|
||||
"""创建版本"""
|
||||
# 检查版本号是否已存在
|
||||
exist_obj = await VersionCRUD(auth).get_by_number_crud(version_number=data.version_number)
|
||||
if exist_obj:
|
||||
raise CustomException(msg='创建失败,版本号已存在')
|
||||
obj = await VersionCRUD(auth).create_crud(data=data)
|
||||
return VersionOutSchema.model_validate(obj).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def update_version_service(cls, auth: AuthSchema, id: int, data: VersionUpdateSchema) -> Dict:
|
||||
"""更新版本"""
|
||||
# 检查版本是否存在
|
||||
obj = await VersionCRUD(auth).get_by_id_crud(id=id)
|
||||
if not obj:
|
||||
raise CustomException(msg='更新失败,该版本不存在')
|
||||
|
||||
# 检查版本号是否重复(如果提供了版本号)
|
||||
if data.version_number:
|
||||
exist_obj = await VersionCRUD(auth).get_by_number_crud(version_number=data.version_number)
|
||||
if exist_obj and exist_obj.id != id:
|
||||
raise CustomException(msg='更新失败,版本号已存在')
|
||||
|
||||
obj = await VersionCRUD(auth).update_crud(id=id, data=data)
|
||||
return VersionOutSchema.model_validate(obj).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def delete_version_service(cls, auth: AuthSchema, ids: List[int]) -> None:
|
||||
"""删除版本"""
|
||||
if len(ids) < 1:
|
||||
raise CustomException(msg='删除失败,删除对象不能为空')
|
||||
for id in ids:
|
||||
obj = await VersionCRUD(auth).get_by_id_crud(id=id)
|
||||
if not obj:
|
||||
raise CustomException(msg='删除失败,该版本不存在')
|
||||
await VersionCRUD(auth).delete_crud(ids=ids)
|
||||
|
||||
@classmethod
|
||||
async def get_version_by_status_service(cls, auth: AuthSchema, search: Optional[VersionQueryParam] = None, order_by: Optional[Union[str, List[Dict[str, str]]]] = None) -> List[Dict]:
|
||||
"""根据状态获取版本列表"""
|
||||
# 处理排序参数
|
||||
processed_order_by = None
|
||||
if order_by:
|
||||
if isinstance(order_by, str):
|
||||
processed_order_by = eval(order_by)
|
||||
else:
|
||||
processed_order_by = order_by
|
||||
|
||||
obj_list = await VersionCRUD(auth).get_by_status_crud(
|
||||
search=search.__dict__ if search else None,
|
||||
order_by=processed_order_by
|
||||
)
|
||||
return [VersionOutSchema.model_validate(obj).model_dump() for obj in obj_list]
|
||||
Reference in New Issue
Block a user