feat: restore source parity and harden agent runtime
This commit is contained in:
@@ -0,0 +1,393 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
表单管理 API(异步版本)
|
||||
表单元数据的 CRUD、发布、复制、导入导出
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import get_db
|
||||
from app.base_schema import PaginatedResponse, ResponseModel
|
||||
from online_dev.form_manager.schema import (
|
||||
FormImportCheckIn,
|
||||
FormImportCheckOut,
|
||||
FormImportIn,
|
||||
FormValidateTablesIn,
|
||||
FormValidateTablesOut,
|
||||
FormMetaCreateIn,
|
||||
FormMetaListOut,
|
||||
FormMetaOut,
|
||||
FormMetaUpdateIn,
|
||||
FormPublishIn,
|
||||
FormSubTableOut,
|
||||
)
|
||||
from online_dev.form_manager.service import FormService, FormServiceException
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/form", tags=["表单管理"])
|
||||
|
||||
|
||||
# ============ 辅助函数 ============
|
||||
|
||||
def _format_datetime(dt) -> str:
|
||||
"""格式化日期时间"""
|
||||
if dt:
|
||||
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
return ""
|
||||
|
||||
|
||||
async def _build_form_out(db: AsyncSession, form) -> dict:
|
||||
"""构建表单详情输出"""
|
||||
sub_tables = await FormService.get_sub_tables(db, form.id)
|
||||
return {
|
||||
"id": str(form.id),
|
||||
"application_id": form.application_id,
|
||||
"name": form.name,
|
||||
"code": form.code,
|
||||
"form_type": form.form_type,
|
||||
"description": form.description or "",
|
||||
"status": form.status,
|
||||
"version": form.version,
|
||||
"db_config": form.db_config,
|
||||
"main_table": form.main_table,
|
||||
"main_table_schema": form.main_table_schema or "",
|
||||
"main_table_database": form.main_table_database or "",
|
||||
"form_config": form.form_config or {},
|
||||
"list_config": form.list_config or {},
|
||||
"sort": form.sort or 0,
|
||||
"show_in_mobile": form.show_in_mobile or False,
|
||||
"globally_visible": form.globally_visible or False,
|
||||
"icon": form.icon or "",
|
||||
"icon_bg_color": form.icon_bg_color or "",
|
||||
"sys_create_datetime": _format_datetime(form.sys_create_datetime),
|
||||
"sys_update_datetime": _format_datetime(form.sys_update_datetime),
|
||||
"sub_tables": [
|
||||
{
|
||||
"id": str(sub.id),
|
||||
"table_name": sub.table_name,
|
||||
"table_schema": sub.table_schema or "",
|
||||
"table_database": sub.table_database or "",
|
||||
"alias": sub.alias or "",
|
||||
"foreign_key": sub.foreign_key,
|
||||
"related_field": sub.related_field or "id",
|
||||
"relation_type": sub.relation_type or "one-to-many",
|
||||
"sort": sub.sort or 0,
|
||||
}
|
||||
for sub in sub_tables
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _build_form_list_out(form, application_name: str = None, application_code: str = "") -> dict:
|
||||
"""构建表单列表输出"""
|
||||
return {
|
||||
"id": str(form.id),
|
||||
"application_id": form.application_id,
|
||||
"application_name": application_name or "主应用",
|
||||
"application_code": application_code or "",
|
||||
"name": form.name,
|
||||
"code": form.code,
|
||||
"form_type": form.form_type,
|
||||
"description": form.description or "",
|
||||
"status": form.status,
|
||||
"version": form.version,
|
||||
"main_table": form.main_table,
|
||||
"sort": form.sort or 0,
|
||||
"show_in_mobile": form.show_in_mobile or False,
|
||||
"globally_visible": form.globally_visible or False,
|
||||
"icon": form.icon or "",
|
||||
"icon_bg_color": form.icon_bg_color or "",
|
||||
"sys_create_datetime": _format_datetime(form.sys_create_datetime),
|
||||
"sys_update_datetime": _format_datetime(form.sys_update_datetime),
|
||||
}
|
||||
|
||||
|
||||
# ============ 表单元数据 CRUD ============
|
||||
|
||||
@router.get("/list", response_model=PaginatedResponse[FormMetaListOut], summary="表单列表")
|
||||
async def list_forms(
|
||||
application_id: str = Query(None, alias="applicationId", description="所属应用ID"),
|
||||
name: str = Query(None, description="表单名称"),
|
||||
code: str = Query(None, description="表单编码"),
|
||||
form_type: str = Query(None, alias="formType", description="表单类型"),
|
||||
status: str = Query(None, description="状态"),
|
||||
include_globally_visible: bool = Query(
|
||||
False, alias="includeGloballyVisible", description="是否包含其他应用全局可见的表单"
|
||||
),
|
||||
page: int = Query(default=1, ge=1, description="页码"),
|
||||
page_size: int = Query(default=20, ge=1, le=100, alias="pageSize", description="每页数量"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""分页查询表单列表(自动应用数据权限)"""
|
||||
result = await FormService.list_with_data_scope(
|
||||
db=db,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
application_id=application_id,
|
||||
name=name,
|
||||
code=code,
|
||||
form_type=form_type,
|
||||
status=status,
|
||||
include_globally_visible=include_globally_visible,
|
||||
)
|
||||
|
||||
return PaginatedResponse(
|
||||
items=[_build_form_list_out(
|
||||
item,
|
||||
getattr(item, 'application_name', '主应用'),
|
||||
getattr(item, 'application_code', ''),
|
||||
) for item in result["items"]],
|
||||
total=result["total"],
|
||||
)
|
||||
|
||||
|
||||
@router.get("/form-types", summary="获取表单类型列表")
|
||||
async def get_form_types():
|
||||
"""获取所有表单类型"""
|
||||
return FormService.get_form_types()
|
||||
|
||||
|
||||
@router.get("/published/simple", summary="获取已发布表单简单列表")
|
||||
async def get_published_forms_simple(
|
||||
application_id: str = Query(None, alias="applicationId", description="所属应用ID"),
|
||||
all_apps: bool = Query(False, alias="allApps", description="是否返回所有应用的表单(移动端工作台使用)"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取已发布表单的简单列表(用于下拉选择)
|
||||
返回格式: [{code, name, mainTable, application_id, application_name, fields: [{field, label, type}]}]
|
||||
"""
|
||||
try:
|
||||
return await FormService.get_published_forms_simple(db, application_id=application_id, all_apps=all_apps)
|
||||
except FormServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/{form_id}", response_model=FormMetaOut, summary="表单详情")
|
||||
async def get_form(
|
||||
form_id: str,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""获取表单详情"""
|
||||
try:
|
||||
form = await FormService.get(db, form_id)
|
||||
return await _build_form_out(db, form)
|
||||
except FormServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/code/{code}", response_model=FormMetaOut, summary="根据编码获取表单")
|
||||
async def get_form_by_code(
|
||||
code: str,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""根据编码获取表单详情"""
|
||||
try:
|
||||
form = await FormService.get_by_code(db, code)
|
||||
return await _build_form_out(db, form)
|
||||
except FormServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("", response_model=FormMetaOut, summary="创建表单")
|
||||
async def create_form(
|
||||
request: Request,
|
||||
data: FormMetaCreateIn,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""创建表单"""
|
||||
user_id = request.state.user_id
|
||||
|
||||
try:
|
||||
form = await FormService.create(db, data.model_dump(), user_id)
|
||||
return await _build_form_out(db, form)
|
||||
except FormServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.put("/{form_id}", response_model=FormMetaOut, summary="更新表单")
|
||||
async def update_form(
|
||||
request: Request,
|
||||
form_id: str,
|
||||
data: FormMetaUpdateIn,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""更新表单"""
|
||||
user_id = request.state.user_id
|
||||
|
||||
try:
|
||||
form = await FormService.update(db, form_id, data.model_dump(exclude_none=True), user_id)
|
||||
return await _build_form_out(db, form)
|
||||
except FormServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/batch/delete", response_model=dict, summary="批量删除表单")
|
||||
async def batch_delete_forms(
|
||||
ids: List[str] = Query(..., description="表单ID列表"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""批量删除表单"""
|
||||
count = await FormService.batch_delete(db, ids)
|
||||
return {"count": count}
|
||||
|
||||
|
||||
@router.delete("/{form_id}", response_model=FormMetaOut, summary="删除表单")
|
||||
async def delete_form(
|
||||
form_id: str,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""删除表单"""
|
||||
try:
|
||||
form = await FormService.get(db, form_id)
|
||||
form_out = await _build_form_out(db, form)
|
||||
await FormService.delete(db, form_id)
|
||||
return form_out
|
||||
except FormServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
# ============ 发布/取消发布 ============
|
||||
|
||||
@router.post("/{form_id}/publish", response_model=FormMetaOut, summary="发布表单")
|
||||
async def publish_form(
|
||||
form_id: str,
|
||||
data: FormPublishIn,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""发布表单并创建菜单"""
|
||||
try:
|
||||
form = await FormService.publish(db, form_id, data.model_dump())
|
||||
return await _build_form_out(db, form)
|
||||
except FormServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/{form_id}/unpublish", response_model=FormMetaOut, summary="取消发布")
|
||||
async def unpublish_form(
|
||||
form_id: str,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""取消发布表单并删除菜单"""
|
||||
try:
|
||||
form = await FormService.unpublish(db, form_id)
|
||||
return await _build_form_out(db, form)
|
||||
except FormServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
# ============ 复制 ============
|
||||
|
||||
@router.post("/{form_id}/copy", response_model=FormMetaOut, summary="复制表单")
|
||||
async def copy_form(
|
||||
request: Request,
|
||||
form_id: str,
|
||||
new_code: str = Query(..., alias="new_code", description="新表单编码"),
|
||||
new_name: str = Query(None, alias="new_name", description="新表单名称"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""复制表单"""
|
||||
user_id = request.state.user_id
|
||||
|
||||
try:
|
||||
form = await FormService.copy(db, form_id, new_code, new_name, user_id)
|
||||
return await _build_form_out(db, form)
|
||||
except FormServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
# ============ 导入/导出配置 ============
|
||||
|
||||
@router.get("/{form_id}/export", summary="导出表单配置")
|
||||
async def export_form_config(
|
||||
form_id: str,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""导出表单配置为 JSON"""
|
||||
try:
|
||||
config = await FormService.export_config(db, form_id)
|
||||
|
||||
# 返回 JSON 文件
|
||||
content = json.dumps(config, ensure_ascii=False, indent=2)
|
||||
|
||||
return StreamingResponse(
|
||||
iter([content]),
|
||||
media_type="application/json",
|
||||
headers={
|
||||
"Content-Disposition": f'attachment; filename="{config["code"]}.json"'
|
||||
}
|
||||
)
|
||||
except FormServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/batch/export", summary="批量导出表单配置")
|
||||
async def batch_export_form_config(
|
||||
ids: List[str],
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""批量导出多个表单配置为 JSON 数组"""
|
||||
try:
|
||||
configs = []
|
||||
for form_id in ids:
|
||||
config = await FormService.export_config(db, form_id)
|
||||
configs.append(config)
|
||||
|
||||
content = json.dumps(configs, ensure_ascii=False, indent=2)
|
||||
return StreamingResponse(
|
||||
iter([content]),
|
||||
media_type="application/json",
|
||||
headers={
|
||||
"Content-Disposition": 'attachment; filename="forms_export.json"'
|
||||
}
|
||||
)
|
||||
except FormServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/validate-tables", response_model=FormValidateTablesOut, summary="校验表单物理表")
|
||||
async def validate_form_tables(
|
||||
data: FormValidateTablesIn,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""校验目标连接可用且主/子表存在(保存/发布前)"""
|
||||
try:
|
||||
return await FormService.validate_form_tables(db, data.model_dump())
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/import/check", response_model=FormImportCheckOut, summary="导入预检查")
|
||||
async def check_import_form_config(
|
||||
data: FormImportCheckIn,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""检查导入配置:编码冲突、目标表是否存在"""
|
||||
try:
|
||||
result = await FormService.check_import(db, data.model_dump())
|
||||
return result
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/import", response_model=FormMetaOut, summary="导入表单配置")
|
||||
async def import_form_config(
|
||||
request: Request,
|
||||
data: FormImportIn,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""导入表单配置(支持自动建表)"""
|
||||
user_id = request.state.user_id
|
||||
|
||||
try:
|
||||
form = await FormService.import_config(db, data.model_dump(), user_id)
|
||||
return await _build_form_out(db, form)
|
||||
except FormServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
@@ -0,0 +1,259 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
表单管理 Schema 定义
|
||||
"""
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
|
||||
|
||||
# ============ 表单元数据 Schema ============
|
||||
|
||||
class FormMetaBase(BaseModel):
|
||||
"""表单基础信息"""
|
||||
application_id: Optional[str] = Field(None, description="所属应用ID")
|
||||
name: str = Field(..., description="表单名称")
|
||||
code: str = Field(..., pattern=r"^[a-zA-Z][a-zA-Z0-9_]*$", description="表单编码(字母开头,只能包含字母、数字和下划线)")
|
||||
form_type: str = Field("normal", description="表单类型: normal-普通表单, workflow-流程表单")
|
||||
description: str = Field("", description="描述")
|
||||
sort: int = Field(0, description="排序")
|
||||
show_in_mobile: bool = Field(False, description="是否在移动端显示")
|
||||
globally_visible: bool = Field(False, description="是否全局可见(供其他应用引用)")
|
||||
icon: str = Field("", description="图标")
|
||||
icon_bg_color: str = Field("", description="图标背景色")
|
||||
|
||||
|
||||
class FormSubTableSchema(BaseModel):
|
||||
"""子表关联配置"""
|
||||
table_name: str = Field(..., description="从表名")
|
||||
table_schema: str = Field("", description="从表Schema")
|
||||
table_database: str = Field("", description="从表数据库")
|
||||
alias: str = Field("", description="别名")
|
||||
foreign_key: str = Field(..., description="外键字段")
|
||||
related_field: str = Field("id", description="关联主表字段")
|
||||
relation_type: str = Field("one-to-many", description="关联类型")
|
||||
sort: int = Field(0, description="排序")
|
||||
|
||||
|
||||
class FormMetaCreateIn(FormMetaBase):
|
||||
"""创建表单请求"""
|
||||
db_config: str = Field(..., description="数据库配置名")
|
||||
main_table: str = Field(..., description="主表名")
|
||||
main_table_schema: str = Field("", description="主表Schema")
|
||||
main_table_database: str = Field("", description="主表数据库")
|
||||
form_config: Dict[str, Any] = Field(default_factory=dict, description="表单设计配置")
|
||||
list_config: Dict[str, Any] = Field(default_factory=dict, description="列表设计配置")
|
||||
sub_tables: List[FormSubTableSchema] = Field(default_factory=list, description="子表配置")
|
||||
|
||||
|
||||
class FormMetaUpdateIn(BaseModel):
|
||||
"""更新表单请求"""
|
||||
name: Optional[str] = Field(None, description="表单名称")
|
||||
form_type: Optional[str] = Field(None, description="表单类型")
|
||||
description: Optional[str] = Field(None, description="描述")
|
||||
sort: Optional[int] = Field(None, description="排序")
|
||||
show_in_mobile: Optional[bool] = Field(None, description="是否在移动端显示")
|
||||
globally_visible: Optional[bool] = Field(None, description="是否全局可见(供其他应用引用)")
|
||||
icon: Optional[str] = Field(None, description="图标")
|
||||
icon_bg_color: Optional[str] = Field(None, description="图标背景色")
|
||||
db_config: Optional[str] = Field(None, description="数据库配置名")
|
||||
main_table: Optional[str] = Field(None, description="主表名")
|
||||
main_table_schema: Optional[str] = Field(None, description="主表Schema")
|
||||
main_table_database: Optional[str] = Field(None, description="主表数据库")
|
||||
form_config: Optional[Dict[str, Any]] = Field(None, description="表单设计配置")
|
||||
list_config: Optional[Dict[str, Any]] = Field(None, description="列表设计配置")
|
||||
sub_tables: Optional[List[FormSubTableSchema]] = Field(None, description="子表配置")
|
||||
|
||||
|
||||
class FormSubTableOut(BaseModel):
|
||||
"""子表关联输出"""
|
||||
id: str
|
||||
table_name: str
|
||||
table_schema: str = ""
|
||||
table_database: str = ""
|
||||
alias: str = ""
|
||||
foreign_key: str
|
||||
related_field: str = "id"
|
||||
relation_type: str = "one-to-many"
|
||||
sort: int = 0
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class FormMetaOut(BaseModel):
|
||||
"""表单详情输出"""
|
||||
id: str
|
||||
name: str
|
||||
code: str
|
||||
form_type: str
|
||||
description: str = ""
|
||||
status: str
|
||||
version: int
|
||||
db_config: str
|
||||
main_table: str
|
||||
main_table_schema: str = ""
|
||||
main_table_database: str = ""
|
||||
show_in_mobile: bool = False
|
||||
globally_visible: bool = False
|
||||
icon: str = ""
|
||||
icon_bg_color: str = ""
|
||||
form_config: Dict[str, Any] = {}
|
||||
list_config: Dict[str, Any] = {}
|
||||
sort: int = 0
|
||||
sys_create_datetime: Optional[str] = None
|
||||
sys_update_datetime: Optional[str] = None
|
||||
sub_tables: List[FormSubTableOut] = []
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class FormMetaListOut(BaseModel):
|
||||
"""表单列表输出"""
|
||||
id: str
|
||||
application_id: Optional[str] = None
|
||||
application_name: str = "主应用"
|
||||
application_code: str = ""
|
||||
name: str
|
||||
code: str
|
||||
form_type: str
|
||||
description: str = ""
|
||||
status: str
|
||||
version: int
|
||||
main_table: str
|
||||
show_in_mobile: bool = False
|
||||
globally_visible: bool = False
|
||||
icon: str = ""
|
||||
icon_bg_color: str = ""
|
||||
sort: int = 0
|
||||
sys_create_datetime: Optional[str] = None
|
||||
sys_update_datetime: Optional[str] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
# ============ 导入导出 Schema ============
|
||||
|
||||
class TableDDLSchema(BaseModel):
|
||||
"""表 DDL 信息"""
|
||||
main_table: str = Field("", description="主表 DDL")
|
||||
sub_tables: Dict[str, str] = Field(default_factory=dict, description="子表 DDL,key 为表名")
|
||||
|
||||
|
||||
class FormExportOut(BaseModel):
|
||||
"""表单配置导出"""
|
||||
name: str
|
||||
code: str
|
||||
form_type: str
|
||||
description: str = ""
|
||||
globally_visible: bool = False
|
||||
show_in_mobile: bool = False
|
||||
db_config: str
|
||||
main_table: str
|
||||
main_table_schema: str = ""
|
||||
main_table_database: str = ""
|
||||
form_config: Dict[str, Any] = {}
|
||||
list_config: Dict[str, Any] = {}
|
||||
sub_tables: List[FormSubTableSchema] = []
|
||||
table_ddl: Optional[TableDDLSchema] = None
|
||||
|
||||
|
||||
class TableRenameMapping(BaseModel):
|
||||
"""表重命名映射"""
|
||||
original_name: str = Field(..., description="原始表名")
|
||||
new_name: str = Field(..., description="新表名")
|
||||
new_schema: Optional[str] = Field(None, description="新Schema")
|
||||
|
||||
|
||||
class FormImportIn(BaseModel):
|
||||
"""表单配置导入"""
|
||||
application_id: Optional[str] = Field(None, description="所属应用ID")
|
||||
name: str = Field(..., description="表单名称")
|
||||
code: str = Field(..., description="表单编码")
|
||||
form_type: str = Field("normal", description="表单类型")
|
||||
description: str = Field("", description="描述")
|
||||
db_config: str = Field(..., description="数据库配置名")
|
||||
main_table: str = Field(..., description="主表名")
|
||||
main_table_schema: str = Field("", description="主表Schema")
|
||||
main_table_database: str = Field("", description="主表数据库")
|
||||
form_config: Dict[str, Any] = Field(default_factory=dict, description="表单设计配置")
|
||||
list_config: Dict[str, Any] = Field(default_factory=dict, description="列表设计配置")
|
||||
show_in_mobile: bool = Field(False, description="是否在移动端显示")
|
||||
globally_visible: bool = Field(False, description="是否全局可见(供其他应用引用)")
|
||||
sub_tables: List[FormSubTableSchema] = Field(default_factory=list, description="子表配置")
|
||||
table_ddl: Optional[TableDDLSchema] = Field(None, description="表 DDL(用于自动建表)")
|
||||
auto_create_tables: bool = Field(False, description="是否自动创建不存在的表")
|
||||
create_schema_if_not_exists: bool = Field(
|
||||
False, description="目标 Schema 不存在时是否自动创建(PostgreSQL/SQL Server)"
|
||||
)
|
||||
table_rename_mappings: List[TableRenameMapping] = Field(default_factory=list, description="表重命名映射列表")
|
||||
|
||||
|
||||
class TableCheckResult(BaseModel):
|
||||
"""单表检查结果"""
|
||||
table_name: str = Field(..., description="表名")
|
||||
schema_name: str = Field("", description="Schema名")
|
||||
exists: bool = Field(..., description="表是否存在")
|
||||
has_ddl: bool = Field(False, description="导入数据中是否包含该表的 DDL")
|
||||
|
||||
|
||||
class FormImportCheckIn(BaseModel):
|
||||
"""导入预检查请求"""
|
||||
code: str = Field(..., description="表单编码")
|
||||
db_config: str = Field("default", description="数据库配置名")
|
||||
main_table: str = Field(..., description="主表名")
|
||||
main_table_schema: str = Field("", description="主表 Schema")
|
||||
main_table_database: str = Field("", description="主表数据库")
|
||||
sub_tables: List[FormSubTableSchema] = Field(default_factory=list, description="子表配置")
|
||||
table_ddl: Optional[TableDDLSchema] = Field(None, description="表 DDL")
|
||||
|
||||
|
||||
class FormImportCheckOut(BaseModel):
|
||||
"""导入预检查结果"""
|
||||
code_exists: bool = Field(..., description="表单编码是否已存在")
|
||||
main_table_check: TableCheckResult = Field(..., description="主表检查结果")
|
||||
sub_table_checks: List[TableCheckResult] = Field(default_factory=list, description="子表检查结果")
|
||||
can_import: bool = Field(..., description="是否可以直接导入(所有表都存在且编码不冲突)")
|
||||
available_schemas: List[str] = Field(default_factory=list, description="可用的Schema列表")
|
||||
target_db_type: str = Field("", description="目标连接数据库类型")
|
||||
|
||||
|
||||
class FormValidateTablesIn(BaseModel):
|
||||
"""表单物理表校验请求"""
|
||||
db_config: str = Field("default", description="数据库连接 code")
|
||||
main_table: str = Field("", description="主表名")
|
||||
main_table_schema: str = Field("", description="主表 Schema")
|
||||
main_table_database: str = Field("", description="主表数据库")
|
||||
sub_tables: List[FormSubTableSchema] = Field(default_factory=list, description="子表配置")
|
||||
|
||||
|
||||
class FormValidateTablesOut(BaseModel):
|
||||
"""表单物理表校验结果"""
|
||||
valid: bool = Field(..., description="是否全部通过")
|
||||
connection_ok: bool = Field(True, description="连接是否可用")
|
||||
connection_message: str = Field("", description="连接错误信息")
|
||||
db_config: str = Field("default", description="数据库连接 code")
|
||||
main_table_exists: bool = Field(False, description="主表是否存在")
|
||||
sub_table_checks: List[TableCheckResult] = Field(default_factory=list, description="子表检查")
|
||||
database_warnings: List[str] = Field(
|
||||
default_factory=list,
|
||||
description="库名/连接配置风险提示(不阻断 valid,供前端展示)",
|
||||
)
|
||||
|
||||
|
||||
# ============ 发布配置 Schema ============
|
||||
|
||||
class FormPublishIn(BaseModel):
|
||||
"""发布表单请求(含菜单配置)"""
|
||||
menu_name: str = Field(..., description="菜单名称")
|
||||
menu_parent_id: Optional[str] = Field(None, description="上级菜单ID")
|
||||
menu_icon: str = Field("lucide:file-text", description="菜单图标")
|
||||
menu_order: int = Field(0, description="菜单排序")
|
||||
|
||||
# 功能开关
|
||||
allow_add: bool = Field(True, description="允许新增")
|
||||
allow_edit: bool = Field(True, description="允许编辑")
|
||||
allow_delete: bool = Field(True, description="允许删除")
|
||||
allow_export: bool = Field(True, description="允许导出")
|
||||
allow_import: bool = Field(False, description="允许导入")
|
||||
Reference in New Issue
Block a user