mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-20 20:39:55 +00:00
refactor: 重构代码结构并优化命名一致性
feat(backend): 新增模块示例和路径配置 fix(backend): 修复菜单模型名称唯一性约束 refactor(backend): 重构日志模块和路径配置 style(backend): 统一API方法命名规范 refactor(frontend): 重构API方法命名规范 fix(frontend): 修复权限校验问题 refactor: 移动定时任务测试文件到正确模块 docs: 更新模板文件路径 refactor: 优化日志记录和错误处理 fix: 修复排序默认值问题
This commit is contained in:
@@ -8,7 +8,7 @@ from app.common.request import PaginationService
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .param import McpQueryParam
|
||||
@@ -34,7 +34,7 @@ async def chat_controller(
|
||||
- StreamingResponse: 流式响应,每次返回一个聊天响应
|
||||
"""
|
||||
user_name = auth.user.name if auth.user else "未知用户"
|
||||
logger.info(f"用户 {user_name} 发起智能对话: {query.message[:50]}...")
|
||||
log.info(f"用户 {user_name} 发起智能对话: {query.message[:50]}...")
|
||||
|
||||
async def generate_response():
|
||||
try:
|
||||
@@ -43,7 +43,7 @@ async def chat_controller(
|
||||
if chunk:
|
||||
yield chunk.encode('utf-8') if isinstance(chunk, str) else chunk
|
||||
except Exception as e:
|
||||
logger.error(f"流式响应出错: {str(e)}")
|
||||
log.error(f"流式响应出错: {str(e)}")
|
||||
yield f"抱歉,处理您的请求时出现了错误: {str(e)}".encode('utf-8')
|
||||
|
||||
return StreamResponse(generate_response(), media_type="text/plain; charset=utf-8")
|
||||
@@ -64,7 +64,7 @@ async def detail_controller(
|
||||
- JSONResponse: 包含 MCP 服务器详情的 JSON 响应
|
||||
"""
|
||||
result_dict = await McpService.detail_service(auth=auth, id=id)
|
||||
logger.info(f"获取 MCP 服务器详情成功 {id}")
|
||||
log.info(f"获取 MCP 服务器详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取 MCP 服务器详情成功")
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ async def list_controller(
|
||||
"""
|
||||
result_dict_list = await McpService.list_service(auth=auth, search=search, order_by=page.order_by)
|
||||
result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
|
||||
logger.info(f"查询 MCP 服务器列表成功")
|
||||
log.info(f"查询 MCP 服务器列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询 MCP 服务器列表成功")
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ async def create_controller(
|
||||
- JSONResponse: 包含创建 MCP 服务器结果的 JSON 响应
|
||||
"""
|
||||
result_dict = await McpService.create_service(auth=auth, data=data)
|
||||
logger.info(f"创建 MCP 服务器成功: {result_dict}")
|
||||
log.info(f"创建 MCP 服务器成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建 MCP 服务器成功")
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ async def update_controller(
|
||||
- JSONResponse: 包含修改 MCP 服务器结果的 JSON 响应
|
||||
"""
|
||||
result_dict = await McpService.update_service(auth=auth, id=id, data=data)
|
||||
logger.info(f"修改 MCP 服务器成功: {result_dict}")
|
||||
log.info(f"修改 MCP 服务器成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改 MCP 服务器成功")
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ async def delete_controller(
|
||||
- JSONResponse: 包含删除 MCP 服务器结果的 JSON 响应
|
||||
"""
|
||||
await McpService.delete_service(auth=auth, ids=ids)
|
||||
logger.info(f"删除 MCP 服务器成功: {ids}")
|
||||
log.info(f"删除 MCP 服务器成功: {ids}")
|
||||
return SuccessResponse(msg="删除 MCP 服务器成功")
|
||||
|
||||
|
||||
@@ -172,9 +172,9 @@ async def websocket_chat_controller(
|
||||
if chunk:
|
||||
await websocket.send_text(chunk)
|
||||
except Exception as e:
|
||||
logger.error(f"处理聊天查询出错: {str(e)}")
|
||||
log.error(f"处理聊天查询出错: {str(e)}")
|
||||
await websocket.send_text(f"抱歉,处理您的请求时出现了错误: {str(e)}")
|
||||
except Exception as e:
|
||||
logger.error(f"WebSocket聊天出错: {str(e)}")
|
||||
log.error(f"WebSocket聊天出错: {str(e)}")
|
||||
finally:
|
||||
await websocket.close()
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
from typing import List, Dict, Optional, Any
|
||||
|
||||
from app.core.exceptions import CustomException
|
||||
from app.utils.ai_util import AIClient
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .tools.ai_util import AIClient
|
||||
from .schema import McpCreateSchema, McpUpdateSchema, McpOutSchema, ChatQuerySchema
|
||||
from .param import McpQueryParam
|
||||
from .crud import McpCRUD
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Any, AsyncGenerator
|
||||
from openai import AsyncOpenAI, OpenAI
|
||||
from openai.types.chat.chat_completion import ChatCompletion
|
||||
import httpx
|
||||
|
||||
from app.config.setting import settings
|
||||
from app.core.logger import log
|
||||
|
||||
|
||||
class AIClient:
|
||||
"""
|
||||
AI客户端类,用于与OpenAI API交互。
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.model = settings.OPENAI_MODEL
|
||||
# 创建一个不带冲突参数的httpx客户端
|
||||
self.http_client = httpx.AsyncClient(
|
||||
timeout=30.0,
|
||||
follow_redirects=True
|
||||
)
|
||||
|
||||
# 使用自定义的http客户端
|
||||
self.client = AsyncOpenAI(
|
||||
api_key=settings.OPENAI_API_KEY,
|
||||
base_url=settings.OPENAI_BASE_URL,
|
||||
http_client=self.http_client
|
||||
)
|
||||
|
||||
def _friendly_error_message(self, e: Exception) -> str:
|
||||
"""将 OpenAI 或网络异常转换为友好的中文提示。"""
|
||||
# 尝试获取状态码与错误体
|
||||
status_code = getattr(e, "status_code", None)
|
||||
body = getattr(e, "body", None)
|
||||
message = None
|
||||
error_type = None
|
||||
error_code = None
|
||||
try:
|
||||
if isinstance(body, dict) and "error" in body:
|
||||
err = body.get("error") or {}
|
||||
error_type = err.get("type")
|
||||
error_code = err.get("code")
|
||||
message = err.get("message")
|
||||
except Exception:
|
||||
# 忽略解析失败
|
||||
pass
|
||||
|
||||
text = str(e)
|
||||
msg = message or text
|
||||
|
||||
# 特定错误映射
|
||||
# 欠费/账户状态异常
|
||||
if (error_code == "Arrearage") or (error_type == "Arrearage") or ("in good standing" in (msg or "")):
|
||||
return "账户欠费或结算异常,访问被拒绝。请检查账号状态或更换有效的 API Key。"
|
||||
# 鉴权失败
|
||||
if status_code == 401 or "invalid api key" in msg.lower():
|
||||
return "鉴权失败,API Key 无效或已过期。请检查系统配置中的 API Key。"
|
||||
# 权限不足或被拒绝
|
||||
if status_code == 403 or error_type in {"PermissionDenied", "permission_denied"}:
|
||||
return "访问被拒绝,权限不足或账号受限。请检查账户权限设置。"
|
||||
# 配额不足或限流
|
||||
if status_code == 429 or error_type in {"insufficient_quota", "rate_limit_exceeded"}:
|
||||
return "请求过于频繁或配额已用尽。请稍后重试或提升账户配额。"
|
||||
# 客户端错误
|
||||
if status_code == 400:
|
||||
return f"请求参数错误或服务拒绝:{message or '请检查输入内容。'}"
|
||||
# 服务端错误
|
||||
if status_code in {500, 502, 503, 504}:
|
||||
return "服务暂时不可用,请稍后重试。"
|
||||
|
||||
# 默认兜底
|
||||
return f"处理您的请求时出现错误:{msg}"
|
||||
|
||||
async def process(self, query: str) -> AsyncGenerator[str, None]:
|
||||
"""
|
||||
处理查询并返回流式响应
|
||||
|
||||
参数:
|
||||
- query (str): 用户查询。
|
||||
|
||||
返回:
|
||||
- AsyncGenerator[str, None]: 流式响应内容。
|
||||
"""
|
||||
system_prompt = """你是一个有用的AI助手,可以帮助用户回答问题和提供帮助。请用中文回答用户的问题。"""
|
||||
|
||||
try:
|
||||
# 使用 await 调用异步客户端
|
||||
response = await self.client.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": query}
|
||||
],
|
||||
stream=True
|
||||
)
|
||||
|
||||
# 流式返回响应
|
||||
async for chunk in response:
|
||||
if chunk.choices and chunk.choices[0].delta.content:
|
||||
yield chunk.choices[0].delta.content
|
||||
|
||||
except Exception as e:
|
||||
# 记录详细错误,返回友好提示
|
||||
log.error(f"AI处理查询失败: {str(e)}")
|
||||
yield self._friendly_error_message(e)
|
||||
|
||||
async def close(self) -> None:
|
||||
"""
|
||||
关闭客户端连接
|
||||
"""
|
||||
if hasattr(self, 'client'):
|
||||
await self.client.close()
|
||||
if hasattr(self, 'http_client'):
|
||||
await self.http_client.aclose()
|
||||
@@ -9,7 +9,7 @@ from app.utils.common_util import bytes2file_response
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .param import JobQueryParam, JobLogQueryParam
|
||||
@@ -39,7 +39,7 @@ async def get_obj_detail_controller(
|
||||
- JSONResponse: 包含定时任务详情的JSON响应
|
||||
"""
|
||||
result_dict = await JobService.get_job_detail_service(id=id, auth=auth)
|
||||
logger.info(f"获取定时任务详情成功 {id}")
|
||||
log.info(f"获取定时任务详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取定时任务详情成功")
|
||||
|
||||
@JobRouter.get("/list", summary="查询定时任务", description="查询定时任务")
|
||||
@@ -61,7 +61,7 @@ async def get_obj_list_controller(
|
||||
"""
|
||||
result_dict_list = await JobService.get_job_list_service(auth=auth, search=search, order_by=page.order_by)
|
||||
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
|
||||
logger.info(f"查询定时任务列表成功")
|
||||
log.info(f"查询定时任务列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询定时任务列表成功")
|
||||
|
||||
@JobRouter.post("/create", summary="创建定时任务", description="创建定时任务")
|
||||
@@ -80,7 +80,7 @@ async def create_obj_controller(
|
||||
- JSONResponse: 包含创建定时任务结果的JSON响应
|
||||
"""
|
||||
result_dict = await JobService.create_job_service(auth=auth, data=data)
|
||||
logger.info(f"创建定时任务成功: {result_dict}")
|
||||
log.info(f"创建定时任务成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建定时任务成功")
|
||||
|
||||
@JobRouter.put("/update/{id}", summary="修改定时任务", description="修改定时任务")
|
||||
@@ -101,7 +101,7 @@ async def update_obj_controller(
|
||||
- JSONResponse: 包含修改定时任务结果的JSON响应
|
||||
"""
|
||||
result_dict = await JobService.update_job_service(auth=auth, id=id, data=data)
|
||||
logger.info(f"修改定时任务成功: {result_dict}")
|
||||
log.info(f"修改定时任务成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改定时任务成功")
|
||||
|
||||
@JobRouter.delete("/delete", summary="删除定时任务", description="删除定时任务")
|
||||
@@ -120,7 +120,7 @@ async def delete_obj_controller(
|
||||
- JSONResponse: 包含删除定时任务结果的JSON响应
|
||||
"""
|
||||
await JobService.delete_job_service(auth=auth, ids=ids)
|
||||
logger.info(f"删除定时任务成功: {ids}")
|
||||
log.info(f"删除定时任务成功: {ids}")
|
||||
return SuccessResponse(msg="删除定时任务成功")
|
||||
|
||||
@JobRouter.post('/export', summary="导出定时任务", description="导出定时任务")
|
||||
@@ -140,7 +140,7 @@ async def export_obj_list_controller(
|
||||
"""
|
||||
result_dict_list = await JobService.get_job_list_service(search=search, auth=auth)
|
||||
export_result = await JobService.export_job_service(data_list=result_dict_list)
|
||||
logger.info('导出定时任务成功')
|
||||
log.info('导出定时任务成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(export_result),
|
||||
@@ -164,7 +164,7 @@ async def clear_obj_log_controller(
|
||||
- JSONResponse: 包含清空定时任务结果的JSON响应
|
||||
"""
|
||||
await JobService.clear_job_service(auth=auth)
|
||||
logger.info(f"清空定时任务成功")
|
||||
log.info(f"清空定时任务成功")
|
||||
return SuccessResponse(msg="清空定时任务成功")
|
||||
|
||||
@JobRouter.put("/option", summary="暂停/恢复/重启定时任务", description="暂停/恢复/重启定时任务")
|
||||
@@ -185,7 +185,7 @@ async def option_obj_controller(
|
||||
- JSONResponse: 包含操作定时任务结果的JSON响应
|
||||
"""
|
||||
await JobService.option_job_service(auth=auth, id=id, option=option)
|
||||
logger.info(f"操作定时任务成功: {id}")
|
||||
log.info(f"操作定时任务成功: {id}")
|
||||
return SuccessResponse(msg="操作定时任务成功")
|
||||
|
||||
@JobRouter.get("/log", summary="获取定时任务日志", description="获取定时任务日志", dependencies=[Depends(AuthPermission(["module_application:job:query"]))])
|
||||
@@ -235,7 +235,7 @@ async def get_job_log_detail_controller(
|
||||
- JSONResponse: 获取定时任务日志详情的JSON响应
|
||||
"""
|
||||
result_dict = await JobLogService.get_job_log_detail_service(id=id, auth=auth)
|
||||
logger.info(f"获取定时任务日志详情成功 {id}")
|
||||
log.info(f"获取定时任务日志详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取定时任务日志详情成功")
|
||||
|
||||
|
||||
@@ -259,7 +259,7 @@ async def get_job_log_list_controller(
|
||||
order_by = [{"create_time": "desc"}]
|
||||
result_dict_list = await JobLogService.get_job_log_list_service(auth=auth, search=search, order_by=order_by)
|
||||
result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
|
||||
logger.info(f"查询定时任务日志列表成功")
|
||||
log.info(f"查询定时任务日志列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询定时任务日志列表成功")
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ async def delete_job_log_controller(
|
||||
- JSONResponse: 包含删除定时任务日志结果的JSON响应
|
||||
"""
|
||||
await JobLogService.delete_job_log_service(auth=auth, ids=ids)
|
||||
logger.info(f"删除定时任务日志成功: {ids}")
|
||||
log.info(f"删除定时任务日志成功: {ids}")
|
||||
return SuccessResponse(msg="删除定时任务日志成功")
|
||||
|
||||
|
||||
@@ -297,7 +297,7 @@ async def clear_job_log_controller(
|
||||
- JSONResponse: 包含清空定时任务日志结果的JSON响应
|
||||
"""
|
||||
await JobLogService.clear_job_log_service(auth=auth)
|
||||
logger.info(f"清空定时任务日志成功")
|
||||
log.info(f"清空定时任务日志成功")
|
||||
return SuccessResponse(msg="清空定时任务日志成功")
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@ async def export_job_log_list_controller(
|
||||
"""
|
||||
result_dict_list = await JobLogService.get_job_log_list_service(search=search, auth=auth)
|
||||
export_result = await JobLogService.export_job_log_service(data_list=result_dict_list)
|
||||
logger.info('导出定时任务日志成功')
|
||||
log.info('导出定时任务日志成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(export_result),
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
from app.core.logger import log
|
||||
|
||||
def job(*args, **kwargs) -> None:
|
||||
"""
|
||||
定时任务执行同步函数示例
|
||||
|
||||
参数:
|
||||
- args: 位置参数。
|
||||
- kwargs: 关键字参数。
|
||||
"""
|
||||
try:
|
||||
print(f"开始执行任务: {args}-{kwargs}")
|
||||
time.sleep(3)
|
||||
print(f'{datetime.now()}同步函数执行完成')
|
||||
except Exception as e:
|
||||
log.error(f"同步任务执行失败: {e}")
|
||||
raise
|
||||
|
||||
async def async_job(*args, **kwargs) -> None:
|
||||
"""
|
||||
定时任务执行异步函数示例
|
||||
|
||||
参数:
|
||||
- args: 位置参数。
|
||||
- kwargs: 关键字参数。
|
||||
"""
|
||||
try:
|
||||
print(f"开始执行任务: {args}-{kwargs}")
|
||||
time.sleep(3)
|
||||
print(f'{datetime.now()}异步函数执行完成')
|
||||
except Exception as e:
|
||||
log.error(f"异步任务执行失败: {e}")
|
||||
raise
|
||||
|
||||
@@ -9,7 +9,7 @@ from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .param import ApplicationQueryParam
|
||||
@@ -38,7 +38,7 @@ async def get_obj_detail_controller(
|
||||
- JSONResponse: 包含应用详情的JSON响应
|
||||
"""
|
||||
result_dict = await ApplicationService.detail_service(id=id, auth=auth)
|
||||
logger.info(f"获取应用详情成功 {id}")
|
||||
log.info(f"获取应用详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取应用详情成功")
|
||||
|
||||
@MyAppRouter.get("/list", summary="查询应用列表", description="查询应用列表")
|
||||
@@ -60,7 +60,7 @@ async def get_obj_list_controller(
|
||||
"""
|
||||
result_dict_list = await ApplicationService.list_service(auth=auth, search=search, order_by=page.order_by)
|
||||
result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
|
||||
logger.info(f"查询应用列表成功")
|
||||
log.info(f"查询应用列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询应用列表成功")
|
||||
|
||||
@MyAppRouter.post("/create", summary="创建应用", description="创建应用")
|
||||
@@ -79,7 +79,7 @@ async def create_obj_controller(
|
||||
- JSONResponse: 包含创建应用详情的JSON响应
|
||||
"""
|
||||
result_dict = await ApplicationService.create_service(auth=auth, data=data)
|
||||
logger.info(f"创建应用成功: {result_dict}")
|
||||
log.info(f"创建应用成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建应用成功")
|
||||
|
||||
@MyAppRouter.put("/update/{id}", summary="修改应用", description="修改应用")
|
||||
@@ -100,7 +100,7 @@ async def update_obj_controller(
|
||||
- JSONResponse: 包含修改应用详情的JSON响应
|
||||
"""
|
||||
result_dict = await ApplicationService.update_service(auth=auth, id=id, data=data)
|
||||
logger.info(f"修改应用成功: {result_dict}")
|
||||
log.info(f"修改应用成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改应用成功")
|
||||
|
||||
@MyAppRouter.delete("/delete", summary="删除应用", description="删除应用")
|
||||
@@ -119,7 +119,7 @@ async def delete_obj_controller(
|
||||
- JSONResponse: 包含删除应用详情的JSON响应
|
||||
"""
|
||||
await ApplicationService.delete_service(auth=auth, ids=ids)
|
||||
logger.info(f"删除应用成功: {ids}")
|
||||
log.info(f"删除应用成功: {ids}")
|
||||
return SuccessResponse(msg="删除应用成功")
|
||||
|
||||
@MyAppRouter.patch("/available/setting", summary="批量修改应用状态", description="批量修改应用状态")
|
||||
@@ -138,5 +138,5 @@ async def batch_set_available_obj_controller(
|
||||
- JSONResponse: 批量修改应用状态成功
|
||||
"""
|
||||
await ApplicationService.set_available_service(auth=auth, data=data)
|
||||
logger.info(f"批量修改应用状态成功: {data.ids}")
|
||||
log.info(f"批量修改应用状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改应用状态成功")
|
||||
@@ -6,7 +6,7 @@ from fastapi.responses import JSONResponse, FileResponse
|
||||
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
from app.common.response import SuccessResponse, UploadFileResponse
|
||||
from app.utils.upload_util import UploadUtil
|
||||
|
||||
@@ -31,7 +31,7 @@ async def upload_controller(
|
||||
- JSONResponse: 包含上传文件详情的JSON响应
|
||||
"""
|
||||
result_dict = await FileService.upload_service(base_url=str(request.base_url), file=file)
|
||||
logger.info(f"上传文件成功 {result_dict}")
|
||||
log.info(f"上传文件成功 {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="上传文件成功")
|
||||
|
||||
@FileRouter.post("/download", summary="下载文件", description="下载文件", dependencies=[Depends(AuthPermission(["module_common:file:download"]))])
|
||||
@@ -54,5 +54,5 @@ async def download_controller(
|
||||
result = await FileService.download_service(file_path=file_path)
|
||||
if delete:
|
||||
background_tasks.add_task(UploadUtil.delete_file, Path(file_path))
|
||||
logger.info(f"下载文件成功")
|
||||
log.info(f"下载文件成功")
|
||||
return UploadFileResponse(file_path=result.file_path, filename=result.file_name)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
+19
-19
@@ -10,7 +10,7 @@ from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
@@ -27,7 +27,7 @@ DemoRouter = APIRouter(route_class=OperationLogRoute, prefix="/demo", tags=["示
|
||||
@DemoRouter.get("/detail/{id}", summary="获取示例详情", description="获取示例详情")
|
||||
async def get_obj_detail_controller(
|
||||
id: int = Path(..., description="示例ID"),
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_generator:demo:query"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_example:demo:query"]))
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
获取示例详情
|
||||
@@ -40,14 +40,14 @@ async def get_obj_detail_controller(
|
||||
- JSONResponse: 包含示例详情的JSON响应
|
||||
"""
|
||||
result_dict = await DemoService.detail_service(id=id, auth=auth)
|
||||
logger.info(f"获取示例详情成功 {id}")
|
||||
log.info(f"获取示例详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取示例详情成功")
|
||||
|
||||
@DemoRouter.get("/list", summary="查询示例列表", description="查询示例列表")
|
||||
async def get_obj_list_controller(
|
||||
page: PaginationQueryParam = Depends(),
|
||||
search: DemoQueryParam = Depends(),
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_generator:demo:query"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_example:demo:query"]))
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
查询示例列表
|
||||
@@ -68,13 +68,13 @@ async def get_obj_list_controller(
|
||||
search=search,
|
||||
order_by=page.order_by
|
||||
)
|
||||
logger.info("查询示例列表成功")
|
||||
log.info("查询示例列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询示例列表成功")
|
||||
|
||||
@DemoRouter.post("/create", summary="创建示例", description="创建示例")
|
||||
async def create_obj_controller(
|
||||
data: DemoCreateSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_generator:demo:create"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_example:demo:create"]))
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
创建示例
|
||||
@@ -87,14 +87,14 @@ async def create_obj_controller(
|
||||
- JSONResponse: 包含创建示例详情的JSON响应
|
||||
"""
|
||||
result_dict = await DemoService.create_service(auth=auth, data=data)
|
||||
logger.info(f"创建示例成功: {result_dict.get('name')}")
|
||||
log.info(f"创建示例成功: {result_dict.get('name')}")
|
||||
return SuccessResponse(data=result_dict, msg="创建示例成功")
|
||||
|
||||
@DemoRouter.put("/update/{id}", summary="修改示例", description="修改示例")
|
||||
async def update_obj_controller(
|
||||
data: DemoUpdateSchema,
|
||||
id: int = Path(..., description="示例ID"),
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_generator:demo:update"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_example:demo:update"]))
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
修改示例
|
||||
@@ -108,13 +108,13 @@ async def update_obj_controller(
|
||||
- JSONResponse: 包含修改示例详情的JSON响应
|
||||
"""
|
||||
result_dict = await DemoService.update_service(auth=auth, id=id, data=data)
|
||||
logger.info(f"修改示例成功: {result_dict.get('name')}")
|
||||
log.info(f"修改示例成功: {result_dict.get('name')}")
|
||||
return SuccessResponse(data=result_dict, msg="修改示例成功")
|
||||
|
||||
@DemoRouter.delete("/delete", summary="删除示例", description="删除示例")
|
||||
async def delete_obj_controller(
|
||||
ids: list[int] = Body(..., description="ID列表"),
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_generator:demo:delete"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_example:demo:delete"]))
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
删除示例
|
||||
@@ -127,13 +127,13 @@ async def delete_obj_controller(
|
||||
- JSONResponse: 包含删除示例详情的JSON响应
|
||||
"""
|
||||
await DemoService.delete_service(auth=auth, ids=ids)
|
||||
logger.info(f"删除示例成功: {ids}")
|
||||
log.info(f"删除示例成功: {ids}")
|
||||
return SuccessResponse(msg="删除示例成功")
|
||||
|
||||
@DemoRouter.patch("/available/setting", summary="批量修改示例状态", description="批量修改示例状态")
|
||||
async def batch_set_available_obj_controller(
|
||||
data: BatchSetAvailable,
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_generator:demo:patch"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_example:demo:patch"]))
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
批量修改示例状态
|
||||
@@ -146,13 +146,13 @@ async def batch_set_available_obj_controller(
|
||||
- JSONResponse: 包含批量修改示例状态详情的JSON响应
|
||||
"""
|
||||
await DemoService.set_available_service(auth=auth, data=data)
|
||||
logger.info(f"批量修改示例状态成功: {data.ids}")
|
||||
log.info(f"批量修改示例状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改示例状态成功")
|
||||
|
||||
@DemoRouter.post('/export', summary="导出示例", description="导出示例")
|
||||
async def export_obj_list_controller(
|
||||
search: DemoQueryParam = Depends(),
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_generator:demo:export"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_example:demo:export"]))
|
||||
) -> StreamingResponse:
|
||||
"""
|
||||
导出示例
|
||||
@@ -166,7 +166,7 @@ async def export_obj_list_controller(
|
||||
"""
|
||||
result_dict_list = await DemoService.list_service(search=search, auth=auth)
|
||||
export_result = await DemoService.batch_export_service(obj_list=result_dict_list)
|
||||
logger.info('导出示例成功')
|
||||
log.info('导出示例成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(export_result),
|
||||
@@ -179,7 +179,7 @@ async def export_obj_list_controller(
|
||||
@DemoRouter.post('/import', summary="导入示例", description="导入示例")
|
||||
async def import_obj_list_controller(
|
||||
file: UploadFile,
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_generator:demo:import"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_example:demo:import"]))
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
导入示例
|
||||
@@ -192,10 +192,10 @@ async def import_obj_list_controller(
|
||||
- JSONResponse: 包含导入示例详情的JSON响应
|
||||
"""
|
||||
batch_import_result = await DemoService.batch_import_service(file=file, auth=auth, update_support=True)
|
||||
logger.info(f"导入示例成功: {batch_import_result}")
|
||||
log.info(f"导入示例成功: {batch_import_result}")
|
||||
return SuccessResponse(data=batch_import_result, msg="导入示例成功")
|
||||
|
||||
@DemoRouter.post('/download/template', summary="获取示例导入模板", description="获取示例导入模板", dependencies=[Depends(AuthPermission(["module_generator:demo:download"]))])
|
||||
@DemoRouter.post('/download/template', summary="获取示例导入模板", description="获取示例导入模板", dependencies=[Depends(AuthPermission(["module_example:demo:download"]))])
|
||||
async def export_obj_template_controller() -> StreamingResponse:
|
||||
"""
|
||||
获取示例导入模板
|
||||
@@ -204,7 +204,7 @@ async def export_obj_template_controller() -> StreamingResponse:
|
||||
- StreamingResponse: 包含示例导入模板的Excel文件流响应
|
||||
"""
|
||||
example_import_template_result = await DemoService.import_template_download_service()
|
||||
logger.info('获取示例导入模板成功')
|
||||
log.info('获取示例导入模板成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(example_import_template_result),
|
||||
+2
-2
@@ -8,7 +8,7 @@ import pandas as pd
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.exceptions import CustomException
|
||||
from app.utils.excel_util import ExcelUtil
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .schema import DemoCreateSchema, DemoUpdateSchema, DemoOutSchema
|
||||
@@ -286,7 +286,7 @@ class DemoService:
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"批量导入用户失败: {str(e)}")
|
||||
log.error(f"批量导入用户失败: {str(e)}")
|
||||
raise CustomException(msg=f"导入失败: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
@@ -0,0 +1,129 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from fastapi import APIRouter, Depends, UploadFile, Body, Path
|
||||
from fastapi.responses import StreamingResponse, JSONResponse
|
||||
from app.common.response import SuccessResponse, StreamResponse
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.utils.common_util import bytes2file_response
|
||||
from app.core.logger import log
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
|
||||
from .service import GenDemo01Service
|
||||
from .schema import GenDemo01CreateSchema, GenDemo01UpdateSchema
|
||||
from .param import GenDemo01QueryParam
|
||||
|
||||
GenDemo01Router = APIRouter(route_class=OperationLogRoute, prefix='/gen_demo01', tags=["示例模块"])
|
||||
|
||||
@GenDemo01Router.get("/detail/{id}", summary="获取示例详情", description="获取示例详情")
|
||||
async def get_gen_demo01_detail_controller(
|
||||
id: int = Path(..., description="ID"),
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_gencode:gen_demo01:query"]))
|
||||
) -> JSONResponse:
|
||||
"""获取示例详情接口"""
|
||||
result_dict = await GenDemo01Service.detail_gen_demo01_service(auth=auth, id=id)
|
||||
log.info(f"获取示例详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取示例详情成功")
|
||||
|
||||
@GenDemo01Router.get("/list", summary="查询示例列表", description="查询示例列表")
|
||||
async def get_gen_demo01_list_controller(
|
||||
page: PaginationQueryParam = Depends(),
|
||||
search: GenDemo01QueryParam = Depends(),
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_gencode:gen_demo01:query"]))
|
||||
) -> JSONResponse:
|
||||
"""查询示例列表接口(数据库分页)"""
|
||||
result_dict = await GenDemo01Service.page_service(
|
||||
auth=auth,
|
||||
page_no=page.page_no if page.page_no is not None else 1,
|
||||
page_size=page.page_size if page.page_size is not None else 10,
|
||||
search=search,
|
||||
order_by=page.order_by
|
||||
)
|
||||
log.info("查询示例列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询示例列表成功")
|
||||
|
||||
@GenDemo01Router.post("/create", summary="创建示例", description="创建示例")
|
||||
async def create_gen_demo01_controller(
|
||||
data: GenDemo01CreateSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_gencode:gen_demo01:create"]))
|
||||
) -> JSONResponse:
|
||||
"""创建示例接口"""
|
||||
result_dict = await GenDemo01Service.create_gen_demo01_service(auth=auth, data=data)
|
||||
log.info("创建示例成功")
|
||||
return SuccessResponse(data=result_dict, msg="创建示例成功")
|
||||
|
||||
@GenDemo01Router.put("/update/{id}", summary="修改示例", description="修改示例")
|
||||
async def update_gen_demo01_controller(
|
||||
data: GenDemo01UpdateSchema,
|
||||
id: int = Path(..., description="ID"),
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_gencode:gen_demo01:update"]))
|
||||
) -> JSONResponse:
|
||||
"""修改示例接口"""
|
||||
result_dict = await GenDemo01Service.update_gen_demo01_service(auth=auth, id=id, data=data)
|
||||
log.info("修改示例成功")
|
||||
return SuccessResponse(data=result_dict, msg="修改示例成功")
|
||||
|
||||
@GenDemo01Router.delete("/delete", summary="删除示例", description="删除示例")
|
||||
async def delete_gen_demo01_controller(
|
||||
ids: list[int] = Body(..., description="ID列表"),
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_gencode:gen_demo01:delete"]))
|
||||
) -> JSONResponse:
|
||||
"""删除示例接口"""
|
||||
await GenDemo01Service.delete_gen_demo01_service(auth=auth, ids=ids)
|
||||
log.info(f"删除示例成功: {ids}")
|
||||
return SuccessResponse(msg="删除示例成功")
|
||||
|
||||
@GenDemo01Router.patch("/available/setting", summary="批量修改示例状态", description="批量修改示例状态")
|
||||
async def batch_set_available_gen_demo01_controller(
|
||||
data: BatchSetAvailable,
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_gencode:gen_demo01:patch"]))
|
||||
) -> JSONResponse:
|
||||
"""批量修改示例状态接口"""
|
||||
await GenDemo01Service.set_available_gen_demo01_service(auth=auth, data=data)
|
||||
log.info(f"批量修改示例状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改示例状态成功")
|
||||
|
||||
@GenDemo01Router.post('/export', summary="导出示例", description="导出示例")
|
||||
async def export_gen_demo01_list_controller(
|
||||
search: GenDemo01QueryParam = Depends(),
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_gencode:gen_demo01:export"]))
|
||||
) -> StreamingResponse:
|
||||
"""导出示例接口"""
|
||||
result_dict_list = await GenDemo01Service.list_gen_demo01_service(search=search, auth=auth)
|
||||
export_result = await GenDemo01Service.batch_export_service(obj_list=result_dict_list)
|
||||
log.info('导出示例成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(export_result),
|
||||
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
headers={
|
||||
'Content-Disposition': 'attachment; filename=gen_demo01.xlsx'
|
||||
}
|
||||
)
|
||||
|
||||
@GenDemo01Router.post('/import', summary="导入示例", description="导入示例")
|
||||
async def import_gen_demo01_list_controller(
|
||||
file: UploadFile,
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_gencode:gen_demo01:import"]))
|
||||
) -> JSONResponse:
|
||||
"""导入示例接口"""
|
||||
batch_import_result = await GenDemo01Service.batch_import_gen_demo01_service(file=file, auth=auth, update_support=True)
|
||||
log.info("导入示例成功")
|
||||
|
||||
return SuccessResponse(data=batch_import_result, msg="导入示例成功")
|
||||
|
||||
@GenDemo01Router.post('/download/template', summary="获取示例导入模板", description="获取示例导入模板", dependencies=[Depends(AuthPermission(["module_gencode:gen_demo01:download"]))])
|
||||
async def export_gen_demo01_template_controller() -> StreamingResponse:
|
||||
"""获取示例导入模板接口"""
|
||||
example_import_template_result = await GenDemo01Service.import_template_download_gen_demo01_service()
|
||||
log.info('获取示例导入模板成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(example_import_template_result),
|
||||
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
headers={
|
||||
'Content-Disposition': 'attachment; filename=gen_demo01_template.xlsx'
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,123 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .model import GenDemo01Model
|
||||
from .schema import GenDemo01CreateSchema, GenDemo01UpdateSchema, GenDemo01OutSchema
|
||||
|
||||
|
||||
class GenDemo01CRUD(CRUDBase[GenDemo01Model, GenDemo01CreateSchema, GenDemo01UpdateSchema]):
|
||||
"""示例数据层"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
"""
|
||||
初始化CRUD数据层
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
"""
|
||||
super().__init__(model=GenDemo01Model, auth=auth)
|
||||
|
||||
async def get_by_id_gen_demo01_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[GenDemo01Model]:
|
||||
"""
|
||||
详情
|
||||
|
||||
参数:
|
||||
- id (int): 对象ID
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[GenDemo01Model]: 模型实例或None
|
||||
"""
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def list_gen_demo01_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[GenDemo01Model]:
|
||||
"""
|
||||
列表查询
|
||||
|
||||
参数:
|
||||
- search (Optional[Dict]): 查询参数
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[GenDemo01Model]: 模型实例序列
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def create_gen_demo01_crud(self, data: GenDemo01CreateSchema) -> Optional[GenDemo01Model]:
|
||||
"""
|
||||
创建
|
||||
|
||||
参数:
|
||||
- data (GenDemo01CreateSchema): 创建模型
|
||||
|
||||
返回:
|
||||
- Optional[GenDemo01Model]: 模型实例或None
|
||||
"""
|
||||
return await self.create(data=data)
|
||||
|
||||
async def update_gen_demo01_crud(self, id: int, data: GenDemo01UpdateSchema) -> Optional[GenDemo01Model]:
|
||||
"""
|
||||
更新
|
||||
|
||||
参数:
|
||||
- id (int): 对象ID
|
||||
- data (GenDemo01UpdateSchema): 更新模型
|
||||
|
||||
返回:
|
||||
- Optional[GenDemo01Model]: 模型实例或None
|
||||
"""
|
||||
return await self.update(id=id, data=data)
|
||||
|
||||
async def delete_gen_demo01_crud(self, ids: List[int]) -> None:
|
||||
"""
|
||||
批量删除
|
||||
|
||||
参数:
|
||||
- ids (List[int]): 对象ID列表
|
||||
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
return await self.delete(ids=ids)
|
||||
|
||||
async def set_available_gen_demo01_crud(self, ids: List[int], status: bool) -> None:
|
||||
"""
|
||||
批量设置可用状态
|
||||
|
||||
参数:
|
||||
- ids (List[int]): 对象ID列表
|
||||
- status (bool): 可用状态
|
||||
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
return await self.set(ids=ids, status=status)
|
||||
|
||||
async def page_gen_demo01_crud(self, offset: int, limit: int, order_by: Optional[List[Dict[str, str]]] = None, search: Optional[Dict] = None, preload: Optional[List[Union[str, Any]]] = None) -> Dict:
|
||||
"""
|
||||
分页查询
|
||||
|
||||
参数:
|
||||
- offset (int): 偏移量
|
||||
- limit (int): 每页数量
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数
|
||||
- search (Optional[Dict]): 查询参数
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Dict: 分页数据
|
||||
"""
|
||||
order_by_list = order_by or [{'id': 'asc'}]
|
||||
search_dict = search or {}
|
||||
return await self.page(
|
||||
offset=offset,
|
||||
limit=limit,
|
||||
order_by=order_by_list,
|
||||
search=search_dict,
|
||||
out_schema=GenDemo01OutSchema,
|
||||
preload=preload
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from sqlalchemy import Integer, Text, String, SmallInteger, DateTime
|
||||
from typing import Optional
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base_model import CreatorMixin
|
||||
|
||||
|
||||
class GenDemo01Model(CreatorMixin):
|
||||
"""
|
||||
示例表
|
||||
"""
|
||||
|
||||
__tablename__ = 'gen_demo01'
|
||||
__table_args__ = {'comment': '示例'}
|
||||
__loader_options__ = ["creator"]
|
||||
|
||||
name: Mapped[Optional[str]] = mapped_column(String(64), nullable=True, comment='名称')
|
||||
status: Mapped[Optional[int]] = mapped_column(SmallInteger, nullable=True, comment='是否启用(True:启用 False:禁用)')
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Optional
|
||||
from fastapi import Query
|
||||
|
||||
from app.core.validator import DateTimeStr
|
||||
|
||||
class GenDemo01QueryParam:
|
||||
"""示例查询参数"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: Optional[str] = Query(None, description="名称"),
|
||||
|
||||
|
||||
creator: Optional[int] = Query(None, description="创建人"),
|
||||
start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2025-01-01 00:00:00"),
|
||||
end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2025-12-31 23:59:59"),
|
||||
) -> None:
|
||||
|
||||
# 模糊查询字段
|
||||
self.name = ("like", name)
|
||||
|
||||
# 精确查询字段
|
||||
self.status = status
|
||||
self.creator_id = creator_id
|
||||
self.creator_id = creator
|
||||
|
||||
# 时间范围查询
|
||||
if start_time and end_time:
|
||||
self.created_at = ("between", (start_time, end_time))
|
||||
@@ -0,0 +1,29 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.core.base_schema import BaseSchema
|
||||
|
||||
class GenDemo01CreateSchema(BaseModel):
|
||||
"""
|
||||
示例新增模型
|
||||
"""
|
||||
|
||||
name: Optional[str] = Field(default=None, description='名称')
|
||||
status: Optional[int] = Field(default=None, description='是否启用(True:启用 False:禁用)')
|
||||
description: Optional[str] = Field(default=None, description='备注/描述')
|
||||
|
||||
|
||||
class GenDemo01UpdateSchema(GenDemo01CreateSchema):
|
||||
"""
|
||||
示例更新模型
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
class GenDemo01OutSchema(GenDemo01CreateSchema, BaseSchema):
|
||||
"""
|
||||
示例响应模型
|
||||
"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
@@ -0,0 +1,202 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import io
|
||||
from typing import Any, List, Dict, Optional
|
||||
from fastapi import UploadFile
|
||||
import pandas as pd
|
||||
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.exceptions import CustomException
|
||||
from app.utils.excel_util import ExcelUtil
|
||||
from app.core.logger import log
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .schema import GenDemo01CreateSchema, GenDemo01UpdateSchema, GenDemo01OutSchema
|
||||
from .param import GenDemo01QueryParam
|
||||
from .crud import GenDemo01CRUD
|
||||
|
||||
|
||||
class GenDemo01Service:
|
||||
"""
|
||||
示例服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def detail_gen_demo01_service(cls, auth: AuthSchema, id: int) -> Dict:
|
||||
"""详情"""
|
||||
obj = await GenDemo01CRUD(auth).get_by_id_gen_demo01_crud(id=id)
|
||||
if not obj:
|
||||
raise CustomException(msg="该数据不存在")
|
||||
return GenDemo01OutSchema.model_validate(obj).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def list_gen_demo01_service(cls, auth: AuthSchema, search: Optional[GenDemo01QueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]:
|
||||
"""列表查询"""
|
||||
search_dict = search.__dict__ if search else None
|
||||
obj_list = await GenDemo01CRUD(auth).list_gen_demo01_crud(search=search_dict, order_by=order_by)
|
||||
return [GenDemo01OutSchema.model_validate(obj).model_dump() for obj in obj_list]
|
||||
|
||||
@classmethod
|
||||
async def page_gen_demo01_service(cls, auth: AuthSchema, page_no: int, page_size: int, search: Optional[GenDemo01QueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Dict:
|
||||
"""分页查询(数据库分页)"""
|
||||
search_dict = search.__dict__ if search else {}
|
||||
order_by_list = order_by or [{'id': 'asc'}]
|
||||
offset = (page_no - 1) * page_size
|
||||
result = await GenDemo01CRUD(auth).page_gen_demo01_crud(
|
||||
offset=offset,
|
||||
limit=page_size,
|
||||
order_by=order_by_list,
|
||||
search=search_dict
|
||||
)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def create_gen_demo01_service(cls, auth: AuthSchema, data: GenDemo01CreateSchema) -> Dict:
|
||||
"""创建"""
|
||||
# 检查唯一性约束
|
||||
obj = await GenDemo01CRUD(auth).create_gen_demo01_crud(data=data)
|
||||
return GenDemo01OutSchema.model_validate(obj).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def update_gen_demo01_service(cls, auth: AuthSchema, id: int, data: GenDemo01UpdateSchema) -> Dict:
|
||||
"""更新"""
|
||||
# 检查数据是否存在
|
||||
obj = await GenDemo01CRUD(auth).get_by_id_gen_demo01_crud(id=id)
|
||||
if not obj:
|
||||
raise CustomException(msg='更新失败,该数据不存在')
|
||||
|
||||
# 检查唯一性约束
|
||||
|
||||
obj = await GenDemo01CRUD(auth).update_gen_demo01_crud(id=id, data=data)
|
||||
return GenDemo01OutSchema.model_validate(obj).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def delete_gen_demo01_service(cls, auth: AuthSchema, ids: List[int]) -> None:
|
||||
"""删除"""
|
||||
if len(ids) < 1:
|
||||
raise CustomException(msg='删除失败,删除对象不能为空')
|
||||
for id in ids:
|
||||
obj = await GenDemo01CRUD(auth).get_by_id_gen_demo01_crud(id=id)
|
||||
if not obj:
|
||||
raise CustomException(msg=f'删除失败,ID为{id}的数据不存在')
|
||||
await GenDemo01CRUD(auth).delete_gen_demo01_crud(ids=ids)
|
||||
|
||||
@classmethod
|
||||
async def set_available_gen_demo01_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None:
|
||||
"""批量设置状态"""
|
||||
await GenDemo01CRUD(auth).set_available_gen_demo01_crud(ids=data.ids, status=data.status)
|
||||
|
||||
@classmethod
|
||||
async def batch_export_gen_demo01_service(cls, obj_list: List[Dict[str, Any]]) -> bytes:
|
||||
"""批量导出"""
|
||||
mapping_dict = {
|
||||
'name': '名称',
|
||||
'status': '是否启用(True:启用 False:禁用)',
|
||||
'creator_id': '创建人ID',
|
||||
'id': '主键ID',
|
||||
'description': '备注/描述',
|
||||
'created_at': '创建时间',
|
||||
'updated_at': '更新时间',
|
||||
'creator': '创建者',
|
||||
}
|
||||
|
||||
data = obj_list.copy()
|
||||
for item in data:
|
||||
# 状态转换
|
||||
if 'status' in item:
|
||||
item['status'] = '正常' if item.get('status') else '停用'
|
||||
# 创建者转换
|
||||
creator_info = item.get('creator')
|
||||
if isinstance(creator_info, dict):
|
||||
item['creator'] = creator_info.get('name', '未知')
|
||||
elif creator_info is None:
|
||||
item['creator'] = '未知'
|
||||
|
||||
return ExcelUtil.export_list2excel(list_data=data, mapping_dict=mapping_dict)
|
||||
|
||||
@classmethod
|
||||
async def batch_import_gen_demo01_service(cls, auth: AuthSchema, file: UploadFile, update_support: bool = False) -> str:
|
||||
"""批量导入"""
|
||||
header_dict = {
|
||||
'名称': 'name',
|
||||
'是否启用(True:启用 False:禁用)': 'status',
|
||||
'创建人ID': 'creator_id',
|
||||
'主键ID': 'id',
|
||||
'备注/描述': 'description',
|
||||
'创建时间': 'created_at',
|
||||
'更新时间': 'updated_at',
|
||||
}
|
||||
|
||||
try:
|
||||
contents = await file.read()
|
||||
df = pd.read_excel(io.BytesIO(contents))
|
||||
await file.close()
|
||||
|
||||
if df.empty:
|
||||
raise CustomException(msg="导入文件为空")
|
||||
|
||||
missing_headers = [header for header in header_dict.keys() if header not in df.columns]
|
||||
if missing_headers:
|
||||
raise CustomException(msg=f"导入文件缺少必要的列: {', '.join(missing_headers)}")
|
||||
|
||||
df.rename(columns=header_dict, inplace=True)
|
||||
|
||||
# 验证必填字段
|
||||
|
||||
error_msgs = []
|
||||
success_count = 0
|
||||
count = 0
|
||||
|
||||
for index, row in df.iterrows():
|
||||
count += 1
|
||||
try:
|
||||
data = {
|
||||
"name": row['name'],
|
||||
"status": row['status'],
|
||||
"creator_id": row['creator_id'],
|
||||
"id": row['id'],
|
||||
"description": row['description'],
|
||||
"created_at": row['created_at'],
|
||||
"updated_at": row['updated_at'],
|
||||
}
|
||||
# 使用CreateSchema做校验后入库
|
||||
create_schema = GenDemo01CreateSchema.model_validate(data)
|
||||
|
||||
# 检查唯一性约束
|
||||
|
||||
await GenDemo01CRUD(auth).create_crud(data=create_schema)
|
||||
success_count += 1
|
||||
except Exception as e:
|
||||
error_msgs.append(f"第{count}行: {str(e)}")
|
||||
continue
|
||||
|
||||
result = f"成功导入 {success_count} 条数据"
|
||||
if error_msgs:
|
||||
result += "\n错误信息:\n" + "\n".join(error_msgs)
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
log.error(f"批量导入失败: {str(e)}")
|
||||
raise CustomException(msg=f"导入失败: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
async def import_template_download_gen_demo01_service(cls) -> bytes:
|
||||
"""下载导入模板"""
|
||||
header_list = [
|
||||
'名称',
|
||||
'是否启用(True:启用 False:禁用)',
|
||||
'创建人ID',
|
||||
'主键ID',
|
||||
'备注/描述',
|
||||
'创建时间',
|
||||
'更新时间',
|
||||
]
|
||||
selector_header_list = []
|
||||
option_list = []
|
||||
|
||||
# 添加下拉选项
|
||||
|
||||
return ExcelUtil.get_excel_template(
|
||||
header_list=header_list,
|
||||
selector_header_list=selector_header_list,
|
||||
option_list=option_list
|
||||
)
|
||||
@@ -10,7 +10,7 @@ from app.core.router_class import OperationLogRoute
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.common.request import PaginationService
|
||||
from app.utils.common_util import bytes2file_response
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .param import GenTableQueryParam
|
||||
@@ -40,7 +40,7 @@ async def gen_table_list_controller(
|
||||
"""
|
||||
result_dict_list = await GenTableService.get_gen_table_list_service(auth=auth, search=search)
|
||||
result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
|
||||
logger.info('获取代码生成业务表列表成功')
|
||||
log.info('获取代码生成业务表列表成功')
|
||||
return SuccessResponse(data=result_dict, msg="获取代码生成业务表列表成功")
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ async def get_gen_db_table_list_controller(
|
||||
"""
|
||||
result_dict_list = await GenTableService.get_gen_db_table_list_service(auth=auth, search=search)
|
||||
result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
|
||||
logger.info('获取数据库表列表成功')
|
||||
log.info('获取数据库表列表成功')
|
||||
return SuccessResponse(data=result_dict, msg="获取数据库表列表成功")
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ async def import_gen_table_controller(
|
||||
"""
|
||||
add_gen_table_list = await GenTableService.get_gen_db_table_list_by_name_service(auth, table_names)
|
||||
result = await GenTableService.import_gen_table_service(auth, add_gen_table_list)
|
||||
logger.info('导入表结构成功')
|
||||
log.info('导入表结构成功')
|
||||
return SuccessResponse(msg="导入表结构成功", data=result)
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ async def gen_table_detail_controller(
|
||||
- JSONResponse: 包含业务表详细信息的JSON响应
|
||||
"""
|
||||
gen_table_detail_result = await GenTableService.get_gen_table_detail_service(auth, table_id)
|
||||
logger.info(f'获取table_id为{table_id}的信息成功')
|
||||
log.info(f'获取table_id为{table_id}的信息成功')
|
||||
return SuccessResponse(data=gen_table_detail_result, msg="获取业务表详细信息成功")
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ async def create_table_controller(
|
||||
- JSONResponse: 包含创建结果的JSON响应
|
||||
"""
|
||||
result = await GenTableService.create_table_service(auth, sql)
|
||||
logger.info('创建表结构成功')
|
||||
log.info('创建表结构成功')
|
||||
return SuccessResponse(msg="创建表结构成功", data=result)
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ async def update_gen_table_controller(
|
||||
- JSONResponse: 包含编辑结果的JSON响应
|
||||
"""
|
||||
result_dict = await GenTableService.update_gen_table_service(auth, data, table_id)
|
||||
logger.info('编辑业务表信息成功')
|
||||
log.info('编辑业务表信息成功')
|
||||
return SuccessResponse(data=result_dict, msg="编辑业务表信息成功")
|
||||
|
||||
|
||||
@@ -166,14 +166,14 @@ async def delete_gen_table_controller(
|
||||
- JSONResponse: 包含删除结果的JSON响应
|
||||
"""
|
||||
result = await GenTableService.delete_gen_table_service(auth, ids)
|
||||
logger.info('删除业务表信息成功')
|
||||
log.info('删除业务表信息成功')
|
||||
return SuccessResponse(msg="删除业务表信息成功", data=result)
|
||||
|
||||
|
||||
@GenRouter.patch("/batch/output", summary="批量生成代码", description="批量生成代码")
|
||||
async def batch_gen_code_controller(
|
||||
table_names: List[str] = Body(..., description="表名列表"),
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_generator:gencode:operate"]))
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_generator:gencode:patch"]))
|
||||
) -> StreamResponse:
|
||||
"""
|
||||
批量生成代码
|
||||
@@ -186,7 +186,7 @@ async def batch_gen_code_controller(
|
||||
- StreamResponse: 包含批量生成代码的ZIP文件流响应
|
||||
"""
|
||||
batch_gen_code_result = await GenTableService.batch_gen_code_service(auth, table_names)
|
||||
logger.info(f'批量生成代码成功,表名列表:{table_names}')
|
||||
log.info(f'批量生成代码成功,表名列表:{table_names}')
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(batch_gen_code_result),
|
||||
media_type='application/zip',
|
||||
@@ -210,7 +210,7 @@ async def gen_code_local_controller(
|
||||
- JSONResponse: 包含生成结果的JSON响应
|
||||
"""
|
||||
result = await GenTableService.generate_code_service(auth, table_name)
|
||||
logger.info(f'生成代码,表名:{table_name},到指定路径成功')
|
||||
log.info(f'生成代码,表名:{table_name},到指定路径成功')
|
||||
return SuccessResponse(msg="生成代码到指定路径成功", data=result)
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ async def preview_code_controller(
|
||||
- JSONResponse: 包含预览代码的JSON响应
|
||||
"""
|
||||
preview_code_result = await GenTableService.preview_code_service(auth, table_id)
|
||||
logger.info(f'预览代码,表id:{table_id},成功')
|
||||
log.info(f'预览代码,表id:{table_id},成功')
|
||||
return SuccessResponse(data=preview_code_result, msg="预览代码成功")
|
||||
|
||||
|
||||
@@ -250,5 +250,5 @@ async def sync_db_controller(
|
||||
- JSONResponse: 包含同步数据库结果的JSON响应
|
||||
"""
|
||||
result = await GenTableService.sync_db_service(auth, table_name)
|
||||
logger.info(f'同步数据库,表名:{table_name},成功')
|
||||
log.info(f'同步数据库,表名:{table_name},成功')
|
||||
return SuccessResponse(msg="同步数据库成功", data=result)
|
||||
@@ -5,7 +5,7 @@ from sqlalchemy import and_, select, text
|
||||
from typing import List, Optional, Sequence, Dict, Union, Any
|
||||
from sqlglot.expressions import Expression
|
||||
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
from app.config.setting import settings
|
||||
from app.core.base_crud import CRUDBase
|
||||
|
||||
@@ -262,7 +262,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
else:
|
||||
gen_db_table_list = (await self.db.execute(text(query_sql), {"table_names": tuple(unique_table_names)})).fetchall()
|
||||
except Exception as e:
|
||||
logger.error(f"查询表信息时发生错误: {e}")
|
||||
log.error(f"查询表信息时发生错误: {e}")
|
||||
# 查询错误时直接抛出,不需要事务处理
|
||||
raise
|
||||
|
||||
@@ -299,7 +299,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
result = await self.db.execute(query, {"table_name": table_name})
|
||||
return result.scalar() is not None
|
||||
except Exception as e:
|
||||
logger.error(f"检查表格存在性时发生错误: {e}")
|
||||
log.error(f"检查表格存在性时发生错误: {e}")
|
||||
# 出错时返回False,避免误报表已存在
|
||||
return False
|
||||
|
||||
@@ -324,7 +324,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
await self.db.execute(text(sql))
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"创建表时发生错误: {e}")
|
||||
log.error(f"创建表时发生错误: {e}")
|
||||
return False
|
||||
|
||||
|
||||
@@ -487,7 +487,7 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
)
|
||||
return columns_list
|
||||
except Exception as e:
|
||||
logger.error(f"获取表{table_name}的字段列表时出错: {str(e)}")
|
||||
log.error(f"获取表{table_name}的字段列表时出错: {str(e)}")
|
||||
# 确保即使出错也返回空列表而不是None
|
||||
raise
|
||||
|
||||
|
||||
@@ -7,13 +7,14 @@ from typing import Any, List, Dict, Literal, Optional
|
||||
from sqlglot.expressions import Add, Alter, Create, Delete, Drop, Expression, Insert, Table, TruncateTable, Update
|
||||
from sqlglot import parse as sqlglot_parse
|
||||
|
||||
from app.config.path_conf import BASE_DIR
|
||||
from app.config.setting import settings
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
from app.core.exceptions import CustomException
|
||||
from app.utils.gen_util import GenUtils
|
||||
from app.utils.jinja2_template_util import Jinja2TemplateUtil
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .tools.jinja2_template_util import Jinja2TemplateUtil
|
||||
from .tools.gen_util import GenUtils
|
||||
from .schema import GenTableSchema, GenTableOutSchema, GenTableColumnSchema, GenTableColumnOutSchema
|
||||
from .param import GenTableQueryParam
|
||||
from .crud import GenTableColumnCRUD, GenTableCRUD
|
||||
@@ -294,7 +295,7 @@ class GenTableService:
|
||||
table_out = GenTableOutSchema.model_validate(gen_table)
|
||||
result.append(table_out)
|
||||
except Exception as e:
|
||||
logger.warning(f"转换业务表时出错: {str(e)}")
|
||||
log.error(f"转换业务表时出错: {str(e)}")
|
||||
continue
|
||||
return result
|
||||
|
||||
@@ -318,7 +319,7 @@ class GenTableService:
|
||||
render_content = await env.get_template(template).render_async(**context)
|
||||
preview_code_result[template] = render_content
|
||||
except Exception as e:
|
||||
logger.error(f"渲染模板 {template} 时出错: {str(e)}")
|
||||
log.error(f"渲染模板 {template} 时出错: {str(e)}")
|
||||
# 即使某个模板渲染失败,也继续处理其他模板
|
||||
preview_code_result[template] = f"渲染错误: {str(e)}"
|
||||
return preview_code_result
|
||||
@@ -376,7 +377,7 @@ class GenTableService:
|
||||
render_content = await env.get_template(template_file).render_async(**render_info[2])
|
||||
zip_file.writestr(output_file, render_content)
|
||||
except Exception as e:
|
||||
logger.error(f"批量生成代码时处理表 {table_name} 出错: {str(e)}")
|
||||
log.error(f"批量生成代码时处理表 {table_name} 出错: {str(e)}")
|
||||
# 继续处理其他表,不中断整个过程
|
||||
continue
|
||||
|
||||
@@ -507,18 +508,18 @@ class GenTableService:
|
||||
try:
|
||||
file_name = Jinja2TemplateUtil.get_file_name(template, gen_table)
|
||||
# 默认写入到项目根目录(backend的上一级)
|
||||
project_root = str(settings.BASE_DIR.parent)
|
||||
project_root = str(BASE_DIR.parent)
|
||||
full_path = os.path.join(project_root, file_name)
|
||||
|
||||
# 确保路径在项目根目录内,防止路径遍历攻击
|
||||
if not os.path.abspath(full_path).startswith(os.path.abspath(project_root)):
|
||||
logger.warning(f"路径越界,回退到项目根目录: {file_name}")
|
||||
log.error(f"路径越界,回退到项目根目录: {file_name}")
|
||||
# 回退到项目根目录下的generated文件夹
|
||||
full_path = os.path.join(project_root, "generated", os.path.basename(file_name))
|
||||
|
||||
return full_path
|
||||
except Exception as e:
|
||||
logger.error(f"生成路径时出错: {str(e)}")
|
||||
log.error(f"生成路径时出错: {str(e)}")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from fastapi import APIRouter, Depends, UploadFile, Body, Path
|
||||
from fastapi.responses import StreamingResponse, JSONResponse
|
||||
from app.common.response import SuccessResponse, StreamResponse
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.utils.common_util import bytes2file_response
|
||||
from app.core.logger import log
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
|
||||
from .service import {{ class_name }}Service
|
||||
from .schema import {{ class_name }}CreateSchema, {{ class_name }}UpdateSchema
|
||||
from .param import {{ class_name }}QueryParam
|
||||
|
||||
{{ class_name }}Router = APIRouter(route_class=OperationLogRoute, prefix='/{{ business_name }}', tags=["{{ function_name }}模块"])
|
||||
|
||||
@{{ class_name }}Router.get("/detail/{id}", summary="获取{{ function_name }}详情", description="获取{{ function_name }}详情")
|
||||
async def get_{{ business_name }}_detail_controller(
|
||||
id: int = Path(..., description="ID"),
|
||||
auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:query"]))
|
||||
) -> JSONResponse:
|
||||
"""获取{{ function_name }}详情接口"""
|
||||
result_dict = await {{ class_name }}Service.detail_{{ business_name }}_service(auth=auth, id=id)
|
||||
log.info(f"获取{{ function_name }}详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取{{ function_name }}详情成功")
|
||||
|
||||
@{{ class_name }}Router.get("/list", summary="查询{{ function_name }}列表", description="查询{{ function_name }}列表")
|
||||
async def get_{{ business_name }}_list_controller(
|
||||
page: PaginationQueryParam = Depends(),
|
||||
search: {{ class_name }}QueryParam = Depends(),
|
||||
auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:query"]))
|
||||
) -> JSONResponse:
|
||||
"""查询{{ function_name }}列表接口(数据库分页)"""
|
||||
result_dict = await {{ class_name }}Service.page_service(
|
||||
auth=auth,
|
||||
page_no=page.page_no if page.page_no is not None else 1,
|
||||
page_size=page.page_size if page.page_size is not None else 10,
|
||||
search=search,
|
||||
order_by=page.order_by
|
||||
)
|
||||
log.info("查询{{ function_name }}列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询{{ function_name }}列表成功")
|
||||
|
||||
@{{ class_name }}Router.post("/create", summary="创建{{ function_name }}", description="创建{{ function_name }}")
|
||||
async def create_{{ business_name }}_controller(
|
||||
data: {{ class_name }}CreateSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:create"]))
|
||||
) -> JSONResponse:
|
||||
"""创建{{ function_name }}接口"""
|
||||
result_dict = await {{ class_name }}Service.create_{{ business_name }}_service(auth=auth, data=data)
|
||||
log.info("创建{{ function_name }}成功")
|
||||
return SuccessResponse(data=result_dict, msg="创建{{ function_name }}成功")
|
||||
|
||||
@{{ class_name }}Router.put("/update/{id}", summary="修改{{ function_name }}", description="修改{{ function_name }}")
|
||||
async def update_{{ business_name }}_controller(
|
||||
data: {{ class_name }}UpdateSchema,
|
||||
id: int = Path(..., description="ID"),
|
||||
auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:update"]))
|
||||
) -> JSONResponse:
|
||||
"""修改{{ function_name }}接口"""
|
||||
result_dict = await {{ class_name }}Service.update_{{ business_name }}_service(auth=auth, id=id, data=data)
|
||||
log.info("修改{{ function_name }}成功")
|
||||
return SuccessResponse(data=result_dict, msg="修改{{ function_name }}成功")
|
||||
|
||||
@{{ class_name }}Router.delete("/delete", summary="删除{{ function_name }}", description="删除{{ function_name }}")
|
||||
async def delete_{{ business_name }}_controller(
|
||||
ids: list[int] = Body(..., description="ID列表"),
|
||||
auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:delete"]))
|
||||
) -> JSONResponse:
|
||||
"""删除{{ function_name }}接口"""
|
||||
await {{ class_name }}Service.delete_{{ business_name }}_service(auth=auth, ids=ids)
|
||||
log.info(f"删除{{ function_name }}成功: {ids}")
|
||||
return SuccessResponse(msg="删除{{ function_name }}成功")
|
||||
|
||||
@{{ class_name }}Router.patch("/available/setting", summary="批量修改{{ function_name }}状态", description="批量修改{{ function_name }}状态")
|
||||
async def batch_set_available_{{ business_name }}_controller(
|
||||
data: BatchSetAvailable,
|
||||
auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:patch"]))
|
||||
) -> JSONResponse:
|
||||
"""批量修改{{ function_name }}状态接口"""
|
||||
await {{ class_name }}Service.set_available_{{ business_name }}_service(auth=auth, data=data)
|
||||
log.info(f"批量修改{{ function_name }}状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改{{ function_name }}状态成功")
|
||||
|
||||
@{{ class_name }}Router.post('/export', summary="导出{{ function_name }}", description="导出{{ function_name }}")
|
||||
async def export_{{ business_name }}_list_controller(
|
||||
search: {{ class_name }}QueryParam = Depends(),
|
||||
auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:export"]))
|
||||
) -> StreamingResponse:
|
||||
"""导出{{ function_name }}接口"""
|
||||
result_dict_list = await {{ class_name }}Service.list_{{ business_name }}_service(search=search, auth=auth)
|
||||
export_result = await {{ class_name }}Service.batch_export_service(obj_list=result_dict_list)
|
||||
log.info('导出{{ function_name }}成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(export_result),
|
||||
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
headers={
|
||||
'Content-Disposition': 'attachment; filename={{ table_name }}.xlsx'
|
||||
}
|
||||
)
|
||||
|
||||
@{{ class_name }}Router.post('/import', summary="导入{{ function_name }}", description="导入{{ function_name }}")
|
||||
async def import_{{ business_name }}_list_controller(
|
||||
file: UploadFile,
|
||||
auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:import"]))
|
||||
) -> JSONResponse:
|
||||
"""导入{{ function_name }}接口"""
|
||||
batch_import_result = await {{ class_name }}Service.batch_import_{{ business_name }}_service(file=file, auth=auth, update_support=True)
|
||||
log.info("导入{{ function_name }}成功")
|
||||
|
||||
return SuccessResponse(data=batch_import_result, msg="导入{{ function_name }}成功")
|
||||
|
||||
@{{ class_name }}Router.post('/download/template', summary="获取{{ function_name }}导入模板", description="获取{{ function_name }}导入模板", dependencies=[Depends(AuthPermission(["{{ permission_prefix }}:download"]))])
|
||||
async def export_{{ business_name }}_template_controller() -> StreamingResponse:
|
||||
"""获取{{ function_name }}导入模板接口"""
|
||||
example_import_template_result = await {{ class_name }}Service.import_template_download_{{ business_name }}_service()
|
||||
log.info('获取{{ function_name }}导入模板成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(example_import_template_result),
|
||||
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
headers={
|
||||
'Content-Disposition': 'attachment; filename={{ table_name }}_template.xlsx'
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,123 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .model import {{ class_name }}Model
|
||||
from .schema import {{ class_name }}CreateSchema, {{ class_name }}UpdateSchema, {{ class_name }}OutSchema
|
||||
|
||||
|
||||
class {{ class_name }}CRUD(CRUDBase[{{ class_name }}Model, {{ class_name }}CreateSchema, {{ class_name }}UpdateSchema]):
|
||||
"""{{ function_name }}数据层"""
|
||||
|
||||
def __init__(self, auth: AuthSchema) -> None:
|
||||
"""
|
||||
初始化CRUD数据层
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
"""
|
||||
super().__init__(model={{ class_name }}Model, auth=auth)
|
||||
|
||||
async def get_by_id_{{ business_name }}_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[{{ class_name }}Model]:
|
||||
"""
|
||||
详情
|
||||
|
||||
参数:
|
||||
- id (int): 对象ID
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[{{ class_name }}Model]: 模型实例或None
|
||||
"""
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def list_{{ business_name }}_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[{{ class_name }}Model]:
|
||||
"""
|
||||
列表查询
|
||||
|
||||
参数:
|
||||
- search (Optional[Dict]): 查询参数
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[{{ class_name }}Model]: 模型实例序列
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def create_{{ business_name }}_crud(self, data: {{ class_name }}CreateSchema) -> Optional[{{ class_name }}Model]:
|
||||
"""
|
||||
创建
|
||||
|
||||
参数:
|
||||
- data ({{ class_name }}CreateSchema): 创建模型
|
||||
|
||||
返回:
|
||||
- Optional[{{ class_name }}Model]: 模型实例或None
|
||||
"""
|
||||
return await self.create(data=data)
|
||||
|
||||
async def update_{{ business_name }}_crud(self, id: int, data: {{ class_name }}UpdateSchema) -> Optional[{{ class_name }}Model]:
|
||||
"""
|
||||
更新
|
||||
|
||||
参数:
|
||||
- id (int): 对象ID
|
||||
- data ({{ class_name }}UpdateSchema): 更新模型
|
||||
|
||||
返回:
|
||||
- Optional[{{ class_name }}Model]: 模型实例或None
|
||||
"""
|
||||
return await self.update(id=id, data=data)
|
||||
|
||||
async def delete_{{ business_name }}_crud(self, ids: List[int]) -> None:
|
||||
"""
|
||||
批量删除
|
||||
|
||||
参数:
|
||||
- ids (List[int]): 对象ID列表
|
||||
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
return await self.delete(ids=ids)
|
||||
|
||||
async def set_available_{{ business_name }}_crud(self, ids: List[int], status: bool) -> None:
|
||||
"""
|
||||
批量设置可用状态
|
||||
|
||||
参数:
|
||||
- ids (List[int]): 对象ID列表
|
||||
- status (bool): 可用状态
|
||||
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
return await self.set(ids=ids, status=status)
|
||||
|
||||
async def page_{{ business_name }}_crud(self, offset: int, limit: int, order_by: Optional[List[Dict[str, str]]] = None, search: Optional[Dict] = None, preload: Optional[List[Union[str, Any]]] = None) -> Dict:
|
||||
"""
|
||||
分页查询
|
||||
|
||||
参数:
|
||||
- offset (int): 偏移量
|
||||
- limit (int): 每页数量
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数
|
||||
- search (Optional[Dict]): 查询参数
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Dict: 分页数据
|
||||
"""
|
||||
order_by_list = order_by or [{'id': 'asc'}]
|
||||
search_dict = search or {}
|
||||
return await self.page(
|
||||
offset=offset,
|
||||
limit=limit,
|
||||
order_by=order_by_list,
|
||||
search=search_dict,
|
||||
out_schema={{ class_name }}OutSchema,
|
||||
preload=preload
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
{% for model_import in model_import_list %}
|
||||
{{ model_import }}
|
||||
{% endfor %}
|
||||
{% if table.sub %}
|
||||
from sqlalchemy.orm import relationship
|
||||
{% endif %}
|
||||
from typing import Optional
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base_model import CreatorMixin
|
||||
|
||||
|
||||
class {{ class_name }}Model(CreatorMixin):
|
||||
"""
|
||||
{{ function_name }}表
|
||||
"""
|
||||
|
||||
__tablename__ = '{{ table_name }}'
|
||||
__table_args__ = {'comment': '{{ function_name }}'}
|
||||
__loader_options__ = ["creator"]
|
||||
|
||||
{% for column in columns %}
|
||||
{% if column.column_name not in ['id', 'creator_id', 'description', 'created_at', 'updated_at'] %}
|
||||
{{ column.column_name }}: Mapped[Optional[{{ column.python_type }}]] = mapped_column({{ column.column_type|get_sqlalchemy_type }}, {% if column.pk %}primary_key=True, {% endif %}{% if column.increment %}autoincrement=True, {% endif %}{% if column.required or column.pk %}nullable=False{% else %}nullable=True{% endif %}, comment='{{ column.column_comment }}')
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if table.sub %}
|
||||
{{ sub_class_name }}_list = relationship('{{ sub_class_name }}', back_populates='{{ business_name }}')
|
||||
{% endif %}
|
||||
@@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Optional
|
||||
from fastapi import Query
|
||||
|
||||
from app.core.validator import DateTimeStr
|
||||
|
||||
class {{ class_name }}QueryParam:
|
||||
"""{{ function_name }}查询参数"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
{% for column in columns %}
|
||||
{% if column.query_type == 'LIKE' %}
|
||||
{{ column.column_name }}: Optional[{{ column.python_type }}] = Query(None, description="{{ column.column_comment }}"),
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% for column in columns %}
|
||||
{% if column.column_name == 'EQ' %}
|
||||
{{ column.column_name }}: Optional[{{ column.python_type }}] = Query(None, description="{{ column.column_comment }}"),
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
creator: Optional[int] = Query(None, description="创建人"),
|
||||
start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2025-01-01 00:00:00"),
|
||||
end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2025-12-31 23:59:59"),
|
||||
) -> None:
|
||||
|
||||
# 模糊查询字段
|
||||
{% for column in columns %}
|
||||
{% if column.query_type == 'LIKE' %}
|
||||
self.{{ column.column_name }} = ("like", {{ column.column_name }})
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
# 精确查询字段
|
||||
{% for column in columns %}
|
||||
{% if column.query_type == 'EQ' %}
|
||||
self.{{ column.column_name }} = {{ column.column_name }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
self.creator_id = creator
|
||||
|
||||
# 时间范围查询
|
||||
if start_time and end_time:
|
||||
self.created_at = ("between", (start_time, end_time))
|
||||
@@ -0,0 +1,35 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
{% if table.sub %}
|
||||
from typing import List, Optional
|
||||
{% else %}
|
||||
from typing import Optional
|
||||
{% endif %}
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.core.base_schema import BaseSchema
|
||||
|
||||
class {{ class_name }}CreateSchema(BaseModel):
|
||||
"""
|
||||
{{ function_name }}新增模型
|
||||
"""
|
||||
|
||||
{% for column in columns %}
|
||||
{% if column.column_name not in ['id', 'creator_id', 'created_at', 'updated_at'] %}
|
||||
{{ column.column_name }}: Optional[{{ column.python_type }}] = Field(default=None, description='{{ column.column_comment }}')
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
class {{ class_name }}UpdateSchema({{ class_name }}CreateSchema):
|
||||
"""
|
||||
{{ function_name }}更新模型
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
class {{ class_name }}OutSchema({{ class_name }}CreateSchema, BaseSchema):
|
||||
"""
|
||||
{{ function_name }}响应模型
|
||||
"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
@@ -0,0 +1,225 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import io
|
||||
from typing import Any, List, Dict, Optional
|
||||
from fastapi import UploadFile
|
||||
import pandas as pd
|
||||
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.exceptions import CustomException
|
||||
from app.utils.excel_util import ExcelUtil
|
||||
from app.core.logger import log
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .schema import {{ class_name }}CreateSchema, {{ class_name }}UpdateSchema, {{ class_name }}OutSchema
|
||||
from .param import {{ class_name }}QueryParam
|
||||
from .crud import {{ class_name }}CRUD
|
||||
|
||||
|
||||
class {{ class_name }}Service:
|
||||
"""
|
||||
{{ function_name }}服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def detail_{{ business_name }}_service(cls, auth: AuthSchema, id: int) -> Dict:
|
||||
"""详情"""
|
||||
obj = await {{ class_name }}CRUD(auth).get_by_id_{{ business_name }}_crud(id=id)
|
||||
if not obj:
|
||||
raise CustomException(msg="该数据不存在")
|
||||
return {{ class_name }}OutSchema.model_validate(obj).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def list_{{ business_name }}_service(cls, auth: AuthSchema, search: Optional[{{ class_name }}QueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]:
|
||||
"""列表查询"""
|
||||
search_dict = search.__dict__ if search else None
|
||||
obj_list = await {{ class_name }}CRUD(auth).list_{{ business_name }}_crud(search=search_dict, order_by=order_by)
|
||||
return [{{ class_name }}OutSchema.model_validate(obj).model_dump() for obj in obj_list]
|
||||
|
||||
@classmethod
|
||||
async def page_{{ business_name }}_service(cls, auth: AuthSchema, page_no: int, page_size: int, search: Optional[{{ class_name }}QueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Dict:
|
||||
"""分页查询(数据库分页)"""
|
||||
search_dict = search.__dict__ if search else {}
|
||||
order_by_list = order_by or [{'id': 'asc'}]
|
||||
offset = (page_no - 1) * page_size
|
||||
result = await {{ class_name }}CRUD(auth).page_{{ business_name }}_crud(
|
||||
offset=offset,
|
||||
limit=page_size,
|
||||
order_by=order_by_list,
|
||||
search=search_dict
|
||||
)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def create_{{ business_name }}_service(cls, auth: AuthSchema, data: {{ class_name }}CreateSchema) -> Dict:
|
||||
"""创建"""
|
||||
# 检查唯一性约束
|
||||
{% for column in columns %}
|
||||
{% if column.is_unique == '1' %}
|
||||
obj = await {{ class_name }}CRUD(auth).get({{ column.column_name }}=data.{{ column.column_name }})
|
||||
if obj:
|
||||
raise CustomException(msg='创建失败,{{ column.column_comment }}已存在')
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
obj = await {{ class_name }}CRUD(auth).create_{{ business_name }}_crud(data=data)
|
||||
return {{ class_name }}OutSchema.model_validate(obj).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def update_{{ business_name }}_service(cls, auth: AuthSchema, id: int, data: {{ class_name }}UpdateSchema) -> Dict:
|
||||
"""更新"""
|
||||
# 检查数据是否存在
|
||||
obj = await {{ class_name }}CRUD(auth).get_by_id_{{ business_name }}_crud(id=id)
|
||||
if not obj:
|
||||
raise CustomException(msg='更新失败,该数据不存在')
|
||||
|
||||
# 检查唯一性约束
|
||||
{% for column in columns %}
|
||||
{% if column.is_unique == '1' %}
|
||||
exist_obj = await {{ class_name }}CRUD(auth).get({{ column.column_name }}=data.{{ column.column_name }})
|
||||
if exist_obj and exist_obj.id != id:
|
||||
raise CustomException(msg='更新失败,{{ column.column_comment }}重复')
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
obj = await {{ class_name }}CRUD(auth).update_{{ business_name }}_crud(id=id, data=data)
|
||||
return {{ class_name }}OutSchema.model_validate(obj).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def delete_{{ business_name }}_service(cls, auth: AuthSchema, ids: List[int]) -> None:
|
||||
"""删除"""
|
||||
if len(ids) < 1:
|
||||
raise CustomException(msg='删除失败,删除对象不能为空')
|
||||
for id in ids:
|
||||
obj = await {{ class_name }}CRUD(auth).get_by_id_{{ business_name }}_crud(id=id)
|
||||
if not obj:
|
||||
raise CustomException(msg=f'删除失败,ID为{id}的数据不存在')
|
||||
await {{ class_name }}CRUD(auth).delete_{{ business_name }}_crud(ids=ids)
|
||||
|
||||
@classmethod
|
||||
async def set_available_{{ business_name }}_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None:
|
||||
"""批量设置状态"""
|
||||
await {{ class_name }}CRUD(auth).set_available_{{ business_name }}_crud(ids=data.ids, status=data.status)
|
||||
|
||||
@classmethod
|
||||
async def batch_export_{{ business_name }}_service(cls, obj_list: List[Dict[str, Any]]) -> bytes:
|
||||
"""批量导出"""
|
||||
mapping_dict = {
|
||||
{% for column in columns %}
|
||||
'{{ column.column_name }}': '{{ column.column_comment }}',
|
||||
{% endfor %}
|
||||
'creator': '创建者',
|
||||
}
|
||||
|
||||
data = obj_list.copy()
|
||||
for item in data:
|
||||
# 状态转换
|
||||
if 'status' in item:
|
||||
item['status'] = '正常' if item.get('status') else '停用'
|
||||
# 创建者转换
|
||||
creator_info = item.get('creator')
|
||||
if isinstance(creator_info, dict):
|
||||
item['creator'] = creator_info.get('name', '未知')
|
||||
elif creator_info is None:
|
||||
item['creator'] = '未知'
|
||||
|
||||
return ExcelUtil.export_list2excel(list_data=data, mapping_dict=mapping_dict)
|
||||
|
||||
@classmethod
|
||||
async def batch_import_{{ business_name }}_service(cls, auth: AuthSchema, file: UploadFile, update_support: bool = False) -> str:
|
||||
"""批量导入"""
|
||||
header_dict = {
|
||||
{% for column in columns %}
|
||||
'{{ column.column_comment }}': '{{ column.column_name }}',
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
try:
|
||||
contents = await file.read()
|
||||
df = pd.read_excel(io.BytesIO(contents))
|
||||
await file.close()
|
||||
|
||||
if df.empty:
|
||||
raise CustomException(msg="导入文件为空")
|
||||
|
||||
missing_headers = [header for header in header_dict.keys() if header not in df.columns]
|
||||
if missing_headers:
|
||||
raise CustomException(msg=f"导入文件缺少必要的列: {', '.join(missing_headers)}")
|
||||
|
||||
df.rename(columns=header_dict, inplace=True)
|
||||
|
||||
# 验证必填字段
|
||||
{% for column in columns %}
|
||||
{% if column.required == '1' %}
|
||||
missing_rows = df[df['{{ column.column_name }}'].isnull()].index.tolist()
|
||||
if missing_rows:
|
||||
raise CustomException(msg="{{ column.column_comment }}不能为空,第{0}行".format([i+1 for i in missing_rows]))
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
error_msgs = []
|
||||
success_count = 0
|
||||
count = 0
|
||||
|
||||
for index, row in df.iterrows():
|
||||
count += 1
|
||||
try:
|
||||
data = {
|
||||
{% for column in columns %}
|
||||
"{{ column.column_name }}": row['{{ column.column_name }}'],
|
||||
{% endfor %}
|
||||
}
|
||||
# 使用CreateSchema做校验后入库
|
||||
create_schema = {{ class_name }}CreateSchema.model_validate(data)
|
||||
|
||||
# 检查唯一性约束
|
||||
{% for column in columns %}
|
||||
{% if column.is_unique == '1' %}
|
||||
exists_obj = await {{ class_name }}CRUD(auth).get({{ column.column_name }}=create_schema.{{ column.column_name }})
|
||||
if exists_obj:
|
||||
if update_support:
|
||||
await {{ class_name }}CRUD(auth).update(id=exists_obj.id, data=create_schema)
|
||||
success_count += 1
|
||||
else:
|
||||
error_msgs.append(f"第{count}行: {{ column.column_comment }} {create_schema.{{ column.column_name }}} 已存在")
|
||||
continue
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
await {{ class_name }}CRUD(auth).create_crud(data=create_schema)
|
||||
success_count += 1
|
||||
except Exception as e:
|
||||
error_msgs.append(f"第{count}行: {str(e)}")
|
||||
continue
|
||||
|
||||
result = f"成功导入 {success_count} 条数据"
|
||||
if error_msgs:
|
||||
result += "\n错误信息:\n" + "\n".join(error_msgs)
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
log.error(f"批量导入失败: {str(e)}")
|
||||
raise CustomException(msg=f"导入失败: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
async def import_template_download_{{ business_name }}_service(cls) -> bytes:
|
||||
"""下载导入模板"""
|
||||
header_list = [
|
||||
{% for column in columns %}
|
||||
'{{ column.column_comment }}',
|
||||
{% endfor %}
|
||||
]
|
||||
selector_header_list = []
|
||||
option_list = []
|
||||
|
||||
# 添加下拉选项
|
||||
{% for column in columns %}
|
||||
{% if column.html_type == 'select' and column.dict_type %}
|
||||
selector_header_list.append('{{ column.column_comment }}')
|
||||
option_list.append({'{{ column.column_comment }}': []})
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
return ExcelUtil.get_excel_template(
|
||||
header_list=header_list,
|
||||
selector_header_list=selector_header_list,
|
||||
option_list=option_list
|
||||
)
|
||||
@@ -0,0 +1,133 @@
|
||||
-- 统一的菜单 SQL(兼容 MySQL / PostgreSQL),对齐到 system_menu 表结构
|
||||
|
||||
{# 布尔值与保留字列名处理 #}
|
||||
{% set b_true = 1 if db_type == 'mysql' else true %}
|
||||
{% set b_false = 0 if db_type == 'mysql' else false %}
|
||||
{% set order_col = '`order`' if db_type == 'mysql' else '"order"' %}
|
||||
|
||||
{# 公共字段列表(按实际库字段) #}
|
||||
{# name, type, order, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at #}
|
||||
|
||||
{% if db_type == 'mysql' %}
|
||||
-- 父菜单(类型=2:菜单)
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES (
|
||||
'{{ function_name }}',
|
||||
2,
|
||||
1,
|
||||
{{ b_true }},
|
||||
'{{ permission_prefix }}:query',
|
||||
NULL,
|
||||
'{{ business_name|snake_to_camel }}',
|
||||
'/{{ module_name }}/{{ business_name }}',
|
||||
'{{ module_name }}/{{ business_name }}/index',
|
||||
NULL,
|
||||
{{ b_false }},
|
||||
{{ b_true }},
|
||||
{{ b_false }},
|
||||
'{{ function_name }}',
|
||||
NULL,
|
||||
{{ b_false }},
|
||||
{{ parent_menu_id }},
|
||||
'{{ function_name }}菜单',
|
||||
now(),
|
||||
now()
|
||||
);
|
||||
|
||||
-- 获取父菜单ID(MySQL)
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
|
||||
-- 按钮权限(类型=3:按钮/权限)
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}查询', 3, 1, {{ b_true }}, '{{ permission_prefix }}:query', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}查询', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}新增', 3, 2, {{ b_true }}, '{{ permission_prefix }}:create', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}新增', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}修改', 3, 3, {{ b_true }}, '{{ permission_prefix }}:update', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}修改', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}删除', 3, 4, {{ b_true }}, '{{ permission_prefix }}:delete', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}删除', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}导出', 3, 5, {{ b_true }}, '{{ permission_prefix }}:export', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}导出', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}导入', 3, 6, {{ b_true }}, '{{ permission_prefix }}:import', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}导入', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}批量状态修改', 3, 7, {{ b_true }}, '{{ permission_prefix }}:patch', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}批量状态修改', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}下载导入模板', 3, 8, {{ b_true }}, '{{ permission_prefix }}:download', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}下载导入模板', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
|
||||
{% elif db_type == 'postgres' %}
|
||||
-- 菜单 SQL(PostgreSQL DO 块方案)
|
||||
DO $$
|
||||
DECLARE
|
||||
parent_id INTEGER;
|
||||
BEGIN
|
||||
-- 插入父菜单并获取ID
|
||||
INSERT INTO public.system_menu (
|
||||
name, type, {{ order_col }}, status, permission, icon, route_name, route_path,
|
||||
component_path, redirect, hidden, keep_alive, always_show, title,
|
||||
params, affix, parent_id, description, created_at, updated_at
|
||||
)
|
||||
VALUES (
|
||||
'{{ function_name }}',
|
||||
2,
|
||||
1,
|
||||
{{ b_true }},
|
||||
'{{ permission_prefix }}:query',
|
||||
NULL,
|
||||
'{{ business_name|snake_to_camel }}',
|
||||
'/{{ module_name }}/{{ business_name }}',
|
||||
'{{ module_name }}/{{ business_name }}/index',
|
||||
NULL,
|
||||
{{ b_false }},
|
||||
{{ b_true }},
|
||||
{{ b_false }},
|
||||
'{{ function_name }}',
|
||||
NULL,
|
||||
{{ b_false }},
|
||||
{{ parent_menu_id }},
|
||||
'{{ function_name }}菜单',
|
||||
NOW(),
|
||||
NOW()
|
||||
) RETURNING id INTO parent_id;
|
||||
|
||||
-- 插入所有子菜单按钮(单条 INSERT 语句,性能更好)
|
||||
INSERT INTO public.system_menu (
|
||||
name, type, {{ order_col }}, status, permission, icon, route_name, route_path,
|
||||
component_path, redirect, hidden, keep_alive, always_show, title,
|
||||
params, affix, parent_id, description, created_at, updated_at
|
||||
) VALUES
|
||||
('{{ function_name }}查询', 3, 1, {{ b_true }}, '{{ permission_prefix }}:query', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}查询', NULL, {{ b_false }}, parent_id, NULL, NOW(), NOW()),
|
||||
('{{ function_name }}新增', 3, 2, {{ b_true }}, '{{ permission_prefix }}:create', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}新增', NULL, {{ b_false }}, parent_id, NULL, NOW(), NOW()),
|
||||
('{{ function_name }}修改', 3, 3, {{ b_true }}, '{{ permission_prefix }}:update', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}修改', NULL, {{ b_false }}, parent_id, NULL, NOW(), NOW()),
|
||||
('{{ function_name }}删除', 3, 4, {{ b_true }}, '{{ permission_prefix }}:delete', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}删除', NULL, {{ b_false }}, parent_id, NULL, NOW(), NOW()),
|
||||
('{{ function_name }}导出', 3, 5, {{ b_true }}, '{{ permission_prefix }}:export', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}导出', NULL, {{ b_false }}, parent_id, NULL, NOW(), NOW()),
|
||||
('{{ function_name }}导入', 3, 6, {{ b_true }}, '{{ permission_prefix }}:import', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}导入', NULL, {{ b_false }}, parent_id, NULL, NOW(), NOW()),
|
||||
('{{ function_name }}批量状态修改', 3, 7, {{ b_true }}, '{{ permission_prefix }}:patch', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}批量状态修改', NULL, {{ b_false }}, parent_id, NULL, NOW(), NOW()),
|
||||
('{{ function_name }}下载导入模板', 3, 8, {{ b_true }}, '{{ permission_prefix }}:download', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}下载导入模板', NULL, {{ b_false }}, parent_id, NULL, NOW(), NOW());
|
||||
|
||||
-- 可选:输出插入的父菜单ID(调试用)
|
||||
RAISE NOTICE '{{ function_name }}菜单创建完成,父菜单ID: %', parent_id;
|
||||
END $$;
|
||||
|
||||
{% else %}
|
||||
-- 未识别的数据库类型,默认按 MySQL 处理
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}', 2, 1, {{ b_true }}, '{{ permission_prefix }}:query', NULL, '{{ business_name|snake_to_camel }}', '/{{ module_name }}/{{ business_name }}', '{{ module_name }}/{{ business_name }}/index', NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}', NULL, {{ b_false }}, {{ parent_menu_id }}, '{{ function_name }}菜单', now(), now());
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}查询', 3, 1, {{ b_true }}, '{{ permission_prefix }}:query', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}查询', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}新增', 3, 2, {{ b_true }}, '{{ permission_prefix }}:create', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}新增', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}修改', 3, 3, {{ b_true }}, '{{ permission_prefix }}:update', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}修改', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}删除', 3, 4, {{ b_true }}, '{{ permission_prefix }}:delete', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}删除', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}导出', 3, 5, {{ b_true }}, '{{ permission_prefix }}:export', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}导出', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}导入', 3, 6, {{ b_true }}, '{{ permission_prefix }}:import', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}导入', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}批量状态修改', 3, 7, {{ b_true }}, '{{ permission_prefix }}:patch', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}批量状态修改', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}下载导入模板', 3, 8, {{ b_true }}, '{{ permission_prefix }}:download', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}下载导入模板', NULL, {{ b_false }}, @parentId, NULL, NOW(), NOW());
|
||||
{% endif %}
|
||||
@@ -0,0 +1,132 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
const API_PATH = "/{{ module_name }}/{{ business_name|lower }}";
|
||||
|
||||
// 参考 demo.ts 的风格,提供标准的 CRUD 与导入/导出 API(TypeScript)
|
||||
const {{ class_name }}API = {
|
||||
// 列表查询
|
||||
list{{ class_name }}(query: {{ class_name }}PageQuery) {
|
||||
return request<ApiResponse<PageResult<{{ class_name }}Table[]>>>({
|
||||
url: `${API_PATH}/list`,
|
||||
method: "get",
|
||||
params: query,
|
||||
});
|
||||
},
|
||||
|
||||
// 详情查询
|
||||
detail{{ class_name }}(id: number) {
|
||||
return request<ApiResponse<{{ class_name }}Table>>({
|
||||
url: `${API_PATH}/detail/${id}`,
|
||||
method: "get",
|
||||
});
|
||||
},
|
||||
|
||||
// 新增
|
||||
create{{ class_name }}(data: {{ class_name }}Form) {
|
||||
return request<ApiResponse>({
|
||||
url: `${API_PATH}/create`,
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
},
|
||||
|
||||
// 修改(带主键)
|
||||
update{{ class_name }}(id: number, data: {{ class_name }}Form) {
|
||||
return request<ApiResponse>({
|
||||
url: `${API_PATH}/update/${id}`,
|
||||
method: "put",
|
||||
data,
|
||||
});
|
||||
},
|
||||
|
||||
// 删除(支持批量)
|
||||
delete{{ class_name }}(ids: number[]) {
|
||||
return request<ApiResponse>({
|
||||
url: `${API_PATH}/delete`,
|
||||
method: "delete",
|
||||
data: ids,
|
||||
});
|
||||
},
|
||||
|
||||
// 导出
|
||||
export{{ class_name }}(query: {{ class_name }}PageQuery) {
|
||||
return request<Blob>({
|
||||
url: `${API_PATH}/export`,
|
||||
method: "post",
|
||||
data: query,
|
||||
responseType: "blob",
|
||||
});
|
||||
},
|
||||
|
||||
// 下载导入模板
|
||||
downloadTemplate{{ class_name }}() {
|
||||
return request<Blob>({
|
||||
url: `${API_PATH}/download/template`,
|
||||
method: "post",
|
||||
responseType: "blob",
|
||||
});
|
||||
},
|
||||
|
||||
// 导入
|
||||
import{{ class_name }}(data: FormData) {
|
||||
return request<ApiResponse>({
|
||||
url: `${API_PATH}/import`,
|
||||
method: "post",
|
||||
data,
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
},
|
||||
// 批量启用/停用
|
||||
batchAvailable{{ class_name }}(body: { ids: number[]; status: boolean }) {
|
||||
return request<ApiResponse>({
|
||||
url: `${API_PATH}/available/setting`,
|
||||
method: "patch",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default {{ class_name }}API;
|
||||
|
||||
// ------------------------------
|
||||
// TS 类型声明
|
||||
// ------------------------------
|
||||
|
||||
export interface {{ class_name }}PageQuery extends PageQuery {
|
||||
{% for column in columns %}
|
||||
{% if column.is_query == "1" and column.query_type != "BETWEEN" %}
|
||||
{{ column.python_field }}?: {{
|
||||
'boolean' if ('status' in (column.python_field|lower)) or (column.html_type == 'radio')
|
||||
else 'number' if column.is_pk == '1'
|
||||
else 'string'
|
||||
}};
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
// 时间范围查询(按示例统一字段名,如需按字段拆分可在页面层处理)
|
||||
start_time?: string;
|
||||
end_time?: string;
|
||||
}
|
||||
|
||||
export interface {{ class_name }}Table {
|
||||
{% for column in columns %}
|
||||
{{ column.python_field }}?: {{
|
||||
'boolean' if ('status' in (column.python_field|lower)) or (column.html_type == 'radio')
|
||||
else 'number' if column.is_pk == '1'
|
||||
else 'string'
|
||||
}};
|
||||
{% endfor %}
|
||||
creator?: creatorType;
|
||||
}
|
||||
|
||||
export interface {{ class_name }}Form {
|
||||
id?: number;
|
||||
{% for column in columns %}
|
||||
{% if column.is_insert == "1" or column.is_edit == "1" %}
|
||||
{{ column.python_field }}?: {{
|
||||
'boolean' if ('status' in (column.python_field|lower)) or (column.html_type == 'radio')
|
||||
else 'number' if column.is_pk == '1'
|
||||
else 'string'
|
||||
}};
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
@@ -0,0 +1,646 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 搜索区域 -->
|
||||
<div v-show="visible" class="search-container">
|
||||
<el-form ref="queryFormRef" :model="queryFormData" label-suffix=":" :inline="true" @submit.prevent="handleQuery">
|
||||
{% for column in columns %}
|
||||
{% if column.is_query == "1" %}
|
||||
{% set dict_type = column.dict_type %}
|
||||
{% set column_comment = column.column_comment if column.column_comment else '' %}
|
||||
{% set parentheseIndex = column_comment.find("(") %}
|
||||
{% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %}
|
||||
|
||||
{% if column.html_type == "input" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.python_field }}">
|
||||
<el-input v-model="queryFormData.{{ column.python_field }}" placeholder="请输入{{ comment }}" clearable />
|
||||
</el-form-item>
|
||||
{% elif (column.html_type == "select" or column.html_type == "radio") and dict_type != "" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.python_field }}">
|
||||
<el-select v-model="queryFormData.{{ column.python_field }}" placeholder="请选择{{ comment }}" style="width: 180px" clearable>
|
||||
<el-option v-for="dict in dictStore.getDictArray('{{ dict_type }}')" :key="dict.dict_value" :label="dict.dict_label" :value="dict.dict_value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
{% elif (column.html_type == "select" or column.html_type == "radio") and dict_type %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.python_field }}">
|
||||
<el-select v-model="queryFormData.{{ column.python_field }}" placeholder="请选择{{ comment }}" clearable>
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
{% elif column.html_type == "datetime" and column.query_type != "BETWEEN" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.python_field }}">
|
||||
<el-date-picker v-model="queryFormData.{{ column.python_field }}" type="date" value-format="YYYY-MM-DD" clearable placeholder="请选择{{ comment }}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<!-- 可选:创建人选择与统一日期范围(展开后显示) -->
|
||||
<el-form-item v-if="isExpand" prop="creator" label="创建人">
|
||||
<UserTableSelect v-model="queryFormData.creator" @confirm-click="handleConfirm" @clear-click="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="isExpand" prop="start_time" label="创建时间">
|
||||
<DatePicker v-model="dateRange" @update:model-value="handleDateRangeChange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name }}:query']" type="primary" icon="search" @click="handleQuery">查询</el-button>
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name }}:query']" icon="refresh" @click="handleResetQuery">重置</el-button>
|
||||
<template v-if="isExpandable">
|
||||
<el-link class="ml-3" type="primary" underline="never" @click="isExpand = !isExpand">
|
||||
{{ '{{' }} isExpand ? "收起" : "展开" {{ '}}' }}
|
||||
<el-icon>
|
||||
<template v-if="isExpand">
|
||||
<ArrowUp />
|
||||
</template>
|
||||
<template v-else>
|
||||
<ArrowDown />
|
||||
</template>
|
||||
</el-icon>
|
||||
</el-link>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<el-card shadow="hover" class="data-table">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>
|
||||
<el-tooltip content="{{ function_name }}列表">
|
||||
<QuestionFilled class="w-4 h-4 mx-1" />
|
||||
</el-tooltip>
|
||||
{{ function_name }}列表
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 功能区域 -->
|
||||
<div class="data-table__toolbar">
|
||||
<div class="data-table__toolbar--left">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name }}:create']" type="success" icon="plus" @click="handleOpenDialog('create')">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name }}:delete']" type="danger" icon="delete" :disabled="selectIds.length === 0" @click="handleDelete(selectIds)">批量删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-dropdown v-hasPerm="['{{ module_name }}:{{ business_name }}:batch']" trigger="click">
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">更多</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Check" @click="handleMoreClick(true)">批量启用</el-dropdown-item>
|
||||
<el-dropdown-item icon="CircleClose" @click="handleMoreClick(false)">批量停用</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="data-table__toolbar--right">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-tooltip content="导入">
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name }}:import']" type="success" icon="upload" circle @click="handleOpenImportDialog" />
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-tooltip content="导出">
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name }}:export']" type="warning" icon="download" circle @click="handleOpenExportsModal" />
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-tooltip content="搜索显示/隐藏">
|
||||
<el-button v-hasPerm="['*:*:*']" type="info" icon="search" circle @click="visible = !visible" />
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-tooltip content="刷新">
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name }}:refresh']" type="primary" icon="refresh" circle @click="handleRefresh" />
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-popover placement="bottom" trigger="click">
|
||||
<template #reference>
|
||||
<el-button type="danger" icon="operation" circle></el-button>
|
||||
</template>
|
||||
<el-scrollbar max-height="350px">
|
||||
<template v-for="column in tableColumns" :key="column.prop">
|
||||
<el-checkbox v-if="column.prop" v-model="column.show" :label="column.label" />
|
||||
</template>
|
||||
</el-scrollbar>
|
||||
</el-popover>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<el-table
|
||||
ref="dataTableRef"
|
||||
v-loading="loading"
|
||||
:data="pageTableData"
|
||||
highlight-current-row
|
||||
class="data-table__content"
|
||||
:height="450"
|
||||
border
|
||||
stripe
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column v-if="tableColumns.find((col) => col.prop === 'selection')?.show" type="selection" min-width="55" align="center" />
|
||||
<el-table-column v-if="tableColumns.find((col) => col.prop === 'index')?.show" fixed label="序号" min-width="60">
|
||||
<template #default="scope">
|
||||
{{ '{{' }} (queryFormData.page_no - 1) * queryFormData.page_size + scope.$index + 1 {{ '}}' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
{% for column in columns %}
|
||||
{% set python_field = column.python_field %}
|
||||
{% set column_comment = column.column_comment if column.column_comment else '' %}
|
||||
{% set parentheseIndex = column_comment.find("(") %}
|
||||
{% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %}
|
||||
{% if column.is_list == "1" %}
|
||||
<el-table-column v-if="tableColumns.find((col) => col.prop === '{{ python_field }}')?.show" label="{{ comment }}" prop="{{ python_field }}" min-width="140">
|
||||
{% if python_field == "status" %}
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status ? 'success' : 'info'">{{ '{{' }} scope.row.status ? '启用' : '停用' {{ '}}' }}</el-tag>
|
||||
</template>
|
||||
{% elif python_field == "creator" %}
|
||||
<template #default="scope">
|
||||
<el-tag>{{ '{{' }} scope.row.creator?.name {{ '}}' }}</el-tag>
|
||||
</template>
|
||||
{% endif %}
|
||||
</el-table-column>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作" align="center" min-width="180">
|
||||
<template #default="scope">
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name }}:detail']" type="info" size="small" link icon="document" @click="handleOpenDialog('detail', scope.row.id)">详情</el-button>
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name }}:update']" type="primary" size="small" link icon="edit" @click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name }}:delete']" type="danger" size="small" link icon="delete" @click="handleDelete([scope.row.id])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<template #footer>
|
||||
<pagination v-model:total="total" v-model:page="queryFormData.page_no" v-model:limit="queryFormData.page_size" @pagination="loadingData" />
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
<!-- 弹窗区域 -->
|
||||
<el-dialog v-model="dialogVisible.visible" :title="dialogVisible.title" @close="handleCloseDialog">
|
||||
<!-- 详情 -->
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<el-descriptions :column="4" border>
|
||||
{% for column in columns %}
|
||||
{% set python_field = column.python_field %}
|
||||
{% set column_comment = column.column_comment if column.column_comment else '' %}
|
||||
{% set parentheseIndex = column_comment.find("(") %}
|
||||
{% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %}
|
||||
<el-descriptions-item label="{{ comment }}" :span="2">
|
||||
{{ '{' }}{{ '{' }} detailFormData.{{ python_field }} {{ '}' }}{{ '}' }}
|
||||
</el-descriptions-item>
|
||||
{% endfor %}
|
||||
</el-descriptions>
|
||||
</template>
|
||||
<!-- 新增、编辑表单 -->
|
||||
<template v-else>
|
||||
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-suffix=":" label-width="auto" label-position="right">
|
||||
{% for column in columns %}
|
||||
{% if column.is_insert == "1" or column.is_edit == "1" %}
|
||||
{% set dict_type = column.dict_type %}
|
||||
{% set column_comment = column.column_comment if column.column_comment else '' %}
|
||||
{% set parentheseIndex = column_comment.find("(") %}
|
||||
{% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %}
|
||||
{% set required = 'true' if column.is_nullable == '1' else 'false' %}
|
||||
|
||||
{% if column.python_field == "status" %}
|
||||
<el-form-item label="状态" prop="status" :required="true">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :value="true">启用</el-radio>
|
||||
<el-radio :value="false">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
{% elif column.html_type == "input" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.python_field }}" :required="{{ required }}">
|
||||
<el-input v-model="formData.{{ column.python_field }}" placeholder="请输入{{ comment }}" />
|
||||
</el-form-item>
|
||||
{% elif column.html_type == "textarea" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.python_field }}" :required="{{ required }}">
|
||||
<el-input v-model="formData.{{ column.python_field }}" type="textarea" placeholder="请输入{{ comment }}" rows="4" />
|
||||
</el-form-item>
|
||||
{% elif (column.html_type == "select" or column.html_type == "radio") and dict_type != "" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.python_field }}" :required="{{ required }}">
|
||||
<el-select v-model="formData.{{ column.python_field }}" placeholder="请选择{{ comment }}">
|
||||
<el-option v-for="dict in dictStore.getDictArray('{{ dict_type }}')" :key="dict.dict_value" :label="dict.dict_label" :value="dict.dict_value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
{% elif (column.html_type == "select" or column.html_type == "radio") and dict_type %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.python_field }}" :required="{{ required }}">
|
||||
<el-select v-model="formData.{{ column.python_field }}" placeholder="请选择{{ comment }}">
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
{% elif column.html_type == "date" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.python_field }}" :required="{{ required }}">
|
||||
<el-date-picker v-model="formData.{{ column.python_field }}" type="date" value-format="YYYY-MM-DD" placeholder="请选择{{ comment }}" />
|
||||
</el-form-item>
|
||||
{% elif column.html_type == "datetime" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.python_field }}" :required="{{ required }}">
|
||||
<el-date-picker v-model="formData.{{ column.python_field }}" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择{{ comment }}" />
|
||||
</el-form-item>
|
||||
{% elif column.html_type == "checkbox" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.python_field }}">
|
||||
<el-checkbox v-model="formData.{{ column.python_field }}">{{ comment }}</el-checkbox>
|
||||
</el-form-item>
|
||||
{% elif column.html_type == "imageUpload" %}
|
||||
<el-form-item label="{{ comment }}" prop="{{ column.python_field }}">
|
||||
<SingleImageUpload v-model="formData.{{ column.python_field }}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleCloseDialog">取消</el-button>
|
||||
<el-button v-if="dialogVisible.type !== 'detail'" v-hasPerm="['{{ module_name }}:{{ business_name }}:submit']" type="primary" @click="handleSubmit">确定</el-button>
|
||||
<el-button v-else v-hasPerm="['{{ module_name }}:{{ business_name }}:detail']" type="primary" @click="handleCloseDialog">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 导入弹窗 -->
|
||||
<ImportModal
|
||||
v-model="importDialogVisible"
|
||||
:content-config="curdContentConfig"
|
||||
@upload="handleUpload"
|
||||
/>
|
||||
|
||||
<!-- 导出弹窗 -->
|
||||
<ExportModal
|
||||
v-model="exportsDialogVisible"
|
||||
:content-config="curdContentConfig"
|
||||
:query-params="queryFormData"
|
||||
:page-data="pageTableData"
|
||||
:selection-data="selectionRows"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: "{{ class_name }}",
|
||||
inheritAttrs: false,
|
||||
});
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ResultEnum } from '@/enums/api/result.enum'
|
||||
import { QuestionFilled, ArrowUp, ArrowDown, Check, CircleClose } from '@element-plus/icons-vue'
|
||||
import { formatToDateTime } from '@/utils/dateUtil'
|
||||
import {{ class_name }}API, { {{ class_name }}PageQuery, {{ class_name }}Table, {{ class_name }}Form } from '@/api/{{ module_name }}/{{ business_name }}'
|
||||
import { useDictStore } from '@/store/index'
|
||||
import SingleImageUpload from '@/components/Upload/SingleImageUpload.vue'
|
||||
import ImportModal from '@/components/CURD/ImportModal.vue'
|
||||
import ExportModal from '@/components/CURD/ExportModal.vue'
|
||||
import DatePicker from '@/components/DatePicker/index.vue'
|
||||
import type { IContentConfig } from '@/components/CURD/types'
|
||||
|
||||
const visible = ref(true)
|
||||
const isExpand = ref(false)
|
||||
const isExpandable = ref(true)
|
||||
|
||||
const queryFormRef = ref()
|
||||
const dataFormRef = ref()
|
||||
const total = ref(0)
|
||||
const selectIds = ref<number[]>([])
|
||||
const selectionRows = ref<{{ class_name }}Table[]>([]);
|
||||
const loading = ref(false)
|
||||
|
||||
// 字典仓库与需要加载的字典类型
|
||||
const dictStore = useDictStore()
|
||||
const dictTypes = [
|
||||
{% for column in columns %}
|
||||
{% if column.dict_type %}
|
||||
'{{ column.dict_type }}',
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
]
|
||||
|
||||
// 分页表单
|
||||
const pageTableData = ref<{{ class_name }}Table[]>([]);
|
||||
|
||||
// 表格列配置(根据列生成,可显隐)
|
||||
const tableColumns = ref([
|
||||
{ prop: 'selection', label: '选择框', show: true },
|
||||
{ prop: 'index', label: '序号', show: true },
|
||||
{% for column in columns %}
|
||||
{% if column.is_list == "1" %}
|
||||
{ prop: '{{ column.python_field }}', label: '{{ column.column_comment or column.python_field }}', show: true },
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{ prop: 'operation', label: '操作', show: true }
|
||||
])
|
||||
|
||||
// 导出列(不含选择/序号/操作)
|
||||
const exportColumns = [
|
||||
{% for column in columns %}
|
||||
{% if column.is_list == "1" %}
|
||||
{ prop: '{{ column.python_field }}', label: '{{ column.column_comment or column.python_field }}' },
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
]
|
||||
|
||||
// 导入/导出配置
|
||||
const curdContentConfig = {
|
||||
permPrefix: '{{ module_name }}:{{ business_name }}',
|
||||
cols: exportColumns as any,
|
||||
importTemplate: () => {{ class_name }}API.downloadTemplate{{ class_name }}(),
|
||||
exportsAction: async (params: any) => {
|
||||
const query: any = { ...params };
|
||||
if (typeof query.status === 'string') {
|
||||
query.status = query.status === 'true';
|
||||
}
|
||||
query.page_no = 1;
|
||||
query.page_size = 9999;
|
||||
const all: any[] = [];
|
||||
while (true) {
|
||||
const res = await {{ class_name }}API.list{{ class_name }}(query)
|
||||
const items = res.data?.data?.items || []
|
||||
const total = res.data?.data?.total || 0
|
||||
all.push(...items)
|
||||
if (all.length >= total || items.length === 0) break
|
||||
query.page_no += 1
|
||||
}
|
||||
return all;
|
||||
},
|
||||
} as unknown as IContentConfig
|
||||
|
||||
// 弹窗状态
|
||||
const dialogVisible = reactive({
|
||||
title: '',
|
||||
visible: false,
|
||||
type: 'create', // 'create' | 'update' | 'detail'
|
||||
})
|
||||
|
||||
// 编辑表单
|
||||
const formData = reactive<{{ class_name }}Form>({
|
||||
id: undefined,
|
||||
{% for column in columns %}
|
||||
{% if column.is_insert == "1" or column.is_edit == "1" %}
|
||||
{{ column.python_field }}: undefined,
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
})
|
||||
|
||||
// 定义初始表单数据常量
|
||||
const initialFormData: {{ class_name }}Form = {
|
||||
id: undefined,
|
||||
{% for column in columns %}
|
||||
{% if column.is_insert == "1" or column.is_edit == "1" %}
|
||||
{{ column.python_field }}: {{ 'true' if column.python_field == 'status' else ('' if column.html_type == 'textarea' else 'undefined') }},
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
async function resetForm() {
|
||||
if (dataFormRef.value) {
|
||||
dataFormRef.value.resetFields();
|
||||
dataFormRef.value.clearValidate();
|
||||
}
|
||||
// 完全重置 formData 为初始状态
|
||||
Object.assign(formData, initialFormData);
|
||||
}
|
||||
|
||||
// 表单验证规则(必填项按 is_nullable 生成)
|
||||
const rules = reactive({
|
||||
{% for column in columns %}
|
||||
{% if column.is_insert == "1" or column.is_edit == "1" %}
|
||||
{% set required = 'true' if column.is_nullable == '1' else 'false' %}
|
||||
{{ column.python_field }}: [
|
||||
{ required: {{ required }}, message: '请输入{{ column.column_comment or column.python_field }}', trigger: 'blur' },
|
||||
],
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
})
|
||||
|
||||
// 详情表单
|
||||
const detailFormData = ref<{{ class_name }}Table>({});
|
||||
|
||||
|
||||
// 统一日期范围
|
||||
const dateRange = ref<[Date, Date] | []>([]);
|
||||
function handleDateRangeChange(range: [Date, Date]) {
|
||||
dateRange.value = range;
|
||||
if (range && range.length === 2) {
|
||||
queryFormData.start_time = formatToDateTime(range[0]);
|
||||
queryFormData.end_time = formatToDateTime(range[1]);
|
||||
} else {
|
||||
queryFormData.start_time = undefined;
|
||||
queryFormData.end_time = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// 查询参数
|
||||
const queryFormData = reactive<{{ class_name }}PageQuery>({
|
||||
page_no: 1,
|
||||
page_size: 10,
|
||||
{% for column in columns %}
|
||||
{% if column.is_query == "1" and column.query_type != "BETWEEN" %}
|
||||
{{ column.python_field }}: undefined,
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
start_time: undefined,
|
||||
end_time: undefined,
|
||||
creator: undefined,
|
||||
})
|
||||
|
||||
// 加载表格数据
|
||||
async function loadingData() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await {{ class_name }}API.list{{ class_name }}(queryFormData);
|
||||
pageTableData.value = response.data.data.items;
|
||||
total.value = response.data.data.total;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 查询(重置页码后获取数据)
|
||||
async function handleQuery() {
|
||||
queryFormData.page_no = 1;
|
||||
loadingData();
|
||||
}
|
||||
|
||||
// 选择创建人后触发查询
|
||||
function handleConfirm() {
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// 重置查询
|
||||
async function handleResetQuery() {
|
||||
queryFormRef.value.resetFields();
|
||||
queryFormData.page_no = 1;
|
||||
dateRange.value = [];
|
||||
queryFormData.start_time = undefined;
|
||||
queryFormData.end_time = undefined;
|
||||
loadingData();
|
||||
}
|
||||
|
||||
// 行复选框选中项变化
|
||||
function handleSelectionChange(selection: any[]) {
|
||||
selectIds.value = selection.map((item: any) => item.id)
|
||||
selectionRows.value = selection
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
async function handleCloseDialog() {
|
||||
dialogVisible.visible = false;
|
||||
resetForm();
|
||||
}
|
||||
|
||||
// 打开弹窗
|
||||
async function handleOpenDialog(type: 'create' | 'update' | 'detail', id?: number) {
|
||||
dialogVisible.type = type
|
||||
if (id) {
|
||||
const response = await {{ class_name }}API.detail{{ class_name }}(id);
|
||||
if (type === 'detail') {
|
||||
dialogVisible.title = '详情';
|
||||
Object.assign(detailFormData.value, response.data.data);
|
||||
} else if (type === 'update') {
|
||||
dialogVisible.title = '修改';
|
||||
Object.assign(formData, response.data.data);
|
||||
}
|
||||
} else {
|
||||
dialogVisible.title = '新增{{ function_name }}';
|
||||
formData.id = undefined;
|
||||
}
|
||||
dialogVisible.visible = true;
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
async function handleSubmit() {
|
||||
dataFormRef.value.validate(async (valid: any) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
try {
|
||||
const id = formData.id
|
||||
if (id) {
|
||||
await {{ class_name }}API.update{{ class_name }}(id, { id, ...formData });
|
||||
dialogVisible.visible = false;
|
||||
resetForm();
|
||||
handleCloseDialog();
|
||||
handleResetQuery();
|
||||
} else {
|
||||
await {{ class_name }}API.create{{ class_name }}(formData);
|
||||
dialogVisible.visible = false;
|
||||
resetForm();
|
||||
handleCloseDialog();
|
||||
handleResetQuery();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 删除、批量删除
|
||||
async function handleDelete(ids: number[]) {
|
||||
ElMessageBox.confirm('确认删除该项数据?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await {{ class_name }}API.delete{{ class_name }}(ids);
|
||||
handleResetQuery()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessageBox.close()
|
||||
})
|
||||
}
|
||||
|
||||
// 批量启用/停用
|
||||
async function handleMoreClick(status: boolean) {
|
||||
if (selectIds.value.length) {
|
||||
ElMessageBox.confirm(`确认${status ? '启用' : '停用'}该项数据?`, '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
await {{ class_name }}API.batchAvailable{{ class_name }}({ ids: selectIds.value, status });
|
||||
handleResetQuery()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessageBox.close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 导入弹窗显示状态
|
||||
const importDialogVisible = ref(false)
|
||||
// 导出弹窗显示状态
|
||||
const exportsDialogVisible = ref(false)
|
||||
|
||||
// 打开导入弹窗
|
||||
function handleOpenImportDialog() {
|
||||
importDialogVisible.value = true
|
||||
}
|
||||
// 打开导出弹窗
|
||||
function handleOpenExportsModal() {
|
||||
exportsDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 处理上传
|
||||
const handleUpload = async (formData: FormData) => {
|
||||
try {
|
||||
const response = await {{ class_name }}API.import{{ class_name }}(formData);
|
||||
if (response.data.code === ResultEnum.SUCCESS) {
|
||||
ElMessage.success(`${response.data.msg},${response.data.data}`)
|
||||
importDialogVisible.value = false
|
||||
await handleQuery()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 列表刷新
|
||||
async function handleRefresh() {
|
||||
await loadingData()
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 预加载字典数据
|
||||
if (dictTypes.length > 0) {
|
||||
await dictStore.getDict(dictTypes)
|
||||
}
|
||||
loadingData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,228 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import re
|
||||
from typing import List
|
||||
|
||||
from app.common.constant import GenConstant
|
||||
from app.utils.string_util import StringUtil
|
||||
|
||||
from app.api.v1.module_generator.gencode.schema import GenTableOutSchema, GenTableSchema, GenTableColumnSchema
|
||||
|
||||
|
||||
class GenUtils:
|
||||
"""代码生成器工具类"""
|
||||
|
||||
@classmethod
|
||||
def init_table(cls, gen_table: GenTableSchema) -> None:
|
||||
"""
|
||||
初始化表信息
|
||||
|
||||
参数:
|
||||
- gen_table (GenTableSchema): 业务表对象。
|
||||
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
# 只有当字段为None时才设置默认值
|
||||
gen_table.class_name = cls.convert_class_name(gen_table.table_name or "")
|
||||
gen_table.package_name = 'module_gencode'
|
||||
gen_table.module_name = gen_table.package_name.split('.')[-1]
|
||||
gen_table.business_name = gen_table.table_name
|
||||
gen_table.function_name = re.sub(r'(?:表|测试)', '', gen_table.table_comment or "")
|
||||
|
||||
@classmethod
|
||||
def init_column_field(cls, column: GenTableColumnSchema, table: GenTableOutSchema) -> None:
|
||||
"""
|
||||
初始化列属性字段
|
||||
|
||||
参数:
|
||||
- column (GenTableColumnSchema): 业务表字段对象。
|
||||
- table (GenTableOutSchema): 业务表对象。
|
||||
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
data_type = cls.get_db_type(column.column_type or "")
|
||||
column_name = column.column_name or ""
|
||||
if not table.id:
|
||||
raise ValueError("业务表ID不能为空")
|
||||
column.table_id = table.id
|
||||
column.python_field = cls.to_camel_case(column_name)
|
||||
# 只有当python_type为None时才设置默认类型
|
||||
column.python_type = StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_PYTHON, data_type)
|
||||
|
||||
if column.column_length is None:
|
||||
column.column_length = ''
|
||||
|
||||
if column.column_default is None:
|
||||
column.column_default = ''
|
||||
|
||||
if column.html_type is None:
|
||||
if cls.arrays_contains(GenConstant.COLUMNTYPE_STR, data_type) or cls.arrays_contains(
|
||||
GenConstant.COLUMNTYPE_TEXT, data_type
|
||||
):
|
||||
# 字符串长度超过500设置为文本域
|
||||
column_length = cls.get_column_length(column.column_type or "")
|
||||
html_type = (
|
||||
GenConstant.HTML_TEXTAREA
|
||||
if column_length >= 500 or cls.arrays_contains(GenConstant.COLUMNTYPE_TEXT, data_type)
|
||||
else GenConstant.HTML_INPUT
|
||||
)
|
||||
column.html_type = html_type
|
||||
elif cls.arrays_contains(GenConstant.COLUMNTYPE_TIME, data_type):
|
||||
column.html_type = GenConstant.HTML_DATETIME
|
||||
elif cls.arrays_contains(GenConstant.COLUMNTYPE_NUMBER, data_type):
|
||||
column.html_type = GenConstant.HTML_INPUT
|
||||
elif column_name.lower().endswith("status"):
|
||||
column.html_type = GenConstant.HTML_RADIO
|
||||
elif column_name.lower().endswith("type") or column_name.lower().endswith("sex"):
|
||||
column.html_type = GenConstant.HTML_SELECT
|
||||
elif column_name.lower().endswith("image"):
|
||||
column.html_type = GenConstant.HTML_IMAGE_UPLOAD
|
||||
elif column_name.lower().endswith("file"):
|
||||
column.html_type = GenConstant.HTML_FILE_UPLOAD
|
||||
elif column_name.lower().endswith("content"):
|
||||
column.html_type = GenConstant.HTML_EDITOR
|
||||
else:
|
||||
column.html_type = GenConstant.HTML_INPUT
|
||||
|
||||
# 只有当is_insert为None时才设置插入字段(默认所有字段都需要插入)
|
||||
if column.is_insert:
|
||||
column.is_insert = GenConstant.REQUIRE
|
||||
else:
|
||||
column.is_insert = False
|
||||
|
||||
# 只有当is_edit为None时才设置编辑字段
|
||||
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_EDIT, column_name) and not column.is_pk:
|
||||
column.is_edit = GenConstant.REQUIRE
|
||||
else:
|
||||
column.is_edit = False
|
||||
|
||||
# 只有当is_list为None时才设置列表字段
|
||||
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_LIST, column_name) and not column.is_pk:
|
||||
column.is_list = GenConstant.REQUIRE
|
||||
else:
|
||||
column.is_list = False
|
||||
|
||||
# 只有当is_query为None时才设置查询字段
|
||||
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_QUERY, column_name) and not column.is_pk:
|
||||
column.is_query = GenConstant.REQUIRE
|
||||
# 直接设置查询类型,因为我们已经确定这是一个查询字段
|
||||
if column_name.lower().endswith('name') or data_type in ['varchar', 'char', 'text']:
|
||||
column.query_type = GenConstant.QUERY_LIKE
|
||||
else:
|
||||
column.query_type = GenConstant.QUERY_EQ
|
||||
else:
|
||||
column.is_query = False
|
||||
column.query_type = None
|
||||
|
||||
@classmethod
|
||||
def arrays_contains(cls, arr, target_value) -> bool:
|
||||
"""
|
||||
检查目标值是否在数组中
|
||||
|
||||
注意:从根本上解决问题,现在确保传入的参数都是正确的类型:
|
||||
- arr 是列表类型,且在GenConstant中定义
|
||||
- target_value 不会是None
|
||||
|
||||
参数:
|
||||
- arr: 数组类型
|
||||
- target_value: 目标值
|
||||
|
||||
返回:
|
||||
- bool: 如果目标值在数组中,返回True;否则返回False
|
||||
"""
|
||||
# 从根本上解决问题,不再需要复杂的防御性检查
|
||||
# 因为现在我们确保传入的arr是GenConstant中定义的列表常量
|
||||
# 并且target_value在调用前已经被处理过不会是None
|
||||
|
||||
# 简单直接地执行包含检查
|
||||
target_str = str(target_value).lower()
|
||||
return any(str(item).lower() == target_str for item in arr)
|
||||
|
||||
@classmethod
|
||||
def convert_class_name(cls, table_name: str) -> str:
|
||||
"""
|
||||
表名转换成 Python 类名
|
||||
|
||||
参数:
|
||||
- table_name (str): 业务表名。
|
||||
|
||||
返回:
|
||||
- str: Python 类名。
|
||||
"""
|
||||
return StringUtil.convert_to_camel_case(table_name)
|
||||
|
||||
@classmethod
|
||||
def replace_first(cls, input_string: str, search_list: List[str]) -> str:
|
||||
"""
|
||||
批量替换前缀
|
||||
|
||||
参数:
|
||||
- input_string (str): 需要被替换的字符串。
|
||||
- search_list (List[str]): 可替换的字符串列表。
|
||||
|
||||
返回:
|
||||
- str: 替换后的字符串。
|
||||
"""
|
||||
for search_string in search_list:
|
||||
if input_string.startswith(search_string):
|
||||
return input_string.replace(search_string, '', 1)
|
||||
return input_string
|
||||
|
||||
@classmethod
|
||||
def get_db_type(cls, column_type: str) -> str:
|
||||
"""
|
||||
获取数据库类型字段
|
||||
|
||||
参数:
|
||||
- column_type (str): 字段类型。
|
||||
|
||||
返回:
|
||||
- str: 数据库类型。
|
||||
"""
|
||||
if '(' in column_type:
|
||||
return column_type.split('(')[0]
|
||||
return column_type
|
||||
|
||||
@classmethod
|
||||
def get_column_length(cls, column_type: str) -> int:
|
||||
"""
|
||||
获取字段长度
|
||||
|
||||
参数:
|
||||
- column_type (str): 字段类型,例如 'varchar(255)' 或 'decimal(10,2)'
|
||||
|
||||
返回:
|
||||
- int: 字段长度(优先取第一个长度值,无法解析时返回0)。
|
||||
"""
|
||||
if '(' in column_type:
|
||||
length = len(column_type.split('(')[1].split(')')[0])
|
||||
return length
|
||||
return 0
|
||||
|
||||
@classmethod
|
||||
def split_column_type(cls, column_type: str) -> List[str]:
|
||||
"""
|
||||
拆分列类型
|
||||
|
||||
参数:
|
||||
- column_type (str): 字段类型。
|
||||
|
||||
返回:
|
||||
- List[str]: 拆分结果。
|
||||
"""
|
||||
if '(' in column_type and ')' in column_type:
|
||||
return column_type.split('(')[1].split(')')[0].split(',')
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def to_camel_case(cls, text: str) -> str:
|
||||
"""
|
||||
将字符串转换为驼峰命名
|
||||
|
||||
param text: 需要转换的字符串
|
||||
:return: 驼峰命名
|
||||
"""
|
||||
parts = text.split('_')
|
||||
return parts[0] + ''.join(word.capitalize() for word in parts[1:])
|
||||
@@ -0,0 +1,395 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from datetime import datetime
|
||||
from jinja2.environment import Environment
|
||||
from jinja2 import Environment, FileSystemLoader, select_autoescape, Template
|
||||
from typing import List, Any, Set
|
||||
|
||||
from app.common.constant import GenConstant
|
||||
from app.config.path_conf import TEMPLATE_DIR
|
||||
from app.config.setting import settings
|
||||
from app.utils.common_util import CamelCaseUtil, SnakeCaseUtil
|
||||
from app.utils.string_util import StringUtil
|
||||
|
||||
from app.api.v1.module_generator.gencode.schema import GenTableOutSchema, GenTableColumnOutSchema
|
||||
|
||||
|
||||
class Jinja2TemplateUtil:
|
||||
"""
|
||||
模板处理工具类
|
||||
"""
|
||||
|
||||
# 项目路径
|
||||
FRONTEND_PROJECT_PATH = 'frontend'
|
||||
BACKEND_PROJECT_PATH = 'backend'
|
||||
# 默认上级菜单,系统工具
|
||||
DEFAULT_PARENT_MENU_ID = "3"
|
||||
|
||||
# 环境对象
|
||||
_env = None
|
||||
|
||||
@classmethod
|
||||
def get_env(cls):
|
||||
"""
|
||||
获取模板环境对象。
|
||||
|
||||
参数:
|
||||
- 无
|
||||
|
||||
返回:
|
||||
- Environment: Jinja2 环境对象。
|
||||
"""
|
||||
try:
|
||||
if cls._env is None:
|
||||
cls._env = Environment(
|
||||
loader=FileSystemLoader(TEMPLATE_DIR),
|
||||
autoescape=False, # 自动转义HTML
|
||||
trim_blocks=True, # 删除多余的空行
|
||||
lstrip_blocks=True, # 删除行首空格
|
||||
keep_trailing_newline=True, # 保留行尾换行符
|
||||
enable_async=True, # 开启异步支持
|
||||
)
|
||||
cls._env.filters.update(
|
||||
{
|
||||
'camel_to_snake': SnakeCaseUtil.camel_to_snake,
|
||||
'snake_to_camel': CamelCaseUtil.snake_to_camel,
|
||||
'get_sqlalchemy_type': cls.get_sqlalchemy_type,
|
||||
}
|
||||
)
|
||||
return cls._env
|
||||
except Exception as e:
|
||||
raise RuntimeError(f'初始化Jinja2模板引擎失败: {e}')
|
||||
|
||||
@classmethod
|
||||
def get_template(cls, template_path: str) -> Template:
|
||||
"""
|
||||
获取模板。
|
||||
|
||||
参数:
|
||||
- template_path (str): 模板路径。
|
||||
|
||||
返回:
|
||||
- Template: Jinja2 模板对象。
|
||||
|
||||
异常:
|
||||
- TemplateNotFound: 模板未找到时抛出。
|
||||
"""
|
||||
return cls.get_env().get_template(template_path)
|
||||
|
||||
@classmethod
|
||||
def prepare_context(cls, gen_table: GenTableOutSchema) -> dict[str, Any]:
|
||||
"""
|
||||
准备模板变量。
|
||||
|
||||
参数:
|
||||
- gen_table (GenTableOutSchema): 生成表的配置信息。
|
||||
|
||||
返回:
|
||||
- Dict[str, Any]: 模板上下文字典。
|
||||
"""
|
||||
# 处理options为None的情况
|
||||
# if not gen_table.options:
|
||||
# raise ValueError('请先完善生成配置信息')
|
||||
class_name = gen_table.class_name or ''
|
||||
module_name = gen_table.module_name or ''
|
||||
business_name = gen_table.business_name or ''
|
||||
package_name = gen_table.package_name or ''
|
||||
function_name = gen_table.function_name or ''
|
||||
|
||||
context = {
|
||||
'table_name': gen_table.table_name or '',
|
||||
'table_comment': gen_table.table_comment or '',
|
||||
'function_name': function_name if StringUtil.is_not_empty(function_name) else '【请填写功能名称】',
|
||||
'class_name': class_name,
|
||||
'module_name': module_name,
|
||||
'business_name': business_name,
|
||||
'base_package': cls.get_package_prefix(package_name),
|
||||
'package_name': package_name,
|
||||
'datetime': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'pk_column': gen_table.pk_column,
|
||||
'model_import_list': cls.get_model_import_list(gen_table),
|
||||
'schema_import_list': cls.get_schema_import_list(gen_table),
|
||||
'permission_prefix': cls.get_permission_prefix(module_name, business_name),
|
||||
'columns': gen_table.columns or [],
|
||||
'table': gen_table,
|
||||
'dicts': cls.get_dicts(gen_table),
|
||||
'db_type': settings.DATABASE_TYPE,
|
||||
'column_not_add_show': GenConstant.COLUMNNAME_NOT_ADD_SHOW,
|
||||
'column_not_edit_show': GenConstant.COLUMNNAME_NOT_EDIT_SHOW,
|
||||
'parent_menu_id': int(gen_table.parent_menu_id) if gen_table.parent_menu_id is not None else int(cls.DEFAULT_PARENT_MENU_ID),
|
||||
}
|
||||
|
||||
return context
|
||||
|
||||
@classmethod
|
||||
def get_template_list(cls):
|
||||
"""
|
||||
获取模板列表。
|
||||
|
||||
参数:
|
||||
- 无
|
||||
返回:
|
||||
- List[str]: 模板路径列表。
|
||||
"""
|
||||
templates = [
|
||||
'python/controller.py.j2',
|
||||
'python/service.py.j2',
|
||||
'python/crud.py.j2',
|
||||
'python/schema.py.j2',
|
||||
'python/param.py.j2',
|
||||
'python/model.py.j2',
|
||||
'sql/sql.sql.j2',
|
||||
'ts/api.ts.j2',
|
||||
'vue/index.vue.j2',
|
||||
]
|
||||
return templates
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_file_name(cls, template: str, gen_table: GenTableOutSchema):
|
||||
"""
|
||||
根据模板生成文件名。
|
||||
|
||||
参数:
|
||||
- template (str): 模板路径字符串。
|
||||
- gen_table (GenTableOutSchema): 生成表的配置信息。
|
||||
|
||||
返回:
|
||||
- str: 模板生成的文件名。
|
||||
|
||||
异常:
|
||||
- ValueError: 当无法生成有效文件名时抛出。
|
||||
"""
|
||||
module_name = gen_table.module_name or ''
|
||||
business_name = gen_table.business_name or ''
|
||||
|
||||
# 验证必要的参数
|
||||
if not module_name or not business_name:
|
||||
raise ValueError(f"无法为模板 {template} 生成文件名:模块名或业务名未设置")
|
||||
|
||||
# 映射表方式简化
|
||||
template_mapping = {
|
||||
'controller.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/{module_name}/{business_name}/controller.py',
|
||||
'service.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/{module_name}/{business_name}/service.py',
|
||||
'crud.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/{module_name}/{business_name}/crud.py',
|
||||
'model.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/{module_name}/{business_name}/model.py',
|
||||
'param.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/{module_name}/{business_name}/param.py',
|
||||
'schema.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/{module_name}/{business_name}/schema.py',
|
||||
'sql.sql.j2': f'{cls.BACKEND_PROJECT_PATH}/sql/menu/{module_name}/{business_name}.sql',
|
||||
'api.ts.j2': f'{cls.FRONTEND_PROJECT_PATH}/src/api/{module_name}/{business_name}.ts',
|
||||
'index.vue.j2': f'{cls.FRONTEND_PROJECT_PATH}/src/views/{module_name}/{business_name}/index.vue'
|
||||
}
|
||||
|
||||
# 查找匹配的模板路径
|
||||
for key, path in template_mapping.items():
|
||||
if key in template:
|
||||
return path
|
||||
|
||||
# 默认处理
|
||||
template_name = template.split('/')[-1].replace('.j2', '')
|
||||
return f'{cls.BACKEND_PROJECT_PATH}/generated/{template_name}'
|
||||
|
||||
@classmethod
|
||||
def get_package_prefix(cls, package_name: str) -> str:
|
||||
"""
|
||||
获取包前缀。
|
||||
|
||||
参数:
|
||||
- package_name (str): 包名。
|
||||
|
||||
返回:
|
||||
- str: 包前缀。
|
||||
"""
|
||||
# 修复:当包名中不存在'.'时,直接返回原包名
|
||||
return package_name[: package_name.rfind('.')] if '.' in package_name else package_name
|
||||
|
||||
@classmethod
|
||||
def get_schema_import_list(cls, gen_table: GenTableOutSchema):
|
||||
"""
|
||||
获取schema模板导入包列表
|
||||
|
||||
:param gen_table: 生成表的配置信息
|
||||
:return: 导入包列表
|
||||
"""
|
||||
columns = gen_table.columns or []
|
||||
import_list = set()
|
||||
for column in columns:
|
||||
if column.python_type in GenConstant.TYPE_DATE:
|
||||
import_list.add(f'from datetime import {column.python_type}')
|
||||
elif column.python_type == GenConstant.TYPE_DECIMAL:
|
||||
import_list.add('from decimal import Decimal')
|
||||
if gen_table.sub:
|
||||
if gen_table.sub_table and gen_table.sub_table.columns:
|
||||
sub_columns = gen_table.sub_table.columns or []
|
||||
for sub_column in sub_columns:
|
||||
if sub_column.python_type in GenConstant.TYPE_DATE:
|
||||
import_list.add(f'from datetime import {sub_column.python_type}')
|
||||
elif sub_column.python_type == GenConstant.TYPE_DECIMAL:
|
||||
import_list.add('from decimal import Decimal')
|
||||
return cls.merge_same_imports(list(import_list), 'from datetime import')
|
||||
|
||||
@classmethod
|
||||
def get_model_import_list(cls, gen_table: GenTableOutSchema):
|
||||
"""
|
||||
获取do模板导入包列表
|
||||
|
||||
:param gen_table: 生成表的配置信息
|
||||
:return: 导入包列表
|
||||
"""
|
||||
columns = gen_table.columns or []
|
||||
import_list = set()
|
||||
|
||||
for column in columns:
|
||||
if column.column_type:
|
||||
data_type = cls.get_db_type(column.column_type)
|
||||
if data_type in GenConstant.COLUMNTYPE_GEOMETRY:
|
||||
import_list.add('from geoalchemy2 import Geometry')
|
||||
import_list.add(
|
||||
f'from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, data_type)}'
|
||||
)
|
||||
if gen_table.sub:
|
||||
import_list.add('from sqlalchemy import ForeignKey')
|
||||
if gen_table.sub_table and gen_table.sub_table.columns:
|
||||
sub_columns = gen_table.sub_table.columns or []
|
||||
for sub_column in sub_columns:
|
||||
if sub_column.column_type:
|
||||
data_type = cls.get_db_type(sub_column.column_type)
|
||||
import_list.add(
|
||||
f'from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, data_type)}'
|
||||
)
|
||||
return cls.merge_same_imports(list(import_list), 'from sqlalchemy import')
|
||||
|
||||
@classmethod
|
||||
def get_db_type(cls, column_type: str) -> str:
|
||||
"""
|
||||
获取数据库字段类型。
|
||||
|
||||
参数:
|
||||
- column_type (str): 字段类型字符串。
|
||||
|
||||
返回:
|
||||
- str: 数据库类型(去除长度等修饰)。
|
||||
"""
|
||||
if '(' in column_type:
|
||||
return column_type.split('(')[0]
|
||||
return column_type
|
||||
|
||||
@classmethod
|
||||
def merge_same_imports(cls, imports: List[str], import_start: str) -> List[str]:
|
||||
"""
|
||||
合并相同的导入语句。
|
||||
|
||||
参数:
|
||||
- imports (List[str]): 导入语句列表。
|
||||
- import_start (str): 导入语句的起始字符串。
|
||||
|
||||
返回:
|
||||
- List[str]: 合并后的导入语句列表。
|
||||
"""
|
||||
merged_imports = []
|
||||
_imports = []
|
||||
for import_stmt in imports:
|
||||
if import_stmt.startswith(import_start):
|
||||
imported_items = import_stmt.split('import')[1].strip()
|
||||
_imports.extend(imported_items.split(', '))
|
||||
else:
|
||||
merged_imports.append(import_stmt)
|
||||
|
||||
if _imports:
|
||||
merged_datetime_import = f'{import_start} {", ".join(_imports)}'
|
||||
merged_imports.append(merged_datetime_import)
|
||||
|
||||
return merged_imports
|
||||
|
||||
@classmethod
|
||||
def get_dicts(cls, gen_table: GenTableOutSchema):
|
||||
"""
|
||||
获取字典列表。
|
||||
|
||||
参数:
|
||||
- gen_table (GenTableOutSchema): 生成表的配置信息。
|
||||
|
||||
返回:
|
||||
- str: 以逗号分隔的字典类型字符串。
|
||||
"""
|
||||
columns = gen_table.columns or []
|
||||
dicts = set()
|
||||
cls.add_dicts(dicts, columns)
|
||||
# 处理sub_table为None的情况
|
||||
if gen_table.sub_table is not None:
|
||||
# 处理sub_table.columns为None的情况
|
||||
sub_columns = gen_table.sub_table.columns or []
|
||||
cls.add_dicts(dicts, sub_columns)
|
||||
return ', '.join(dicts)
|
||||
|
||||
@classmethod
|
||||
def add_dicts(cls, dicts: Set[str], columns: List[GenTableColumnOutSchema]):
|
||||
"""
|
||||
添加字典类型到集合。
|
||||
|
||||
参数:
|
||||
- dicts (Set[str]): 字典类型集合。
|
||||
- columns (List[GenTableColumnOutSchema]): 字段列表。
|
||||
|
||||
返回:
|
||||
- Set[str]: 更新后的字典类型集合。
|
||||
"""
|
||||
for column in columns:
|
||||
super_column = column.super_column if column.super_column is not None else '0'
|
||||
dict_type = column.dict_type or ''
|
||||
html_type = column.html_type or ''
|
||||
|
||||
if (
|
||||
not super_column
|
||||
and StringUtil.is_not_empty(dict_type)
|
||||
and StringUtil.equals_any_ignore_case(
|
||||
html_type, [GenConstant.HTML_SELECT, GenConstant.HTML_RADIO, GenConstant.HTML_CHECKBOX]
|
||||
)
|
||||
):
|
||||
dicts.add(f"'{dict_type}'")
|
||||
|
||||
@classmethod
|
||||
def get_permission_prefix(cls, module_name: str | None, business_name: str | None) -> str:
|
||||
"""
|
||||
获取权限前缀。
|
||||
|
||||
参数:
|
||||
- module_name (str | None): 模块名。
|
||||
- business_name (str | None): 业务名。
|
||||
|
||||
返回:
|
||||
- str: 权限前缀字符串。
|
||||
"""
|
||||
return f'{module_name}:{business_name}'
|
||||
|
||||
@classmethod
|
||||
def get_sqlalchemy_type(cls, column):
|
||||
"""
|
||||
获取 SQLAlchemy 类型。
|
||||
|
||||
参数:
|
||||
- column_type (Any): 列类型或包含 `column_type` 属性的对象。
|
||||
|
||||
返回:
|
||||
- str: SQLAlchemy 类型字符串。
|
||||
"""
|
||||
if '(' in column:
|
||||
column_type_list = column.split('(')
|
||||
if column_type_list[0] in GenConstant.COLUMNTYPE_STR:
|
||||
sqlalchemy_type = (
|
||||
StringUtil.get_mapping_value_by_key_ignore_case(
|
||||
GenConstant.DB_TO_SQLALCHEMY, column_type_list[0]
|
||||
)
|
||||
+ '('
|
||||
+ column_type_list[1]
|
||||
)
|
||||
else:
|
||||
sqlalchemy_type = StringUtil.get_mapping_value_by_key_ignore_case(
|
||||
GenConstant.DB_TO_SQLALCHEMY, column_type_list[0]
|
||||
)
|
||||
else:
|
||||
sqlalchemy_type = StringUtil.get_mapping_value_by_key_ignore_case(
|
||||
GenConstant.DB_TO_SQLALCHEMY, column
|
||||
)
|
||||
|
||||
return sqlalchemy_type
|
||||
+8
-8
@@ -8,7 +8,7 @@ from app.common.response import SuccessResponse
|
||||
from app.core.exceptions import CustomException
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.dependencies import AuthPermission, redis_getter
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from .service import CacheService
|
||||
|
||||
@@ -32,7 +32,7 @@ async def get_monitor_cache_info_controller(
|
||||
- JSONResponse: 包含缓存监控统计信息的JSON响应
|
||||
"""
|
||||
result = await CacheService.get_cache_monitor_statistical_info_service(redis=redis)
|
||||
logger.info('获取缓存监控信息成功')
|
||||
log.info('获取缓存监控信息成功')
|
||||
return SuccessResponse(data=result, msg='获取缓存监控信息成功')
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ async def get_monitor_cache_name_controller() -> JSONResponse:
|
||||
- JSONResponse: 包含缓存名称列表的JSON响应
|
||||
"""
|
||||
result = await CacheService.get_cache_monitor_cache_name_service()
|
||||
logger.info('获取缓存名称列表成功')
|
||||
log.info('获取缓存名称列表成功')
|
||||
return SuccessResponse(data=result, msg='获取缓存名称列表成功')
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ async def get_monitor_cache_key_controller(
|
||||
- JSONResponse: 包含缓存键名列表的JSON响应
|
||||
"""
|
||||
result = await CacheService.get_cache_monitor_cache_key_service(redis=redis, cache_name=cache_name)
|
||||
logger.info(f'获取缓存{cache_name}的键名列表成功')
|
||||
log.info(f'获取缓存{cache_name}的键名列表成功')
|
||||
return SuccessResponse(data=result, msg=f'获取缓存{cache_name}的键名列表成功')
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ async def get_monitor_cache_value_controller(
|
||||
- JSONResponse: 包含缓存值的JSON响应
|
||||
"""
|
||||
result = await CacheService.get_cache_monitor_cache_value_service(redis=redis, cache_name=cache_name, cache_key=cache_key)
|
||||
logger.info(f'获取缓存{cache_name}:{cache_key}的值成功')
|
||||
log.info(f'获取缓存{cache_name}:{cache_key}的值成功')
|
||||
return SuccessResponse(data=result, msg=f'获取缓存{cache_name}:{cache_key}的值成功')
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ async def clear_monitor_cache_name_controller(
|
||||
result = await CacheService.clear_cache_monitor_cache_name_service(redis=redis, cache_name=cache_name)
|
||||
if not result:
|
||||
raise CustomException(msg='清除缓存失败', data=result)
|
||||
logger.info(f'清除缓存{cache_name}成功')
|
||||
log.info(f'清除缓存{cache_name}成功')
|
||||
return SuccessResponse(msg=f'{cache_name}对应键值清除成功', data=result)
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ async def clear_monitor_cache_key_controller(
|
||||
result = await CacheService.clear_cache_monitor_cache_key_service(redis=redis, cache_key=cache_key)
|
||||
if not result:
|
||||
raise CustomException(msg='清除缓存失败', data=result)
|
||||
logger.info(f'清除缓存键{cache_key}成功')
|
||||
log.info(f'清除缓存键{cache_key}成功')
|
||||
return SuccessResponse(msg=f'{cache_key}清除成功', data=result)
|
||||
|
||||
|
||||
@@ -174,5 +174,5 @@ async def clear_monitor_cache_all_controller(
|
||||
result = await CacheService.clear_cache_monitor_all_service(redis=redis)
|
||||
if not result:
|
||||
raise CustomException(msg='清除缓存失败', data=result)
|
||||
logger.info('清除所有缓存成功')
|
||||
log.info('清除所有缓存成功')
|
||||
return SuccessResponse(msg='所有缓存清除成功', data=result)
|
||||
|
||||
@@ -9,7 +9,7 @@ from app.common.response import SuccessResponse,ErrorResponse
|
||||
from app.core.dependencies import AuthPermission, redis_getter
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from .param import OnlineQueryParam
|
||||
from .service import OnlineService
|
||||
@@ -42,7 +42,7 @@ async def get_online_list_controller(
|
||||
"""
|
||||
result_dict_list = await OnlineService.get_online_list_service(redis=redis, search=search)
|
||||
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= paging_query.page_no, page_size = paging_query.page_size)
|
||||
logger.info('获取成功')
|
||||
log.info('获取成功')
|
||||
|
||||
return SuccessResponse(data=result_dict,msg='获取成功')
|
||||
|
||||
@@ -69,10 +69,10 @@ async def delete_online_controller(
|
||||
"""
|
||||
is_ok = await OnlineService.delete_online_service(redis=redis, session_id=session_id)
|
||||
if is_ok:
|
||||
logger.info("强制下线成功")
|
||||
log.info("强制下线成功")
|
||||
return SuccessResponse(msg="强制下线成功")
|
||||
else:
|
||||
logger.info("强制下线失败")
|
||||
log.info("强制下线失败")
|
||||
return ErrorResponse(msg="强制下线失败")
|
||||
|
||||
@OnlineRouter.delete(
|
||||
@@ -95,8 +95,8 @@ async def clear_online_controller(
|
||||
"""
|
||||
is_ok = await OnlineService.clear_online_service(redis=redis)
|
||||
if is_ok:
|
||||
logger.info("清除所有在线用户成功")
|
||||
log.info("清除所有在线用户成功")
|
||||
return SuccessResponse(msg="清除所有在线用户成功")
|
||||
else:
|
||||
logger.info("清除所有在线用户失败")
|
||||
log.info("清除所有在线用户失败")
|
||||
return ErrorResponse(msg="清除所有在线用户失败")
|
||||
@@ -7,7 +7,7 @@ from redis.asyncio.client import Redis
|
||||
from app.common.enums import RedisInitKeyConfig
|
||||
from app.core.redis_crud import RedisCURD
|
||||
from app.core.security import decode_access_token
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
from .param import OnlineQueryParam
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ class OnlineService:
|
||||
if cls._match_search_conditions(session_info, search):
|
||||
online_users.append(session_info)
|
||||
except Exception as e:
|
||||
logger.error(f"解析在线用户数据失败: {e}")
|
||||
log.error(f"解析在线用户数据失败: {e}")
|
||||
continue
|
||||
# 按照 login_time 倒序排序
|
||||
online_users.sort(key=lambda x: x.get('login_time', ''), reverse=True)
|
||||
@@ -65,7 +65,7 @@ class OnlineService:
|
||||
await RedisCURD(redis).delete(f"{RedisInitKeyConfig.REFRESH_TOKEN.key}:{session_id}")
|
||||
|
||||
|
||||
logger.info(f"强制下线用户会话: {session_id}")
|
||||
log.info(f"强制下线用户会话: {session_id}")
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
@@ -83,7 +83,7 @@ class OnlineService:
|
||||
await RedisCURD(redis).clear(f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:*")
|
||||
await RedisCURD(redis).clear(f"{RedisInitKeyConfig.REFRESH_TOKEN.key}:*")
|
||||
|
||||
logger.info(f"清除所有在线用户会话成功")
|
||||
log.info(f"清除所有在线用户会话成功")
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from app.utils.common_util import bytes2file_response
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from .param import ResourceSearchQueryParam
|
||||
from .service import ResourceService
|
||||
@@ -58,7 +58,7 @@ async def get_directory_list_controller(
|
||||
page_size=page.page_size
|
||||
)
|
||||
|
||||
logger.info(f"获取目录列表成功: {getattr(search, 'name', None) or ''}")
|
||||
log.info(f"获取目录列表成功: {getattr(search, 'name', None) or ''}")
|
||||
return SuccessResponse(data=result_dict, msg="获取目录列表成功")
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ async def upload_file_controller(
|
||||
target_path=target_path,
|
||||
base_url=str(request.base_url)
|
||||
)
|
||||
logger.info(f"上传文件成功: {result_dict['filename']}")
|
||||
log.info(f"上传文件成功: {result_dict['filename']}")
|
||||
return SuccessResponse(data=result_dict, msg="上传文件成功")
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ async def download_file_controller(
|
||||
import os
|
||||
filename = os.path.basename(file_path)
|
||||
|
||||
logger.info(f"下载文件成功: {filename}")
|
||||
log.info(f"下载文件成功: {filename}")
|
||||
return FileResponse(
|
||||
path=file_path,
|
||||
filename=filename,
|
||||
@@ -148,7 +148,7 @@ async def delete_files_controller(
|
||||
- JSONResponse: 包含删除结果的JSON响应。
|
||||
"""
|
||||
await ResourceService.delete_file_service(paths=paths)
|
||||
logger.info(f"删除文件成功: {paths}")
|
||||
log.info(f"删除文件成功: {paths}")
|
||||
return SuccessResponse(msg="删除文件成功")
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ async def move_file_controller(
|
||||
- JSONResponse: 包含移动结果的JSON响应。
|
||||
"""
|
||||
await ResourceService.move_file_service(data=data)
|
||||
logger.info(f"移动文件成功: {data.source_path} -> {data.target_path}")
|
||||
log.info(f"移动文件成功: {data.source_path} -> {data.target_path}")
|
||||
return SuccessResponse(msg="移动文件成功")
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ async def copy_file_controller(
|
||||
- JSONResponse: 包含复制结果的JSON响应。
|
||||
"""
|
||||
await ResourceService.copy_file_service(data=data)
|
||||
logger.info(f"复制文件成功: {data.source_path} -> {data.target_path}")
|
||||
log.info(f"复制文件成功: {data.source_path} -> {data.target_path}")
|
||||
return SuccessResponse(msg="复制文件成功")
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ async def rename_file_controller(
|
||||
- JSONResponse: 包含重命名结果的JSON响应。
|
||||
"""
|
||||
await ResourceService.rename_file_service(data=data)
|
||||
logger.info(f"重命名文件成功: {data.old_path} -> {data.new_name}")
|
||||
log.info(f"重命名文件成功: {data.old_path} -> {data.new_name}")
|
||||
return SuccessResponse(msg="重命名文件成功")
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ async def create_directory_controller(
|
||||
- JSONResponse: 包含创建目录结果的JSON响应。
|
||||
"""
|
||||
await ResourceService.create_directory_service(data=data)
|
||||
logger.info(f"创建目录成功: {data.parent_path}/{data.dir_name}")
|
||||
log.info(f"创建目录成功: {data.parent_path}/{data.dir_name}")
|
||||
return SuccessResponse(msg="创建目录成功")
|
||||
|
||||
|
||||
@@ -271,7 +271,7 @@ async def export_resource_list_controller(
|
||||
)
|
||||
export_result = await ResourceService.export_resource_service(data_list=result_dict_list)
|
||||
|
||||
logger.info("导出资源列表成功")
|
||||
log.info("导出资源列表成功")
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(export_result),
|
||||
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
|
||||
@@ -9,7 +9,7 @@ from urllib.parse import urlparse
|
||||
from fastapi import UploadFile
|
||||
|
||||
from app.core.exceptions import CustomException
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
from app.utils.excel_util import ExcelUtil
|
||||
from app.config.setting import settings
|
||||
|
||||
@@ -216,7 +216,7 @@ class ResourceService:
|
||||
'is_hidden': is_hidden
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f'获取文件信息失败: {str(e)}')
|
||||
log.error(f'获取文件信息失败: {str(e)}')
|
||||
return {}
|
||||
|
||||
@classmethod
|
||||
@@ -285,7 +285,7 @@ class ResourceService:
|
||||
except CustomException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f'获取目录列表失败: {str(e)}')
|
||||
log.error(f'获取目录列表失败: {str(e)}')
|
||||
raise CustomException(msg=f'获取目录列表失败: {str(e)}')
|
||||
|
||||
@classmethod
|
||||
@@ -349,7 +349,7 @@ class ResourceService:
|
||||
return sorted_resources
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f'搜索资源失败: {str(e)}')
|
||||
log.error(f'搜索资源失败: {str(e)}')
|
||||
raise CustomException(msg=f'搜索资源失败: {str(e)}')
|
||||
|
||||
@classmethod
|
||||
@@ -528,7 +528,7 @@ class ResourceService:
|
||||
# 生成文件URL
|
||||
file_url = cls._generate_http_url(file_path, base_url)
|
||||
|
||||
logger.info(f"文件上传成功: {filename}")
|
||||
log.info(f"文件上传成功: {filename}")
|
||||
|
||||
return ResourceUploadSchema(
|
||||
filename=filename,
|
||||
@@ -538,7 +538,7 @@ class ResourceService:
|
||||
).model_dump(mode='json')
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"文件上传失败: {str(e)}")
|
||||
log.error(f"文件上传失败: {str(e)}")
|
||||
raise CustomException(msg=f"文件上传失败: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
@@ -563,13 +563,13 @@ class ResourceService:
|
||||
raise CustomException(msg='路径不是文件')
|
||||
|
||||
# 返回本地文件路径给 FileResponse 使用
|
||||
logger.info(f"定位文件路径: {safe_path}")
|
||||
log.info(f"定位文件路径: {safe_path}")
|
||||
return safe_path
|
||||
|
||||
except CustomException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"下载文件失败: {str(e)}")
|
||||
log.error(f"下载文件失败: {str(e)}")
|
||||
raise CustomException(msg=f"下载文件失败: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
@@ -591,18 +591,18 @@ class ResourceService:
|
||||
safe_path = cls._get_safe_path(path)
|
||||
|
||||
if not os.path.exists(safe_path):
|
||||
logger.warning(f"路径不存在,跳过: {path}")
|
||||
log.error(f"路径不存在,跳过: {path}")
|
||||
continue
|
||||
|
||||
if os.path.isfile(safe_path):
|
||||
os.remove(safe_path)
|
||||
logger.info(f"删除文件成功: {safe_path}")
|
||||
log.info(f"删除文件成功: {safe_path}")
|
||||
elif os.path.isdir(safe_path):
|
||||
shutil.rmtree(safe_path)
|
||||
logger.info(f"删除目录成功: {safe_path}")
|
||||
log.info(f"删除目录成功: {safe_path}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"删除失败 {path}: {str(e)}")
|
||||
log.error(f"删除失败 {path}: {str(e)}")
|
||||
raise CustomException(msg=f"删除失败 {path}: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
@@ -633,14 +633,14 @@ class ResourceService:
|
||||
if os.path.isfile(safe_path):
|
||||
os.remove(safe_path)
|
||||
success_paths.append(path)
|
||||
logger.info(f"删除文件成功: {safe_path}")
|
||||
log.info(f"删除文件成功: {safe_path}")
|
||||
elif os.path.isdir(safe_path):
|
||||
shutil.rmtree(safe_path)
|
||||
success_paths.append(path)
|
||||
logger.info(f"删除目录成功: {safe_path}")
|
||||
log.info(f"删除目录成功: {safe_path}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"删除失败 {path}: {str(e)}")
|
||||
log.error(f"删除失败 {path}: {str(e)}")
|
||||
failed_paths.append(path)
|
||||
|
||||
return {
|
||||
@@ -683,12 +683,12 @@ class ResourceService:
|
||||
|
||||
# 移动文件
|
||||
shutil.move(source_path, target_path)
|
||||
logger.info(f"移动成功: {source_path} -> {target_path}")
|
||||
log.info(f"移动成功: {source_path} -> {target_path}")
|
||||
|
||||
except CustomException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"移动失败: {str(e)}")
|
||||
log.error(f"移动失败: {str(e)}")
|
||||
raise CustomException(msg=f"移动失败: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
@@ -723,12 +723,12 @@ class ResourceService:
|
||||
else:
|
||||
shutil.copytree(source_path, target_path, dirs_exist_ok=data.overwrite)
|
||||
|
||||
logger.info(f"复制成功: {source_path} -> {target_path}")
|
||||
log.info(f"复制成功: {source_path} -> {target_path}")
|
||||
|
||||
except CustomException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"复制失败: {str(e)}")
|
||||
log.error(f"复制失败: {str(e)}")
|
||||
raise CustomException(msg=f"复制失败: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
@@ -757,12 +757,12 @@ class ResourceService:
|
||||
|
||||
# 重命名
|
||||
os.rename(old_path, new_path)
|
||||
logger.info(f"重命名成功: {old_path} -> {new_path}")
|
||||
log.info(f"重命名成功: {old_path} -> {new_path}")
|
||||
|
||||
except CustomException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"重命名失败: {str(e)}")
|
||||
log.error(f"重命名失败: {str(e)}")
|
||||
raise CustomException(msg=f"重命名失败: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
@@ -797,12 +797,12 @@ class ResourceService:
|
||||
|
||||
# 创建目录
|
||||
os.makedirs(new_dir_path)
|
||||
logger.info(f"创建目录成功: {new_dir_path}")
|
||||
log.info(f"创建目录成功: {new_dir_path}")
|
||||
|
||||
except CustomException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"创建目录失败: {str(e)}")
|
||||
log.error(f"创建目录失败: {str(e)}")
|
||||
raise CustomException(msg=f"创建目录失败: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -7,7 +7,7 @@ from fastapi.responses import JSONResponse
|
||||
from app.common.response import SuccessResponse
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from .service import ServerService
|
||||
|
||||
@@ -28,6 +28,6 @@ async def get_monitor_server_info_controller() -> JSONResponse:
|
||||
- JSONResponse: 包含服务器监控信息的JSON响应。
|
||||
"""
|
||||
result_dict = await ServerService.get_server_monitor_info_service()
|
||||
logger.info(f'获取服务器监控信息成功: {result_dict}')
|
||||
log.info(f'获取服务器监控信息成功: {result_dict}')
|
||||
|
||||
return SuccessResponse(data=result_dict, msg='获取服务器监控信息成功')
|
||||
|
||||
@@ -10,7 +10,7 @@ from redis.asyncio.client import Redis
|
||||
from app.common.response import ErrorResponse, SuccessResponse
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.security import CustomOAuth2PasswordRequestForm
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
from app.config.setting import settings
|
||||
from app.core.dependencies import (
|
||||
db_getter,
|
||||
@@ -56,7 +56,7 @@ async def login_for_access_token_controller(
|
||||
"""
|
||||
login_token = await LoginService.authenticate_user_service(request=request, redis=redis, login_form=login_form, db=db)
|
||||
|
||||
logger.info(f"用户{login_form.username}登录成功")
|
||||
log.info(f"用户{login_form.username}登录成功")
|
||||
|
||||
# 如果是文档请求,则不记录日志:http://localhost:8000/api/v1/docs
|
||||
if settings.DOCS_URL in request.headers.get("referer", ""):
|
||||
@@ -87,7 +87,7 @@ async def get_new_token_controller(
|
||||
# 解析当前的访问Token以获取用户名
|
||||
new_token = await LoginService.refresh_token_service(db=db, request=request, redis=redis, refresh_token=payload)
|
||||
token_dict = new_token.model_dump()
|
||||
logger.info(f"刷新token成功: {token_dict}")
|
||||
log.info(f"刷新token成功: {token_dict}")
|
||||
return SuccessResponse(data=token_dict, msg="刷新成功")
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ async def get_captcha_for_login_controller(
|
||||
"""
|
||||
# 获取验证码
|
||||
captcha = await CaptchaService.get_captcha_service(redis=redis)
|
||||
logger.info(f"获取验证码成功")
|
||||
log.info(f"获取验证码成功")
|
||||
return SuccessResponse(data=captcha, msg="获取验证码成功")
|
||||
|
||||
|
||||
@@ -132,6 +132,6 @@ async def logout_controller(
|
||||
- CustomException: 退出登录失败时抛出异常。
|
||||
"""
|
||||
if await LoginService.logout_service(redis=redis, token=payload):
|
||||
logger.info('退出成功')
|
||||
log.info('退出成功')
|
||||
return SuccessResponse(msg='退出成功')
|
||||
return ErrorResponse(msg='退出失败')
|
||||
|
||||
@@ -16,7 +16,7 @@ from app.utils.ip_local_util import IpLocalUtil
|
||||
from app.utils.hash_bcrpy_util import PwdUtil
|
||||
from app.core.redis_crud import RedisCURD
|
||||
from app.core.exceptions import CustomException
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
from app.config.setting import settings
|
||||
from app.core.security import (
|
||||
CustomOAuth2PasswordRequestForm,
|
||||
@@ -280,7 +280,7 @@ class LoginService:
|
||||
await RedisCURD(redis).delete(f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:{session_id}")
|
||||
await RedisCURD(redis).delete(f"{RedisInitKeyConfig.REFRESH_TOKEN.key}:{session_id}")
|
||||
|
||||
logger.info(f"用户退出登录成功,会话编号:{session_id}")
|
||||
log.info(f"用户退出登录成功,会话编号:{session_id}")
|
||||
|
||||
return True
|
||||
|
||||
@@ -317,7 +317,7 @@ class CaptchaService:
|
||||
expire=settings.CAPTCHA_EXPIRE_SECONDS
|
||||
)
|
||||
|
||||
logger.info(f"生成验证码成功,验证码:{captcha_value}")
|
||||
log.info(f"生成验证码成功,验证码:{captcha_value}")
|
||||
|
||||
# 返回验证码信息
|
||||
return CaptchaOutSchema(
|
||||
@@ -350,15 +350,15 @@ class CaptchaService:
|
||||
|
||||
captcha_value = await RedisCURD(redis).get(redis_key)
|
||||
if not captcha_value:
|
||||
logger.warning('验证码已过期或不存在')
|
||||
log.error('验证码已过期或不存在')
|
||||
raise CustomException(msg="验证码已过期")
|
||||
|
||||
# 验证码不区分大小写比对
|
||||
if captcha.lower() != captcha_value.lower():
|
||||
logger.warning(f'验证码错误,用户输入:{captcha},正确值:{captcha_value}')
|
||||
log.error(f'验证码错误,用户输入:{captcha},正确值:{captcha_value}')
|
||||
raise CustomException(msg="验证码错误")
|
||||
|
||||
# 验证成功后删除验证码,避免重复使用
|
||||
await RedisCURD(redis).delete(redis_key)
|
||||
logger.info(f'验证码校验成功,key:{key}')
|
||||
log.info(f'验证码校验成功,key:{key}')
|
||||
return True
|
||||
|
||||
@@ -7,7 +7,7 @@ from app.common.response import SuccessResponse
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from ..auth.schema import AuthSchema
|
||||
from .param import DeptQueryParam
|
||||
@@ -41,7 +41,7 @@ async def get_dept_tree_controller(
|
||||
"""
|
||||
order_by = [{"order": "asc"}]
|
||||
result_dict_list = await DeptService.get_dept_tree_service(search=search, auth=auth, order_by=order_by)
|
||||
logger.info(f"查询部门树成功")
|
||||
log.info(f"查询部门树成功")
|
||||
return SuccessResponse(data=result_dict_list, msg="查询部门树成功")
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ async def get_obj_detail_controller(
|
||||
- CustomException: 查询部门详情失败时抛出异常。
|
||||
"""
|
||||
result_dict = await DeptService.get_dept_detail_service(id=id, auth=auth)
|
||||
logger.info(f"查询部门详情成功 {id}")
|
||||
log.info(f"查询部门详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="查询部门详情成功")
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ async def create_obj_controller(
|
||||
- CustomException: 创建部门失败时抛出异常。
|
||||
"""
|
||||
result_dict = await DeptService.create_dept_service(data=data, auth=auth)
|
||||
logger.info(f"创建部门成功: {result_dict}")
|
||||
log.info(f"创建部门成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建部门成功")
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ async def update_obj_controller(
|
||||
- CustomException: 修改部门失败时抛出异常。
|
||||
"""
|
||||
result_dict = await DeptService.update_dept_service(auth=auth, id=id, data=data)
|
||||
logger.info(f"修改部门成功: {result_dict}")
|
||||
log.info(f"修改部门成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改部门成功")
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ async def delete_obj_controller(
|
||||
- CustomException: 删除部门失败时抛出异常。
|
||||
"""
|
||||
await DeptService.delete_dept_service(ids=ids, auth=auth)
|
||||
logger.info(f"删除部门成功: {ids}")
|
||||
log.info(f"删除部门成功: {ids}")
|
||||
return SuccessResponse(msg="删除部门成功")
|
||||
|
||||
|
||||
@@ -158,5 +158,5 @@ async def batch_set_available_obj_controller(
|
||||
- CustomException: 批量修改部门状态失败时抛出异常。
|
||||
"""
|
||||
await DeptService.batch_set_available_service(data=data, auth=auth)
|
||||
logger.info(f"批量修改部门状态成功: {data.ids}")
|
||||
log.info(f"批量修改部门状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改部门状态成功")
|
||||
|
||||
@@ -10,7 +10,7 @@ from app.core.base_params import PaginationQueryParam
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.dependencies import AuthPermission, redis_getter
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
from app.common.request import PaginationService
|
||||
from app.utils.common_util import bytes2file_response
|
||||
|
||||
@@ -46,7 +46,7 @@ async def get_type_detail_controller(
|
||||
- CustomException: 获取字典类型详情失败时抛出异常。
|
||||
"""
|
||||
result_dict = await DictTypeService.get_obj_detail_service(id=id, auth=auth)
|
||||
logger.info(f"获取字典类型详情成功 {id}")
|
||||
log.info(f"获取字典类型详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取字典类型详情成功")
|
||||
|
||||
@DictRouter.get("/type/list", summary="查询字典类型", description="查询字典类型")
|
||||
@@ -71,7 +71,7 @@ async def get_type_list_controller(
|
||||
"""
|
||||
result_dict_list = await DictTypeService.get_obj_list_service(auth=auth, search=search, order_by=page.order_by)
|
||||
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
|
||||
logger.info(f"查询字典类型列表成功")
|
||||
log.info(f"查询字典类型列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询字典类型列表成功")
|
||||
|
||||
@DictRouter.get("/type/optionselect", summary="获取全部字典类型", description="获取全部字典类型")
|
||||
@@ -91,7 +91,7 @@ async def get_type_loptionselect_controller(
|
||||
- CustomException: 获取字典类型列表失败时抛出异常。
|
||||
"""
|
||||
result_dict_list = await DictTypeService.get_obj_list_service(auth=auth)
|
||||
logger.info(f"获取字典类型列表成功")
|
||||
log.info(f"获取字典类型列表成功")
|
||||
return SuccessResponse(data=result_dict_list, msg="获取字典类型列表成功")
|
||||
|
||||
@DictRouter.post("/type/create", summary="创建字典类型", description="创建字典类型")
|
||||
@@ -115,7 +115,7 @@ async def create_type_controller(
|
||||
- CustomException: 创建字典类型失败时抛出异常。
|
||||
"""
|
||||
result_dict = await DictTypeService.create_obj_service(auth=auth, redis=redis, data=data)
|
||||
logger.info(f"创建字典类型成功: {result_dict}")
|
||||
log.info(f"创建字典类型成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建字典类型成功")
|
||||
|
||||
@DictRouter.put("/type/update/{id}", summary="修改字典类型", description="修改字典类型")
|
||||
@@ -141,7 +141,7 @@ async def update_type_controller(
|
||||
- CustomException: 修改字典类型失败时抛出异常。
|
||||
"""
|
||||
result_dict = await DictTypeService.update_obj_service(auth=auth, redis=redis, id=id, data=data)
|
||||
logger.info(f"修改字典类型成功: {result_dict}")
|
||||
log.info(f"修改字典类型成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改字典类型成功")
|
||||
|
||||
@DictRouter.delete("/type/delete", summary="删除字典类型", description="删除字典类型")
|
||||
@@ -165,7 +165,7 @@ async def delete_type_controller(
|
||||
- CustomException: 删除字典类型失败时抛出异常。
|
||||
"""
|
||||
await DictTypeService.delete_obj_service(auth=auth, redis=redis, ids=ids)
|
||||
logger.info(f"删除字典类型成功: {ids}")
|
||||
log.info(f"删除字典类型成功: {ids}")
|
||||
return SuccessResponse(msg="删除字典类型成功")
|
||||
|
||||
@DictRouter.patch("/type/available/setting", summary="批量修改字典类型状态", description="批量修改字典类型状态")
|
||||
@@ -187,7 +187,7 @@ async def batch_set_available_dict_type_controller(
|
||||
- CustomException: 批量修改字典类型状态失败时抛出异常。
|
||||
"""
|
||||
await DictTypeService.set_obj_available_service(auth=auth, data=data)
|
||||
logger.info(f"批量修改字典类型状态成功: {data.ids}")
|
||||
log.info(f"批量修改字典类型状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改字典类型状态成功")
|
||||
|
||||
@DictRouter.post('/type/export', summary="导出字典类型", description="导出字典类型")
|
||||
@@ -211,7 +211,7 @@ async def export_type_list_controller(
|
||||
# 获取全量数据
|
||||
result_dict_list = await DictTypeService.get_obj_list_service(search=search, auth=auth)
|
||||
export_result = await DictTypeService.export_obj_service(data_list=result_dict_list)
|
||||
logger.info('导出字典类型成功')
|
||||
log.info('导出字典类型成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(export_result),
|
||||
@@ -240,7 +240,7 @@ async def get_data_detail_controller(
|
||||
- CustomException: 获取字典数据详情失败时抛出异常。
|
||||
"""
|
||||
result_dict = await DictDataService.get_obj_detail_service(id=id, auth=auth)
|
||||
logger.info(f"获取字典数据详情成功 {id}")
|
||||
log.info(f"获取字典数据详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取字典数据详情成功")
|
||||
|
||||
@DictRouter.get("/data/list", summary="查询字典数据", description="查询字典数据")
|
||||
@@ -268,7 +268,7 @@ async def get_data_list_controller(
|
||||
order_by = page.order_by
|
||||
result_dict_list = await DictDataService.get_obj_list_service(auth=auth, search=search, order_by=order_by)
|
||||
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
|
||||
logger.info(f"查询字典数据列表成功")
|
||||
log.info(f"查询字典数据列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询字典数据列表成功")
|
||||
|
||||
@DictRouter.post("/data/create", summary="创建字典数据", description="创建字典数据")
|
||||
@@ -292,7 +292,7 @@ async def create_data_controller(
|
||||
- CustomException: 创建字典数据失败时抛出异常。
|
||||
"""
|
||||
result_dict = await DictDataService.create_obj_service(auth=auth, redis=redis, data=data)
|
||||
logger.info(f"创建字典数据成功: {result_dict}")
|
||||
log.info(f"创建字典数据成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建字典数据成功")
|
||||
|
||||
@DictRouter.put("/data/update/{id}", summary="修改字典数据", description="修改字典数据")
|
||||
@@ -318,7 +318,7 @@ async def update_data_controller(
|
||||
- CustomException: 修改字典数据失败时抛出异常。
|
||||
"""
|
||||
result_dict = await DictDataService.update_obj_service(auth=auth, redis=redis, id=id, data=data)
|
||||
logger.info(f"修改字典数据成功: {result_dict}")
|
||||
log.info(f"修改字典数据成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改字典数据成功")
|
||||
|
||||
@DictRouter.delete("/data/delete", summary="删除字典数据", description="删除字典数据")
|
||||
@@ -342,7 +342,7 @@ async def delete_data_controller(
|
||||
- CustomException: 删除字典数据失败时抛出异常。
|
||||
"""
|
||||
await DictDataService.delete_obj_service(auth=auth, redis=redis, ids=ids)
|
||||
logger.info(f"删除字典数据成功: {ids}")
|
||||
log.info(f"删除字典数据成功: {ids}")
|
||||
return SuccessResponse(msg="删除字典数据成功")
|
||||
|
||||
@DictRouter.patch("/data/available/setting", summary="批量修改字典数据状态", description="批量修改字典数据状态")
|
||||
@@ -364,7 +364,7 @@ async def batch_set_available_dict_data_controller(
|
||||
- CustomException: 批量修改字典数据状态失败时抛出异常。
|
||||
"""
|
||||
await DictDataService.set_obj_available_service(auth=auth, data=data)
|
||||
logger.info(f"批量修改字典数据状态成功: {data.ids}")
|
||||
log.info(f"批量修改字典数据状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改字典数据状态成功")
|
||||
|
||||
@DictRouter.post('/data/export', summary="导出字典数据", description="导出字典数据")
|
||||
@@ -389,7 +389,7 @@ async def export_data_list_controller(
|
||||
"""
|
||||
result_dict_list = await DictDataService.get_obj_list_service(auth=auth, search=search, order_by=page.order_by)
|
||||
export_result = await DictDataService.export_obj_service(data_list=result_dict_list)
|
||||
logger.info('导出字典数据成功')
|
||||
log.info('导出字典数据成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(export_result),
|
||||
@@ -420,7 +420,7 @@ async def get_init_dict_data_controller(
|
||||
dict_data_query_result = await DictDataService.get_init_dict_service(
|
||||
redis=redis, dict_type=dict_type
|
||||
)
|
||||
logger.info(f"获取初始化字典数据成功:{dict_data_query_result}")
|
||||
log.info(f"获取初始化字典数据成功:{dict_data_query_result}")
|
||||
|
||||
# 确保数据是字符串类型再进行 JSON 解析
|
||||
if isinstance(dict_data_query_result, bytes):
|
||||
|
||||
@@ -10,7 +10,7 @@ from app.core.database import async_db_session
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.redis_crud import RedisCURD
|
||||
from app.core.exceptions import CustomException
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .schema import DictDataCreateSchema,DictDataOutSchema,DictDataUpdateSchema,DictTypeCreateSchema,DictTypeOutSchema,DictTypeUpdateSchema
|
||||
@@ -81,9 +81,9 @@ class DictTypeService:
|
||||
key=redis_key,
|
||||
value="",
|
||||
)
|
||||
logger.info(f"创建字典类型成功: {new_obj_dict}")
|
||||
log.info(f"创建字典类型成功: {new_obj_dict}")
|
||||
except Exception as e:
|
||||
logger.error(f"创建字典类型失败: {e}")
|
||||
log.error(f"创建字典类型失败: {e}")
|
||||
raise CustomException(msg=f"创建字典类型失败 {e}")
|
||||
|
||||
return new_obj_dict
|
||||
@@ -146,9 +146,9 @@ class DictTypeService:
|
||||
key=redis_key,
|
||||
value=value,
|
||||
)
|
||||
logger.info(f"更新字典类型成功并刷新缓存: {new_obj_dict}")
|
||||
log.info(f"更新字典类型成功并刷新缓存: {new_obj_dict}")
|
||||
except Exception as e:
|
||||
logger.error(f"更新字典类型缓存失败: {e}")
|
||||
log.error(f"更新字典类型缓存失败: {e}")
|
||||
raise CustomException(msg=f"更新字典类型缓存失败 {e}")
|
||||
|
||||
return new_obj_dict
|
||||
@@ -181,9 +181,9 @@ class DictTypeService:
|
||||
redis_key = f"{RedisInitKeyConfig.SYSTEM_DICT.key}:{exist_obj.dict_type}"
|
||||
try:
|
||||
await RedisCURD(redis).delete(redis_key)
|
||||
logger.info(f"删除字典类型成功: {id}")
|
||||
log.info(f"删除字典类型成功: {id}")
|
||||
except Exception as e:
|
||||
logger.error(f"删除字典类型失败: {e}")
|
||||
log.error(f"删除字典类型失败: {e}")
|
||||
raise CustomException(msg=f"删除字典类型失败")
|
||||
await DictTypeCRUD(auth).delete_obj_crud(ids=ids)
|
||||
|
||||
@@ -286,14 +286,14 @@ class DictDataService:
|
||||
auth = AuthSchema(db=session)
|
||||
obj_list = await DictTypeCRUD(auth).get_obj_list_crud()
|
||||
if not obj_list:
|
||||
logger.warning("❗️ 未找到任何字典类型数据")
|
||||
log.error("❗️ 未找到任何字典类型数据")
|
||||
return
|
||||
for obj in obj_list:
|
||||
dict_type = obj.dict_type
|
||||
dict_data_list = await DictDataCRUD(auth).get_obj_list_crud(search={'dict_type': dict_type})
|
||||
|
||||
if not dict_data_list:
|
||||
logger.warning(f"❗️ 字典类型 {dict_type} 未找到对应的字典数据")
|
||||
log.error(f"❗️ 字典类型 {dict_type} 未找到对应的字典数据")
|
||||
continue
|
||||
|
||||
dict_data = [DictDataOutSchema.model_validate(row).model_dump() for row in dict_data_list if row]
|
||||
@@ -307,7 +307,7 @@ class DictDataService:
|
||||
value=value,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"❌️ 初始化字典数据失败: {e}")
|
||||
log.error(f"❌️ 初始化字典数据失败: {e}")
|
||||
raise CustomException(msg=f"初始化字典数据失败 {e}")
|
||||
|
||||
@classmethod
|
||||
@@ -357,9 +357,9 @@ class DictDataService:
|
||||
key=redis_key,
|
||||
value=value,
|
||||
)
|
||||
logger.info(f"创建字典数据写入缓存成功: {obj}")
|
||||
log.info(f"创建字典数据写入缓存成功: {obj}")
|
||||
except Exception as e:
|
||||
logger.error(f"创建字典数据写入缓存失败: {e}")
|
||||
log.error(f"创建字典数据写入缓存失败: {e}")
|
||||
raise CustomException(msg=f"创建字典数据失败 {e}")
|
||||
|
||||
return DictDataOutSchema.model_validate(obj).model_dump()
|
||||
@@ -399,7 +399,7 @@ class DictDataService:
|
||||
value=value,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"更新字典数据类型变更时刷新旧缓存失败: {e}")
|
||||
log.error(f"更新字典数据类型变更时刷新旧缓存失败: {e}")
|
||||
|
||||
obj = await DictDataCRUD(auth).update_obj_crud(id=id, data=data)
|
||||
redis_key = f"{RedisInitKeyConfig.SYSTEM_DICT.key}:{data.dict_type}"
|
||||
@@ -413,9 +413,9 @@ class DictDataService:
|
||||
key=redis_key,
|
||||
value=value,
|
||||
)
|
||||
logger.info(f"更新字典数据写入缓存成功: {obj}")
|
||||
log.info(f"更新字典数据写入缓存成功: {obj}")
|
||||
except Exception as e:
|
||||
logger.error(f"更新字典数据写入缓存失败: {e}")
|
||||
log.error(f"更新字典数据写入缓存失败: {e}")
|
||||
raise CustomException(msg=f"更新字典数据失败 {e}")
|
||||
|
||||
return DictDataOutSchema.model_validate(obj).model_dump()
|
||||
@@ -449,9 +449,9 @@ class DictDataService:
|
||||
try:
|
||||
# 删除Redis缓存
|
||||
await RedisCURD(redis).delete(redis_key)
|
||||
logger.info(f"删除字典数据成功: {id}")
|
||||
log.info(f"删除字典数据成功: {id}")
|
||||
except Exception as e:
|
||||
logger.error(f"删除字典数据失败: {e}")
|
||||
log.error(f"删除字典数据失败: {e}")
|
||||
raise CustomException(msg=f"删除字典数据失败 {e}")
|
||||
await DictDataCRUD(auth).delete_obj_crud(ids=ids)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from app.utils.common_util import bytes2file_response
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from ..auth.schema import AuthSchema
|
||||
from .param import OperationLogQueryParam
|
||||
@@ -41,7 +41,7 @@ async def get_obj_list_controller(
|
||||
order_by = page.order_by
|
||||
result_dict_list = await OperationLogService.get_log_list_service(search=search, auth=auth, order_by=order_by)
|
||||
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
|
||||
logger.info(f"查询日志成功")
|
||||
log.info(f"查询日志成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询日志成功")
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ async def get_obj_detail_controller(
|
||||
- JSONResponse: 包含日志详情的 JSON 响应模型
|
||||
"""
|
||||
result_dict = await OperationLogService.get_log_detail_service(id=id, auth=auth)
|
||||
logger.info(f"查询日志成功 {id}")
|
||||
log.info(f"查询日志成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取日志详情成功")
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ async def delete_obj_log_controller(
|
||||
- JSONResponse: 包含删除结果的 JSON 响应模型
|
||||
"""
|
||||
await OperationLogService.delete_log_service(ids=ids, auth=auth)
|
||||
logger.info(f"删除日志成功 {ids}")
|
||||
log.info(f"删除日志成功 {ids}")
|
||||
return SuccessResponse(msg="删除日志成功")
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ async def export_obj_list_controller(
|
||||
"""
|
||||
operation_log_list = await OperationLogService.get_log_list_service(search=search, auth=auth)
|
||||
operation_log_export_result = await OperationLogService.export_log_list_service(operation_log_list=operation_log_list)
|
||||
logger.info('导出日志成功')
|
||||
log.info('导出日志成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(operation_log_export_result),
|
||||
|
||||
@@ -7,7 +7,7 @@ from app.common.response import SuccessResponse
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from ..auth.schema import AuthSchema
|
||||
from .param import MenuQueryParam
|
||||
@@ -36,7 +36,7 @@ async def get_menu_tree_controller(
|
||||
"""
|
||||
order_by = [{"order": "asc"}]
|
||||
result_dict_list = await MenuService.get_menu_tree_service(search=search, auth=auth, order_by=order_by)
|
||||
logger.info(f"查询菜单树成功")
|
||||
log.info(f"查询菜单树成功")
|
||||
return SuccessResponse(data=result_dict_list, msg="查询菜单树成功")
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ async def get_obj_detail_controller(
|
||||
- JSONResponse: 包含菜单详情的 JSON 响应。
|
||||
"""
|
||||
result_dict = await MenuService.get_menu_detail_service(id=id, auth=auth)
|
||||
logger.info(f"查询菜单情成功 {id}")
|
||||
log.info(f"查询菜单情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取菜单成功")
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ async def create_obj_controller(
|
||||
- JSONResponse: 包含创建菜单的 JSON 响应。
|
||||
"""
|
||||
result_dict = await MenuService.create_menu_service(data=data, auth=auth)
|
||||
logger.info(f"创建菜单成功: {result_dict}")
|
||||
log.info(f"创建菜单成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建菜单成功")
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ async def update_obj_controller(
|
||||
- JSONResponse: 包含修改菜单的 JSON 响应。
|
||||
"""
|
||||
result_dict = await MenuService.update_menu_service(id=id, data=data, auth=auth)
|
||||
logger.info(f"修改菜单成功: {result_dict}")
|
||||
log.info(f"修改菜单成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改菜单成功")
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ async def delete_obj_controller(
|
||||
- JSONResponse: 包含删除菜单的 JSON 响应。
|
||||
"""
|
||||
await MenuService.delete_menu_service(ids=ids, auth=auth)
|
||||
logger.info(f"删除菜单成功: {ids}")
|
||||
log.info(f"删除菜单成功: {ids}")
|
||||
return SuccessResponse(msg="删除菜单成功")
|
||||
|
||||
|
||||
@@ -133,5 +133,5 @@ async def batch_set_available_obj_controller(
|
||||
- JSONResponse: 批量修改菜单状态的 JSON 响应。
|
||||
"""
|
||||
await MenuService.set_menu_available_service(data=data, auth=auth)
|
||||
logger.info(f"批量修改菜单状态成功: {data.ids}")
|
||||
log.info(f"批量修改菜单状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改菜单状态成功")
|
||||
@@ -29,7 +29,7 @@ class MenuModel(ModelMixin):
|
||||
__loader_options__ = ["roles"]
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment='主键ID')
|
||||
name: Mapped[str] = mapped_column(String(50), nullable=False, comment='菜单名称', unique=True)
|
||||
name: Mapped[str] = mapped_column(String(50), nullable=False, comment='菜单名称')
|
||||
type: Mapped[int] = mapped_column(Integer, nullable=False, default=2, comment='菜单类型(1:目录 2:菜单 3:按钮/权限 4:链接)')
|
||||
order: Mapped[int] = mapped_column(Integer, nullable=False, default=999, comment='显示排序')
|
||||
status: Mapped[bool] = mapped_column(Boolean(), default=True, nullable=False, comment="是否启用(True:启用 False:禁用)")
|
||||
|
||||
@@ -8,7 +8,7 @@ from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission, get_current_user
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
from app.common.request import PaginationService
|
||||
from app.utils.common_util import bytes2file_response
|
||||
|
||||
@@ -39,7 +39,7 @@ async def get_obj_detail_controller(
|
||||
- JSONResponse: 包含公告详情的响应模型。
|
||||
"""
|
||||
result_dict = await NoticeService.get_notice_detail_service(id=id, auth=auth)
|
||||
logger.info(f"获取公告详情成功 {id}")
|
||||
log.info(f"获取公告详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取公告详情成功")
|
||||
|
||||
@NoticeRouter.get("/list", summary="查询公告", description="查询公告")
|
||||
@@ -61,7 +61,7 @@ async def get_obj_list_controller(
|
||||
"""
|
||||
result_dict_list = await NoticeService.get_notice_list_service(auth=auth, search=search, order_by=page.order_by)
|
||||
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
|
||||
logger.info(f"查询公告列表成功")
|
||||
log.info(f"查询公告列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询公告列表成功")
|
||||
|
||||
@NoticeRouter.post("/create", summary="创建公告", description="创建公告")
|
||||
@@ -80,7 +80,7 @@ async def create_obj_controller(
|
||||
- JSONResponse: 包含创建公告结果的响应模型。
|
||||
"""
|
||||
result_dict = await NoticeService.create_notice_service(auth=auth, data=data)
|
||||
logger.info(f"创建公告成功: {result_dict}")
|
||||
log.info(f"创建公告成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建公告成功")
|
||||
|
||||
@NoticeRouter.put("/update/{id}", summary="修改公告", description="修改公告")
|
||||
@@ -101,7 +101,7 @@ async def update_obj_controller(
|
||||
- JSONResponse: 包含修改公告结果的响应模型。
|
||||
"""
|
||||
result_dict = await NoticeService.update_notice_service(auth=auth, id=id, data=data)
|
||||
logger.info(f"修改公告成功: {result_dict}")
|
||||
log.info(f"修改公告成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改公告成功")
|
||||
|
||||
@NoticeRouter.delete("/delete", summary="删除公告", description="删除公告")
|
||||
@@ -120,7 +120,7 @@ async def delete_obj_controller(
|
||||
- JSONResponse: 包含删除公告结果的响应模型。
|
||||
"""
|
||||
await NoticeService.delete_notice_service(auth=auth, ids=ids)
|
||||
logger.info(f"删除公告成功: {ids}")
|
||||
log.info(f"删除公告成功: {ids}")
|
||||
return SuccessResponse(msg="删除公告成功")
|
||||
|
||||
@NoticeRouter.patch("/available/setting", summary="批量修改公告状态", description="批量修改公告状态")
|
||||
@@ -139,7 +139,7 @@ async def batch_set_available_obj_controller(
|
||||
- JSONResponse: 包含批量修改公告状态结果的响应模型。
|
||||
"""
|
||||
await NoticeService.set_notice_available_service(auth=auth, data=data)
|
||||
logger.info(f"批量修改公告状态成功: {data.ids}")
|
||||
log.info(f"批量修改公告状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改公告状态成功")
|
||||
|
||||
@NoticeRouter.post('/export', summary="导出公告", description="导出公告")
|
||||
@@ -159,7 +159,7 @@ async def export_obj_list_controller(
|
||||
"""
|
||||
result_dict_list = await NoticeService.get_notice_list_service(search=search, auth=auth)
|
||||
export_result = await NoticeService.export_notice_service(notice_list=result_dict_list)
|
||||
logger.info('导出公告成功')
|
||||
log.info('导出公告成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(export_result),
|
||||
@@ -185,5 +185,5 @@ async def get_obj_list_available_controller(
|
||||
"""
|
||||
result_dict_list = await NoticeService.get_notice_list_available_service(auth=auth)
|
||||
result_dict = await PaginationService.paginate(data_list= result_dict_list)
|
||||
logger.info(f"查询已启用公告列表成功")
|
||||
log.info(f"查询已启用公告列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询已启用公告列表成功")
|
||||
|
||||
@@ -10,7 +10,7 @@ from app.utils.common_util import bytes2file_response
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission, redis_getter
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from ..auth.schema import AuthSchema
|
||||
from .param import ParamsQueryParam
|
||||
@@ -36,7 +36,7 @@ async def get_type_detail_controller(
|
||||
- JSONResponse: 包含参数详情的 JSON 响应
|
||||
"""
|
||||
result_dict = await ParamsService.get_obj_detail_service(id=id, auth=auth)
|
||||
logger.info(f"获取参数详情成功 {id}")
|
||||
log.info(f"获取参数详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取参数详情成功")
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ async def get_obj_by_key_controller(
|
||||
- JSONResponse: 包含参数详情的 JSON 响应
|
||||
"""
|
||||
result_dict = await ParamsService.get_obj_by_key_service(config_key=config_key, auth=auth)
|
||||
logger.info(f"根据配置键获取参数详情成功 {config_key}")
|
||||
log.info(f"根据配置键获取参数详情成功 {config_key}")
|
||||
return SuccessResponse(data=result_dict, msg="根据配置键获取参数详情成功")
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ async def get_config_value_by_key_controller(
|
||||
- JSONResponse: 包含参数值的 JSON 响应
|
||||
"""
|
||||
result_value = await ParamsService.get_config_value_by_key_service(config_key=config_key, auth=auth)
|
||||
logger.info(f"根据配置键获取参数值成功 {config_key}")
|
||||
log.info(f"根据配置键获取参数值成功 {config_key}")
|
||||
return SuccessResponse(data=result_value, msg="根据配置键获取参数值成功")
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ async def get_obj_list_controller(
|
||||
"""
|
||||
result_dict_list = await ParamsService.get_obj_list_service(auth=auth, search=search, order_by=page.order_by)
|
||||
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
|
||||
logger.info(f"获取参数列表成功")
|
||||
log.info(f"获取参数列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询参数列表成功")
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ async def create_obj_controller(
|
||||
- JSONResponse: 包含创建参数结果的 JSON 响应
|
||||
"""
|
||||
result_dict = await ParamsService.create_obj_service(auth=auth, redis=redis, data=data)
|
||||
logger.info(f"创建参数成功: {result_dict}")
|
||||
log.info(f"创建参数成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建参数成功")
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ async def update_objs_controller(
|
||||
- JSONResponse: 包含修改参数结果的 JSON 响应
|
||||
"""
|
||||
result_dict = await ParamsService.update_obj_service(auth=auth, redis=redis, id=id, data=data)
|
||||
logger.info(f"更新参数成功 {result_dict}")
|
||||
log.info(f"更新参数成功 {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="更新参数成功")
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ async def delete_obj_controller(
|
||||
- JSONResponse: 包含删除参数结果的 JSON 响应
|
||||
"""
|
||||
await ParamsService.delete_obj_service(auth=auth, redis=redis, ids=ids)
|
||||
logger.info(f"删除参数成功: {ids}")
|
||||
log.info(f"删除参数成功: {ids}")
|
||||
return SuccessResponse(msg="删除参数成功")
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ async def export_obj_list_controller(
|
||||
"""
|
||||
result_dict_list = await ParamsService.get_obj_list_service(search=search, auth=auth)
|
||||
export_result = await ParamsService.export_obj_service(data_list=result_dict_list)
|
||||
logger.info('导出参数成功')
|
||||
log.info('导出参数成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(export_result),
|
||||
@@ -215,7 +215,7 @@ async def upload_file_controller(
|
||||
- JSONResponse: 包含上传文件结果的 JSON 响应
|
||||
"""
|
||||
result_str = await ParamsService.upload_service(base_url=str(request.base_url), file=file)
|
||||
logger.info(f"上传文件: {result_str}")
|
||||
log.info(f"上传文件: {result_str}")
|
||||
return SuccessResponse(data=result_str, msg='上传文件成功')
|
||||
|
||||
|
||||
@@ -233,5 +233,5 @@ async def get_init_obj_controller(
|
||||
- JSONResponse: 获取初始化缓存参数的 JSON 响应
|
||||
"""
|
||||
result_dict = await ParamsService.get_init_config_service(redis=redis)
|
||||
logger.info(f"获取初始化缓存参数成功 {result_dict}")
|
||||
log.info(f"获取初始化缓存参数成功 {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="获取初始化缓存参数成功")
|
||||
@@ -14,7 +14,7 @@ from app.utils.excel_util import ExcelUtil
|
||||
from app.utils.upload_util import UploadUtil
|
||||
from app.core.base_schema import UploadResponseSchema
|
||||
from app.core.exceptions import CustomException
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from ..auth.schema import AuthSchema
|
||||
from .param import ParamsQueryParam
|
||||
@@ -123,10 +123,10 @@ class ParamsService:
|
||||
value="",
|
||||
)
|
||||
if not result:
|
||||
logger.error(f"同步配置到缓存失败: {new_obj_dict}")
|
||||
log.error(f"同步配置到缓存失败: {new_obj_dict}")
|
||||
raise CustomException(msg="同步配置到缓存失败")
|
||||
except Exception as e:
|
||||
logger.error(f"创建字典类型失败: {e}")
|
||||
log.error(f"创建字典类型失败: {e}")
|
||||
raise CustomException(msg=f"创建字典类型失败 {e}")
|
||||
|
||||
return new_obj_dict
|
||||
@@ -165,10 +165,10 @@ class ParamsService:
|
||||
value=value,
|
||||
)
|
||||
if not result:
|
||||
logger.error(f"同步配置到缓存失败: {new_obj_dict}")
|
||||
log.error(f"同步配置到缓存失败: {new_obj_dict}")
|
||||
raise CustomException(msg="同步配置到缓存失败")
|
||||
except Exception as e:
|
||||
logger.error(f"更新系统配置失败: {e}")
|
||||
log.error(f"更新系统配置失败: {e}")
|
||||
raise CustomException(msg="更新系统配置失败")
|
||||
|
||||
return new_obj_dict
|
||||
@@ -207,9 +207,9 @@ class ParamsService:
|
||||
redis_key = f"{RedisInitKeyConfig.SYSTEM_CONFIG.key}:{exist_obj.config_key}"
|
||||
try:
|
||||
await RedisCURD(redis).delete(redis_key)
|
||||
logger.info(f"删除系统配置成功: {id}")
|
||||
log.info(f"删除系统配置成功: {id}")
|
||||
except Exception as e:
|
||||
logger.error(f"删除系统配置失败: {e}")
|
||||
log.error(f"删除系统配置失败: {e}")
|
||||
raise CustomException(msg="删除字典类型失败")
|
||||
|
||||
@classmethod
|
||||
@@ -294,10 +294,10 @@ class ParamsService:
|
||||
value=value,
|
||||
)
|
||||
if not result:
|
||||
logger.error(f"❌️ 初始化系统配置失败: {config_obj_dict}")
|
||||
log.error(f"❌️ 初始化系统配置失败: {config_obj_dict}")
|
||||
raise CustomException(msg="初始化系统配置失败")
|
||||
except Exception as e:
|
||||
logger.error(f"❌️ 初始化系统配置失败: {e}")
|
||||
log.error(f"❌️ 初始化系统配置失败: {e}")
|
||||
raise CustomException(msg="初始化系统配置失败")
|
||||
|
||||
@classmethod
|
||||
@@ -321,7 +321,7 @@ class ParamsService:
|
||||
new_config = json.loads(config)
|
||||
configs.append(new_config)
|
||||
except Exception as e:
|
||||
logger.error(f"解析系统配置数据失败: {e}")
|
||||
log.error(f"解析系统配置数据失败: {e}")
|
||||
continue
|
||||
|
||||
return configs
|
||||
@@ -362,7 +362,7 @@ class ParamsService:
|
||||
demo_config = json.loads(config_values[0])
|
||||
config_result["demo_enable"] = demo_config.get("config_value", False) if isinstance(demo_config, dict) else False
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"解析演示模式配置失败")
|
||||
log.error(f"解析演示模式配置失败")
|
||||
|
||||
# 解析IP白名单配置
|
||||
if config_values[1]:
|
||||
@@ -372,7 +372,7 @@ class ParamsService:
|
||||
# 确保是列表类型
|
||||
config_result["ip_white_list"] = json.loads(ip_white_config.get("config_value", []))
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"解析IP白名单配置失败")
|
||||
log.error(f"解析IP白名单配置失败")
|
||||
# 解析IP黑名单
|
||||
# 解析API路径白名单
|
||||
if config_values[2]:
|
||||
@@ -381,7 +381,7 @@ class ParamsService:
|
||||
# 确保是列表类型
|
||||
config_result["white_api_list_path"] = json.loads(white_api_config.get("config_value", []))
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"解析API白名单配置失败")
|
||||
log.error(f"解析API白名单配置失败")
|
||||
|
||||
# 解析IP黑名单
|
||||
if config_values[3]:
|
||||
@@ -390,5 +390,5 @@ class ParamsService:
|
||||
# 确保是列表类型
|
||||
config_result["ip_black_list"] = json.loads(black_ip_config.get("config_value", []))
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"解析IP黑名单配置失败")
|
||||
log.error(f"解析IP黑名单配置失败")
|
||||
return config_result
|
||||
@@ -10,7 +10,7 @@ from app.core.base_params import PaginationQueryParam
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from ..auth.schema import AuthSchema
|
||||
from .service import PositionService
|
||||
@@ -46,7 +46,7 @@ async def get_obj_list_controller(
|
||||
order_by = page.order_by
|
||||
result_dict_list = await PositionService.get_position_list_service(search=search, auth=auth, order_by=order_by)
|
||||
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
|
||||
logger.info(f"查询岗位列表成功")
|
||||
log.info(f"查询岗位列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询岗位列表成功")
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ async def get_obj_detail_controller(
|
||||
- JSONResponse: 岗位详情对象
|
||||
"""
|
||||
result_dict = await PositionService.get_position_detail_service(id=id, auth=auth)
|
||||
logger.info(f"查询岗位详情成功 {id}")
|
||||
log.info(f"查询岗位详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取岗位详情成功")
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ async def create_obj_controller(
|
||||
- JSONResponse: 岗位详情对象
|
||||
"""
|
||||
result_dict = await PositionService.create_position_service(data=data, auth=auth)
|
||||
logger.info(f"创建岗位成功: {result_dict}")
|
||||
log.info(f"创建岗位成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建岗位成功")
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ async def update_obj_controller(
|
||||
- JSONResponse: 岗位详情对象
|
||||
"""
|
||||
result_dict = await PositionService.update_position_service(id=id, data=data, auth=auth)
|
||||
logger.info(f"修改岗位成功: {result_dict}")
|
||||
log.info(f"修改岗位成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改岗位成功")
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ async def delete_obj_controller(
|
||||
- JSONResponse: 成功消息
|
||||
"""
|
||||
await PositionService.delete_position_service(ids=ids, auth=auth)
|
||||
logger.info(f"删除岗位成功: {ids}")
|
||||
log.info(f"删除岗位成功: {ids}")
|
||||
return SuccessResponse(msg="删除岗位成功")
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ async def batch_set_available_obj_controller(
|
||||
- JSONResponse: 成功消息
|
||||
"""
|
||||
await PositionService.set_position_available_service(data=data, auth=auth)
|
||||
logger.info(f"批量修改岗位状态成功: {data.ids}")
|
||||
log.info(f"批量修改岗位状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改岗位状态成功")
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ async def export_obj_list_controller(
|
||||
"""
|
||||
position_query_result = await PositionService.get_position_list_service(search=search, auth=auth)
|
||||
position_export_result = await PositionService.export_position_list_service(position_list=position_query_result)
|
||||
logger.info('导出岗位成功')
|
||||
log.info('导出岗位成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(position_export_result),
|
||||
|
||||
@@ -10,7 +10,7 @@ from app.core.router_class import OperationLogRoute
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from ..auth.schema import AuthSchema
|
||||
from .service import RoleService
|
||||
@@ -47,7 +47,7 @@ async def get_obj_list_controller(
|
||||
order_by = page.order_by
|
||||
result_dict_list = await RoleService.get_role_list_service(search=search, auth=auth, order_by=order_by)
|
||||
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
|
||||
logger.info(f"查询角色成功")
|
||||
log.info(f"查询角色成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询角色成功")
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ async def get_obj_detail_controller(
|
||||
- JSONResponse: 角色详情JSON响应
|
||||
"""
|
||||
result_dict = await RoleService.get_role_detail_service(id=id, auth=auth)
|
||||
logger.info(f"获取角色详情成功 {id}")
|
||||
log.info(f"获取角色详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取角色详情成功")
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ async def create_obj_controller(
|
||||
- JSONResponse: 创建角色JSON响应
|
||||
"""
|
||||
result_dict = await RoleService.create_role_service(data=data, auth=auth)
|
||||
logger.info(f"创建角色成功: {result_dict}")
|
||||
log.info(f"创建角色成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建角色成功")
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ async def update_obj_controller(
|
||||
- JSONResponse: 修改角色JSON响应
|
||||
"""
|
||||
result_dict = await RoleService.update_role_service(id=id, data=data, auth=auth)
|
||||
logger.info(f"修改角色成功: {result_dict}")
|
||||
log.info(f"修改角色成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改角色成功")
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ async def delete_obj_controller(
|
||||
- JSONResponse: 删除角色JSON响应
|
||||
"""
|
||||
await RoleService.delete_role_service(ids=ids, auth=auth)
|
||||
logger.info(f"删除角色成功: {ids}")
|
||||
log.info(f"删除角色成功: {ids}")
|
||||
return SuccessResponse(msg="删除角色成功")
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ async def batch_set_available_obj_controller(
|
||||
- JSONResponse: 批量修改角色状态JSON响应
|
||||
"""
|
||||
await RoleService.set_role_available_service(data=data, auth=auth)
|
||||
logger.info(f"批量修改角色状态成功: {data.ids}")
|
||||
log.info(f"批量修改角色状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改角色状态成功")
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ async def set_role_permission_controller(
|
||||
- JSONResponse: 角色授权JSON响应
|
||||
"""
|
||||
await RoleService.set_role_permission_service(data=data, auth=auth)
|
||||
logger.info(f"设置角色权限成功: {data}")
|
||||
log.info(f"设置角色权限成功: {data}")
|
||||
return SuccessResponse(msg="授权角色成功")
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ async def export_obj_list_controller(
|
||||
"""
|
||||
role_query_result = await RoleService.get_role_list_service(search=search, auth=auth)
|
||||
role_export_result = await RoleService.export_role_list_service(role_list=role_query_result)
|
||||
logger.info('导出角色成功')
|
||||
log.info('导出角色成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(role_export_result),
|
||||
|
||||
@@ -10,7 +10,7 @@ from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .param import TenantQueryParam
|
||||
@@ -39,7 +39,7 @@ async def get_obj_detail_controller(
|
||||
- JSONResponse: 包含租户详情的JSON响应
|
||||
"""
|
||||
result_dict = await TenantService.detail_service(id=id, auth=auth)
|
||||
logger.info(f"获取租户详情成功 {id}")
|
||||
log.info(f"获取租户详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg="获取租户详情成功")
|
||||
|
||||
@TenantRouter.get("/list", summary="查询租户列表", description="查询租户列表")
|
||||
@@ -67,7 +67,7 @@ async def get_obj_list_controller(
|
||||
search=search,
|
||||
order_by=page.order_by
|
||||
)
|
||||
logger.info("查询租户列表成功")
|
||||
log.info("查询租户列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询租户列表成功")
|
||||
|
||||
@TenantRouter.post("/create", summary="创建租户", description="创建租户")
|
||||
@@ -86,7 +86,7 @@ async def create_obj_controller(
|
||||
- JSONResponse: 包含创建租户详情的JSON响应
|
||||
"""
|
||||
result_dict = await TenantService.create_service(auth=auth, data=data)
|
||||
logger.info(f"创建租户成功: {result_dict.get('name')}")
|
||||
log.info(f"创建租户成功: {result_dict.get('name')}")
|
||||
return SuccessResponse(data=result_dict, msg="创建租户成功")
|
||||
|
||||
@TenantRouter.put("/update/{id}", summary="修改租户", description="修改租户")
|
||||
@@ -107,7 +107,7 @@ async def update_obj_controller(
|
||||
- JSONResponse: 包含修改租户详情的JSON响应
|
||||
"""
|
||||
result_dict = await TenantService.update_service(auth=auth, id=id, data=data)
|
||||
logger.info(f"修改租户成功: {result_dict.get('name')}")
|
||||
log.info(f"修改租户成功: {result_dict.get('name')}")
|
||||
return SuccessResponse(data=result_dict, msg="修改租户成功")
|
||||
|
||||
@TenantRouter.delete("/delete", summary="删除租户", description="删除租户")
|
||||
@@ -126,7 +126,7 @@ async def delete_obj_controller(
|
||||
- JSONResponse: 包含删除租户详情的JSON响应
|
||||
"""
|
||||
await TenantService.delete_service(auth=auth, ids=ids)
|
||||
logger.info(f"删除租户成功: {ids}")
|
||||
log.info(f"删除租户成功: {ids}")
|
||||
return SuccessResponse(msg="删除租户成功")
|
||||
|
||||
@TenantRouter.patch("/available/setting", summary="批量修改租户状态", description="批量修改租户状态")
|
||||
@@ -145,7 +145,7 @@ async def batch_set_available_obj_controller(
|
||||
- JSONResponse: 包含批量修改租户状态详情的JSON响应
|
||||
"""
|
||||
await TenantService.set_available_service(auth=auth, data=data)
|
||||
logger.info(f"批量修改租户状态成功: {data.ids}")
|
||||
log.info(f"批量修改租户状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改租户状态成功")
|
||||
|
||||
@TenantRouter.post('/export', summary="导出租户", description="导出租户")
|
||||
@@ -165,7 +165,7 @@ async def export_obj_list_controller(
|
||||
"""
|
||||
result_dict_list = await TenantService.list_service(search=search, auth=auth)
|
||||
export_result = await TenantService.batch_export_service(obj_list=result_dict_list)
|
||||
logger.info('导出租户成功')
|
||||
log.info('导出租户成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(export_result),
|
||||
@@ -191,7 +191,7 @@ async def import_obj_list_controller(
|
||||
- JSONResponse: 包含导入租户详情的JSON响应
|
||||
"""
|
||||
batch_import_result = await TenantService.batch_import_service(file=file, auth=auth, update_support=True)
|
||||
logger.info(f"导入租户成功: {batch_import_result}")
|
||||
log.info(f"导入租户成功: {batch_import_result}")
|
||||
return SuccessResponse(data=batch_import_result, msg="导入租户成功")
|
||||
|
||||
@TenantRouter.post('/download/template', summary="获取租户导入模板", description="获取租户导入模板", dependencies=[Depends(AuthPermission(["module_system:tenant:download"]))])
|
||||
@@ -203,7 +203,7 @@ async def export_obj_template_controller() -> StreamingResponse:
|
||||
- StreamingResponse: 包含租户导入模板的Excel文件流响应
|
||||
"""
|
||||
example_import_template_result = await TenantService.import_template_download_service()
|
||||
logger.info('获取租户导入模板成功')
|
||||
log.info('获取租户导入模板成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(example_import_template_result),
|
||||
|
||||
@@ -8,7 +8,7 @@ import pandas as pd
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.exceptions import CustomException
|
||||
from app.utils.excel_util import ExcelUtil
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .schema import TenantCreateSchema, TenantUpdateSchema, TenantOutSchema
|
||||
@@ -286,7 +286,7 @@ class TenantService:
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"批量导入用户失败: {str(e)}")
|
||||
log.error(f"批量导入用户失败: {str(e)}")
|
||||
raise CustomException(msg=f"导入失败: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -12,7 +12,7 @@ from app.core.router_class import OperationLogRoute
|
||||
from app.core.dependencies import db_getter, get_current_user, AuthPermission
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.base_schema import BatchSetAvailable
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
|
||||
from ..auth.schema import AuthSchema
|
||||
from .service import UserService
|
||||
@@ -45,7 +45,7 @@ async def get_current_user_info_controller(
|
||||
- JSONResponse: 当前用户信息JSON响应
|
||||
"""
|
||||
result_dict = await UserService.get_current_user_info_service(auth=auth)
|
||||
logger.info(f"获取当前用户信息成功")
|
||||
log.info(f"获取当前用户信息成功")
|
||||
return SuccessResponse(data=result_dict, msg='获取当前用户信息成功')
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ async def user_avatar_upload_controller(
|
||||
- JSONResponse: 上传头像JSON响应
|
||||
"""
|
||||
result_str = await UserService.upload_avatar_service(base_url=str(request.base_url), file=file)
|
||||
logger.info(f"上传头像成功: {result_str}")
|
||||
log.info(f"上传头像成功: {result_str}")
|
||||
return SuccessResponse(data=result_str, msg='上传头像成功')
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ async def update_current_user_info_controller(
|
||||
- JSONResponse: 更新当前用户基本信息JSON响应
|
||||
"""
|
||||
result_dict = await UserService.update_current_user_info_service(data=data, auth=auth)
|
||||
logger.info(f"更新当前用户基本信息成功: {result_dict}")
|
||||
log.info(f"更新当前用户基本信息成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg='更新当前用户基本信息成功')
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ async def change_current_user_password_controller(
|
||||
- JSONResponse: 修改密码JSON响应
|
||||
"""
|
||||
result_dict = await UserService.change_user_password_service(data=data, auth=auth)
|
||||
logger.info(f"修改密码成功: {result_dict}")
|
||||
log.info(f"修改密码成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg='修改密码成功, 请重新登录')
|
||||
|
||||
@UserRouter.put("/reset/password", summary="重置密码", description="重置密码")
|
||||
@@ -124,7 +124,7 @@ async def reset_password_controller(
|
||||
- JSONResponse: 重置密码JSON响应
|
||||
"""
|
||||
result_dict = await UserService.reset_user_password_service(data=data, auth=auth)
|
||||
logger.info(f"重置密码成功: {result_dict}")
|
||||
log.info(f"重置密码成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg='重置密码成功')
|
||||
|
||||
@UserRouter.post('/register', summary="注册用户", description="注册用户")
|
||||
@@ -144,7 +144,7 @@ async def register_user_controller(
|
||||
"""
|
||||
auth = AuthSchema(db=db)
|
||||
user_register_result = await UserService.register_user_service(data=data, auth=auth)
|
||||
logger.info(f"{data.username} 注册用户成功: {user_register_result}")
|
||||
log.info(f"{data.username} 注册用户成功: {user_register_result}")
|
||||
return SuccessResponse(data=user_register_result, msg='注册用户成功')
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ async def forget_password_controller(
|
||||
"""
|
||||
auth = AuthSchema(db=db)
|
||||
user_forget_password_result = await UserService.forget_password_service(data=data, auth=auth)
|
||||
logger.info(f"{data.username} 重置密码成功: {user_forget_password_result}")
|
||||
log.info(f"{data.username} 重置密码成功: {user_forget_password_result}")
|
||||
return SuccessResponse(data=user_forget_password_result, msg='重置密码成功')
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ async def get_obj_list_controller(
|
||||
"""
|
||||
result_dict_list = await UserService.get_user_list_service(search=search, auth=auth, order_by=page.order_by)
|
||||
result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size)
|
||||
logger.info(f"查询用户成功")
|
||||
log.info(f"查询用户成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询用户成功")
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ async def get_obj_detail_controller(
|
||||
- JSONResponse: 用户详情JSON响应
|
||||
"""
|
||||
result_dict = await UserService.get_detail_by_id_service(id=id, auth=auth)
|
||||
logger.info(f"获取用户详情成功 {id}")
|
||||
log.info(f"获取用户详情成功 {id}")
|
||||
return SuccessResponse(data=result_dict, msg='获取用户详情成功')
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ async def create_obj_controller(
|
||||
- JSONResponse: 创建用户JSON响应
|
||||
"""
|
||||
result_dict = await UserService.create_user_service(data=data, auth=auth)
|
||||
logger.info(f"创建用户成功: {result_dict}")
|
||||
log.info(f"创建用户成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="创建用户成功")
|
||||
|
||||
|
||||
@@ -254,7 +254,7 @@ async def update_obj_controller(
|
||||
- JSONResponse: 修改用户JSON响应
|
||||
"""
|
||||
result_dict = await UserService.update_user_service(id=id, data=data, auth=auth)
|
||||
logger.info(f"修改用户成功: {result_dict}")
|
||||
log.info(f"修改用户成功: {result_dict}")
|
||||
return SuccessResponse(data=result_dict, msg="修改用户成功")
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ async def delete_obj_controller(
|
||||
- JSONResponse: 删除用户JSON响应
|
||||
"""
|
||||
await UserService.delete_user_service(ids=ids, auth=auth)
|
||||
logger.info(f"删除用户成功: {ids}")
|
||||
log.info(f"删除用户成功: {ids}")
|
||||
return SuccessResponse(msg="删除用户成功")
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ async def batch_set_available_obj_controller(
|
||||
- JSONResponse: 批量修改用户状态JSON响应
|
||||
"""
|
||||
await UserService.set_user_available_service(data=data, auth=auth)
|
||||
logger.info(f"批量修改用户状态成功: {data.ids}")
|
||||
log.info(f"批量修改用户状态成功: {data.ids}")
|
||||
return SuccessResponse(msg="批量修改用户状态成功")
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ async def export_obj_template_controller()-> StreamingResponse:
|
||||
- StreamingResponse: 用户导入模板流响应
|
||||
"""
|
||||
user_import_template_result = await UserService.get_import_template_user_service()
|
||||
logger.info('获取用户导入模板成功')
|
||||
log.info('获取用户导入模板成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(user_import_template_result),
|
||||
@@ -338,7 +338,7 @@ async def export_obj_list_controller(
|
||||
"""
|
||||
user_list = await UserService.get_user_list_service(auth=auth, search=search, order_by=page.order_by)
|
||||
user_export_result = await UserService.export_user_list_service(user_list)
|
||||
logger.info('导出用户成功')
|
||||
log.info('导出用户成功')
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(user_export_result),
|
||||
@@ -365,5 +365,5 @@ async def import_obj_list_controller(
|
||||
- JSONResponse: 导入用户JSON响应
|
||||
"""
|
||||
batch_import_result = await UserService.batch_import_user_service(file=file, auth=auth, update_support=True)
|
||||
logger.info(f"导入用户成功: {batch_import_result}")
|
||||
log.info(f"导入用户成功: {batch_import_result}")
|
||||
return SuccessResponse(data=batch_import_result, msg="导入用户成功")
|
||||
|
||||
@@ -8,7 +8,7 @@ import pandas as pd
|
||||
from app.core.exceptions import CustomException
|
||||
from app.utils.hash_bcrpy_util import PwdUtil
|
||||
from app.core.base_schema import BatchSetAvailable, UploadResponseSchema
|
||||
from app.core.logger import logger
|
||||
from app.core.logger import log
|
||||
from app.utils.common_util import traversal_to_tree
|
||||
from app.utils.excel_util import ExcelUtil
|
||||
from app.utils.upload_util import UploadUtil
|
||||
@@ -570,7 +570,7 @@ class UserService:
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"批量导入用户失败: {str(e)}")
|
||||
log.error(f"批量导入用户失败: {str(e)}")
|
||||
raise CustomException(msg=f"导入失败: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
|
||||
Reference in New Issue
Block a user