Files
FastapiAdmin/backend/app/utils/common_util.py
T
zhangtao e159b31e62 feat: Add role code to system_role.json and update system_users.json creator_id to null
fix: Refactor initialize.py to handle nested children data during initialization

feat: Implement tree structure traversal functions in common_util.py

chore: Update requirements.txt to specify sqlalchemy-crud-plus version and add rich

refactor: Change API endpoints in dept.ts and menu.ts to return tree structure

feat: Add code field to role, dept, and menu interfaces in respective TypeScript files

fix: Update dept and role Vue components to display and handle code field

docs: Add comprehensive project documentation for FastAPI Vue3 Admin
2025-09-15 00:19:11 +08:00

204 lines
6.4 KiB
Python

# -*- coding: utf-8 -*-
import importlib
import uuid
import re
from sqlalchemy.orm import DeclarativeBase
from typing import Any, List, Dict, Sequence, Optional
from app.core.logger import logger
from app.core.exceptions import CustomException
def worship():
print("""
______ _ _
| ____| | | /\ (_)
| |__ __ _ ___| |_ / \ _ __ _
| __/ _` / __| __| / /\ \ | '_ \| |
| | | (_| \__ \ |_ / ____ \| |_) | |
|_| \__,_|___/\__/_/ \_\ .__/|_|
| |
|_|
""")
def import_module(module: str, desc: str) -> Any:
"""
动态导入模块
:param module: 模块名称
:param desc: 模块描述
:return: 模块对象
"""
try:
module_path, module_class = module.rsplit(".", 1)
module = importlib.import_module(module_path)
return getattr(module, module_class)
except ModuleNotFoundError:
logger.error(f"导入{desc}失败,未找到模块:{module}")
raise ModuleNotFoundError(f"导入{desc}失败,未找到模块:{module}")
except AttributeError:
logger.error(f"导入{desc}失败,未找到模块方法:{module}")
raise AttributeError(f"导入{desc}失败,未找到模块方法:{module}")
async def import_modules_async(modules: list, desc: str, **kwargs):
"""
异步导入模块列表
:param modules: 模块列表
:param desc: 模块描述
:param kwargs: 额外参数
"""
for module in modules:
if not module:
continue
try:
module_path = module[0:module.rindex(".")]
module_name = module[module.rindex(".") + 1:]
module_obj = importlib.import_module(module_path)
await getattr(module_obj, module_name)(**kwargs)
except ModuleNotFoundError:
logger.error(f"导入{desc}失败,未找到模块:{module}")
raise ModuleNotFoundError(f"导入{desc}失败,未找到模块:{module}")
except AttributeError:
logger.error(f"导入{desc}失败,未找到模块方法:{module}")
raise AttributeError(f"导入{desc}失败,未找到模块方法:{module}")
def get_random_character() -> str:
"""生成随机字符串"""
return uuid.uuid4().hex
def get_parent_id_map(model_list: Sequence[DeclarativeBase]) -> Dict[int, int]:
"""
获取父级ID映射字典
:param model_list: 模型列表
:return: {id: parent_id} 映射字典
"""
return {item.id: item.parent_id for item in model_list}
def get_parent_recursion(
id: int,
id_map: Dict[int, int],
ids: Optional[List[int]] = None
) -> List[int]:
"""
递归获取所有父级ID
:param id: 当前ID
:param id_map: ID映射字典
:param ids: 已收集的ID列表
:return: 所有父级ID列表
"""
ids = ids or []
if id in ids:
raise CustomException(msg="递归获取父级ID失败,不可以自引用")
ids.append(id)
parent_id = id_map.get(id)
if parent_id:
get_parent_recursion(parent_id, id_map, ids)
return ids
def get_child_id_map(model_list: Sequence[DeclarativeBase]) -> Dict[int, List[int]]:
"""
获取子级ID映射字典
:param model_list: 模型列表
:return: {id: [child_ids]} 映射字典
"""
data_map = {}
for model in model_list:
data_map.setdefault(model.id, [])
if model.parent_id:
data_map.setdefault(model.parent_id, []).append(model.id)
return data_map
def get_child_recursion(
id: int,
id_map: Dict[int, List[int]],
ids: Optional[List[int]] = None
) -> List[int]:
"""
递归获取所有子级ID
:param id: 当前ID
:param id_map: ID映射字典
:param ids: 已收集的ID列表
:return: 所有子级ID列表
"""
ids = ids or []
ids.append(id)
for child in id_map.get(id, []):
get_child_recursion(child, id_map, ids)
return ids
def traversal_to_tree(nodes: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""
通过遍历算法构造树形结构
:param nodes: 树节点列表
:return:
"""
tree: list[dict[str, Any]] = []
node_dict = {node['id']: node for node in nodes}
for node in nodes:
# 确保每个节点都有children字段,即使没有子节点也设置为null
if 'children' not in node:
node['children'] = None
parent_id = node['parent_id']
if parent_id is None:
tree.append(node)
else:
parent_node = node_dict.get(parent_id)
if parent_node is not None:
if 'children' not in parent_node or parent_node['children'] is None:
parent_node['children'] = []
if node not in parent_node['children']:
parent_node['children'].append(node)
else:
if node not in tree:
tree.append(node)
# 确保所有节点都有children字段
for node in tree:
if 'children' not in node:
node['children'] = None
return tree
def recursive_to_tree(nodes: list[dict[str, Any]], *, parent_id: int | None = None) -> list[dict[str, Any]]:
"""
通过递归算法构造树形结构(性能影响较大)
:param nodes: 树节点列表
:param parent_id: 父节点 ID,默认为 None 表示根节点
:return:
"""
tree: list[dict[str, Any]] = []
for node in nodes:
if node['parent_id'] == parent_id:
child_nodes = recursive_to_tree(nodes, parent_id=node['id'])
if child_nodes:
node['children'] = child_nodes
tree.append(node)
return tree
def bytes2human(n: int, format_str: str = '%(value).1f%(symbol)s') -> str:
"""
字节数转人类可读格式
:param n: 字节数
:param format_str: 格式化字符串
:return: 可读的字节字符串,如 '1.5MB'
"""
symbols = ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB')
prefix = {s: 1 << (i + 1) * 10 for i, s in enumerate(symbols[1:])}
for symbol in reversed(symbols[1:]):
if n >= prefix[symbol]:
value = float(n) / prefix[symbol]
return format_str % locals()
return format_str % dict(symbol=symbols[0], value=n)
def bytes2file_response(bytes_info: bytes):
"""生成文件响应"""
yield bytes_info