feat: 添加代码生成功能
This commit is contained in:
444
api/generate.py
Normal file
444
api/generate.py
Normal file
@@ -0,0 +1,444 @@
|
||||
# _*_ coding : UTF-8 _*_
|
||||
# @Time : 2025/02/28 17:28
|
||||
# @UpdateTime : 2025/02/28 17:28
|
||||
# @Author : sonder
|
||||
# @File : generate.py
|
||||
# @Software : PyCharm
|
||||
# @Comment : 本程序
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Path, Query, Depends, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from tortoise import Tortoise
|
||||
|
||||
from annotation.auth import Auth
|
||||
from annotation.log import Log
|
||||
from config.constant import MYSQL_TO_PYTHON_TYPE, BusinessType
|
||||
from controller.login import LoginController
|
||||
from models.generate import GenerateInfo, GenerateColumn
|
||||
from schemas.common import BaseResponse, DeleteListParams
|
||||
from schemas.generate import GetTablesListResponse, AddGenerateInfoParams, UpdateGenerateInfoParams, \
|
||||
GetGenerateInfoResponse, GetGenerateInfoListResponse
|
||||
from utils.common import bytes2human
|
||||
from utils.generate import Generate
|
||||
from utils.response import Response
|
||||
|
||||
generateAPI = APIRouter(
|
||||
prefix="/generate",
|
||||
dependencies=[Depends(LoginController.get_current_user)]
|
||||
)
|
||||
|
||||
|
||||
@generateAPI.get("/tables", response_class=JSONResponse, response_model=GetTablesListResponse,
|
||||
summary="获取数据库中的所有表信息")
|
||||
@Log(title="获取数据库中的所有表信息", business_type=BusinessType.SELECT)
|
||||
@Auth(permission_list=["generate:btn:tables"])
|
||||
async def get_database_tables(request: Request):
|
||||
"""
|
||||
获取当前数据库中的所有表信息,包括:
|
||||
- 表名
|
||||
- 表注释
|
||||
- 记录行数
|
||||
- 数据大小
|
||||
- 索引大小
|
||||
- 创建时间
|
||||
- 最后更新时间(如果支持)
|
||||
"""
|
||||
sql = """SELECT TABLE_NAME,TABLE_COMMENT,TABLE_ROWS,DATA_LENGTH,INDEX_LENGTH,CREATE_TIME,UPDATE_TIME FROM information_schema.tables WHERE table_schema = DATABASE();"""
|
||||
result = await Tortoise.get_connection("default").execute_query_dict(sql)
|
||||
# 将结果中的键转换为小写
|
||||
formatted_result = [{k.lower(): v for k, v in row.items()} for row in result]
|
||||
for row in formatted_result:
|
||||
for key, value in row.items():
|
||||
if key == "data_length" or key == "index_length":
|
||||
row[key] = bytes2human(value)
|
||||
return Response.success(data={
|
||||
"page": 1,
|
||||
"total": len(formatted_result),
|
||||
"pageSize": 99999,
|
||||
"result": formatted_result
|
||||
})
|
||||
|
||||
|
||||
@generateAPI.post("/add", response_class=JSONResponse, response_model=BaseResponse, summary="添加生成表信息")
|
||||
@Log(title="添加生成表信息", business_type=BusinessType.INSERT)
|
||||
@Auth(permission_list=["generate:btn:add"])
|
||||
async def add_generate_info(request: Request, params: AddGenerateInfoParams):
|
||||
if await GenerateInfo.get_or_none(table_name=params.table_name, del_flag=1):
|
||||
return Response.error(msg="该表信息已存在!")
|
||||
gen = await GenerateInfo.create(
|
||||
author=params.author,
|
||||
class_name=params.class_name,
|
||||
table_comment=params.table_comment,
|
||||
table_name=params.table_name,
|
||||
prefix=params.prefix,
|
||||
remark=params.remark,
|
||||
permission_id=params.permission_id,
|
||||
description=params.description
|
||||
)
|
||||
if gen:
|
||||
sql = """SELECT * FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = %s;"""
|
||||
result = await Tortoise.get_connection("default").execute_query_dict(sql, [params.table_name])
|
||||
# 转换为小写键名
|
||||
formatted_result = [{k.lower(): v for k, v in row.items()} for row in result]
|
||||
formatted_result.sort(key=lambda x: x['ordinal_position'])
|
||||
for column in formatted_result:
|
||||
if await GenerateColumn.get_or_none(table_id=gen.id, column_name=column["column_name"], del_flag=1):
|
||||
continue
|
||||
await GenerateColumn.create(
|
||||
table=gen,
|
||||
index=column["ordinal_position"],
|
||||
column_name=column["column_name"],
|
||||
column_comment=column["column_comment"],
|
||||
column_type=column["column_type"],
|
||||
python_type=MYSQL_TO_PYTHON_TYPE.get(column["column_type"]) if MYSQL_TO_PYTHON_TYPE.get(
|
||||
column["column_type"]) else MYSQL_TO_PYTHON_TYPE.get(column["data_type"], "str"),
|
||||
python_name=column["column_name"].lower(),
|
||||
is_insert=True,
|
||||
is_edit=True,
|
||||
is_list=True,
|
||||
is_query=True,
|
||||
is_required=False,
|
||||
query_way="__icontains",
|
||||
show_type="input",
|
||||
is_hide=False,
|
||||
)
|
||||
return Response.success(msg="添加成功!")
|
||||
return Response.error(msg="添加失败!")
|
||||
|
||||
|
||||
@generateAPI.delete("/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除生成表信息")
|
||||
@generateAPI.post("/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除生成表信息")
|
||||
@Log(title="删除生成表信息", business_type=BusinessType.DELETE)
|
||||
@Auth(permission_list=["generate:btn:delete"])
|
||||
async def delete_generate_info(request: Request, id: str = Path(description="生成表信息ID")):
|
||||
if table := await GenerateInfo.get_or_none(id=id, del_flag=1):
|
||||
table.del_flag = 0
|
||||
await GenerateColumn.filter(table_id=table.id, del_flag=1).update(del_flag=0)
|
||||
await table.save()
|
||||
return Response.success(msg="删除成功!")
|
||||
return Response.error(msg="该生成表信息不存在!")
|
||||
|
||||
|
||||
@generateAPI.delete("/deleteList", response_class=JSONResponse, response_model=BaseResponse,
|
||||
summary="批量删除生成表信息")
|
||||
@generateAPI.post("/deleteList", response_class=JSONResponse, response_model=BaseResponse, summary="批量删除生成表信息")
|
||||
@Log(title="批量删除生成表信息", business_type=BusinessType.DELETE)
|
||||
@Auth(permission_list=["generate:btn:delete"])
|
||||
async def delete_generate_info_list(request: Request, params: DeleteListParams):
|
||||
for id in set(params.ids):
|
||||
if table := await GenerateInfo.get_or_none(id=id, del_flag=1):
|
||||
table.del_flag = 0
|
||||
await GenerateColumn.filter(table_id=table.id, del_flag=1).update(del_flag=0)
|
||||
await table.save()
|
||||
return Response.success(msg="删除成功!")
|
||||
|
||||
|
||||
@generateAPI.put("/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="更新生成表信息")
|
||||
@generateAPI.post("/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="更新生成表信息")
|
||||
@Log(title="更新生成表信息", business_type=BusinessType.UPDATE)
|
||||
@Auth(permission_list=["generate:btn:update"])
|
||||
async def update_generate_info(
|
||||
request: Request,
|
||||
params: AddGenerateInfoParams,
|
||||
id: str = Path(description="生成表信息ID")
|
||||
):
|
||||
if table := await GenerateInfo.get_or_none(id=id, del_flag=1):
|
||||
table.author = params.info.author
|
||||
table.class_name = params.info.class_name
|
||||
table.permission_id = params.info.permission_id
|
||||
table.prefix = params.info.prefix
|
||||
table.remark = params.info.remark
|
||||
table.table_comment = params.info.table_comment
|
||||
table.table_name = params.info.table_name
|
||||
table.description = params.info.description
|
||||
await table.save()
|
||||
|
||||
return Response.success(msg="更新成功!")
|
||||
return Response.error(msg="该生成表信息不存在!")
|
||||
|
||||
|
||||
@generateAPI.get("/info/{id}", response_class=JSONResponse, response_model=GetGenerateInfoResponse,
|
||||
summary="获取生成表信息")
|
||||
@Log(title="获取生成表信息", business_type=BusinessType.SELECT)
|
||||
@Auth(permission_list=["generate:btn:info"])
|
||||
async def get_generate_info(request: Request, id: str = Path(description="生成表信息ID")):
|
||||
if table := await GenerateInfo.get_or_none(id=id, del_flag=1):
|
||||
table = {
|
||||
"id": table.id,
|
||||
"author": table.author,
|
||||
"class_name": table.class_name,
|
||||
"permission_id": table.permission_id,
|
||||
"prefix": table.prefix,
|
||||
"remark": table.remark,
|
||||
"table_comment": table.table_comment,
|
||||
"table_name": table.table_name,
|
||||
"description": table.description,
|
||||
"create_time": table.create_time,
|
||||
"update_time": table.update_time,
|
||||
}
|
||||
columns = await GenerateColumn.filter(table_id=table["id"], del_flag=1).order_by("index").values(
|
||||
id="id",
|
||||
table_id="table__id",
|
||||
table_name="table__table_name",
|
||||
index="index",
|
||||
column_comment="column_comment",
|
||||
column_name="column_name",
|
||||
column_type="column_type",
|
||||
python_name="python_name",
|
||||
python_type="python_type",
|
||||
query_way="query_way",
|
||||
show_type="show_type",
|
||||
is_insert="is_insert",
|
||||
is_edit="is_edit",
|
||||
is_list="is_list",
|
||||
is_query="is_query",
|
||||
is_required="is_required",
|
||||
is_hide="is_hide",
|
||||
create_time="create_time",
|
||||
update_time="update_time",
|
||||
)
|
||||
table["columns"] = columns
|
||||
return Response.success(data=table)
|
||||
return Response.error(msg="该生成表信息不存在!")
|
||||
|
||||
|
||||
@generateAPI.put("/updateColumns/{id}", response_class=JSONResponse, response_model=BaseResponse,
|
||||
summary="更新生成表列信息")
|
||||
@generateAPI.post("/updateColumns/{id}", response_class=JSONResponse, response_model=BaseResponse,
|
||||
summary="更新生成表列信息")
|
||||
@Log(title="更新生成表列信息", business_type=BusinessType.UPDATE)
|
||||
@Auth(permission_list=["generate:btn:updateColumns"])
|
||||
async def update_generate_info_columns(
|
||||
request: Request,
|
||||
params: UpdateGenerateInfoParams,
|
||||
id: str = Path(description="生成表信息ID")
|
||||
):
|
||||
if table := await GenerateInfo.get_or_none(id=id, del_flag=1):
|
||||
for column in params.columns:
|
||||
if gen_column := await GenerateColumn.get_or_none(id=column.id, table_id=table.id, del_flag=1):
|
||||
gen_column.column_comment = column.column_comment
|
||||
gen_column.column_name = column.column_name
|
||||
gen_column.column_type = column.column_type
|
||||
gen_column.python_name = column.python_name
|
||||
gen_column.python_type = column.python_type
|
||||
gen_column.query_way = column.query_way
|
||||
gen_column.show_type = column.show_type
|
||||
gen_column.is_insert = column.is_insert
|
||||
gen_column.is_edit = column.is_edit
|
||||
gen_column.is_list = column.is_list
|
||||
gen_column.is_query = column.is_query
|
||||
gen_column.is_required = column.is_required
|
||||
gen_column.is_hide = column.is_hide
|
||||
await gen_column.save()
|
||||
return Response.success(msg="更新成功!")
|
||||
return Response.error(msg="该生成表信息不存在!")
|
||||
|
||||
|
||||
@generateAPI.get("/list", response_class=JSONResponse, response_model=GetGenerateInfoListResponse,
|
||||
summary="查询生成表信息列表")
|
||||
@Log(title="查询生成表信息列表", business_type=BusinessType.SELECT)
|
||||
@Auth(permission_list=["generate:btn:list"])
|
||||
async def get_generate_info_list(
|
||||
request: Request,
|
||||
page: int = Query(default=1, description="页码"),
|
||||
pageSize: int = Query(default=10, description="每页数量"),
|
||||
table_comment: Optional[str] = Query(default=None, description="表注释"),
|
||||
permission_id: Optional[str] = Query(default=None, description="权限ID"),
|
||||
):
|
||||
filterArgs = {
|
||||
f'{k}__contains': v for k, v in {
|
||||
'table_comment': table_comment,
|
||||
'permission_id': permission_id
|
||||
}.items() if v
|
||||
}
|
||||
result = await GenerateInfo.filter(**filterArgs, del_flag=1).order_by("-create_time").offset(
|
||||
(page - 1) * pageSize).limit(
|
||||
pageSize).values(
|
||||
id="id",
|
||||
author="author",
|
||||
class_name="class_name",
|
||||
permission_id="permission_id",
|
||||
prefix="prefix",
|
||||
remark="remark",
|
||||
table_comment="table_comment",
|
||||
table_name="table_name",
|
||||
description="description",
|
||||
create_time="create_time",
|
||||
update_time="update_time",
|
||||
)
|
||||
for item in result:
|
||||
columns = await GenerateColumn.filter(table_id=item["id"], del_flag=1).order_by("index").values(
|
||||
id="id",
|
||||
table_id="table__id",
|
||||
table_name="table__table_name",
|
||||
index="index",
|
||||
column_comment="column_comment",
|
||||
column_name="column_name",
|
||||
column_type="column_type",
|
||||
python_name="python_name",
|
||||
python_type="python_type",
|
||||
query_way="query_way",
|
||||
show_type="show_type",
|
||||
is_insert="is_insert",
|
||||
is_edit="is_edit",
|
||||
is_list="is_list",
|
||||
is_query="is_query",
|
||||
is_required="is_required",
|
||||
is_hide="is_hide",
|
||||
create_time="create_time",
|
||||
update_time="update_time",
|
||||
)
|
||||
item["columns"] = columns
|
||||
return Response.success(data={
|
||||
"result": result,
|
||||
"total": await GenerateInfo.filter(**filterArgs, del_flag=1).count(),
|
||||
"page": page,
|
||||
"pageSize": pageSize,
|
||||
})
|
||||
|
||||
|
||||
@generateAPI.get("/code/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="生成代码")
|
||||
@Log(title="生成代码", business_type=BusinessType.GENCODE)
|
||||
@Auth(permission_list=["generate:btn:code"])
|
||||
async def generate_code(
|
||||
request: Request,
|
||||
id: str = Path(description="生成表信息ID")
|
||||
):
|
||||
if table := await GenerateInfo.get_or_none(id=id, del_flag=1):
|
||||
info = {
|
||||
"author": table.author,
|
||||
"class_name": table.class_name,
|
||||
"permission_id": table.permission_id,
|
||||
"prefix": table.prefix,
|
||||
"remark": table.remark,
|
||||
"table_comment": table.table_comment,
|
||||
"table_name": table.table_name,
|
||||
"description": table.description,
|
||||
}
|
||||
columns = await GenerateColumn.filter(table_id=table.id, del_flag=1).order_by("index").values(
|
||||
id="id",
|
||||
table_id="table__id",
|
||||
table_name="table__table_name",
|
||||
index="index",
|
||||
column_comment="column_comment",
|
||||
column_name="column_name",
|
||||
column_type="column_type",
|
||||
python_name="python_name",
|
||||
python_type="python_type",
|
||||
query_way="query_way",
|
||||
show_type="show_type",
|
||||
is_insert="is_insert",
|
||||
is_edit="is_edit",
|
||||
is_list="is_list",
|
||||
is_query="is_query",
|
||||
is_required="is_required",
|
||||
is_hide="is_hide",
|
||||
create_time="create_time",
|
||||
update_time="update_time",
|
||||
)
|
||||
|
||||
def generate_uuid():
|
||||
return str(uuid.uuid4())
|
||||
|
||||
def current_time():
|
||||
return datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
|
||||
|
||||
comment_env = Environment(loader=FileSystemLoader('templates'))
|
||||
comment_env.globals["uuid4"] = generate_uuid # 注册到 Jinja2
|
||||
comment_env.globals["now"] = current_time
|
||||
data = Generate.prepare_template_data(info, columns)
|
||||
model_template = comment_env.get_template('python/model.py.jinja')
|
||||
model_code = model_template.render(data)
|
||||
schemas_template = comment_env.get_template('python/schemas.py.jinja')
|
||||
schemas_code = schemas_template.render(data)
|
||||
api_py_template = comment_env.get_template('python/api.py.jinja')
|
||||
api_py_code = api_py_template.render(data)
|
||||
type_template = comment_env.get_template('typescript/type.d.ts.jinja')
|
||||
type_code = type_template.render(data)
|
||||
api_ts_template = comment_env.get_template('typescript/api.ts.jinja')
|
||||
api_ts_code = api_ts_template.render(data)
|
||||
hook_template = comment_env.get_template('typescript/hook.tsx.jinja')
|
||||
hook_code = hook_template.render(data)
|
||||
vue_env = Environment(
|
||||
loader=FileSystemLoader('templates/vue'),
|
||||
block_start_string="[%", # 替换 Jinja2 代码块的起始标记
|
||||
block_end_string="%]", # 替换 Jinja2 代码块的结束标记
|
||||
variable_start_string="[[",
|
||||
variable_end_string="]]",
|
||||
comment_start_string="[#",
|
||||
comment_end_string="#]"
|
||||
)
|
||||
index_template = vue_env.get_template('index.vue.jinja')
|
||||
index_code = index_template.render(data)
|
||||
form_template = vue_env.get_template('form.vue.jinja')
|
||||
form_code = form_template.render(data)
|
||||
sql_template = comment_env.get_template('sql.jinja')
|
||||
sql_code = sql_template.render(data)
|
||||
data = {
|
||||
"backend": [
|
||||
{
|
||||
"label": f"/models/{info['class_name'].lower()}.py",
|
||||
"value": model_code,
|
||||
"type": "python",
|
||||
"path": f"/python/models/{info['class_name'].lower()}.py"
|
||||
},
|
||||
{
|
||||
"label": f"/schemas/{info['class_name'].lower()}.py",
|
||||
"value": schemas_code,
|
||||
"type": "python",
|
||||
"path": f"/python/schemas/{info['class_name'].lower()}.py"
|
||||
},
|
||||
{
|
||||
"label": f"/api/{info['class_name'].lower()}.py",
|
||||
"value": api_py_code,
|
||||
"type": "python",
|
||||
"path": f"/python/api/{info['class_name'].lower()}.py"
|
||||
},
|
||||
],
|
||||
"frontend": [
|
||||
{
|
||||
"label": f"/types/{info['class_name'].lower()}.d.ts",
|
||||
"value": type_code,
|
||||
"type": "typescript",
|
||||
"path": f"/types/{info['class_name'].lower()}.d.ts"
|
||||
},
|
||||
{
|
||||
"label": f"/api/{info['class_name'].lower()}.ts",
|
||||
"value": api_ts_code,
|
||||
"type": "typescript",
|
||||
"path": f"/api/{info['class_name'].lower()}.ts"
|
||||
},
|
||||
{
|
||||
"label": f"/{info['class_name'].lower()}/utils/hook.tsx",
|
||||
"value": hook_code,
|
||||
"type": "typescript",
|
||||
"path": f"/{info['class_name'].lower()}/utils/hook.tsx"
|
||||
},
|
||||
{
|
||||
"label": f"/{info['class_name'].lower()}/index.vue",
|
||||
"value": index_code,
|
||||
"type": "vue",
|
||||
"path": f"/{info['class_name'].lower()}/index.vue"
|
||||
},
|
||||
{
|
||||
"label": f"/{info['class_name'].lower()}/components/form.vue",
|
||||
"value": form_code,
|
||||
"type": "vue",
|
||||
"path": f"/{info['class_name'].lower()}/components/form.vue"
|
||||
},
|
||||
],
|
||||
"sql": [
|
||||
{
|
||||
"label": f"{info['table_name']}.sql",
|
||||
"value": sql_code,
|
||||
"type": "sql",
|
||||
"path": f"{info['table_name']}.sql"
|
||||
}
|
||||
]
|
||||
}
|
||||
return Response.success(data=data)
|
||||
return Response.error(msg="生成失败!")
|
||||
Reference in New Issue
Block a user