feat: 初始化仓库
This commit is contained in:
7
api/__init__.py
Normal file
7
api/__init__.py
Normal 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
116
api/cache.py
Normal 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
390
api/department.py
Normal 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
198
api/file.py
Normal 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
266
api/i18n.py
Normal 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
183
api/log.py
Normal 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
210
api/login.py
Normal 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
228
api/permission.py
Normal 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
333
api/role.py
Normal 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
112
api/server.py
Normal 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
349
api/user.py
Normal 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="用户不存在!")
|
||||
Reference in New Issue
Block a user