调整部分前端布局和后端增删改逻辑
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
from .service import GEN_RUNTIME, GenRuntimeService
|
||||
|
||||
__all__ = ['GEN_RUNTIME', 'GenRuntimeService']
|
||||
@@ -0,0 +1,110 @@
|
||||
from importlib import import_module
|
||||
from typing import Any
|
||||
|
||||
|
||||
class GenInfrastructureGateway:
|
||||
"""
|
||||
代码生成基础设施网关。
|
||||
|
||||
该对象负责延迟加载 sqlglot、数据库会话、分页模型、配置对象、
|
||||
业务异常和代码生成服务依赖,供代码生成运行时 facade 与其协作对象统一复用。
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_sqlglot_module() -> Any:
|
||||
"""
|
||||
获取 `sqlglot` 模块。
|
||||
|
||||
:return: `sqlglot` 模块
|
||||
"""
|
||||
return import_module('sqlglot')
|
||||
|
||||
@staticmethod
|
||||
def get_sqlglot_expressions_module() -> Any:
|
||||
"""
|
||||
获取 `sqlglot.expressions` 模块。
|
||||
|
||||
:return: `sqlglot.expressions` 模块
|
||||
"""
|
||||
return import_module('sqlglot.expressions')
|
||||
|
||||
@staticmethod
|
||||
def get_async_session_local() -> Any:
|
||||
"""
|
||||
获取异步数据库会话工厂。
|
||||
|
||||
:return: 异步数据库会话工厂
|
||||
"""
|
||||
return import_module('config.database').AsyncSessionLocal
|
||||
|
||||
@staticmethod
|
||||
def get_page_model() -> Any:
|
||||
"""
|
||||
获取分页模型类型。
|
||||
|
||||
:return: 分页模型类型
|
||||
"""
|
||||
return import_module('common.vo').PageModel
|
||||
|
||||
@staticmethod
|
||||
def get_database_config() -> Any:
|
||||
"""
|
||||
获取数据库配置对象。
|
||||
|
||||
:return: 数据库配置对象
|
||||
"""
|
||||
return import_module('config.env').DataBaseConfig
|
||||
|
||||
@staticmethod
|
||||
def get_gen_config() -> Any:
|
||||
"""
|
||||
获取代码生成配置对象。
|
||||
|
||||
:return: 代码生成配置对象
|
||||
"""
|
||||
return import_module('config.env').GenConfig
|
||||
|
||||
@staticmethod
|
||||
def get_service_exception_class() -> type[Exception]:
|
||||
"""
|
||||
获取业务异常类型。
|
||||
|
||||
:return: 业务异常类型
|
||||
"""
|
||||
return import_module('exceptions.exception').ServiceException
|
||||
|
||||
@staticmethod
|
||||
def get_user_vo_module() -> Any:
|
||||
"""
|
||||
获取用户 VO 模块。
|
||||
|
||||
:return: 用户 VO 模块
|
||||
"""
|
||||
return import_module('module_admin.entity.vo.user_vo')
|
||||
|
||||
@staticmethod
|
||||
def get_gen_vo_module() -> Any:
|
||||
"""
|
||||
获取代码生成 VO 模块。
|
||||
|
||||
:return: 代码生成 VO 模块
|
||||
"""
|
||||
return import_module('module_generator.entity.vo.gen_vo')
|
||||
|
||||
@staticmethod
|
||||
def get_gen_table_service() -> Any:
|
||||
"""
|
||||
获取代码生成服务类。
|
||||
|
||||
:return: 代码生成服务类
|
||||
"""
|
||||
return import_module('module_generator.service.gen_service').GenTableService
|
||||
|
||||
@staticmethod
|
||||
def get_gen_table_column_service() -> Any:
|
||||
"""
|
||||
获取代码生成字段服务类。
|
||||
|
||||
:return: 代码生成字段服务类
|
||||
"""
|
||||
return import_module('module_generator.service.gen_service').GenTableColumnService
|
||||
@@ -0,0 +1,412 @@
|
||||
from typing import Any
|
||||
|
||||
from cli.exit_codes import ARGUMENT_ERROR, DATABASE_ERROR, RUNTIME_ERROR
|
||||
|
||||
from .gateway import GenInfrastructureGateway
|
||||
from .support import GenDomainSupport
|
||||
|
||||
|
||||
class GenRuntimeService:
|
||||
"""
|
||||
代码生成运行时服务。
|
||||
|
||||
该服务作为代码生成运行时 facade,对外统一暴露业务表、数据库物理表、
|
||||
建表 SQL、代码预览、导出与数据库同步等入口。
|
||||
|
||||
:param infrastructure_gateway: 代码生成基础设施网关
|
||||
:param domain_support: 代码生成领域支持对象
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
infrastructure_gateway: GenInfrastructureGateway | None = None,
|
||||
domain_support: GenDomainSupport | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
初始化代码生成运行时服务。
|
||||
|
||||
:param infrastructure_gateway: 代码生成基础设施网关
|
||||
:param domain_support: 代码生成领域支持对象
|
||||
:return: None
|
||||
"""
|
||||
self.infrastructure_gateway = infrastructure_gateway or GenInfrastructureGateway()
|
||||
self.domain_support = domain_support or GenDomainSupport(self.infrastructure_gateway)
|
||||
|
||||
async def import_tables(self, table_names: list[str], *, dry_run: bool = False) -> dict[str, Any]:
|
||||
"""
|
||||
导入数据库表到代码生成业务表。
|
||||
|
||||
:param table_names: 待导入表名列表
|
||||
:param dry_run: 是否仅演练执行
|
||||
:return: 导入结果
|
||||
"""
|
||||
normalized_table_names = self.domain_support.normalize_table_names(table_names)
|
||||
if not normalized_table_names:
|
||||
return {'ok': False, 'message': '至少需要传入一个表名', 'exit_code': ARGUMENT_ERROR}
|
||||
|
||||
async_session_local = self.infrastructure_gateway.get_async_session_local()
|
||||
gen_table_service = self.infrastructure_gateway.get_gen_table_service()
|
||||
service_exception = self.infrastructure_gateway.get_service_exception_class()
|
||||
try:
|
||||
async with async_session_local() as session:
|
||||
gen_table_list = await gen_table_service.get_gen_db_table_list_by_name_services(
|
||||
session,
|
||||
normalized_table_names,
|
||||
)
|
||||
matched_table_names = [gen_table.table_name for gen_table in gen_table_list if gen_table.table_name]
|
||||
missing_table_names = [
|
||||
table_name for table_name in normalized_table_names if table_name not in matched_table_names
|
||||
]
|
||||
|
||||
if dry_run:
|
||||
return {
|
||||
'ok': True,
|
||||
'message': '导入表结构演练完成,未执行实际写入',
|
||||
'dryRun': True,
|
||||
'requestedTables': normalized_table_names,
|
||||
'matchedTables': matched_table_names,
|
||||
'missingTables': missing_table_names,
|
||||
}
|
||||
|
||||
result = await gen_table_service.import_gen_table_services(
|
||||
session,
|
||||
gen_table_list,
|
||||
self.domain_support.build_cli_current_user(),
|
||||
)
|
||||
except service_exception as exc:
|
||||
return {'ok': False, 'message': '导入表结构失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
except Exception as exc:
|
||||
return {'ok': False, 'message': '导入表结构失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
|
||||
return {
|
||||
'ok': bool(result.is_success),
|
||||
'message': result.message,
|
||||
'requestedTables': normalized_table_names,
|
||||
}
|
||||
|
||||
async def list_gen_tables(
|
||||
self,
|
||||
*,
|
||||
table_name: str = '',
|
||||
table_comment: str = '',
|
||||
paged: bool = False,
|
||||
page_num: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
查询代码生成业务表列表。
|
||||
|
||||
:param table_name: 表名称过滤条件
|
||||
:param table_comment: 表描述过滤条件
|
||||
:param paged: 是否启用分页
|
||||
:param page_num: 页码
|
||||
:param page_size: 每页数量
|
||||
:return: 查询结果
|
||||
"""
|
||||
async_session_local = self.infrastructure_gateway.get_async_session_local()
|
||||
gen_table_service = self.infrastructure_gateway.get_gen_table_service()
|
||||
try:
|
||||
gen_vo_module = self.infrastructure_gateway.get_gen_vo_module()
|
||||
query_model = gen_vo_module.GenTablePageQueryModel(
|
||||
tableName=table_name or None,
|
||||
tableComment=table_comment or None,
|
||||
pageNum=page_num,
|
||||
pageSize=page_size,
|
||||
)
|
||||
async with async_session_local() as session:
|
||||
result = await gen_table_service.get_gen_table_list_services(session, query_model, is_page=paged)
|
||||
except Exception as exc:
|
||||
return {
|
||||
'ok': False,
|
||||
'message': '读取代码生成业务表列表失败',
|
||||
'error': str(exc),
|
||||
'exit_code': DATABASE_ERROR,
|
||||
}
|
||||
|
||||
filters = {
|
||||
'tableName': table_name,
|
||||
'tableComment': table_comment,
|
||||
'paged': paged,
|
||||
'pageNum': page_num,
|
||||
'pageSize': page_size,
|
||||
}
|
||||
return self.domain_support.build_list_payload(result, filters=filters, paged=paged)
|
||||
|
||||
async def list_gen_db_tables(
|
||||
self,
|
||||
*,
|
||||
table_name: str = '',
|
||||
table_comment: str = '',
|
||||
paged: bool = False,
|
||||
page_num: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
查询数据库中可导入的物理表列表。
|
||||
|
||||
:param table_name: 表名称过滤条件
|
||||
:param table_comment: 表描述过滤条件
|
||||
:param paged: 是否启用分页
|
||||
:param page_num: 页码
|
||||
:param page_size: 每页数量
|
||||
:return: 查询结果
|
||||
"""
|
||||
async_session_local = self.infrastructure_gateway.get_async_session_local()
|
||||
gen_table_service = self.infrastructure_gateway.get_gen_table_service()
|
||||
try:
|
||||
gen_vo_module = self.infrastructure_gateway.get_gen_vo_module()
|
||||
query_model = gen_vo_module.GenTablePageQueryModel(
|
||||
tableName=table_name or None,
|
||||
tableComment=table_comment or None,
|
||||
pageNum=page_num,
|
||||
pageSize=page_size,
|
||||
)
|
||||
async with async_session_local() as session:
|
||||
result = await gen_table_service.get_gen_db_table_list_services(session, query_model, is_page=paged)
|
||||
except Exception as exc:
|
||||
return {'ok': False, 'message': '读取数据库表列表失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
|
||||
filters = {
|
||||
'tableName': table_name,
|
||||
'tableComment': table_comment,
|
||||
'paged': paged,
|
||||
'pageNum': page_num,
|
||||
'pageSize': page_size,
|
||||
}
|
||||
return self.domain_support.build_list_payload(result, filters=filters, paged=paged)
|
||||
|
||||
async def create_tables(self, sql: str, sql_file: str, *, dry_run: bool = False) -> dict[str, Any]:
|
||||
"""
|
||||
根据 SQL 创建表并导入代码生成业务表。
|
||||
|
||||
:param sql: 直接传入的 SQL 文本
|
||||
:param sql_file: SQL 文件路径
|
||||
:param dry_run: 是否仅演练执行
|
||||
:return: 创建结果
|
||||
"""
|
||||
try:
|
||||
sql_text = self.domain_support.resolve_sql_text(sql, sql_file)
|
||||
sql_statements, table_names = self.domain_support.parse_create_table_sql(sql_text)
|
||||
except ValueError as exc:
|
||||
return {'ok': False, 'message': '创建表结构失败', 'error': str(exc), 'exit_code': ARGUMENT_ERROR}
|
||||
except Exception as exc:
|
||||
return {'ok': False, 'message': '创建表结构失败', 'error': str(exc), 'exit_code': RUNTIME_ERROR}
|
||||
|
||||
if dry_run:
|
||||
return {
|
||||
'ok': True,
|
||||
'message': '建表语句演练完成,未执行实际建表',
|
||||
'dryRun': True,
|
||||
'statementCount': len(sql_statements),
|
||||
'tableNames': table_names,
|
||||
'sql': sql_text,
|
||||
}
|
||||
|
||||
async_session_local = self.infrastructure_gateway.get_async_session_local()
|
||||
gen_table_service = self.infrastructure_gateway.get_gen_table_service()
|
||||
service_exception = self.infrastructure_gateway.get_service_exception_class()
|
||||
try:
|
||||
async with async_session_local() as session:
|
||||
result = await gen_table_service.create_table_services(
|
||||
session,
|
||||
sql_text,
|
||||
self.domain_support.build_cli_current_user(),
|
||||
)
|
||||
except service_exception as exc:
|
||||
return {'ok': False, 'message': '创建表结构失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
except Exception as exc:
|
||||
return {'ok': False, 'message': '创建表结构失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
|
||||
return {
|
||||
'ok': bool(result.is_success),
|
||||
'message': result.message,
|
||||
'tableNames': table_names,
|
||||
}
|
||||
|
||||
async def preview_code(self, table_id: int) -> dict[str, Any]:
|
||||
"""
|
||||
预览指定业务表的代码生成结果。
|
||||
|
||||
:param table_id: 业务表 ID
|
||||
:return: 预览结果
|
||||
"""
|
||||
async_session_local = self.infrastructure_gateway.get_async_session_local()
|
||||
gen_table_service = self.infrastructure_gateway.get_gen_table_service()
|
||||
service_exception = self.infrastructure_gateway.get_service_exception_class()
|
||||
try:
|
||||
async with async_session_local() as session:
|
||||
preview_payload = await gen_table_service.preview_code_services(session, table_id)
|
||||
except service_exception as exc:
|
||||
return {'ok': False, 'message': '预览代码失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
except Exception as exc:
|
||||
return {'ok': False, 'message': '预览代码失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
|
||||
return {
|
||||
'ok': True,
|
||||
'tableId': table_id,
|
||||
'templateCount': len(preview_payload),
|
||||
'preview': preview_payload,
|
||||
}
|
||||
|
||||
async def get_gen_table_detail(self, table_id: int) -> dict[str, Any]:
|
||||
"""
|
||||
读取单个代码生成业务表详情。
|
||||
|
||||
:param table_id: 业务表 ID
|
||||
:return: 详情结果
|
||||
"""
|
||||
async_session_local = self.infrastructure_gateway.get_async_session_local()
|
||||
gen_table_service = self.infrastructure_gateway.get_gen_table_service()
|
||||
gen_table_column_service = self.infrastructure_gateway.get_gen_table_column_service()
|
||||
try:
|
||||
async with async_session_local() as session:
|
||||
info = await gen_table_service.get_gen_table_by_id_services(session, table_id)
|
||||
if not getattr(info, 'table_id', None):
|
||||
return {
|
||||
'ok': False,
|
||||
'message': f'代码生成业务表不存在:{table_id}',
|
||||
'tableId': table_id,
|
||||
'exit_code': RUNTIME_ERROR,
|
||||
}
|
||||
rows = await gen_table_column_service.get_gen_table_column_list_by_table_id_services(session, table_id)
|
||||
tables = await gen_table_service.get_gen_table_all_services(session)
|
||||
except Exception as exc:
|
||||
return {
|
||||
'ok': False,
|
||||
'message': '读取代码生成业务表详情失败',
|
||||
'error': str(exc),
|
||||
'exit_code': DATABASE_ERROR,
|
||||
}
|
||||
|
||||
detail_payload = {
|
||||
'info': self.domain_support.serialize_gen_item(info),
|
||||
'rows': self.domain_support.serialize_gen_items(rows),
|
||||
'tables': self.domain_support.serialize_gen_items(tables),
|
||||
}
|
||||
return {
|
||||
'ok': True,
|
||||
'tableId': table_id,
|
||||
'tableName': detail_payload['info'].get('tableName', ''),
|
||||
'columnCount': len(detail_payload['rows']),
|
||||
'tableCount': len(detail_payload['tables']),
|
||||
'detail': detail_payload,
|
||||
}
|
||||
|
||||
async def export_code(
|
||||
self,
|
||||
table_names: list[str],
|
||||
*,
|
||||
mode: str = 'zip',
|
||||
output_file: str = '',
|
||||
dry_run: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
导出代码生成结果。
|
||||
|
||||
:param table_names: 业务表名称列表
|
||||
:param mode: 导出模式,支持 `zip` 与 `local`
|
||||
:param output_file: zip 导出目标文件路径
|
||||
:param dry_run: 是否仅演练执行
|
||||
:return: 导出结果
|
||||
"""
|
||||
normalized_table_names = self.domain_support.normalize_table_names(table_names)
|
||||
if not normalized_table_names:
|
||||
return {'ok': False, 'message': '至少需要传入一个表名', 'exit_code': ARGUMENT_ERROR}
|
||||
if mode not in {'zip', 'local'}:
|
||||
return {'ok': False, 'message': '导出模式仅支持 zip 或 local', 'exit_code': ARGUMENT_ERROR}
|
||||
|
||||
gen_config = self.infrastructure_gateway.get_gen_config()
|
||||
if mode == 'local' and not gen_config.allow_overwrite:
|
||||
return {
|
||||
'ok': False,
|
||||
'message': '当前系统配置不允许生成文件覆盖到本地',
|
||||
'hint': '请检查 GenConfig.allow_overwrite 配置',
|
||||
'exit_code': RUNTIME_ERROR,
|
||||
}
|
||||
|
||||
if dry_run:
|
||||
dry_run_payload: dict[str, Any] = {
|
||||
'ok': True,
|
||||
'message': '代码导出演练完成,未执行实际导出',
|
||||
'dryRun': True,
|
||||
'mode': mode,
|
||||
'tableNames': normalized_table_names,
|
||||
}
|
||||
if mode == 'zip':
|
||||
target_file = output_file.strip() or f'gen_code_{"_".join(normalized_table_names)}.zip'
|
||||
dry_run_payload['outputFile'] = self.domain_support.resolve_output_file_path(target_file)
|
||||
else:
|
||||
dry_run_payload['genPath'] = gen_config.GEN_PATH
|
||||
return dry_run_payload
|
||||
|
||||
async_session_local = self.infrastructure_gateway.get_async_session_local()
|
||||
gen_table_service = self.infrastructure_gateway.get_gen_table_service()
|
||||
service_exception = self.infrastructure_gateway.get_service_exception_class()
|
||||
try:
|
||||
async with async_session_local() as session:
|
||||
if mode == 'zip':
|
||||
zip_bytes = await gen_table_service.batch_gen_code_services(session, normalized_table_names)
|
||||
else:
|
||||
messages = []
|
||||
for table_name in normalized_table_names:
|
||||
result = await gen_table_service.generate_code_services(session, table_name)
|
||||
messages.append({'tableName': table_name, 'message': result.message, 'ok': result.is_success})
|
||||
return {
|
||||
'ok': True,
|
||||
'message': '代码已生成到本地目录',
|
||||
'mode': mode,
|
||||
'tableNames': normalized_table_names,
|
||||
'genPath': gen_config.GEN_PATH,
|
||||
'results': messages,
|
||||
}
|
||||
except service_exception as exc:
|
||||
return {'ok': False, 'message': '导出代码失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
except Exception as exc:
|
||||
return {'ok': False, 'message': '导出代码失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
|
||||
target_file = output_file.strip() or f'gen_code_{"_".join(normalized_table_names)}.zip'
|
||||
try:
|
||||
target_path = self.domain_support.write_export_zip(target_file, zip_bytes)
|
||||
except Exception as exc:
|
||||
return {'ok': False, 'message': '写出导出文件失败', 'error': str(exc), 'exit_code': RUNTIME_ERROR}
|
||||
|
||||
return {
|
||||
'ok': True,
|
||||
'message': '代码压缩包导出完成',
|
||||
'mode': mode,
|
||||
'tableNames': normalized_table_names,
|
||||
'outputFile': target_path,
|
||||
'size': len(zip_bytes),
|
||||
}
|
||||
|
||||
async def sync_gen_table_from_db(self, table_name: str) -> dict[str, Any]:
|
||||
"""
|
||||
将指定代码生成业务表与数据库表结构进行同步。
|
||||
|
||||
:param table_name: 业务表名称
|
||||
:return: 同步结果
|
||||
"""
|
||||
normalized_table_name = table_name.strip()
|
||||
if not normalized_table_name:
|
||||
return {'ok': False, 'message': '表名不能为空', 'exit_code': ARGUMENT_ERROR}
|
||||
|
||||
async_session_local = self.infrastructure_gateway.get_async_session_local()
|
||||
gen_table_service = self.infrastructure_gateway.get_gen_table_service()
|
||||
service_exception = self.infrastructure_gateway.get_service_exception_class()
|
||||
try:
|
||||
async with async_session_local() as session:
|
||||
result = await gen_table_service.sync_db_services(session, normalized_table_name)
|
||||
except service_exception as exc:
|
||||
return {'ok': False, 'message': '同步数据库表结构失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
except Exception as exc:
|
||||
return {'ok': False, 'message': '同步数据库表结构失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
|
||||
return {
|
||||
'ok': bool(result.is_success),
|
||||
'message': result.message,
|
||||
'tableName': normalized_table_name,
|
||||
}
|
||||
|
||||
|
||||
GEN_RUNTIME = GenRuntimeService()
|
||||
@@ -0,0 +1,191 @@
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from cli.runtime.base import RUNTIME_OPERATOR, RuntimeOperatorService
|
||||
|
||||
from .gateway import GenInfrastructureGateway
|
||||
|
||||
|
||||
class GenDomainSupport:
|
||||
"""
|
||||
代码生成领域支持对象。
|
||||
|
||||
该对象负责 CLI 当前用户构建、表名规整、记录序列化、SQL 解析
|
||||
以及导出路径处理,避免主运行时服务继续承载过多局部规则。
|
||||
|
||||
:param infrastructure_gateway: 代码生成基础设施网关
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
infrastructure_gateway: GenInfrastructureGateway,
|
||||
operator_service: RuntimeOperatorService = RUNTIME_OPERATOR,
|
||||
) -> None:
|
||||
"""
|
||||
初始化代码生成领域支持对象。
|
||||
|
||||
:param infrastructure_gateway: 代码生成基础设施网关
|
||||
:param operator_service: 运行时操作者解析服务
|
||||
:return: None
|
||||
"""
|
||||
self.infrastructure_gateway = infrastructure_gateway
|
||||
self.operator_service = operator_service
|
||||
|
||||
def build_cli_current_user(self) -> Any:
|
||||
"""
|
||||
构建 CLI 场景使用的最小当前用户模型。
|
||||
|
||||
:return: CLI 当前用户模型
|
||||
"""
|
||||
operator = self.operator_service.resolve_operator()
|
||||
user_vo_module = self.infrastructure_gateway.get_user_vo_module()
|
||||
return user_vo_module.CurrentUserModel(
|
||||
permissions=[],
|
||||
roles=['admin'],
|
||||
user=user_vo_module.UserInfoModel(
|
||||
user_id=1,
|
||||
user_name=operator,
|
||||
nick_name=operator,
|
||||
user_type='00',
|
||||
status='0',
|
||||
del_flag='0',
|
||||
),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def normalize_table_names(table_names: list[str]) -> list[str]:
|
||||
"""
|
||||
规范化表名列表。
|
||||
|
||||
:param table_names: 原始表名列表
|
||||
:return: 规范化后的表名列表
|
||||
"""
|
||||
return [table_name.strip() for table_name in table_names if table_name.strip()]
|
||||
|
||||
@staticmethod
|
||||
def serialize_gen_item(item: Any) -> dict[str, Any]:
|
||||
"""
|
||||
序列化单个代码生成表记录。
|
||||
|
||||
:param item: 原始记录对象
|
||||
:return: 可输出的字典
|
||||
"""
|
||||
if hasattr(item, 'model_dump'):
|
||||
return dict(item.model_dump(by_alias=True, exclude_none=True))
|
||||
return dict(item)
|
||||
|
||||
def serialize_gen_items(self, items: list[Any]) -> list[dict[str, Any]]:
|
||||
"""
|
||||
序列化代码生成表记录列表。
|
||||
|
||||
:param items: 原始记录列表
|
||||
:return: 序列化后的字典列表
|
||||
"""
|
||||
return [self.serialize_gen_item(item) for item in items]
|
||||
|
||||
def parse_create_table_sql(self, sql: str) -> tuple[list[Any], list[str]]:
|
||||
"""
|
||||
解析建表 SQL 并提取建表语句信息。
|
||||
|
||||
:param sql: 原始 SQL 文本
|
||||
:return: SQL AST 列表与建表表名列表
|
||||
:raises ValueError: SQL 非法时抛出异常
|
||||
"""
|
||||
sqlglot_module = self.infrastructure_gateway.get_sqlglot_module()
|
||||
expressions_module = self.infrastructure_gateway.get_sqlglot_expressions_module()
|
||||
database_config = self.infrastructure_gateway.get_database_config()
|
||||
sql_statements = sqlglot_module.parse(sql, dialect=database_config.sqlglot_parse_dialect)
|
||||
has_create = any(isinstance(sql_statement, expressions_module.Create) for sql_statement in sql_statements)
|
||||
has_forbidden_keyword = any(
|
||||
isinstance(
|
||||
sql_statement,
|
||||
(
|
||||
expressions_module.Add,
|
||||
expressions_module.Alter,
|
||||
expressions_module.Delete,
|
||||
expressions_module.Drop,
|
||||
expressions_module.Insert,
|
||||
expressions_module.TruncateTable,
|
||||
expressions_module.Update,
|
||||
),
|
||||
)
|
||||
for sql_statement in sql_statements
|
||||
)
|
||||
if not has_create or has_forbidden_keyword:
|
||||
raise ValueError('建表语句不合法,仅允许 CREATE TABLE 语句')
|
||||
|
||||
table_names = [
|
||||
sql_statement.find(expressions_module.Table).name
|
||||
for sql_statement in sql_statements
|
||||
if isinstance(sql_statement, expressions_module.Create)
|
||||
]
|
||||
if not table_names:
|
||||
raise ValueError('未解析到建表表名')
|
||||
return sql_statements, table_names
|
||||
|
||||
@staticmethod
|
||||
def resolve_sql_text(sql: str, sql_file: str) -> str:
|
||||
"""
|
||||
解析命令输入中的 SQL 文本。
|
||||
|
||||
:param sql: 直接传入的 SQL 文本
|
||||
:param sql_file: SQL 文件路径
|
||||
:return: 最终 SQL 文本
|
||||
:raises ValueError: 参数非法时抛出异常
|
||||
"""
|
||||
if bool(sql.strip()) == bool(sql_file.strip()):
|
||||
raise ValueError('必须且只能传入 --sql 或 --sql-file 其中一种方式')
|
||||
if sql.strip():
|
||||
return sql.strip()
|
||||
|
||||
sql_path = Path(sql_file).expanduser().resolve()
|
||||
if not sql_path.is_file():
|
||||
raise ValueError(f'SQL 文件不存在:{sql_path}')
|
||||
return sql_path.read_text(encoding='utf-8').strip()
|
||||
|
||||
@staticmethod
|
||||
def resolve_output_file_path(target_file: str) -> str:
|
||||
"""
|
||||
解析导出文件绝对路径。
|
||||
|
||||
:param target_file: 原始导出文件路径
|
||||
:return: 绝对路径字符串
|
||||
"""
|
||||
return str(Path(target_file).expanduser().resolve())
|
||||
|
||||
@staticmethod
|
||||
def write_export_zip(target_file: str, zip_bytes: bytes) -> str:
|
||||
"""
|
||||
将导出的 zip 数据写入目标文件。
|
||||
|
||||
:param target_file: 目标文件路径
|
||||
:param zip_bytes: zip 二进制内容
|
||||
:return: 实际写入的绝对路径
|
||||
"""
|
||||
target_path = Path(target_file).expanduser().resolve()
|
||||
target_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
target_path.write_bytes(zip_bytes)
|
||||
return str(target_path)
|
||||
|
||||
def build_list_payload(
|
||||
self,
|
||||
result: Any,
|
||||
*,
|
||||
filters: dict[str, Any],
|
||||
paged: bool,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
统一构建代码生成列表返回结构。
|
||||
|
||||
:param result: 原始结果对象
|
||||
:param filters: 查询过滤条件
|
||||
:param paged: 是否分页
|
||||
:return: 可输出结果
|
||||
"""
|
||||
if paged and isinstance(result, self.infrastructure_gateway.get_page_model()):
|
||||
page_payload = result.model_dump(by_alias=True)
|
||||
page_payload['rows'] = self.serialize_gen_items(page_payload.get('rows', []))
|
||||
return {'ok': True, 'filters': filters, 'page': page_payload}
|
||||
|
||||
items = self.serialize_gen_items(result)
|
||||
return {'ok': True, 'filters': filters, 'count': len(items), 'items': items}
|
||||
Reference in New Issue
Block a user