feat: 初始化仓库

This commit is contained in:
2025-02-12 02:38:29 +08:00
commit 46e9e79670
67 changed files with 8960 additions and 0 deletions

7
api/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
# _*_ coding : UTF-8 _*_
# @Time : 2025/01/19 00:55
# @UpdateTime : 2025/01/19 00:55
# @Author : sonder
# @File : __init__.py.py
# @Software : PyCharm
# @Comment : 本程序

116
api/cache.py Normal file
View File

@@ -0,0 +1,116 @@
# _*_ coding : UTF-8 _*_
# @Time : 2025/02/04 15:18
# @UpdateTime : 2025/02/04 15:18
# @Author : sonder
# @File : cache.py
# @Software : PyCharm
# @Comment : 本程序
from fastapi import APIRouter, Depends, Path, Request
from fastapi.responses import JSONResponse
from annotation.log import Log
from config.constant import BusinessType, RedisKeyConfig
from controller.login import LoginController
from schemas.cache import CacheMonitor, GetCacheInfoResponse, CacheInfo, GetCacheKeysListResponse, \
GetCacheMonitorResponse
from schemas.common import BaseResponse
from utils.response import Response
cacheAPI = APIRouter(
prefix="/cache",
dependencies=[Depends(LoginController.get_current_user)],
)
@cacheAPI.get("/monitor", response_class=JSONResponse, response_model=GetCacheMonitorResponse,
summary="获取缓存监控信息")
@Log(title="获取缓存监控信息", business_type=BusinessType.SELECT)
async def get_cache_info(request: Request):
info = await request.app.state.redis.info()
db_size = await request.app.state.redis.dbsize()
command_stats_dict = await request.app.state.redis.info('commandstats')
command_stats = [
dict(name=key.split('_')[1], value=str(value.get('calls'))) for key, value in command_stats_dict.items()
]
cache_info = CacheMonitor(
info=info,
dbSize=db_size,
commandStats=command_stats
)
return Response.success(data=cache_info)
@cacheAPI.get("/names", response_class=JSONResponse, response_model=GetCacheInfoResponse,
summary="获取缓存名称列表")
@Log(title="获取缓存名称列表", business_type=BusinessType.SELECT)
async def get_cache_names(request: Request):
name_list = []
for key_config in RedisKeyConfig:
name_list.append(
CacheInfo(
cacheKey='',
cacheName=key_config.key,
cacheValue='',
remark=key_config.remark,
)
)
return Response.success(data=name_list)
@cacheAPI.get("/keys/{cacheName}", response_class=JSONResponse, response_model=GetCacheKeysListResponse,
summary="获取缓存键名列表")
@Log(title="获取缓存键名列表", business_type=BusinessType.SELECT)
async def get_cache_keys(request: Request, cacheName: str = Path(description="缓存名称")):
cache_keys = await request.app.state.redis.keys(f'{cacheName}*')
cache_key_list = [key.split(':', 1)[1] for key in cache_keys if key.startswith(f'{cacheName}:')]
return Response.success(data=cache_key_list)
@cacheAPI.get("/info/{cacheName}/{cacheKey}", response_class=JSONResponse, response_model=GetCacheInfoResponse,
summary="获取缓存信息")
@Log(title="获取缓存信息", business_type=BusinessType.SELECT)
async def get_cache_info(request: Request, cacheName: str = Path(description="缓存名称"),
cacheKey: str = Path(description="缓存键名")):
cache_value = await request.app.state.redis.get(f'{cacheName}:{cacheKey}')
cache_info = CacheInfo(
cacheKey=cacheKey,
cacheName=cacheName,
cacheValue=cache_value,
remark="",
)
return Response.success(data=cache_info)
@cacheAPI.delete("/cacheName/{name}", response_class=JSONResponse, response_model=BaseResponse,
summary="通过键名删除缓存")
@cacheAPI.post("/cacheName/{name}", response_class=JSONResponse, response_model=BaseResponse,
summary="通过键名删除缓存")
@Log(title="通过键名删除缓存", business_type=BusinessType.DELETE)
async def delete_cache(request: Request, name: str = Path(description="缓存名称")):
cache_keys = await request.app.state.redis.keys(f'{name}*')
if cache_keys:
await request.app.state.redis.delete(*cache_keys)
return Response.success(msg=f"删除{name}缓存成功!")
@cacheAPI.delete("/cacheKey/{key}", response_class=JSONResponse, response_model=BaseResponse,
summary="通过键值删除缓存")
@cacheAPI.post("/cacheKey/{key}", response_class=JSONResponse, response_model=BaseResponse, summary="通过键值删除缓存")
@Log(title="通过键值删除缓存", business_type=BusinessType.DELETE)
async def delete_cache_key(request: Request, key: str = Path(description="缓存键名")):
cache_keys = await request.app.state.redis.keys(f'*{key}')
if cache_keys:
await request.app.state.redis.delete(*cache_keys)
return Response.success(msg=f"删除{key}缓存成功!")
@cacheAPI.delete("/clearAll", response_class=JSONResponse, response_model=BaseResponse, summary="删除所有缓存")
@cacheAPI.post("/clearAll", response_class=JSONResponse, response_model=BaseResponse, summary="删除所有缓存")
@Log(title="删除所有缓存", business_type=BusinessType.DELETE)
async def delete_all_cache(request: Request):
cache_keys = await request.app.state.redis.keys()
if cache_keys:
await request.app.state.redis.delete(*cache_keys)
return Response.success(msg="删除所有缓存成功!")

390
api/department.py Normal file
View File

@@ -0,0 +1,390 @@
# _*_ coding : UTF-8 _*_
# @Time : 2025/01/20 01:30
# @UpdateTime : 2025/01/20 01:30
# @Author : sonder
# @File : department.py
# @Software : PyCharm
# @Comment : 本程序
from typing import Optional
from fastapi import APIRouter, Depends, Query, Path, Request
from fastapi.responses import JSONResponse
from annotation.log import Log
from config.constant import BusinessType
from controller.login import LoginController
from models import Department, Role, DepartmentRole
from schemas.common import BaseResponse
from schemas.department import AddDepartmentParams, GetDepartmentInfoResponse, \
GetDepartmentListResponse, AddDepartmentRoleParams, GetDepartmentRoleInfoResponse, DeleteDepartmentListParams
from utils.response import Response
departmentAPI = APIRouter(prefix="/department", dependencies=[Depends(LoginController.get_current_user)])
@departmentAPI.post("/add", response_model=BaseResponse, response_class=JSONResponse, summary="新增部门")
@Log(title="新增部门", business_type=BusinessType.INSERT)
async def add_department(request: Request, params: AddDepartmentParams,
current_user: dict = Depends(LoginController.get_current_user)):
parent_id = current_user.get("department_id")
if not params.parent_id:
params.parent_id = parent_id
department = await Department.create(
name=params.name,
parent_id=params.parent_id,
principal=params.principal,
phone=params.phone,
email=params.email,
remark=params.remark,
sort=params.sort,
status=params.status
)
if department:
return Response.success(msg="添加成功!")
else:
return Response.error(msg="添加失败!")
@departmentAPI.delete("/delete/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除部门")
@departmentAPI.post("/delete/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除部门")
@Log(title="删除部门", business_type=BusinessType.DELETE)
async def delete_department(request: Request, id: str = Path(description="部门ID")):
if department := await Department.get_or_none(id=id, del_flag=1):
if await delete_department_recursive(department_id=department.id):
return Response.success(msg="删除成功!")
return Response.error(msg="删除失败!")
else:
return Response.error(msg="删除失败,部门不存在!")
@departmentAPI.delete("/deleteList", response_model=BaseResponse, response_class=JSONResponse, summary="批量删除部门")
@departmentAPI.post("/deleteList", response_model=BaseResponse, response_class=JSONResponse, summary="批量删除部门")
@Log(title="批量删除部门", business_type=BusinessType.DELETE)
async def delete_department_list(request: Request, params: DeleteDepartmentListParams):
for item in set(params.ids):
if department := await Department.get_or_none(id=item, del_flag=1):
await delete_department_recursive(department_id=department.id)
return Response.success(msg="删除成功!")
async def delete_department_recursive(department_id: str):
"""
递归删除部门及其附属部门
:param department_id: 部门ID
:return:
"""
await Department.filter(id=department_id).delete()
sub_departments = await Department.filter(parentId=department_id).all()
for sub_department in sub_departments:
await delete_department_recursive(sub_department.id)
return True
@departmentAPI.put("/update/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="修改部门")
@departmentAPI.post("/update/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="修改部门")
@Log(title="修改部门", business_type=BusinessType.UPDATE)
async def update_department(request: Request, params: AddDepartmentParams, id: str = Path(description="部门ID")):
if department := await Department.get_or_none(id=id, del_flag=1):
department.name = params.name
department.parent_id = params.parent_id
department.principal = params.principal
department.phone = params.phone
department.email = params.email
department.remark = params.remark
department.sort = params.sort
department.status = params.status
await department.save()
return Response.success(msg="修改成功!")
else:
return Response.error(msg="修改失败,部门不存在!")
@departmentAPI.get("/info/{id}", response_model=GetDepartmentInfoResponse, response_class=JSONResponse,
summary="查询部门详情")
@Log(title="查询部门详情", business_type=BusinessType.SELECT)
async def get_department(request: Request, id: str = Path(description="部门ID")):
if department := await Department.get_or_none(id=id, del_flag=1).values(
id="id",
name="name",
parent_id="parent_id",
principal="principal",
phone="phone",
email="email",
remark="remark",
sort="sort",
status="status",
create_time="create_time",
update_time="update_time",
create_by="create_by",
update_by="update_by"
):
return Response.success(data=department)
else:
return Response.error(msg="部门不存在!")
@departmentAPI.get("/list", response_model=GetDepartmentListResponse, response_class=JSONResponse,
summary="查询部门列表")
@Log(title="查询部门列表", business_type=BusinessType.SELECT)
async def get_department_list(
request: Request,
page: int = Query(default=1, description="当前页码"),
pageSize: int = Query(default=10, description="每页条数"),
name: Optional[str] = Query(default=None, description="部门名称"),
principal: Optional[str] = Query(default=None, description="负责人"),
phone: Optional[str] = Query(default=None, description="电话"),
email: Optional[str] = Query(default=None, description="邮箱"),
remark: Optional[str] = Query(default=None, description="备注"),
sort: Optional[int] = Query(default=None, description="排序权重"),
current_user: dict = Depends(LoginController.get_current_user)
):
filterArgs = {
f'{k}__contains': v for k, v in {
'name': name,
'principal': principal,
'phone': phone,
'email': email,
'remark': remark,
'sort': sort
}.items() if v
}
department_id = current_user.get("department_id", "")
# 递归查询所有部门
all_departments = await get_department_and_subdepartments(department_id, filterArgs)
# 分页处理
total = len(all_departments)
paginated_departments = all_departments[(page - 1) * pageSize: page * pageSize]
return Response.success(data={
"result": paginated_departments,
"total": total,
"page": page
})
async def get_department_and_subdepartments(department_id: str, filterArgs: dict, visited: set = None):
"""
查询当前部门及其所有下属部门的数据,并根据 id 去重。
:param department_id: 当前部门 ID
:param filterArgs: 过滤条件
:param visited: 已访问的部门 ID 集合,用于避免循环依赖
:return: 去重后的部门列表
"""
if visited is None:
visited = set() # 初始化已访问的部门 ID 集合
# 如果当前部门 ID 已经访问过,直接返回空列表,避免死循环
if department_id in visited:
return []
visited.add(department_id) # 标记当前部门 ID 为已访问
# 查询当前部门
current_department = await Department.filter(
id=department_id,
**filterArgs
).values(
id="id",
name="name",
parent_id="parent_id",
principal="principal",
phone="phone",
email="email",
remark="remark",
sort="sort",
status="status",
create_time="create_time",
update_time="update_time",
create_by="create_by",
update_by="update_by"
)
# 查询直接子部门
sub_departments = await Department.filter(
parent_id=department_id, # 只根据 parent_id 查询
**filterArgs
).values(
id="id",
name="name",
parent_id="parent_id",
principal="principal",
phone="phone",
email="email",
remark="remark",
sort="sort",
status="status",
create_time="create_time",
update_time="update_time",
create_by="create_by",
update_by="update_by"
)
# 递归查询子部门的子部门
for department in sub_departments[:]: # 使用切片复制避免修改迭代中的列表
sub_sub_departments = await get_department_and_subdepartments(department["id"], filterArgs, visited)
sub_departments.extend(sub_sub_departments)
# 合并当前部门和所有下属部门的数据
all_departments = current_department + sub_departments
# 根据 id 去重
unique_departments = []
seen_ids = set() # 用于记录已经处理过的部门 ID
for department in all_departments:
if department["id"] not in seen_ids:
unique_departments.append(department)
seen_ids.add(department["id"])
return unique_departments
@departmentAPI.post("/addRole", response_model=BaseResponse, response_class=JSONResponse, summary="添加部门角色")
@Log(title="添加部门角色", business_type=BusinessType.INSERT)
async def add_department_role(request: Request, params: AddDepartmentRoleParams):
if await DepartmentRole.get_or_none(department_id=params.department_id, role_id=params.role_id, del_flag=1):
return Response.error(msg="该部门已存在该角色!")
if department := await Department.get_or_none(id=params.department_id, del_flag=1):
if role := await Role.get_or_none(id=params.role_id, del_flag=1):
departmentRole = await DepartmentRole.create(department_id=department.id, role_id=role.id)
if departmentRole:
return Response.success(msg="添加成功!")
else:
return Response.error(msg="添加失败!")
else:
return Response.error(msg="添加失败,角色不存在!")
else:
return Response.error(msg="添加失败,部门不存在!")
@departmentAPI.delete("/deleteRole/{id}", response_model=BaseResponse, response_class=JSONResponse,
summary="删除部门角色")
@departmentAPI.post("/deleteRole/{id}", response_model=BaseResponse, response_class=JSONResponse,
summary="删除部门角色")
@Log(title="删除部门角色", business_type=BusinessType.DELETE)
async def delete_department_role(request: Request, id: str = Path(description="部门角色ID")):
if departmentRole := await DepartmentRole.get_or_none(id=id, del_flag=1):
await departmentRole.delete()
return Response.success(msg="删除成功!")
else:
return Response.error(msg="删除失败,部门角色不存在!")
@departmentAPI.put("/updateRole/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="修改部门角色")
@departmentAPI.post("/updateRole/{id}", response_model=BaseResponse, response_class=JSONResponse,
summary="修改部门角色")
@Log(title="修改部门角色", business_type=BusinessType.UPDATE)
async def update_department_role(request: Request, params: AddDepartmentRoleParams,
id: str = Path(description="部门角色ID")):
if departmentRole := await DepartmentRole.get_or_none(id=id, del_flag=1):
if department := await Department.get_or_none(id=params.department_id, del_flag=1):
if role := await Role.get_or_none(id=params.role_id, del_flag=1):
departmentRole.department_id = department.id
departmentRole.role_id = role.id
await departmentRole.save()
return Response.success(msg="修改成功!")
else:
return Response.error(msg="修改失败,角色不存在!")
else:
return Response.error(msg="修改失败,部门不存在!")
else:
return Response.error(msg="修改失败,部门角色不存在!")
@departmentAPI.get("/roleInfo", response_model=GetDepartmentRoleInfoResponse, response_class=JSONResponse,
summary="获取部门角色信息")
@Log(title="获取部门角色信息", business_type=BusinessType.SELECT)
async def get_department_role_info(request: Request, id: str = Query(description="部门角色ID")):
if departmentRole := await DepartmentRole.get_or_none(id=id, del_flag=1):
data = await departmentRole.first().values(
id="id",
department_id="department__id",
department_name="department__name",
department_phone="department__phone",
department_principal="department__principal",
department_email="department__email",
role_name="role__name",
role_code="role__code",
role_id="role__id",
create_time="create_time",
update_time="update_time"
)
return Response.success(data=data)
else:
return Response.error(msg="获取失败,部门角色不存在!")
@departmentAPI.get("/roleList", response_model=GetDepartmentListResponse, response_class=JSONResponse,
summary="获取部门角色列表")
@Log(title="获取部门角色列表", business_type=BusinessType.SELECT)
async def get_department_role_list(
request: Request,
page: int = Query(default=1, description="当前页码"),
pageSize: int = Query(default=10, description="每页条数"),
department_id: Optional[str] = Query(default=None, description="部门ID"),
department_name: Optional[str] = Query(default=None, description="部门名称"),
department_phone: Optional[str] = Query(default=None, description="部门电话"),
department_principal: Optional[str] = Query(default=None, description="部门负责人"),
department_email: Optional[str] = Query(default=None, description="部门邮箱"),
role_id: Optional[str] = Query(default=None, description="角色ID"),
role_name: Optional[str] = Query(default=None, description="角色名称"),
role_code: Optional[str] = Query(default=None, description="角色编码"),
):
filterArgs = {
f'{k}__contains': v for k, v in {
'department__id': department_id,
'department__name': department_name,
'department__phone': department_phone,
'department__principal': department_principal,
'department__email': department_email,
'role__id': role_id,
'role__name': role_name,
'role__code': role_code
}.items() if v
}
total = await DepartmentRole.filter(**filterArgs).count()
data = await DepartmentRole.filter(**filterArgs).offset((page - 1) * pageSize).limit(pageSize).values(
id="id",
department_id="department__id",
department_name="department__name",
department_phone="department__phone",
department_principal="department__principal",
department_email="department__email",
role_name="role__name",
role_code="role__code",
role_id="role__id",
create_time="create_time",
update_time="update_time"
)
return Response.success(data={
"result": data,
"total": total,
"page": page
})
@departmentAPI.get("/roleList/{id}", response_model=GetDepartmentListResponse, response_class=JSONResponse,
summary="用户获取部门角色列表")
@Log(title="获取部门角色列表", business_type=BusinessType.OTHER)
async def get_department_role_list(
request: Request,
id: str = Path(..., description="部门ID")
):
data = await Role.filter(department__id=id).values(
id="id",
department_id="department__id",
department_name="department__name",
department_phone="department__phone",
department_principal="department__principal",
department_email="department__email",
role_name="name",
role_code="code",
role_id="id",
create_time="create_time",
update_time="update_time"
)
return Response.success(data={
"result": data,
"total": len(data),
"page": 1
})

198
api/file.py Normal file
View File

@@ -0,0 +1,198 @@
# _*_ coding : UTF-8 _*_
# @Time : 2025/01/25 22:16
# @UpdateTime : 2025/01/25 22:16
# @Author : sonder
# @File : file.py
# @Software : PyCharm
# @Comment : 本程序
import os
from datetime import datetime
from fastapi import APIRouter, UploadFile, File, Path, Depends, Request, Query
from fastapi.responses import FileResponse, JSONResponse
from annotation.log import Log
from config.constant import BusinessType
from config.env import UploadConfig
from controller.login import LoginController
from exceptions.exception import ModelValidatorException, ServiceException
from models import File as FileModel
from schemas.common import BaseResponse
from schemas.file import UploadFileResponse, GetFileInfoResponse, GetFileListResponse
from utils.response import Response
from utils.upload import Upload
fileAPI = APIRouter(
prefix="/file",
)
@fileAPI.post("/upload", response_model=UploadFileResponse, response_class=JSONResponse, summary="上传文件")
@Log(title="上传文件", business_type=BusinessType.INSERT)
async def upload_file(
request: Request,
file: UploadFile = File(..., description="上传的文件"),
current_user: dict = Depends(LoginController.get_current_user),
):
# 1. 检查文件扩展名是否允许
file_extension = os.path.splitext(file.filename)[1][1:].lower() # 获取文件扩展名并转换为小写
if file_extension not in UploadConfig.DEFAULT_ALLOWED_EXTENSION:
raise ModelValidatorException(message="文件类型不支持")
# 2. 生成唯一的文件名
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
unique_filename = f"{current_user.get('id')}_{timestamp}.{file_extension}"
# 3. 保存文件到服务器
file_path = os.path.join(UploadConfig.UPLOAD_PATH, unique_filename)
with open(file_path, "wb") as buffer:
buffer.write(await file.read())
# 4. 构建文件的相对路径和绝对路径
relative_path = os.path.join(UploadConfig.UPLOAD_PREFIX, unique_filename) # 相对路径
absolute_path = os.path.abspath(file_path) # 绝对路径
# 5. 将文件信息保存到数据库
file_record = await FileModel.create(
name=file.filename,
size=os.path.getsize(file_path),
file_type=file.content_type,
absolute_path=absolute_path,
relative_path=relative_path,
uploader=current_user.get("id"),
)
result = await file_record.first().values(
id="id",
name="name",
size="size",
file_type="file_type",
relative_path="relative_path",
absolute_path="absolute_path",
uploader_id="uploader__id",
uploader_username="uploader__username",
uploader_nickname="uploader__nickname",
uploader_department_id="uploader__department__id",
uploader_department_name="uploader__department__name",
create_time="create_time",
update_time="update_time",
)
return Response.success(data=result)
@fileAPI.get("/{id}", summary="下载文件")
@Log(title="获取文件", business_type=BusinessType.SELECT)
async def download_file(
request: Request,
id: str = Path(..., description="文件ID"),
):
# 1. 查询文件记录
file_record = await FileModel.get_or_none(id=id)
if not file_record:
raise ServiceException(message="文件不存在!")
# 2. 检查文件是否存在
if not os.path.exists(file_record.absolute_path):
raise ServiceException(message="文件不存在!")
# 3. 返回文件内容
return FileResponse(
path=file_record.absolute_path,
filename=file_record.name,
media_type=file_record.file_type,
)
@fileAPI.get("/info/{id}", response_class=JSONResponse, response_model=GetFileInfoResponse, summary="获取文件信息")
@Log(title="获取文件信息", business_type=BusinessType.SELECT)
async def get_file_info(
request: Request,
id: str = Path(..., description="文件ID"),
current_user: dict = Depends(LoginController.get_current_user),
):
# 1. 查询文件记录
file_record = await FileModel.get_or_none(id=id)
if not file_record:
raise ServiceException(message="文件不存在!")
result = await file_record.first().values(
id="id",
name="name",
size="size",
file_type="file_type",
relative_path="relative_path",
absolute_path="absolute_path",
uploader_id="uploader__id",
uploader_username="uploader__username",
uploader_nickname="uploader__nickname",
uploader_department_id="uploader__department__id",
uploader_department_name="uploader__department__name",
create_time="create_time",
update_time="update_time",
)
return Response.success(data=result)
@fileAPI.delete("/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除文件")
@fileAPI.post("/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除文件")
@Log(title="删除文件", business_type=BusinessType.DELETE)
async def delete_file(
request: Request,
id: str = Path(..., description="文件ID"),
current_user: dict = Depends(LoginController.get_current_user),):
# 1. 查询文件记录
file_record = await FileModel.get_or_none(id=id)
if not file_record:
raise ServiceException(message="文件不存在!")
if await Upload.check_file_exists(file_record.absolute_path):
await Upload.delete_file(file_record.absolute_path)
await file_record.delete()
return Response.success()
@fileAPI.get("/list", response_class=JSONResponse, response_model=GetFileListResponse, summary="获取文件列表")
@Log(title="获取文件列表", business_type=BusinessType.SELECT)
async def get_file_list(
request: Request,
page: int = Query(default=1, description="页码"),
pageSize: int = Query(default=10, description="每页数量"),
name: str = Query(default=None, description="文件名"),
file_type: str = Query(default=None, description="文件类型"),
uploader_id: str = Query(default=None, description="上传者ID"),
uploader_username: str = Query(default=None, description="上传者用户名"),
uploader_nickname: str = Query(default=None, description="上传者昵称"),
department_id: str = Query(default=None, description="上传者部门ID"),
department_name: str = Query(default=None, description="上传者部门名称"),
current_user: dict = Depends(LoginController.get_current_user),):
# 1. 查询文件记录
filterArgs = {
f'{k}__contains': v for k, v in {
'name': name,
'file_type': file_type,
'uploader__id': uploader_id,
'uploader__username': uploader_username,
'uploader__nickname': uploader_nickname,
'uploader__department__id': department_id,
'uploader__department__name': department_name
}.items() if v
}
total = await FileModel.filter(**filterArgs).count()
result = await FileModel.filter(**filterArgs).order_by('-create_time').offset((page - 1) * pageSize).limit(
pageSize).values(
id="id",
name="name",
size="size",
file_type="file_type",
relative_path="relative_path",
absolute_path="absolute_path",
uploader_id="uploader__id",
uploader_username="uploader__username",
uploader_nickname="uploader__nickname",
uploader_department_id="uploader__department__id",
uploader_department_name="uploader__department__name",
create_time="create_time",
update_time="update_time",
)
return Response.success(data={
"total": total,
"result": result,
"page": page,
})

266
api/i18n.py Normal file
View File

@@ -0,0 +1,266 @@
# _*_ coding : UTF-8 _*_
# @Time : 2025/02/04 16:05
# @UpdateTime : 2025/02/04 16:05
# @Author : sonder
# @File : i18n.py
# @Software : PyCharm
# @Comment : 本程序
from datetime import timedelta
from typing import Optional
from fastapi import APIRouter, Depends, Path, Request, Query
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from annotation.log import Log
from config.constant import BusinessType, RedisKeyConfig
from controller.login import LoginController
from schemas.common import BaseResponse
from schemas.i18n import AddLocaleParams, GetLocaleInfoResponse, AddI18nParams, GetI18nInfoResponse, \
GetI18nInfoListResponse, GetI18nListResponse
from utils.response import Response
from models import I18n, Locale
i18nAPI = APIRouter(
prefix="/i18n",
)
@i18nAPI.post("/addLocale", response_class=JSONResponse, response_model=BaseResponse, summary="添加国际化类型")
@Log(title="添加国际化类型", business_type=BusinessType.INSERT)
async def add_locale(request: Request, params: AddLocaleParams, current_user=Depends(LoginController.get_current_user)):
if await Locale.get_or_none(code=params.code):
return Response.error(msg="该语言代码已存在!")
locale = await Locale.create(
code=params.code,
name=params.name
)
if locale:
return Response.success(msg="添加成功!")
else:
return Response.error(msg="添加失败!")
@i18nAPI.delete("/deleteLocale/{id}", response_class=JSONResponse, response_model=BaseResponse,
summary="删除国际化类型")
@i18nAPI.post("/deleteLocale/{id}", response_class=JSONResponse, response_model=BaseResponse,
summary="删除国际化类型")
@Log(title="删除国际化类型", business_type=BusinessType.DELETE)
async def delete_locale(request: Request, id: str = Path(description="国际化类型ID"),
current_user=Depends(LoginController.get_current_user)):
if locale := await Locale.get_or_none(id=id):
# 移除语言
await I18n.filter(locale_id=locale.id).delete()
await locale.delete()
return Response.success(msg="删除成功!")
else:
return Response.error(msg="该国际化类型不存在!")
@i18nAPI.put("/updateLocale/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改国际化类型")
@i18nAPI.post("/updateLocale/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改国际化类型")
@Log(title="修改国际化类型", business_type=BusinessType.UPDATE)
async def update_locale(request: Request, params: AddLocaleParams, id: str = Path(description="国际化类型ID")):
if locale := await Locale.get_or_none(id=id):
if await Locale.get_or_none(code=params.code, name=params.name, id=id):
return Response.error(msg="该国际化类型已存在!")
locale.code = params.code
locale.name = params.name
await locale.save()
return Response.success(msg="修改成功!")
else:
return Response.error(msg="该国际化类型不存在!")
@i18nAPI.get("/locale/info/{id}", response_class=JSONResponse, response_model=GetLocaleInfoResponse,
summary="获取国际化类型信息")
@Log(title="获取国际化类型信息", business_type=BusinessType.SELECT)
async def get_locale_info(request: Request, id: str = Path(description="国际化类型ID")):
if locale := await Locale.get_or_none(id=id):
locale = {
"id": locale.id,
"code": locale.code,
"name": locale.name,
"create_time": locale.create_time,
"update_time": locale.update_time,
"create_by": locale.create_by,
"update_by": locale.update_by,
}
return Response.success(data=locale)
else:
return Response.error(msg="该国际化类型不存在!")
@i18nAPI.get("/locale/list", response_class=JSONResponse, response_model=BaseResponse, summary="获取国际化类型列表")
# @Log(title="获取国际化类型列表", business_type=BusinessType.SELECT)
async def get_locale_list(request: Request,
page: int = Query(default=1, description="当前页码"),
pageSize: int = Query(default=50, description="每页条数"),
name: Optional[str] = Query(default=None, description="国际化类型名称"),
code: Optional[str] = Query(default=None, description="国际化类型代码"),
):
filterArgs = {
f'{k}__contains': v for k, v in {
'name': name,
'code': code
}.items() if v
}
total = await Locale.filter(**filterArgs).count()
data = await Locale.filter(**filterArgs).offset((page - 1) * pageSize).limit(pageSize).distinct().values(
id="id",
code="code",
name="name",
create_time="create_time",
update_time="update_time",
create_by="create_by",
update_by="update_by"
)
return Response.success(data={
"total": total,
"result": data,
"page": page
})
@i18nAPI.post("/addI18n", response_class=JSONResponse, response_model=BaseResponse, summary="添加国际化内容")
@Log(title="添加国际化内容", business_type=BusinessType.INSERT)
async def add_i18n(request: Request, params: AddI18nParams, current_user=Depends(LoginController.get_current_user)):
if await I18n.get_or_none(key=params.key, locale_id=params.locale_id):
return Response.error(msg="该国际化内容已存在!")
locale = await Locale.get_or_none(id=params.locale_id)
i18n = await I18n.create(
key=params.key,
translation=params.translation,
locale=locale
)
if i18n:
return Response.success(msg="添加成功!")
else:
return Response.error(msg="添加失败!")
@i18nAPI.delete("/deleteI18n/{id}", response_class=JSONResponse, response_model=BaseResponse,
summary="删除国际化内容")
@i18nAPI.post("/deleteI18n/{id}", response_class=JSONResponse, response_model=BaseResponse,
summary="删除国际化内容")
@Log(title="删除国际化内容", business_type=BusinessType.DELETE)
async def delete_i18n(request: Request, id: str = Path(description="国际化内容ID"),
current_user=Depends(LoginController.get_current_user)):
if i18n := await I18n.get_or_none(id=id):
await i18n.delete()
return Response.success(msg="删除成功!")
else:
return Response.error(msg="该国际化内容不存在!")
@i18nAPI.put("/updateI18n/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改国际化内容")
@i18nAPI.post("/updateI18n/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改国际化内容")
@Log(title="修改国际化内容", business_type=BusinessType.UPDATE)
async def update_i18n(request: Request, params: AddI18nParams, id: str = Path(description="国际化内容ID"),
current_user=Depends(LoginController.get_current_user)):
if i18n := await I18n.get_or_none(id=id):
locale = await Locale.get_or_none(id=params.locale_id)
i18n.key = params.key
i18n.translation = params.translation
i18n.locale = locale
await i18n.save()
return Response.success(msg="修改成功!")
else:
return Response.error(msg="该国际化内容不存在!")
@i18nAPI.get("/info/{id}", response_class=JSONResponse, response_model=GetI18nInfoResponse,
summary="获取国际化内容信息")
@Log(title="获取国际化内容信息", business_type=BusinessType.SELECT)
async def get_i18n_info(request: Request, id: str = Path(description="国际化内容ID")):
if i18n := await I18n.get_or_none(id=id):
i18n = {
"id": i18n.id,
"key": i18n.key,
"translation": i18n.translation,
"locale_id": i18n.locale.id,
"locale_name": i18n.locale.name,
"create_time": i18n.create_time,
"update_time": i18n.update_time,
"create_by": i18n.create_by,
"update_by": i18n.update_by,
}
return Response.success(data=i18n)
else:
return Response.error(msg="该国际化内容不存在!")
@i18nAPI.get("/list", response_class=JSONResponse, response_model=GetI18nListResponse, summary="获取国际化内容列表")
@Log(title="获取国际化内容列表", business_type=BusinessType.SELECT)
async def get_i18n_list(request: Request,
page: int = Query(default=1, description="当前页码"),
pageSize: int = Query(default=50, description="每页条数"),
key: Optional[str] = Query(default=None, description="国际化内容key"),
locale_id: Optional[str] = Query(default=None, description="国际化内容语言ID"),
translation: Optional[str] = Query(default=None, description="国际化内容翻译内容"),
):
filterArgs = {
f'{k}__contains': v for k, v in {
'key': key,
'locale_id': locale_id,
'translation': translation
}.items() if v
}
total = await I18n.filter(**filterArgs).count()
data = await I18n.filter(**filterArgs).offset((page - 1) * pageSize).limit(pageSize).values(
id="id",
key="key",
translation="translation",
locale_id="locale__id",
locale_code="locale__code",
locale_name="locale__name",
create_time="create_time",
update_time="update_time",
create_by="create_by",
update_by="update_by"
)
return Response.success(data={
"total": total,
"result": data,
"page": page
})
@i18nAPI.get("/infoList/{id}", response_class=JSONResponse, response_model=GetI18nInfoListResponse,
summary="获取国际化列表")
# @Log(title="获取国际化列表", business_type=BusinessType.SELECT)
async def get_i18n_info_list(request: Request, id: str = Path(description="国际化内容语言ID")):
if locale := await Locale.get_or_none(id=id):
result = await request.app.state.redis.get(f'{RedisKeyConfig.TRANSLATION_INFO.key}:{id}')
if result:
result = eval(result)
return Response.success(data=result)
data = await I18n.filter(locale_id=locale.id).values(
id="id",
key="key",
translation="translation",
locale_id="locale__id",
locale_name="locale__name",
create_time="create_time",
update_time="update_time",
create_by="create_by",
update_by="update_by"
)
result = {}
for i18n in data:
result[f"{i18n['key']}"] = i18n["translation"]
await request.app.state.redis.set(f'{RedisKeyConfig.TRANSLATION_INFO.key}:{id}',
str(jsonable_encoder({
"data": result,
"locale": locale.code,
"name": locale.name,
})),
ex=timedelta(minutes=60))
return Response.success(data={
"data": result,
"locale": locale.code,
"name": locale.name,
})
return Response.error(msg="该国际化内容语言不存在!")

183
api/log.py Normal file
View File

@@ -0,0 +1,183 @@
# _*_ coding : UTF-8 _*_
# @Time : 2025/01/27 21:40
# @UpdateTime : 2025/01/27 21:40
# @Author : sonder
# @File : log.py
# @Software : PyCharm
# @Comment : 本程序
from fastapi import APIRouter, Depends, Path, Query, Request
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from annotation.auth import Auth
from annotation.log import Log
from config.constant import BusinessType, RedisKeyConfig
from controller.login import LoginController
from models import LoginLog, OperationLog
from schemas.common import BaseResponse, DeleteListParams
from schemas.log import GetLoginLogResponse, GetOperationLogResponse
from utils.response import Response
logAPI = APIRouter(
prefix="/log",
dependencies=[Depends(LoginController.get_current_user)]
)
@logAPI.get("/login", response_class=JSONResponse, response_model=GetLoginLogResponse, summary="用户获取登录日志")
async def get_login_log(request: Request,
page: int = Query(default=1, description="页码"),
pageSize: int = Query(default=10, description="每页数量"),
current_user: dict = Depends(LoginController.get_current_user),
):
online_user_list = await LoginController.get_online_user(request)
online_user_list = list(
filter(lambda x: x["user_id"] == current_user.get("id"), jsonable_encoder(online_user_list, )))
user_id = current_user.get("id")
result = await LoginLog.filter(user_id=user_id, del_flag=1).offset((page - 1) * pageSize).limit(pageSize).values(
id="id",
user_id="user__id",
username="user__username",
user_nickname="user__nickname",
department_id="user__department__id",
department_name="user__department__name",
login_ip="login_ip",
login_location="login_location",
browser="browser",
os="os",
status="status",
login_time="login_time",
session_id="session_id",
create_time="create_time",
update_time="update_time"
)
for log in result:
log["online"] = False
for item in online_user_list:
if item["session_id"] == log["session_id"]:
log["online"] = True
return Response.success(data={
"total": await LoginLog.filter(user_id=user_id).count(),
"result": result,
"page": page,
})
@logAPI.delete("/logout/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="用户强退")
@logAPI.post("/logout/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="用户强退")
@Log(title="用户强退", business_type=BusinessType.DELETE)
# @Auth(permission_list=["user:btn:logout"])
async def logout_user(request: Request, id: str = Path(description="会话ID"),
current_user: dict = Depends(LoginController.get_current_user)):
if await LoginLog.get_or_none(user_id=current_user.get("id"), session_id=id):
await request.app.state.redis.delete(f"{RedisKeyConfig.ACCESS_TOKEN.key}:{id}")
return Response.success(msg="强退成功!")
return Response.failure(msg="会话不存在!")
@logAPI.delete("/delete/login/{id}", response_model=BaseResponse, response_class=JSONResponse,
summary="用户删除登录日志")
@logAPI.post("/delete/login/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="用户删除登录日志")
@Log(title="用户删除登录日志", business_type=BusinessType.DELETE)
@Auth(permission_list=["login:btn:delete"])
async def delete_login_log(id: str = Path(..., description="登录日志ID"),
current_user: dict = Depends(LoginController.get_current_user)):
if log := await LoginLog.get_or_none(id=id):
if log.user == current_user.get("id"):
log.del_flag = 0
await log.save()
return Response.success(msg="删除成功")
else:
return Response.failure(msg="无权限删除")
else:
return Response.failure(msg="删除失败,登录日志不存在!")
@logAPI.delete("/deleteList/login", response_model=BaseResponse, response_class=JSONResponse,
summary="用户删除登录日志")
@logAPI.post("/deleteList/login", response_model=BaseResponse, response_class=JSONResponse,
summary="用户删除登录日志")
@Log(title="用户批量删除登录日志", business_type=BusinessType.DELETE)
@Auth(permission_list=["login:btn:delete"])
async def delete_login_log(params: DeleteListParams,
current_user: dict = Depends(LoginController.get_current_user)):
for id in set(params.ids):
if log := await LoginLog.get_or_none(id=id):
if log.user == current_user.get("id"):
log.del_flag = 0
await log.save()
return Response.success(msg="删除成功")
@logAPI.get("/operation", response_class=JSONResponse, response_model=GetOperationLogResponse,
summary="用户获取操作日志")
async def get_operation_log(request: Request,
page: int = Query(default=1, description="页码"),
pageSize: int = Query(default=10, description="每页数量"),
current_user: dict = Depends(LoginController.get_current_user),
):
user_id = current_user.get("id")
result = await OperationLog.filter(operator_id=user_id, del_flag=1).offset((page - 1) * pageSize).limit(
pageSize).values(
id="id",
operation_name="operation_name",
operation_type="operation_type",
request_path="request_path",
request_method="request_method",
request_params="request_params",
response_result="response_result",
host="host",
location="location",
browser="browser",
os="os",
user_agent="user_agent",
operator_id="operator__id",
operator_name="operator__username",
operator_nickname="operator__nickname",
department_id="department__id",
department_name="department__name",
status="status",
operation_time="operation_time",
cost_time="cost_time"
)
return Response.success(data={
"total": await OperationLog.filter(operator_id=user_id).count(),
"result": result,
"page": page,
})
@logAPI.delete("/delete/operation/{id}", response_model=BaseResponse, response_class=JSONResponse,
summary="用户删除操作日志")
@logAPI.post("/delete/operation/{id}", response_model=BaseResponse, response_class=JSONResponse,
summary="用户删除操作日志")
@Log(title="用户删除操作日志", business_type=BusinessType.DELETE)
@Auth(permission_list=["operation:btn:delete"])
async def delete_operation_log(id: str = Path(..., description="操作日志id"),
current_user: dict = Depends(LoginController.get_current_user)):
if log := await OperationLog.get_or_none(id=id):
if log.operator == current_user.get("id"):
log.del_flag = 0
await log.save()
return Response.success(msg="删除成功")
else:
return Response.failure(msg="无权限删除")
else:
return Response.failure(msg="删除失败,操作日志不存在!")
@logAPI.delete("/deleteList/operation", response_model=BaseResponse, response_class=JSONResponse,
summary="用户删除操作日志")
@logAPI.post("/deleteList/operation", response_model=BaseResponse, response_class=JSONResponse,
summary="用户删除操作日志")
@Log(title="用户批量删除操作日志", business_type=BusinessType.DELETE)
@Auth(permission_list=["operation:btn:delete"])
async def delete_operation_log(params: DeleteListParams,
current_user: dict = Depends(LoginController.get_current_user)):
for id in set(params.ids):
if log := await OperationLog.get_or_none(id=id):
if log.operator == current_user.get("id"):
log.del_flag = 0
await log.save()
return Response.success(msg="删除成功")

210
api/login.py Normal file
View File

@@ -0,0 +1,210 @@
# _*_ coding : UTF-8 _*_
# @Time : 2025/01/19 01:00
# @UpdateTime : 2025/01/19 01:00
# @Author : sonder
# @File : login.py
# @Software : PyCharm
# @Comment : 本程序
import uuid
from datetime import timedelta, datetime
from fastapi import APIRouter, Request, Depends
from fastapi.encoders import jsonable_encoder
from starlette.responses import JSONResponse
from tortoise.expressions import Q
from annotation.log import Log
from config.constant import BusinessType
from config.constant import RedisKeyConfig
from controller.login import CustomOAuth2PasswordRequestForm, LoginController
from controller.query import QueryController
from models import Department, User
from schemas.common import BaseResponse
from schemas.login import LoginParams, GetUserInfoResponse, LoginResponse, GetCaptchaResponse, GetEmailCodeParams, \
ResetPasswordParams
from schemas.user import RegisterUserParams
from utils.captcha import Captcha
from utils.log import logger
from utils.mail import Email
from utils.password import Password
from utils.response import Response
loginAPI = APIRouter()
@loginAPI.post("/login", response_class=JSONResponse, summary="用户登录")
@Log(title="用户登录", business_type=BusinessType.GRANT, log_type="login")
async def login(
request: Request,
params: CustomOAuth2PasswordRequestForm = Depends()
):
user = LoginParams(
username=params.username,
password=params.password,
loginDays=params.loginDays,
code=params.code,
uuid=params.uuid
)
result = await LoginController.login(user)
if result["status"]:
await request.app.state.redis.set(
f'{RedisKeyConfig.ACCESS_TOKEN.key}:{result["session_id"]}',
result["accessToken"],
ex=timedelta(minutes=result["expiresIn"]),
)
userInfo = str(jsonable_encoder(result["userInfo"]))
await request.app.state.redis.set(
f'{RedisKeyConfig.USER_INFO.key}:{result["userInfo"]["id"]}',
userInfo,
ex=timedelta(minutes=5),
)
request.app.state.session_id = result["session_id"]
# 判断请求是否来自于api文档如果是返回指定格式的结果用于修复api文档认证成功后token显示undefined的bug
request_from_swagger = request.headers.get('referer').endswith('docs') if request.headers.get(
'referer') else False
request_from_redoc = request.headers.get('referer').endswith('redoc') if request.headers.get(
'referer') else False
if request_from_swagger or request_from_redoc:
return {'access_token': result["accessToken"], 'token_type': 'Bearer',
"expires_in": result["expiresIn"] * 60}
result.pop("status")
result.pop("expiresIn")
result.pop("session_id")
result.pop("userInfo")
return Response.success(data=result)
return Response.failure(msg="登录失败,账号或密码错误!")
@loginAPI.post("/register", response_class=JSONResponse, response_model=LoginResponse, summary="用户注册")
async def register(request: Request, params: RegisterUserParams):
result = await Email.verify_code(request, username=params.username, mail=params.email, code=params.code)
if not result["status"]:
return Response.error(msg=result["msg"])
if await QueryController.register_user_before(username=params.username, phone=params.phone, email=params.email):
return Response.error(msg="注册失败,用户已存在!")
params.password = await Password.get_password_hash(input_password=params.password)
department = await Department.get_or_none(id=params.department_id)
user = await User.create(
username=params.username,
password=params.password,
nickname=params.nickname,
phone=params.phone,
email=params.email,
gender=params.gender,
department=department,
status=params.status,
)
if user:
userParams = LoginParams(
username=params.username,
password=params.password
)
result = await LoginController.login(userParams)
if result["status"]:
await request.app.state.redis.set(
f'{RedisKeyConfig.ACCESS_TOKEN.key}:{result["session_id"]}',
result["accessToken"],
ex=timedelta(minutes=result["expiresIn"]),
)
userInfo = str(jsonable_encoder(result["userInfo"]))
await request.app.state.redis.set(
f'{RedisKeyConfig.USER_INFO.key}:{result["userInfo"]["id"]}',
userInfo,
ex=timedelta(minutes=5),
)
result.pop("status")
result.pop("expiresIn")
result.pop("session_id")
result.pop("userInfo")
return Response.success(msg="注册成功!", data=result)
return Response.error(msg="注册成功!")
else:
return Response.error(msg="注册失败!")
@loginAPI.get("/captcha", response_class=JSONResponse, response_model=GetCaptchaResponse, summary="获取验证码")
async def get_captcha(request: Request):
captcha_result = await Captcha.create_captcha("1")
session_id = str(uuid.uuid4())
captcha = captcha_result[0]
result = captcha_result[-1]
await request.app.state.redis.set(
f'{RedisKeyConfig.CAPTCHA_CODES.key}:{session_id}', result, ex=timedelta(minutes=2)
)
logger.info(f'编号为{session_id}的会话获取图片验证码成功')
return Response.success(data={
"uuid": session_id,
"captcha": captcha,
})
@loginAPI.post("/code", response_class=JSONResponse, response_model=BaseResponse, summary="获取邮件验证码")
async def get_code(request: Request, params: GetEmailCodeParams):
result = await Email.send_email(request, username=params.username, title=params.title, mail=params.mail)
if result:
return Response.success(msg="验证码发送成功!")
return Response.error(msg="验证码发送失败!")
@loginAPI.put("/resetPassword", response_class=JSONResponse, response_model=BaseResponse, summary="重置密码")
@loginAPI.post("/resetPassword", response_class=JSONResponse, response_model=BaseResponse, summary="重置密码")
async def reset_password(request: Request, params: ResetPasswordParams):
result = await Email.verify_code(request, username=params.username, mail=params.mail, code=params.code)
if not result["status"]:
return Response.error(msg=result["msg"])
user = await User.get_or_none(Q(username=params.username) | Q(phone=params.username), email=params.mail)
if user:
user.password = await Password.get_password_hash(input_password=params.password)
await user.save()
return Response.success(msg="密码重置成功!")
return Response.error(msg="密码重置失败,用户不存在!")
@loginAPI.get("/info", response_class=JSONResponse, response_model=GetUserInfoResponse, summary="获取用户信息")
@Log(title="获取用户信息", business_type=BusinessType.SELECT)
async def info(
request: Request,
current_user: dict = Depends(LoginController.get_current_user)
):
return Response.success(data=current_user)
@loginAPI.get("/getRoutes", response_class=JSONResponse, summary="获取路由信息")
# @Log(title="获取路由信息", business_type=BusinessType.SELECT)
async def get_routes(request: Request, current_user: dict = Depends(LoginController.get_current_user)):
routes = await request.app.state.redis.get(f'{RedisKeyConfig.USER_ROUTES.key}:{current_user["id"]}')
if routes:
return Response.success(data=eval(routes))
routes = await LoginController.get_user_routes(current_user["id"])
userRoutes = str(jsonable_encoder(routes))
await request.app.state.redis.set(
f'{RedisKeyConfig.USER_ROUTES.key}:{current_user["id"]}',
userRoutes,
ex=timedelta(minutes=5),
)
return Response.success(data=routes)
@loginAPI.post("/refreshToken", response_class=JSONResponse, response_model=LoginResponse, summary="刷新token")
@Log(title="刷新token", business_type=BusinessType.GRANT)
async def refresh_token(request: Request,
current_user: dict = Depends(LoginController.get_current_user)
):
session_id = uuid.uuid4().__str__()
accessToken = await LoginController.create_token(
data={"user": current_user, "id": current_user.get("id"), "session_id": session_id},
expires_delta=timedelta(minutes=2 * 24 * 60))
expiresTime = (datetime.now() + timedelta(minutes=2 * 24 * 60)).timestamp()
refreshToken = await LoginController.create_token(
data={"user": current_user, "id": current_user.get("id"), "session_id": session_id},
expires_delta=timedelta(minutes=(4 * 24 + 2) * 60))
return Response.success(data={"accessToken": accessToken, "refreshToken": refreshToken, "expiresTime": expiresTime})
@loginAPI.post("/logout", response_class=JSONResponse, response_model=BaseResponse, summary="用户登出")
@Log(title="退出登录", business_type=BusinessType.FORCE)
async def logout(request: Request, status: bool = Depends(LoginController.logout)):
if status:
return Response.success(data="退出成功!")
return Response.error(data="登出失败!")

228
api/permission.py Normal file
View File

@@ -0,0 +1,228 @@
# _*_ coding : UTF-8 _*_
# @Time : 2025/01/20 20:33
# @UpdateTime : 2025/01/20 20:33
# @Author : sonder
# @File : permission.py
# @Software : PyCharm
# @Comment : 本程序
from typing import Optional
from fastapi import APIRouter, Depends, Path, Query, Request
from fastapi.responses import JSONResponse
from annotation.log import Log
from config.constant import BusinessType
from controller.login import LoginController
from models import Permission
from schemas.common import BaseResponse
from schemas.permission import AddPermissionParams, GetPermissionInfoResponse, GetPermissionListResponse
from utils.response import Response
permissionAPI = APIRouter(
prefix="/permission",
dependencies=[Depends(LoginController.get_current_user)]
)
@permissionAPI.post("/add", response_model=BaseResponse, response_class=JSONResponse, summary="新增权限")
@Log(title="新增权限", business_type=BusinessType.INSERT)
async def add_permission(request: Request, params: AddPermissionParams):
permission = await Permission.create(
name=params.name,
parent_id=params.parent_id,
path=params.path,
title=params.title,
menu_type=params.menu_type,
rank=params.rank,
show_link=params.show_link,
show_parent=params.show_parent,
active_path=params.active_path,
component=params.component,
redirect=params.redirect,
frame_src=params.frame_src,
frame_loading=params.frame_loading,
keep_alive=params.keep_alive,
auths=params.auths,
icon=params.icon,
extra_icon=params.extra_icon,
enter_transition=params.enter_transition,
leave_transition=params.leave_transition,
fixed_tag=params.fixed_tag,
hidden_tag=params.hidden_tag,
)
if permission:
return Response.success(msg="新增权限成功!")
else:
return Response.error(msg="新增权限失败!")
@permissionAPI.delete("/delete/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除权限")
@permissionAPI.post("/delete/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除权限")
@Log(title="删除权限", business_type=BusinessType.DELETE)
async def delete_permission(request: Request, id: str = Path(description="权限ID")):
if permission := await Permission.get_or_none(id=id):
await permission.delete()
return Response.success(msg="删除权限成功!")
else:
return Response.error(msg="删除权限失败,权限不存在!")
@permissionAPI.put("/update/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="更新权限")
@permissionAPI.post("/update/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="更新权限")
@Log(title="更新权限", business_type=BusinessType.UPDATE)
async def update_permission(request: Request, params: AddPermissionParams, id: str = Path(description="权限ID"), ):
if permission := await Permission.get_or_none(id=id):
permission.name = params.name
permission.parent_id = params.parent_id
permission.path = params.path
permission.title = params.title
permission.menu_type = params.menu_type
permission.rank = params.rank
permission.show_link = params.show_link
permission.show_parent = params.show_parent
permission.active_path = params.active_path
permission.component = params.component
permission.redirect = params.redirect
permission.frame_src = params.frame_src
permission.frame_loading = params.frame_loading
permission.keep_alive = params.keep_alive
permission.auths = params.auths
permission.icon = params.icon
permission.extra_icon = params.extra_icon
permission.enter_transition = params.enter_transition
permission.leave_transition = params.leave_transition
permission.fixed_tag = params.fixed_tag
permission.hidden_tag = params.hidden_tag
await permission.save()
return Response.success(msg="更新权限成功!")
else:
return Response.error(msg="更新权限失败,权限不存在!")
@permissionAPI.get("/info/{id}", response_model=GetPermissionInfoResponse, response_class=JSONResponse,
summary="查询权限详情")
@Log(title="查询权限详情", business_type=BusinessType.SELECT)
async def get_permission(request: Request, id: str = Path(description="权限ID")):
if permission := await Permission.get_or_none(permission_id=id):
permission = await permission.first().values(
id="id",
create_by="create_by",
create_time="create_time",
update_by="update_by",
update_time="update_time",
menu_type="menu_type",
parent_id="parent_id",
path="path",
title="title",
name="name",
rank="rank",
redirect="redirect",
component="component",
icon="icon",
extra_icon="extra_icon",
enter_transition="enter_transition",
leave_transition="leave_transition",
active_path="active_path",
auths="auths",
frame_src="frame_src",
frame_loading="frame_loading",
keep_alive="keep_alive",
hidden_tag="hidden_tag",
fixed_tag="fixed_tag",
show_link="show_link",
show_parent="show_parent",
)
return Response.success(msg="查询权限详情成功!", data=permission)
else:
return Response.error(msg="查询权限详情失败,权限不存在!")
@permissionAPI.get("/list", response_model=GetPermissionListResponse, response_class=JSONResponse,
summary="查询权限列表")
@Log(title="查询权限列表", business_type=BusinessType.SELECT)
async def get_permission_list(
request: Request,
page: int = Query(default=1, description="当前页码"),
pageSize: int = Query(default=10, description="每页条数"),
id: Optional[str] = Query(default=None, description="主键"),
name: Optional[str] = Query(default=None, description="权限名称"),
parentId: Optional[str] = Query(default=None, description="父权限ID"),
path: Optional[str] = Query(default=None, description="权限路径"),
rank: Optional[int] = Query(default=None, description="排序权重"),
menuType: Optional[int] = Query(default=None, description="菜单类型0菜单、1iframe、2外链、3按钮"),
showLink: Optional[bool] = Query(default=None, description="显示菜单"),
showParent: Optional[bool] = Query(default=None, description="显示父级菜单"),
activePath: Optional[str] = Query(default=None, description="激活路径"),
component: Optional[str] = Query(default=None, description="组件路径"),
redirect: Optional[str] = Query(default=None, description="重定向路径"),
frameSrc: Optional[str] = Query(default=None, description="iframe路径"),
frameLoading: Optional[bool] = Query(default=None, description="iframe加载动画"),
keepAlive: Optional[bool] = Query(default=None, description="缓存组件"),
auths: Optional[str] = Query(default=None, description="权限标识"),
icon: Optional[str] = Query(default=None, description="菜单图标"),
extraIcon: Optional[str] = Query(default=None, description="右侧图标"),
enterTransition: Optional[str] = Query(default=None, description="进场动画"),
leaveTransition: Optional[str] = Query(default=None, description="离场动画"),
fixedTag: Optional[bool] = Query(default=None, description="固定标签页"),
hiddenTag: Optional[bool] = Query(default=None, description="隐藏标签页")
):
filterArgs = {
f'{k}__contains': v for k, v in {
"id": id,
"name": name,
"parent_id": parentId,
"path": path,
"rank": rank,
"menu_type": menuType,
"show_link": showLink,
"show_parent": showParent,
"active_path": activePath,
"component": component,
"redirect": redirect,
"frame_src": frameSrc,
"frame_loading": frameLoading,
"keep_alive": keepAlive,
"auths": auths,
"icon": icon,
"extra_icon": extraIcon,
"enter_transition": enterTransition,
"leave_transition": leaveTransition,
"fixed_tag": fixedTag,
"hidden_tag": hiddenTag
}.items() if v
}
total = await Permission.filter(**filterArgs).count()
result = await Permission.filter(**filterArgs).offset((page - 1) * pageSize).limit(pageSize).order_by(
'rank').values(
id="id",
create_by="create_by",
create_time="create_time",
update_by="update_by",
update_time="update_time",
menu_type="menu_type",
parent_id="parent_id",
title="title",
name="name",
path="path",
component="component",
rank="rank",
redirect="redirect",
icon="icon",
extra_icon="extra_icon",
enter_transition="enter_transition",
leave_transition="leave_transition",
active_path="active_path",
auths="auths",
frame_src="frame_src",
frame_loading="frame_loading",
keep_alive="keep_alive",
hidden_tag="hidden_tag",
fixed_tag="fixed_tag",
show_link="show_link",
show_parent="show_parent"
)
return Response.success(data={
"total": total,
"result": result,
"page": page,
})

333
api/role.py Normal file
View File

@@ -0,0 +1,333 @@
# _*_ coding : UTF-8 _*_
# @Time : 2025/01/20 22:41
# @UpdateTime : 2025/01/20 22:41
# @Author : sonder
# @File : role.py
# @Software : PyCharm
# @Comment : 本程序
from typing import Optional
from fastapi import APIRouter, Depends, Path, Query, Request
from fastapi.responses import JSONResponse
from annotation.log import Log
from config.constant import BusinessType
from controller.login import LoginController
from models import Role, Permission, RolePermission, Department
from schemas.common import BaseResponse
from schemas.role import AddRoleParams, AddRolePermissionParams, GetRolePermissionInfoResponse, \
GetRolePermissionListResponse
from utils.common import filterKeyValues
from utils.response import Response
roleAPI = APIRouter(
prefix="/role",
dependencies=[Depends(LoginController.get_current_user)]
)
@roleAPI.post("/add", response_model=BaseResponse, response_class=JSONResponse, summary="新增角色")
@Log(title="新增角色", business_type=BusinessType.INSERT)
async def add_role(request: Request, params: AddRoleParams):
if await Role.get_or_none(code=params.role_code, department_id=params.department_id, del_flag=1):
return Response.error(msg="角色编码已存在!")
department = await Department.get_or_none(id=params.department_id, del_flag=1)
if department:
role = await Role.create(
code=params.code,
name=params.name,
description=params.description,
status=params.status,
department_id=department.id,
)
else:
role = await Role.create(
code=params.role_code,
name=params.role_name,
status=params.status,
description=params.role_description,
department_id=None,
)
if role:
return Response.success(msg="新增角色成功!")
return Response.error(msg="新增角色失败!")
@roleAPI.delete("/delete/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除角色")
@roleAPI.post("/delete/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="删除角色")
@Log(title="删除角色", business_type=BusinessType.DELETE)
async def delete_role(request: Request, id: int = Path(..., description="角色ID")):
if role := await Role.get_or_none(id=id, del_flag=1):
# 移除相应角色权限
await RolePermission.filter(role_id=role.id).delete()
await role.delete()
return Response.success(msg="删除角色成功!")
return Response.error(msg="删除角色失败!")
@roleAPI.put("/update/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="修改角色")
@roleAPI.post("/update/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="修改角色")
@Log(title="修改角色", business_type=BusinessType.UPDATE)
async def update_role(request: Request, params: AddRoleParams, id: str = Path(..., description="角色ID")):
if role := await Role.get_or_none(id=id, del_flag=1):
role.code = params.code
role.name = params.name
role.description = params.description
department = await Department.get_or_none(id=params.department_id, del_flag=1)
role.status = params.status
if department:
role.department_id = department.id
else:
role.department_id = None
await role.save()
return Response.success(msg="修改角色成功!")
return Response.error(msg="修改角色失败!")
@roleAPI.get("/info/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="查询角色详情")
@Log(title="查询角色详情", business_type=BusinessType.SELECT)
async def get_role_info(request: Request, id: int = Path(..., description="角色ID")):
if role := await Role.get_or_none(id=id, del_flag=1).values(
id="id",
create_by="create_by",
create_time="create_time",
update_by="update_by",
update_time="update_time",
code="code",
name="name",
status="status",
description="description",
department_id="department_id",
department_name="department__name",
department_principal="department__principal",
department_phone="department__phone",
department_email="department__email",
):
return Response.success(data=role)
return Response.error(msg="查询角色详情失败!")
@roleAPI.get("/list", response_model=BaseResponse, response_class=JSONResponse, summary="查询角色列表")
@Log(title="查询角色列表", business_type=BusinessType.SELECT)
async def get_role_list(
request: Request,
page: int = Query(1, description="页码"),
pageSize: int = Query(10, description="每页数量"),
name: Optional[str] = Query(None, description="角色名称"),
code: Optional[str] = Query(None, description="角色编码"),
description: Optional[str] = Query(None, description="角色描述"),
department_id: Optional[str] = Query(None, description="所属部门ID"),
status: Optional[int] = Query(None, description="状态"),
current_user: dict = Depends(LoginController.get_current_user)
):
filterArgs = {
f'{k}__contains': v for k, v in {
"name": name,
"code": code,
"description": description,
"status": status
}.items() if v
}
# 如果未提供 department_id则使用当前用户的部门 ID
if not department_id:
department_id = current_user.get("department_id")
# 查询当前部门及其下属部门的角色
all_roles = await get_role_and_subroles(department_id, filterArgs)
# 分页处理
total = len(all_roles)
paginated_roles = all_roles[(page - 1) * pageSize: page * pageSize]
return Response.success(data={
"result": paginated_roles,
"total": total,
"page": page
})
async def get_department_and_subdepartments(department_id: str, visited: set = None):
"""
递归查询当前部门及其所有下属部门的 ID。
:param department_id: 当前部门 ID
:param visited: 已访问的部门 ID 集合,用于避免循环依赖
:return: 部门 ID 列表
"""
if visited is None:
visited = set() # 初始化已访问的部门 ID 集合
# 如果当前部门 ID 已经访问过,直接返回空列表,避免死循环
if department_id in visited:
return []
visited.add(department_id) # 标记当前部门 ID 为已访问
# 查询当前部门
current_department = await Department.filter(
id=department_id
).values_list("id", flat=True)
# 查询直接子部门
sub_departments = await Department.filter(
parent_id=department_id
).values_list("id", flat=True)
# 递归查询子部门的子部门
for sub_department_id in sub_departments[:]: # 使用切片复制避免修改迭代中的列表
sub_sub_departments = await get_department_and_subdepartments(sub_department_id, visited)
sub_departments.extend(sub_sub_departments)
# 合并当前部门和所有下属部门的 ID
return current_department + sub_departments
async def get_role_and_subroles(department_id: str, filterArgs: dict):
"""
查询当前部门及其下属部门的角色。
:param department_id: 当前部门 ID
:param filterArgs: 过滤条件
:return: 角色列表
"""
# 递归查询当前部门及其下属部门的 ID
department_ids = await get_department_and_subdepartments(department_id)
# 查询这些部门的角色
roles = await Role.filter(
department__id__in=department_ids,
**filterArgs
).values(
id="id",
create_by="create_by",
create_time="create_time",
update_by="update_by",
update_time="update_time",
code="code",
name="name",
status="status",
description="description",
department_id="department__id",
department_name="department__name",
department_principal="department__principal",
department_phone="department__phone",
department_email="department__email",
)
# 根据 id 去重
unique_roles = []
seen_ids = set() # 用于记录已经处理过的角色 ID
for role in roles:
if role["id"] not in seen_ids:
unique_roles.append(role)
seen_ids.add(role["id"])
return unique_roles
@roleAPI.post("/addPermission", response_model=BaseResponse, response_class=JSONResponse, summary="新增角色权限")
@Log(title="新增角色权限", business_type=BusinessType.INSERT)
async def add_role_permission(request: Request, params: AddRolePermissionParams,
id: str = Path(..., description="角色ID")):
if role := await Role.get_or_none(id=id, del_flag=1):
# 已有角色权限
rolePermissions = await RolePermission.filter(role_id=id).all().values("permission_id")
rolePermissions = await filterKeyValues(rolePermissions, "permission_id")
# 利用集合筛选出角色权限中不存在的权限
add_list = set(params.permission_ids).difference(set(rolePermissions))
# 循环添加角色权限
for item in add_list:
permission = await Permission.get_or_none(id=item, del_flag=1)
if permission:
await RolePermission.create(
role_id=role.id,
permission_id=permission.id
)
return Response.success(msg="新增角色权限成功!")
return Response.error(msg="新增角色权限失败!")
@roleAPI.delete("/deletePermission/{id}", response_model=BaseResponse, response_class=JSONResponse,
summary="删除角色权限")
@roleAPI.post("/deletePermission/{id}", response_model=BaseResponse, response_class=JSONResponse,
summary="删除角色权限")
@Log(title="删除角色权限", business_type=BusinessType.DELETE)
async def delete_role_permission(request: Request, id: int = Path(..., description="角色权限ID")):
if rolePermission := await RolePermission.get_or_none(id=id, del_flag=1):
await rolePermission.delete()
return Response.success(msg="删除角色权限成功!")
return Response.error(msg="删除角色权限失败!")
@roleAPI.put("/updatePermission/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="修改角色权限")
@roleAPI.post("/updatePermission/{id}", response_model=BaseResponse, response_class=JSONResponse,
summary="修改角色权限")
@Log(title="修改角色权限", business_type=BusinessType.UPDATE)
async def update_role_permission(request: Request, params: AddRolePermissionParams,
id: str = Path(..., description="角色ID")):
if role := await Role.get_or_none(id=id, del_flag=1):
# 已有角色权限
rolePermissions = await RolePermission.filter(role_id=role.id).all().values("permission_id")
rolePermissions = await filterKeyValues(rolePermissions, "permission_id")
# 利用集合筛选出角色权限中不存在的权限
delete_list = set(rolePermissions).difference(set(params.permission_ids))
# 利用集合筛选出角色权限中新增的权限
add_list = set(params.permission_ids).difference(set(rolePermissions))
# 循环删除角色权限
for item in delete_list:
await RolePermission.filter(role_id=id, permission_id=item).delete()
# 循环添加角色权限
for item in add_list:
await RolePermission.create(role_id=id, permission_id=item)
return Response.success(msg="修改角色权限成功!")
return Response.error(msg="修改角色权限失败!")
@roleAPI.get("/permissionInfo/{id}", response_model=GetRolePermissionInfoResponse, response_class=JSONResponse,
summary="获取角色权限信息")
@Log(title="获取角色权限信息", business_type=BusinessType.SELECT)
async def get_role_permission_info(request: Request, id: int = Path(..., description="角色权限ID")):
if rolePermission := await RolePermission.get_or_none(id=id, del_flag=1):
data = await rolePermission.first().values(
id="id",
create_by="create_by",
create_time="create_time",
update_by="update_by",
update_time="update_time",
role_id="role__id",
role_name="role__name",
role_code="role__code",
permission_id="permission__id",
permission_name="permission__title",
permission_auth="permission__auths",
permission_type="permission__menu_type"
)
return Response.success(data=data)
return Response.error(msg="获取角色权限信息失败!")
@roleAPI.get("/permissionList/{id}", response_model=GetRolePermissionListResponse, response_class=JSONResponse,
summary="获取角色权限列表")
@Log(title="获取角色权限列表", business_type=BusinessType.SELECT)
async def get_role_permission_list(request: Request, id: str = Path(..., description="角色ID")):
total = await RolePermission.filter(role_id=id).count()
data = await RolePermission.filter(role_id=id).values(
id="id",
create_by="create_by",
create_time="create_time",
update_by="update_by",
update_time="update_time",
role_id="role__id",
role_name="role__name",
role_code="role__code",
permission_id="permission__id",
permission_parent_id="permission__parent_id",
permission_name="permission__title",
permission_auth="permission__auths",
permission_type="permission__menu_type"
)
return Response.success(data={
"result": data,
"total": total,
"page": 1
})

112
api/server.py Normal file
View File

@@ -0,0 +1,112 @@
# _*_ coding : UTF-8 _*_
# @Time : 2025/02/04 15:25
# @UpdateTime : 2025/02/04 15:25
# @Author : sonder
# @File : server.py
# @Software : PyCharm
# @Comment : 本程序
import os
import platform
import socket
import time
import psutil
from fastapi import APIRouter, Depends, Request
from fastapi.responses import JSONResponse
from annotation.log import Log
from config.constant import BusinessType
from controller.login import LoginController
from schemas.server import GetServerInfoResponse, CpuInfo, MemoryInfo, SystemInfo, PythonInfo, SystemFiles, \
GetSystemInfoResult
from utils.common import bytes2human
from utils.response import Response
serverAPI = APIRouter(
prefix="/server",
dependencies=[Depends(LoginController.get_current_user)]
)
@serverAPI.get("", response_class=JSONResponse, response_model=GetServerInfoResponse, summary="获取服务器信息")
@Log(title="获取服务器信息", business_type=BusinessType.SELECT)
async def get_server_info(request: Request):
# CPU信息
# 获取CPU总核心数
cpu_num = psutil.cpu_count(logical=True)
cpu_usage_percent = psutil.cpu_times_percent()
cpu_used = cpu_usage_percent.user
cpu_sys = cpu_usage_percent.system
cpu_free = cpu_usage_percent.idle
cpu = CpuInfo(cpuNum=cpu_num, used=cpu_used, sys=cpu_sys, free=cpu_free)
# 内存信息
memory_info = psutil.virtual_memory()
memory_total = bytes2human(memory_info.total)
memory_used = bytes2human(memory_info.used)
memory_free = bytes2human(memory_info.free)
memory_usage = memory_info.percent
mem = MemoryInfo(total=memory_total, used=memory_used, free=memory_free, usage=memory_usage)
# 主机信息
# 获取主机名
hostname = socket.gethostname()
# 获取IP
computer_ip = socket.gethostbyname(hostname)
os_name = platform.platform()
computer_name = platform.node()
os_arch = platform.machine()
user_dir = os.path.abspath(os.getcwd())
sys = SystemInfo(
computerIp=computer_ip, computerName=computer_name, osArch=os_arch, osName=os_name, userDir=user_dir
)
# python解释器信息
current_pid = os.getpid()
current_process = psutil.Process(current_pid)
python_name = current_process.name()
python_version = platform.python_version()
python_home = current_process.exe()
start_time_stamp = current_process.create_time()
start_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(start_time_stamp))
current_time_stamp = time.time()
difference = current_time_stamp - start_time_stamp
# 将时间差转换为天、小时和分钟数
days = int(difference // (24 * 60 * 60)) # 每天的秒数
hours = int((difference % (24 * 60 * 60)) // (60 * 60)) # 每小时的秒数
minutes = int((difference % (60 * 60)) // 60) # 每分钟的秒数
run_time = f'{days}{hours}小时{minutes}分钟'
# 获取当前Python程序的pid
pid = os.getpid()
# 获取该进程的内存信息
current_process_memory_info = psutil.Process(pid).memory_info()
py = PythonInfo(
name=python_name,
version=python_version,
startTime=start_time,
runTime=run_time,
home=python_home,
total=bytes2human(memory_info.available),
used=bytes2human(current_process_memory_info.rss),
free=bytes2human(memory_info.available - current_process_memory_info.rss),
usage=round((current_process_memory_info.rss / memory_info.available) * 100, 2),
)
# 磁盘信息
io = psutil.disk_partitions()
sys_files = []
for i in io:
o = psutil.disk_usage(i.device)
disk_data = SystemFiles(
dirName=i.device,
sysTypeName=i.fstype,
typeName='本地固定磁盘(' + i.mountpoint.replace('\\', '') + '',
total=bytes2human(o.total),
used=bytes2human(o.used),
free=bytes2human(o.free),
usage=f'{psutil.disk_usage(i.device).percent}%',
)
sys_files.append(disk_data)
result = GetSystemInfoResult(cpu=cpu, memory=mem, system=sys, python=py, systemFiles=sys_files)
return Response.success(data=result)

349
api/user.py Normal file
View File

@@ -0,0 +1,349 @@
# _*_ coding : UTF-8 _*_
# @Time : 2025/01/20 00:31
# @UpdateTime : 2025/01/20 00:31
# @Author : sonder
# @File : user.py
# @Software : PyCharm
# @Comment : 本程序
import os
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, Path, Query, UploadFile, File, Request
from fastapi.responses import JSONResponse
from annotation.auth import Auth
from annotation.log import Log
from config.constant import BusinessType
from config.env import UploadConfig
from controller.login import LoginController
from controller.query import QueryController
from exceptions.exception import ModelValidatorException
from models import File as FileModel
from models import Role, Department
from models.user import User, UserRole
from schemas.common import BaseResponse
from schemas.department import GetDepartmentListResponse
from schemas.file import UploadFileResponse
from schemas.user import AddUserParams, GetUserListResponse, GetUserInfoResponse, UpdateUserParams, \
AddUserRoleParams, GetUserRoleInfoResponse, UpdateUserRoleParams, GetUserPermissionListResponse, ResetPasswordParams
from utils.common import filterKeyValues
from utils.password import Password
from utils.response import Response
userAPI = APIRouter(prefix="/user", dependencies=[Depends(LoginController.get_current_user)])
@userAPI.post("/add", response_class=JSONResponse, response_model=BaseResponse, summary="新增用户")
@Log(title="新增用户", business_type=BusinessType.INSERT)
async def add_user(
request: Request,
params: AddUserParams
):
if await QueryController.register_user_before(username=params.username, phone=params.phone, email=params.email):
return Response.error(msg="添加失败,用户已存在!")
params.password = await Password.get_password_hash(input_password=params.password)
department = await Department.get_or_none(id=params.department_id)
user = await User.create(
username=params.username,
password=params.password,
nickname=params.nickname,
phone=params.phone,
email=params.email,
gender=params.gender,
department=department,
status=params.status,
)
if user:
return Response.success(msg="添加成功!")
else:
return Response.error(msg="添加失败!")
@userAPI.delete("/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除用户")
@userAPI.post("/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除用户")
@Log(title="删除用户", business_type=BusinessType.DELETE)
async def delete_user(
request: Request,
id: str = Path(..., description="用户ID")):
if user := await User.get_or_none(id=id):
await user.delete()
return Response.success(msg="删除成功!")
else:
return Response.error(msg="删除失败,用户不存在!")
@userAPI.put("/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="更新用户")
@userAPI.post("/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="更新用户")
@Log(title="更新用户", business_type=BusinessType.UPDATE)
async def update_user(
request: Request,
params: UpdateUserParams,
id: str = Path(..., description="用户ID")):
if user := await User.get_or_none(id=id):
user.username = params.username
user.nickname = params.nickname
user.phone = params.phone
user.email = params.email
user.gender = params.gender
user.status = params.status
if department := await Department.get_or_none(id=params.department_id):
user.department = department
else:
user.department = None
await user.save()
return Response.success(msg="更新成功!")
else:
return Response.error(msg="更新失败,用户不存在!")
@userAPI.get("/info/{id}", response_class=JSONResponse, response_model=GetUserInfoResponse, summary="获取用户信息")
@Log(title="获取用户信息", business_type=BusinessType.SELECT)
async def get_user_info(request: Request, id: str = Path(..., description="用户ID")):
if user := await User.get_or_none(id=id):
user = await user.first().values(
id="id",
create_time="create_time",
update_time="update_time",
username="username",
email="email",
phone="phone",
nickname="nickname",
gender="gender",
status="status",
department_id="department__id",
)
return Response.success(data=user)
else:
return Response.error(msg="用户不存在!")
@userAPI.get("/list", response_class=JSONResponse, response_model=GetUserListResponse, summary="获取用户列表")
@Log(title="获取用户列表", business_type=BusinessType.SELECT)
@Auth(["user:btn:queryUser"])
async def get_user_list(
request: Request,
page: int = Query(default=1, description="当前页码"),
pageSize: int = Query(default=10, description="每页数量"),
username: Optional[str] = Query(default=None, description="用户名"),
nickname: Optional[str] = Query(default=None, description="昵称"),
phone: Optional[str] = Query(default=None, description="手机号"),
email: Optional[str] = Query(default=None, description="邮箱"),
gender: Optional[str] = Query(default=None, description="性别"),
status: Optional[str] = Query(default=None, description="状态"),
department_id: Optional[str] = Query(default=None, description="部门ID"),
):
filterArgs = {
f'{k}__contains': v for k, v in {
'username': username,
'nickname': nickname,
'phone': phone,
'email': email,
'gender': gender,
'status': status,
'department_id': department_id
}.items() if v
}
total = await User.filter(**filterArgs).count()
result = await User.filter(**filterArgs).offset((page - 1) * pageSize).limit(pageSize).values(
id="id",
create_time="create_time",
update_time="update_time",
username="username",
email="email",
phone="phone",
nickname="nickname",
gender="gender",
status="status",
department_id="department__id",
)
return Response.success(data={
"result": result,
"total": total,
"page": page
})
@userAPI.post("/addRole", response_model=BaseResponse, response_class=JSONResponse, summary="添加用户角色")
@Log(title="添加用户角色", business_type=BusinessType.INSERT)
async def add_user_role(request: Request, params: AddUserRoleParams):
if await UserRole.get_or_none(user_id=params.user_id, role_id=params.role_id, del_flag=1):
return Response.error(msg="该用户已存在该角色!")
if user := await User.get_or_none(id=params.user_id, del_flag=1):
if role := await Role.get_or_none(id=params.role_id, del_flag=1):
userRole = await UserRole.create(user_id=user.id, role_id=role.id)
if userRole:
return Response.success(msg="添加成功!")
else:
return Response.error(msg="添加失败!")
else:
return Response.error(msg="添加失败,角色不存在!")
else:
return Response.error(msg="添加失败,用户不存在!")
@userAPI.delete("/deleteRole/{id}", response_model=BaseResponse, response_class=JSONResponse,
summary="删除用户角色")
@userAPI.post("/deleteRole/{id}", response_model=BaseResponse, response_class=JSONResponse,
summary="删除用户角色")
@Log(title="删除用户角色", business_type=BusinessType.DELETE)
async def delete_user_role(request: Request, id: str = Path(description="用户角色ID")):
if userRole := await UserRole.get_or_none(id=id, del_flag=1):
await userRole.delete()
return Response.success(msg="删除成功!")
else:
return Response.error(msg="删除失败,用户角色不存在!")
@userAPI.put("/updateRole", response_model=BaseResponse, response_class=JSONResponse, summary="修改用户角色")
@userAPI.post("/updateRole", response_model=BaseResponse, response_class=JSONResponse,
summary="修改用户角色")
@Log(title="修改用户角色", business_type=BusinessType.UPDATE)
async def update_user_role(request: Request, params: UpdateUserRoleParams):
# 获取用户已有角色
userRoles = await UserRole.filter(user_id=params.user_id, del_flag=1).values("role_id")
userRoles = await filterKeyValues(userRoles, "role_id")
# 利用集合找到需要添加的角色
addRoles = set(params.role_ids).difference(set(userRoles))
# 利用集合找到需要删除的角色
deleteRoles = set(userRoles).difference(set(params.role_ids))
# 添加角色
for role_id in addRoles:
if role := await Role.get_or_none(id=role_id, del_flag=1):
await UserRole.create(user_id=params.user_id, role_id=role.id)
# 删除角色
for role_id in deleteRoles:
if userRole := await UserRole.get_or_none(user_id=params.user_id, role_id=role_id, del_flag=1):
await userRole.delete()
return Response.success(msg="修改成功!")
@userAPI.get("/roleInfo/{id}", response_model=GetUserRoleInfoResponse, response_class=JSONResponse,
summary="获取用户角色信息")
@Log(title="获取用户角色信息", business_type=BusinessType.SELECT)
async def get_user_role_info(request: Request, id: str = Path(description="用户角色ID")):
if userRole := await UserRole.get_or_none(id=id, del_flag=1):
data = await userRole.first().values(
id="id",
user_id="user__id",
user_name="user__username",
role_name="role__name",
role_code="role__code",
role_id="role__id",
create_time="create_time",
update_time="update_time"
)
return Response.success(data=data)
else:
return Response.error(msg="获取失败,用户角色不存在!")
@userAPI.get("/roleList/{id}", response_model=GetDepartmentListResponse, response_class=JSONResponse,
summary="获取用户角色列表")
@Log(title="获取用户角色列表", business_type=BusinessType.SELECT)
async def get_user_role_list(
request: Request,
id: str = Path(description="用户ID"),
):
result = await UserRole.filter(user_id=id).values(
id="id",
department_id="user__department__id",
department_name="user__department__name",
department_phone="user__department__phone",
department_principal="user__department__principal",
department_email="user__department__email",
role_name="role__name",
role_code="role__code",
role_id="role__id",
create_time="create_time",
update_time="update_time"
)
return Response.success(data={
"result": result,
"total": len(result),
"page": 1
})
@userAPI.get("/permissionList/{id}", response_class=JSONResponse, response_model=GetUserPermissionListResponse,
summary="获取用户权限列表")
@Log(title="获取用户权限列表", business_type=BusinessType.SELECT)
async def get_user_permission_list(request: Request, id: str = Path(description="用户ID")):
permissions = await QueryController.get_user_permissions(user_id=id)
permissions = await filterKeyValues(permissions, "id")
# 获取用户角色
return Response.success(data=list(set(permissions)))
@userAPI.post("/avatar/{id}", response_model=UploadFileResponse, response_class=JSONResponse, summary="上传用户头像")
@Log(title="上传用户头像", business_type=BusinessType.UPDATE)
async def upload_user_avatar(
request: Request,
id: str = Path(description="用户ID"),
file: UploadFile = File(...)):
if user := await User.get_or_none(id=id):
image_mimetypes = [
'image/jpeg',
'image/png',
'image/gif',
'image/svg+xml',
'image/bmp',
'image/webp',
'image/tiff'
]
if file.content_type not in image_mimetypes:
raise ModelValidatorException(message="文件类型不支持")
# 2. 生成唯一的文件名
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
unique_filename = f"{id}_{timestamp}"
# 3. 保存文件到服务器
file_path = os.path.join(UploadConfig.UPLOAD_PATH, unique_filename)
with open(file_path, "wb") as buffer:
buffer.write(await file.read())
# 4. 构建文件的相对路径和绝对路径
relative_path = os.path.join(UploadConfig.UPLOAD_PREFIX, unique_filename) # 相对路径
absolute_path = os.path.abspath(file_path) # 绝对路径
# 5. 将文件信息保存到数据库
file_record = await FileModel.create(
name=file.filename,
size=os.path.getsize(file_path),
file_type=file.content_type,
absolute_path=absolute_path,
relative_path=relative_path,
uploader_id=id,
)
user.avatar = f"/file/{file_record.id}"
await user.save()
result = await file_record.first().values(
id="id",
name="name",
size="size",
file_type="file_type",
relative_path="relative_path",
absolute_path="absolute_path",
uploader_id="uploader__id",
uploader_username="uploader__username",
uploader_nickname="uploader__nickname",
uploader_department_id="uploader__department__id",
uploader_department_name="uploader__department__name",
create_time="create_time",
update_time="update_time",
)
return Response.success(data=result)
return Response.failure(msg="用户不存在!")
@userAPI.put("/resetPassword/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="重置用户密码")
@userAPI.post("/resetPassword/{id}", response_model=BaseResponse, response_class=JSONResponse, summary="重置用户密码")
@Log(title="重置用户密码", business_type=BusinessType.UPDATE)
@Auth(permission_list=["user:btn:reset_password"])
async def reset_user_password(request: Request, params: ResetPasswordParams, id: str = Path(description="用户ID")):
if user := await User.get_or_none(id=id):
user.password = await Password.get_password_hash(params.password)
await user.save()
return Response.success(msg="重置密码成功!")
return Response.failure(msg="用户不存在!")