mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-23 05:10:57 +00:00
refactor: 完成项目大规模重构与功能优化
这是一次综合性的项目迭代,包含以下核心变更:
1. **目录与模块重构**
- 调整工作流节点类型模块目录结构,迁移节点类型相关代码
- 重命名platform模块为system模块,更新插件配置信息
- 重构代码生成模块导入路径
2. **数据库与CRUD优化**
- 统一所有CRUD类构造函数,新增数据库会话参数
- 修复权限过滤器数据库会话使用问题
- 更新模板生成器的CRUD代码模板
3. **认证与安全改进**
- 重构JWT密钥配置,移除默认密钥强制要求环境变量
- 重命名密码工具类,统一密码加密校验逻辑
- 优化OAuth认证流程,修复匿名认证使用问题
4. **前端与静态资源**
- 重构前端挂载逻辑,增加目录存在性校验
- 使用标准StaticFiles替换自定义前端挂载实现
5. **工具类与依赖更新**
- 修复导入工具的表名重复检测逻辑
- 优化限流回调代码,移除冗余依赖
- 更新用户、租户等模块的响应模型字段
6. **数据与配置修正**
- 修复系统版本数据字段命名不统一问题
- 简化枚举类校验逻辑,移除冗余注释
- 修复测试用例中的密码工具类导入错误
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Body, Path, Query, Security, status
|
||||
from fastapi import APIRouter, Body, Depends, Path, Query, Security, status
|
||||
from fastapi.responses import JSONResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.common.response import ResponseSchema, StreamResponse, SuccessResponse
|
||||
from app.core.base_schema import AuthSchema, PageResultSchema, PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.dependencies import AuthPermission, db_getter
|
||||
from app.core.logger import logger
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.utils.common_util import bytes2file_response
|
||||
@@ -21,11 +22,12 @@ async def gen_table_list_controller(
|
||||
auth: Annotated[AuthSchema, Security(AuthPermission(["module_generator:gencode:query"]))],
|
||||
page: Annotated[PaginationQueryParam, Query(description="分页参数")],
|
||||
search: Annotated[GenTableQueryParam, Query(description="查询参数")],
|
||||
db: Annotated[AsyncSession, Depends(db_getter)],
|
||||
) -> JSONResponse:
|
||||
order_by = [{"created_time": "desc"}]
|
||||
if page.order_by:
|
||||
order_by = page.order_by
|
||||
result_dict = await GenTableService(auth).get_gen_table_page(
|
||||
result_dict = await GenTableService(auth, db).get_gen_table_page(
|
||||
page_no=page.page_no,
|
||||
page_size=page.page_size,
|
||||
search=search,
|
||||
@@ -39,8 +41,9 @@ async def get_gen_db_table_list_controller(
|
||||
auth: Annotated[AuthSchema, Security(AuthPermission(["module_generator:dblist:query"]))],
|
||||
page: Annotated[PaginationQueryParam, Query(description="分页参数")],
|
||||
search: Annotated[GenTableQueryParam, Query(description="查询参数")],
|
||||
db: Annotated[AsyncSession, Depends(db_getter)],
|
||||
) -> JSONResponse:
|
||||
result_dict = await GenTableService(auth).get_gen_db_table_page(
|
||||
result_dict = await GenTableService(auth, db).get_gen_db_table_page(
|
||||
page_no=page.page_no,
|
||||
page_size=page.page_size,
|
||||
search=search,
|
||||
@@ -52,8 +55,9 @@ async def get_gen_db_table_list_controller(
|
||||
async def import_gen_table_controller(
|
||||
auth: Annotated[AuthSchema, Security(AuthPermission(["module_generator:gencode:import"]))],
|
||||
table_names: Annotated[list[str], Body(description="表名列表")],
|
||||
db: Annotated[AsyncSession, Depends(db_getter)],
|
||||
) -> JSONResponse:
|
||||
svc = GenTableService(auth)
|
||||
svc = GenTableService(auth, db)
|
||||
add_gen_table_list = await svc.get_gen_db_table_list_by_name(table_names)
|
||||
result = await svc.import_gen_table(add_gen_table_list)
|
||||
return SuccessResponse(msg="导入表结构成功", data=result)
|
||||
@@ -63,8 +67,9 @@ async def import_gen_table_controller(
|
||||
async def gen_table_detail_controller(
|
||||
auth: Annotated[AuthSchema, Security(AuthPermission(["module_generator:gencode:query"]))],
|
||||
table_id: Annotated[int, Path(description="业务表ID")],
|
||||
db: Annotated[AsyncSession, Depends(db_getter)],
|
||||
) -> JSONResponse:
|
||||
result = await GenTableService(auth).get_gen_table_detail(table_id)
|
||||
result = await GenTableService(auth, db).get_gen_table_detail(table_id)
|
||||
return SuccessResponse(data=result, msg="获取业务表详细信息成功")
|
||||
|
||||
|
||||
@@ -72,8 +77,9 @@ async def gen_table_detail_controller(
|
||||
async def create_table_controller(
|
||||
auth: Annotated[AuthSchema, Security(AuthPermission(["module_generator:gencode:create"]))],
|
||||
body: Annotated[GenCreateTableSqlBody, Body(description="创建表结构参数")],
|
||||
db: Annotated[AsyncSession, Depends(db_getter)],
|
||||
) -> JSONResponse:
|
||||
result = await GenTableService(auth).create_table(body.sql)
|
||||
result = await GenTableService(auth, db).create_table(body.sql)
|
||||
return SuccessResponse(msg="创建表结构成功", data=result)
|
||||
|
||||
|
||||
@@ -82,8 +88,9 @@ async def update_gen_table_controller(
|
||||
auth: Annotated[AuthSchema, Security(AuthPermission(["module_generator:gencode:update"]))],
|
||||
table_id: Annotated[int, Path(description="业务表ID")],
|
||||
data: Annotated[GenTableSchema, Body(description="业务表信息")],
|
||||
db: Annotated[AsyncSession, Depends(db_getter)],
|
||||
) -> JSONResponse:
|
||||
result_dict = await GenTableService(auth).update_gen_table(data, table_id)
|
||||
result_dict = await GenTableService(auth, db).update_gen_table(data, table_id)
|
||||
return SuccessResponse(data=result_dict, msg="编辑业务表信息成功")
|
||||
|
||||
|
||||
@@ -91,8 +98,9 @@ async def update_gen_table_controller(
|
||||
async def delete_gen_table_controller(
|
||||
auth: Annotated[AuthSchema, Security(AuthPermission(["module_generator:gencode:delete"]))],
|
||||
ids: Annotated[list[int], Body(description="业务表ID列表")],
|
||||
db: Annotated[AsyncSession, Depends(db_getter)],
|
||||
) -> JSONResponse:
|
||||
result = await GenTableService(auth).delete_gen_table(ids)
|
||||
result = await GenTableService(auth, db).delete_gen_table(ids)
|
||||
return SuccessResponse(msg="删除业务表信息成功", data=result)
|
||||
|
||||
|
||||
@@ -100,8 +108,9 @@ async def delete_gen_table_controller(
|
||||
async def batch_gen_code_controller(
|
||||
auth: Annotated[AuthSchema, Security(AuthPermission(["module_generator:gencode:operate"]))],
|
||||
table_names: Annotated[list[str], Body(description="表名列表")],
|
||||
db: Annotated[AsyncSession, Depends(db_getter)],
|
||||
) -> StreamResponse:
|
||||
batch_gen_code_result, failed_tables = await GenTableService(auth).batch_gen_code(table_names)
|
||||
batch_gen_code_result, failed_tables = await GenTableService(auth, db).batch_gen_code(table_names)
|
||||
headers = {"Content-Disposition": "attachment; filename=code.zip"}
|
||||
if failed_tables:
|
||||
logger.warning(f"批量生成代码部分失败,跳过表: {failed_tables}")
|
||||
@@ -117,8 +126,9 @@ async def batch_gen_code_controller(
|
||||
async def gen_code_local_controller(
|
||||
auth: Annotated[AuthSchema, Security(AuthPermission(["module_generator:gencode:code"]))],
|
||||
table_name: Annotated[str, Path(description="表名")],
|
||||
db: Annotated[AsyncSession, Depends(db_getter)],
|
||||
) -> JSONResponse:
|
||||
result = await GenTableService(auth).generate_code(table_name)
|
||||
result = await GenTableService(auth, db).generate_code(table_name)
|
||||
return SuccessResponse(msg="生成代码到指定路径成功", data=result)
|
||||
|
||||
|
||||
@@ -126,8 +136,9 @@ async def gen_code_local_controller(
|
||||
async def preview_code_controller(
|
||||
auth: Annotated[AuthSchema, Security(AuthPermission(["module_generator:gencode:query"]))],
|
||||
table_id: Annotated[int, Path(description="业务表ID")],
|
||||
db: Annotated[AsyncSession, Depends(db_getter)],
|
||||
) -> JSONResponse:
|
||||
result = await GenTableService(auth).preview_code(table_id)
|
||||
result = await GenTableService(auth, db).preview_code(table_id)
|
||||
return SuccessResponse(data=result, msg="预览代码成功")
|
||||
|
||||
|
||||
@@ -135,8 +146,9 @@ async def preview_code_controller(
|
||||
async def sync_db_controller(
|
||||
auth: Annotated[AuthSchema, Security(AuthPermission(["module_generator:db:sync"]))],
|
||||
table_name: Annotated[str, Path(description="表名")],
|
||||
db: Annotated[AsyncSession, Depends(db_getter)],
|
||||
) -> JSONResponse:
|
||||
result = await GenTableService(auth).sync_db(table_name)
|
||||
result = await GenTableService(auth, db).sync_db(table_name)
|
||||
return SuccessResponse(msg="同步数据库成功", data=result)
|
||||
|
||||
|
||||
@@ -144,6 +156,7 @@ async def sync_db_controller(
|
||||
async def sync_db_preview_controller(
|
||||
auth: Annotated[AuthSchema, Security(AuthPermission(["module_generator:db:sync"]))],
|
||||
table_name: Annotated[str, Path(description="表名")],
|
||||
db: Annotated[AsyncSession, Depends(db_getter)],
|
||||
) -> JSONResponse:
|
||||
result = await GenTableService(auth).sync_db_preview(table_name)
|
||||
result = await GenTableService(auth, db).sync_db_preview(table_name)
|
||||
return SuccessResponse(msg="获取同步差异预览成功", data=result)
|
||||
|
||||
@@ -3,6 +3,7 @@ from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import Inspector, inspect, select, text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.config.setting import settings
|
||||
from app.core.base_crud import CRUDBase
|
||||
@@ -25,13 +26,14 @@ if TYPE_CHECKING:
|
||||
class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
"""代码生成业务表模块数据库操作层"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
def __init__(self, auth: AuthSchema, db: AsyncSession) -> None:
|
||||
"""初始化CRUD操作层
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
- db (AsyncSession): 数据库会话
|
||||
"""
|
||||
super().__init__(model=GenTableModel, auth=auth)
|
||||
super().__init__(model=GenTableModel, auth=auth, db=db)
|
||||
|
||||
async def get_gen_table_by_id(self, table_id: int, preload: list | None = None) -> GenTableModel | None:
|
||||
"""根据业务表ID获取需要生成的业务表信息。
|
||||
@@ -224,9 +226,9 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
|
||||
count_sql = text(f"SELECT COUNT(1) AS cnt FROM information_schema.tables {where_sql}")
|
||||
rows_sql = text(f"SELECT table_name, table_comment FROM information_schema.tables {where_sql} ORDER BY table_name ASC LIMIT :limit OFFSET :offset")
|
||||
total_res = await self.auth.db.execute(count_sql, params)
|
||||
total_res = await self.db.execute(count_sql, params)
|
||||
total = int(total_res.scalar() or 0)
|
||||
res = await self.auth.db.execute(rows_sql, params)
|
||||
res = await self.db.execute(rows_sql, params)
|
||||
items: list[dict] = []
|
||||
for r in res.fetchall():
|
||||
# r may be Row/tuple depending on driver
|
||||
@@ -257,9 +259,9 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
base_from = "FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_catalog.pg_description d ON d.objoid = c.oid AND d.objsubid = 0 "
|
||||
count_sql = text(f"SELECT COUNT(1) AS cnt {base_from} {where_sql}")
|
||||
rows_sql = text(f"SELECT c.relname AS table_name, COALESCE(d.description,'') AS table_comment {base_from} {where_sql} ORDER BY c.relname ASC LIMIT :limit OFFSET :offset")
|
||||
total_res = await self.auth.db.execute(count_sql, params)
|
||||
total_res = await self.db.execute(count_sql, params)
|
||||
total = int(total_res.scalar() or 0)
|
||||
res = await self.auth.db.execute(rows_sql, params)
|
||||
res = await self.db.execute(rows_sql, params)
|
||||
items = []
|
||||
for r in res.fetchall():
|
||||
table_name = r[0]
|
||||
@@ -368,7 +370,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
"""
|
||||
try:
|
||||
# 执行SQL但不手动提交事务,由框架管理事务生命周期
|
||||
await self.auth.db.execute(text(sql))
|
||||
await self.db.execute(text(sql))
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"执行SQL时发生错误: {e}")
|
||||
@@ -378,13 +380,14 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, GenTableColumnSchema]):
|
||||
"""代码生成业务表字段模块数据库操作层"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
def __init__(self, auth: AuthSchema, db: AsyncSession) -> None:
|
||||
"""初始化CRUD操作层
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
- db (AsyncSession): 数据库会话
|
||||
"""
|
||||
super().__init__(model=GenTableColumnModel, auth=auth)
|
||||
super().__init__(model=GenTableColumnModel, auth=auth, db=db)
|
||||
|
||||
@staticmethod
|
||||
def _sync_get_table_columns(database_type: str, table_name: str) -> list[dict]:
|
||||
@@ -580,7 +583,7 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
"""
|
||||
# 先查询出这些表ID对应的所有字段ID
|
||||
query = select(GenTableColumnModel.id).where(GenTableColumnModel.table_id.in_(table_ids))
|
||||
result = await self.auth.db.execute(query)
|
||||
result = await self.db.execute(query)
|
||||
column_ids = [row[0] for row in result.fetchall()]
|
||||
|
||||
# 如果有字段ID,则删除这些字段
|
||||
|
||||
@@ -7,6 +7,7 @@ from typing import Any
|
||||
|
||||
import anyio
|
||||
import sqlglot
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlglot.expressions import (
|
||||
Alter,
|
||||
Comment,
|
||||
@@ -69,8 +70,9 @@ _MENU_TYPE_MENU = 2
|
||||
class GenTableService:
|
||||
"""代码生成业务表服务层"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
def __init__(self, auth: AuthSchema, db: AsyncSession) -> None:
|
||||
self.auth = auth
|
||||
self.db = db
|
||||
|
||||
async def _effective_package_name(self, parent_catalog_id: int | None, package_name: str | None) -> str:
|
||||
"""根据「是否选择上级目录」计算最终包名(分系统根目录)。
|
||||
@@ -84,7 +86,7 @@ class GenTableService:
|
||||
if parent_catalog_id is not None:
|
||||
from app.api.v1.module_platform.menu.crud import MenuCRUD
|
||||
|
||||
m = await MenuCRUD(self.auth).get(id=parent_catalog_id)
|
||||
m = await MenuCRUD(self.auth, self.db).get(id=parent_catalog_id)
|
||||
if not m:
|
||||
raise CustomException(msg="上级菜单不存在")
|
||||
route_path = (getattr(m, "route_path", None) or "").strip()
|
||||
@@ -109,7 +111,7 @@ class GenTableService:
|
||||
return
|
||||
from app.api.v1.module_platform.menu.crud import MenuCRUD
|
||||
|
||||
m = await MenuCRUD(self.auth).get(id=parent_menu_id)
|
||||
m = await MenuCRUD(self.auth, self.db).get(id=parent_menu_id)
|
||||
if not m:
|
||||
raise CustomException(msg="上级菜单不存在")
|
||||
if m.type != _MENU_TYPE_CATALOG:
|
||||
@@ -246,7 +248,7 @@ class GenTableService:
|
||||
返回:
|
||||
- list[dict]: 包含业务表列表信息的字典列表。
|
||||
"""
|
||||
gen_table_list_result = await GenTableCRUD(auth=self.auth).get_gen_table_list(search)
|
||||
gen_table_list_result = await GenTableCRUD(self.auth, self.db).get_gen_table_list(search)
|
||||
return [GenTableOutSchema.model_validate(obj).model_dump() for obj in gen_table_list_result]
|
||||
|
||||
@handle_service_exception
|
||||
@@ -271,7 +273,7 @@ class GenTableService:
|
||||
"""
|
||||
offset = (page_no - 1) * page_size
|
||||
order = order_by or [{"created_time": "desc"}]
|
||||
return await GenTableCRUD(auth=self.auth).page(
|
||||
return await GenTableCRUD(self.auth, self.db).page(
|
||||
offset=offset,
|
||||
limit=page_size,
|
||||
order_by=order,
|
||||
@@ -290,7 +292,7 @@ class GenTableService:
|
||||
返回:
|
||||
- list[Any]: 包含数据库表列表信息的任意类型列表。
|
||||
"""
|
||||
gen_db_table_list_result = await GenTableCRUD(auth=self.auth).get_db_table_list(search)
|
||||
gen_db_table_list_result = await GenTableCRUD(self.auth, self.db).get_db_table_list(search)
|
||||
return gen_db_table_list_result
|
||||
|
||||
@handle_service_exception
|
||||
@@ -312,7 +314,7 @@ class GenTableService:
|
||||
- dict[str, Any]: 含 items、total、has_next 等字段。
|
||||
"""
|
||||
offset = (page_no - 1) * page_size
|
||||
items, total = await GenTableCRUD(auth=self.auth).get_db_table_page(search=search, offset=offset, limit=page_size)
|
||||
items, total = await GenTableCRUD(self.auth, self.db).get_db_table_page(search=search, offset=offset, limit=page_size)
|
||||
return {
|
||||
"items": items,
|
||||
"total": total,
|
||||
@@ -332,7 +334,7 @@ class GenTableService:
|
||||
返回:
|
||||
- list[GenTableOutSchema]: 包含业务表详细信息的模型列表。
|
||||
"""
|
||||
gen_db_table_list_result = await GenTableCRUD(auth=self.auth).get_db_table_list_by_names(table_names)
|
||||
gen_db_table_list_result = await GenTableCRUD(self.auth, self.db).get_db_table_list_by_names(table_names)
|
||||
|
||||
# 修复:将GenDBTableSchema对象转换为字典后再传递给GenTableOutSchema
|
||||
result = [GenTableOutSchema(**gen_table.model_dump()) for gen_table in gen_db_table_list_result]
|
||||
@@ -359,14 +361,14 @@ class GenTableService:
|
||||
self.normalize_and_validate_master_sub(GenTableSchema.model_validate(_row))
|
||||
table_name = table.table_name
|
||||
# 检查表是否已存在
|
||||
existing_table = await GenTableCRUD(auth=self.auth).get_gen_table_by_name(table_name)
|
||||
existing_table = await GenTableCRUD(self.auth, self.db).get_gen_table_by_name(table_name)
|
||||
if existing_table:
|
||||
raise CustomException(msg=f"以下表已存在,不能重复导入: {table_name}")
|
||||
GenUtils.init_table(table)
|
||||
if not table.columns:
|
||||
table.columns = []
|
||||
add_gen_table = await GenTableCRUD(auth=self.auth).add_gen_table(GenTableSchema.model_validate(table.model_dump()))
|
||||
gen_table_columns = await GenTableColumnCRUD(auth=self.auth).get_gen_db_table_columns_by_name(table_name)
|
||||
add_gen_table = await GenTableCRUD(self.auth, self.db).add_gen_table(GenTableSchema.model_validate(table.model_dump()))
|
||||
gen_table_columns = await GenTableColumnCRUD(self.auth, self.db).get_gen_db_table_columns_by_name(table_name)
|
||||
if len(gen_table_columns) > 0:
|
||||
table.id = add_gen_table.id
|
||||
for column in gen_table_columns:
|
||||
@@ -386,7 +388,7 @@ class GenTableService:
|
||||
python_field=column.python_field,
|
||||
)
|
||||
GenUtils.init_column_field(column_schema, table)
|
||||
await GenTableColumnCRUD(auth=self.auth).create_gen_table_column_crud(column_schema)
|
||||
await GenTableColumnCRUD(self.auth, self.db).create_gen_table_column_crud(column_schema)
|
||||
return True
|
||||
except Exception as e:
|
||||
raise CustomException(msg=f"导入失败, {e!s}")
|
||||
@@ -432,7 +434,7 @@ class GenTableService:
|
||||
table_names = list(set(table_names))
|
||||
|
||||
# 创建CRUD实例
|
||||
gen_table_crud = GenTableCRUD(auth=self.auth)
|
||||
gen_table_crud = GenTableCRUD(self.auth, self.db)
|
||||
|
||||
# 检查每个表是否已存在
|
||||
for table_name in table_names:
|
||||
@@ -492,12 +494,12 @@ class GenTableService:
|
||||
self.normalize_and_validate_master_sub(data)
|
||||
await self._assert_parent_menu_is_catalog(data.parent_menu_id)
|
||||
# 直接调用edit_gen_table方法,它会在内部处理排除嵌套字段的逻辑
|
||||
result = await GenTableCRUD(auth=self.auth).edit_gen_table(table_id, data)
|
||||
result = await GenTableCRUD(self.auth, self.db).edit_gen_table(table_id, data)
|
||||
if not result:
|
||||
raise CustomException(msg="更新业务表信息失败")
|
||||
|
||||
if data.columns is not None:
|
||||
db_columns = await GenTableColumnCRUD(auth=self.auth).list_gen_table_column_crud(search={"table_id": table_id})
|
||||
db_columns = await GenTableColumnCRUD(self.auth, self.db).list_gen_table_column_crud(search={"table_id": table_id})
|
||||
db_column_map = {c.column_name: c for c in db_columns if c.column_name}
|
||||
submitted_names = {c.column_name for c in data.columns if hasattr(c, "column_name") and c.column_name}
|
||||
|
||||
@@ -508,7 +510,7 @@ class GenTableService:
|
||||
# 只更新前端实际修改的字段(利用 Pydantic model_fields_set)
|
||||
update_data = gen_table_column.model_dump(exclude_unset=True, exclude={"id", "super_column"})
|
||||
if update_data:
|
||||
await GenTableColumnCRUD(auth=self.auth).update(id=col_id, data=GenTableColumnSchema(**update_data))
|
||||
await GenTableColumnCRUD(self.auth, self.db).update(id=col_id, data=GenTableColumnSchema(**update_data))
|
||||
else:
|
||||
# 新增列:前端新增但库中无对应记录
|
||||
column_schema = GenTableColumnSchema(
|
||||
@@ -516,16 +518,16 @@ class GenTableService:
|
||||
**gen_table_column.model_dump(exclude={"id", "super_column"}),
|
||||
)
|
||||
GenUtils.init_column_field(column_schema, gen_table_info)
|
||||
await GenTableColumnCRUD(auth=self.auth).create_gen_table_column_crud(column_schema)
|
||||
await GenTableColumnCRUD(self.auth, self.db).create_gen_table_column_crud(column_schema)
|
||||
|
||||
# 删除前端已移除的列
|
||||
for db_name, db_col in db_column_map.items():
|
||||
if db_name not in submitted_names:
|
||||
db_id = getattr(db_col, "id", None)
|
||||
if db_id:
|
||||
await GenTableColumnCRUD(auth=self.auth).delete(ids=[db_id])
|
||||
await GenTableColumnCRUD(self.auth, self.db).delete(ids=[db_id])
|
||||
# 重新获取带有预加载关系的对象,避免懒加载导致的MissingGreenlet错误
|
||||
updated_gen_table = await GenTableCRUD(auth=self.auth).get_gen_table_by_id(table_id)
|
||||
updated_gen_table = await GenTableCRUD(self.auth, self.db).get_gen_table_by_id(table_id)
|
||||
out = GenTableOutSchema.model_validate(updated_gen_table)
|
||||
await self.set_pk_column(out)
|
||||
await self.hydrate_sub_table(out)
|
||||
@@ -554,9 +556,9 @@ class GenTableService:
|
||||
|
||||
try:
|
||||
# 先删除相关的字段信息
|
||||
await GenTableColumnCRUD(auth=self.auth).delete_gen_table_column_by_table_id_crud(ids)
|
||||
await GenTableColumnCRUD(self.auth, self.db).delete_gen_table_column_by_table_id_crud(ids)
|
||||
# 再删除表信息
|
||||
await GenTableCRUD(auth=self.auth).delete_gen_table(ids)
|
||||
await GenTableCRUD(self.auth, self.db).delete_gen_table(ids)
|
||||
except Exception as e:
|
||||
raise CustomException(msg=str(e))
|
||||
|
||||
@@ -571,7 +573,7 @@ class GenTableService:
|
||||
返回:
|
||||
- GenTableOutSchema: 业务表详细信息模型。
|
||||
"""
|
||||
gen_table = await GenTableCRUD(auth=self.auth).get_gen_table_by_id(table_id)
|
||||
gen_table = await GenTableCRUD(self.auth, self.db).get_gen_table_by_id(table_id)
|
||||
if not gen_table:
|
||||
raise CustomException(msg="业务表不存在")
|
||||
|
||||
@@ -590,7 +592,7 @@ class GenTableService:
|
||||
返回:
|
||||
- list[GenTableOutSchema]: 业务表详细信息模型列表。
|
||||
"""
|
||||
gen_table_all = await GenTableCRUD(auth=self.auth).get_gen_table_all() or []
|
||||
gen_table_all = await GenTableCRUD(self.auth, self.db).get_gen_table_all() or []
|
||||
result = []
|
||||
for gen_table in gen_table_all:
|
||||
try:
|
||||
@@ -612,7 +614,7 @@ class GenTableService:
|
||||
返回:
|
||||
- dict[str, Any]: 文件名到渲染内容的映射。
|
||||
"""
|
||||
raw = await GenTableCRUD(auth=self.auth).get_gen_table_by_id(table_id)
|
||||
raw = await GenTableCRUD(self.auth, self.db).get_gen_table_by_id(table_id)
|
||||
if not raw:
|
||||
raise CustomException(msg="业务表不存在")
|
||||
gen_table = GenTableOutSchema.model_validate(raw)
|
||||
@@ -735,7 +737,7 @@ class GenTableService:
|
||||
await _write_templates(sub_templates, sub_ctx, gen_table_schema.sub_table)
|
||||
|
||||
# 2. 代码成功写入后,再创建菜单(避免失败时产生孤儿菜单数据)
|
||||
menu_crud = MenuCRUD(self.auth)
|
||||
menu_crud = MenuCRUD(self.auth, self.db)
|
||||
await self._assert_parent_menu_is_catalog(gen_table_schema.parent_menu_id)
|
||||
# 1. 目录 + 菜单 + 按钮:先取/建模块目录(名称规则见 _catalog_menu_dir_key)
|
||||
dir_menu_id = await self._get_or_create_package_directory_menu(
|
||||
@@ -936,7 +938,7 @@ class GenTableService:
|
||||
# 验证表名非空
|
||||
if not table_name or not table_name.strip():
|
||||
raise CustomException(msg="表名不能为空")
|
||||
gen_table = await GenTableCRUD(auth=self.auth).get_gen_table_by_name(table_name)
|
||||
gen_table = await GenTableCRUD(self.auth, self.db).get_gen_table_by_name(table_name)
|
||||
if not gen_table:
|
||||
raise CustomException(msg="业务表不存在")
|
||||
table = GenTableOutSchema.model_validate(gen_table)
|
||||
@@ -945,7 +947,7 @@ class GenTableService:
|
||||
table_columns = table.columns or []
|
||||
table_column_map = {column.column_name: column for column in table_columns}
|
||||
# 确保db_table_columns始终是列表类型,避免None值
|
||||
db_table_columns = await GenTableColumnCRUD(auth=self.auth).get_gen_db_table_columns_by_name(table_name) or []
|
||||
db_table_columns = await GenTableColumnCRUD(self.auth, self.db).get_gen_db_table_columns_by_name(table_name) or []
|
||||
db_table_columns = [col for col in db_table_columns if col is not None]
|
||||
db_table_column_names = [column.column_name for column in db_table_columns]
|
||||
try:
|
||||
@@ -990,26 +992,26 @@ class GenTableService:
|
||||
# 转换为 GenTableColumnSchema,排除 super_column 等输出专用字段
|
||||
column_data = GenTableColumnSchema(**column.model_dump(exclude={"super_column"}))
|
||||
if hasattr(column, "id") and column.id:
|
||||
await GenTableColumnCRUD(auth=self.auth).update_gen_table_column_crud(column.id, column_data)
|
||||
await GenTableColumnCRUD(self.auth, self.db).update_gen_table_column_crud(column.id, column_data)
|
||||
else:
|
||||
await GenTableColumnCRUD(auth=self.auth).create_gen_table_column_crud(column_data)
|
||||
await GenTableColumnCRUD(self.auth, self.db).create_gen_table_column_crud(column_data)
|
||||
else:
|
||||
# 设置table_id以确保新字段能正确关联到表
|
||||
column.table_id = table.id
|
||||
# 转换为 GenTableColumnSchema,排除 super_column 等输出专用字段
|
||||
column_data = GenTableColumnSchema(**column.model_dump(exclude={"super_column"}))
|
||||
await GenTableColumnCRUD(auth=self.auth).create_gen_table_column_crud(column_data)
|
||||
await GenTableColumnCRUD(self.auth, self.db).create_gen_table_column_crud(column_data)
|
||||
del_columns = [column for column in table_columns if column.column_name not in db_table_column_names]
|
||||
if del_columns:
|
||||
for column in del_columns:
|
||||
if hasattr(column, "id") and column.id:
|
||||
await GenTableColumnCRUD(auth=self.auth).delete_gen_table_column_by_column_id_crud([column.id])
|
||||
await GenTableColumnCRUD(self.auth, self.db).delete_gen_table_column_by_column_id_crud([column.id])
|
||||
|
||||
# 主子表:若子表也已导入生成器,则一并同步子表配置
|
||||
sn = (table.sub_table_name or "").strip()
|
||||
fk = (table.sub_table_fk_name or "").strip()
|
||||
if _sync_sub and sn and fk:
|
||||
sub_cfg = await GenTableCRUD(auth=self.auth).get_gen_table_by_name(sn)
|
||||
sub_cfg = await GenTableCRUD(self.auth, self.db).get_gen_table_by_name(sn)
|
||||
if sub_cfg:
|
||||
await self.sync_db(sn, _sync_sub=False)
|
||||
except Exception as e:
|
||||
@@ -1052,7 +1054,7 @@ class GenTableService:
|
||||
|
||||
# 1) 若子表已作为 gen_table 导入,则使用其 columns 配置(可控、可复用)
|
||||
try:
|
||||
sub_cfg_model = await GenTableCRUD(auth=self.auth).get_gen_table_by_name(sub_name_raw, preload=["columns"])
|
||||
sub_cfg_model = await GenTableCRUD(self.auth, self.db).get_gen_table_by_name(sub_name_raw, preload=["columns"])
|
||||
except Exception:
|
||||
sub_cfg_model = None
|
||||
if sub_cfg_model:
|
||||
@@ -1072,7 +1074,7 @@ class GenTableService:
|
||||
|
||||
# 2) 回退:仅从 DB 读取结构(只读,无法配置子表字段)
|
||||
try:
|
||||
gen_table_columns = await GenTableColumnCRUD(auth=self.auth).get_gen_db_table_columns_by_name(sub_name_raw)
|
||||
gen_table_columns = await GenTableColumnCRUD(self.auth, self.db).get_gen_db_table_columns_by_name(sub_name_raw)
|
||||
except Exception as e:
|
||||
logger.warning(f"获取子表 {sub_name_raw} 字段失败: {e!s}")
|
||||
gen_table.sub = False
|
||||
@@ -1090,7 +1092,7 @@ class GenTableService:
|
||||
gen_table.sub_table = None
|
||||
gen_table.master_sub_hint = f"子表「{sub_name_raw}」中不存在名为「{fk_raw}」的列,请核对外键列名"
|
||||
return
|
||||
table_comment = await GenTableCRUD(auth=self.auth).get_db_table_comment(sub_name_raw)
|
||||
table_comment = await GenTableCRUD(self.auth, self.db).get_db_table_comment(sub_name_raw)
|
||||
sub = GenTableOutSchema.model_validate(
|
||||
{
|
||||
"id": -1,
|
||||
@@ -1187,7 +1189,7 @@ class GenTableService:
|
||||
"""
|
||||
if not table_name or not table_name.strip():
|
||||
raise CustomException(msg="表名不能为空")
|
||||
gen_table = await GenTableCRUD(auth=self.auth).get_gen_table_by_name(table_name, preload=["columns"])
|
||||
gen_table = await GenTableCRUD(self.auth, self.db).get_gen_table_by_name(table_name, preload=["columns"])
|
||||
if not gen_table:
|
||||
raise CustomException(msg="业务表不存在")
|
||||
|
||||
@@ -1195,7 +1197,7 @@ class GenTableService:
|
||||
if not table.id:
|
||||
raise CustomException(msg="业务表ID不能为空")
|
||||
|
||||
db_cols = await GenTableColumnCRUD(auth=self.auth).get_gen_db_table_columns_by_name(table_name)
|
||||
db_cols = await GenTableColumnCRUD(self.auth, self.db).get_gen_db_table_columns_by_name(table_name)
|
||||
added, removed, changed, unchanged = self._sync_preview_diff(
|
||||
current_cols=table.columns or [],
|
||||
db_cols=db_cols or [],
|
||||
@@ -1214,12 +1216,12 @@ class GenTableService:
|
||||
if sn and fk:
|
||||
preview.sub_table_name = sn
|
||||
# 优先取“已导入的子表配置”,否则用 DB 结构(只读)
|
||||
sub_cfg = await GenTableCRUD(auth=self.auth).get_gen_table_by_name(sn, preload=["columns"])
|
||||
sub_cfg = await GenTableCRUD(self.auth, self.db).get_gen_table_by_name(sn, preload=["columns"])
|
||||
if sub_cfg:
|
||||
cur_sub_cols = GenTableOutSchema.model_validate(sub_cfg).columns or []
|
||||
else:
|
||||
cur_sub_cols = []
|
||||
db_sub_cols = await GenTableColumnCRUD(auth=self.auth).get_gen_db_table_columns_by_name(sn)
|
||||
db_sub_cols = await GenTableColumnCRUD(self.auth, self.db).get_gen_db_table_columns_by_name(sn)
|
||||
s_added, s_removed, s_changed, s_unchanged = self._sync_preview_diff(
|
||||
current_cols=cur_sub_cols,
|
||||
db_cols=db_sub_cols or [],
|
||||
@@ -1279,7 +1281,7 @@ class GenTableService:
|
||||
异常:
|
||||
- CustomException: 当业务表不存在或数据转换失败时抛出。
|
||||
"""
|
||||
gen_table_model = await GenTableCRUD(auth=self.auth).get_gen_table_by_name(table_name)
|
||||
gen_table_model = await GenTableCRUD(self.auth, self.db).get_gen_table_by_name(table_name)
|
||||
# 检查表是否存在
|
||||
if gen_table_model is None:
|
||||
raise CustomException(msg=f"业务表 {table_name} 不存在")
|
||||
@@ -1298,8 +1300,9 @@ class GenTableService:
|
||||
class GenTableColumnService:
|
||||
"""代码生成业务表字段服务层"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
def __init__(self, auth: AuthSchema, db: AsyncSession) -> None:
|
||||
self.auth = auth
|
||||
self.db = db
|
||||
|
||||
@handle_service_exception
|
||||
async def get_gen_table_column_list_by_table_id(self, table_id: int) -> list[dict[str, Any]]:
|
||||
@@ -1312,5 +1315,5 @@ class GenTableColumnService:
|
||||
返回:
|
||||
- list[dict[str, Any]]: 业务表字段列表,每个元素为字段详细信息字典。
|
||||
"""
|
||||
gen_table_column_list_result = await GenTableColumnCRUD(auth=self.auth).list_gen_table_column_crud({"table_id": table_id})
|
||||
gen_table_column_list_result = await GenTableColumnCRUD(self.auth, self.db).list_gen_table_column_crud({"table_id": table_id})
|
||||
return [GenTableColumnOutSchema.model_validate(obj).model_dump() for obj in gen_table_column_list_result]
|
||||
|
||||
Reference in New Issue
Block a user