初始提交
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Path, Request, Response
|
||||
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import PreAuthDependency
|
||||
from common.constant import ApiNamespace
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DataResponseModel, ResponseBaseModel
|
||||
from module_admin.entity.vo.cache_vo import CacheInfoModel, CacheMonitorModel
|
||||
from module_admin.service.cache_service import CacheService
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
cache_controller = APIRouterPro(
|
||||
prefix='/monitor/cache', order_num=15, tags=['系统监控-缓存监控'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@cache_controller.get(
|
||||
'',
|
||||
summary='获取缓存监控信息接口',
|
||||
description='用于获取缓存监控信息',
|
||||
response_model=DataResponseModel[CacheMonitorModel],
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
|
||||
)
|
||||
async def get_monitor_cache_info(request: Request) -> Response:
|
||||
# 获取全量数据
|
||||
cache_info_query_result = await CacheService.get_cache_monitor_statistical_info_services(request)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=cache_info_query_result)
|
||||
|
||||
|
||||
@cache_controller.get(
|
||||
'/getNames',
|
||||
summary='获取缓存名称列表接口',
|
||||
description='用于获取缓存名称列表',
|
||||
response_model=DataResponseModel[list[CacheInfoModel]],
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
|
||||
)
|
||||
async def get_monitor_cache_name(request: Request) -> Response:
|
||||
# 获取全量数据
|
||||
cache_name_list_result = await CacheService.get_cache_monitor_cache_name_services()
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=cache_name_list_result)
|
||||
|
||||
|
||||
@cache_controller.get(
|
||||
'/getKeys/{cache_name}',
|
||||
summary='获取缓存键列表接口',
|
||||
description='用于获取指定缓存名称下的所有缓存键列表',
|
||||
response_model=DataResponseModel[list[str]],
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
|
||||
)
|
||||
async def get_monitor_cache_key(request: Request, cache_name: Annotated[str, Path(description='缓存名称')]) -> Response:
|
||||
# 获取全量数据
|
||||
cache_key_list_result = await CacheService.get_cache_monitor_cache_key_services(request, cache_name)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=cache_key_list_result)
|
||||
|
||||
|
||||
@cache_controller.get(
|
||||
'/getValue/{cache_name}/{cache_key}',
|
||||
summary='获取缓存值接口',
|
||||
description='用于获取指定缓存名称下的指定缓存键对应的值',
|
||||
response_model=DataResponseModel[CacheInfoModel],
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
|
||||
)
|
||||
async def get_monitor_cache_value(
|
||||
request: Request,
|
||||
cache_name: Annotated[str, Path(description='缓存名称')],
|
||||
cache_key: Annotated[str, Path(description='缓存键')],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
cache_value_list_result = await CacheService.get_cache_monitor_cache_value_services(request, cache_name, cache_key)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=cache_value_list_result)
|
||||
|
||||
|
||||
@cache_controller.delete(
|
||||
'/clearCacheName/{cache_name}',
|
||||
summary='清除缓存名称接口',
|
||||
description='用于清除指定缓存名称下的所有缓存键值对',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_CACHE_CLEAR_NAME, preset=ApiRateLimitPreset.USER_DESTRUCTIVE_MUTATION)
|
||||
async def clear_monitor_cache_name(
|
||||
request: Request, cache_name: Annotated[str, Path(description='缓存名称')]
|
||||
) -> Response:
|
||||
clear_cache_name_result = await CacheService.clear_cache_monitor_cache_name_services(request, cache_name)
|
||||
logger.info(clear_cache_name_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=clear_cache_name_result.message)
|
||||
|
||||
|
||||
@cache_controller.delete(
|
||||
'/clearCacheKey/{cache_key}',
|
||||
summary='清除缓存键接口',
|
||||
description='用于清除指定缓存键对应的值',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_CACHE_CLEAR_KEY, preset=ApiRateLimitPreset.USER_DESTRUCTIVE_MUTATION)
|
||||
async def clear_monitor_cache_key(request: Request, cache_key: Annotated[str, Path(description='缓存键')]) -> Response:
|
||||
clear_cache_key_result = await CacheService.clear_cache_monitor_cache_key_services(request, cache_key)
|
||||
logger.info(clear_cache_key_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=clear_cache_key_result.message)
|
||||
|
||||
|
||||
@cache_controller.delete(
|
||||
'/clearCacheAll',
|
||||
summary='清除所有缓存接口',
|
||||
description='用于清除所有缓存键值对',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:cache:list')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_CACHE_CLEAR_ALL, preset=ApiRateLimitPreset.USER_DESTRUCTIVE_MUTATION)
|
||||
async def clear_monitor_cache_all(request: Request) -> Response:
|
||||
clear_cache_all_result = await CacheService.clear_cache_monitor_all_services(request)
|
||||
logger.info(clear_cache_all_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=clear_cache_all_result.message)
|
||||
@@ -0,0 +1,46 @@
|
||||
import uuid
|
||||
from datetime import timedelta
|
||||
|
||||
from fastapi import Request, Response
|
||||
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
|
||||
from common.constant import ApiNamespace
|
||||
from common.enums import RedisInitKeyConfig
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DynamicResponseModel
|
||||
from module_admin.entity.vo.login_vo import CaptchaCode
|
||||
from module_admin.service.captcha_service import CaptchaService
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
captcha_controller = APIRouterPro(order_num=2, tags=['验证码模块'])
|
||||
|
||||
|
||||
@captcha_controller.get(
|
||||
'/captchaImage',
|
||||
summary='获取图片验证码接口',
|
||||
description='用于获取图片验证码',
|
||||
response_model=DynamicResponseModel[CaptchaCode],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.CAPTCHA_IMAGE, preset=ApiRateLimitPreset.ANON_AUTH_CAPTCHA)
|
||||
async def get_captcha_image(request: Request) -> Response:
|
||||
captcha_enabled = (
|
||||
await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.captchaEnabled') == 'true'
|
||||
)
|
||||
register_enabled = (
|
||||
await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.registerUser') == 'true'
|
||||
)
|
||||
session_id = str(uuid.uuid4())
|
||||
captcha_result = await CaptchaService.create_captcha_image_service()
|
||||
image = captcha_result[0]
|
||||
computed_result = captcha_result[1]
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.CAPTCHA_CODES.key}:{session_id}', computed_result, ex=timedelta(minutes=2)
|
||||
)
|
||||
logger.info(f'编号为{session_id}的会话获取图片验证码成功')
|
||||
|
||||
return ResponseUtil.success(
|
||||
model_content=CaptchaCode(
|
||||
captchaEnabled=captcha_enabled, registerEnabled=register_enabled, img=image, uuid=session_id
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,77 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import BackgroundTasks, File, Query, Request, Response, UploadFile
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
|
||||
from common.aspect.pre_auth import PreAuthDependency
|
||||
from common.constant import ApiNamespace
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DynamicResponseModel
|
||||
from module_admin.entity.vo.common_vo import UploadResponseModel
|
||||
from module_admin.service.common_service import CommonService
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
common_controller = APIRouterPro(prefix='/common', order_num=16, tags=['通用模块'], dependencies=[PreAuthDependency()])
|
||||
|
||||
|
||||
@common_controller.post(
|
||||
'/upload',
|
||||
summary='通用文件上传接口',
|
||||
description='用于上传文件',
|
||||
response_model=DynamicResponseModel[UploadResponseModel],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.COMMON_UPLOAD, preset=ApiRateLimitPreset.COMMON_UPLOAD)
|
||||
async def common_upload(request: Request, file: Annotated[UploadFile, File(...)]) -> Response:
|
||||
upload_result = await CommonService.upload_service(request, file)
|
||||
logger.info('上传成功')
|
||||
|
||||
return ResponseUtil.success(model_content=upload_result.result)
|
||||
|
||||
|
||||
@common_controller.get(
|
||||
'/download',
|
||||
summary='通用文件下载接口',
|
||||
description='用于下载下载目录中的文件',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
async def common_download(
|
||||
request: Request,
|
||||
background_tasks: BackgroundTasks,
|
||||
file_name: Annotated[str, Query(alias='fileName')],
|
||||
delete: Annotated[bool, Query()],
|
||||
) -> Response:
|
||||
download_result = await CommonService.download_services(background_tasks, file_name, delete)
|
||||
logger.info(download_result.message)
|
||||
|
||||
return ResponseUtil.streaming(data=download_result.result)
|
||||
|
||||
|
||||
@common_controller.get(
|
||||
'/download/resource',
|
||||
summary='通用资源文件下载接口',
|
||||
description='用于下载上传目录中的资源文件',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
async def common_download_resource(request: Request, resource: Annotated[str, Query()]) -> Response:
|
||||
download_resource_result = await CommonService.download_resource_services(resource)
|
||||
logger.info(download_resource_result.message)
|
||||
|
||||
return ResponseUtil.streaming(data=download_resource_result.result)
|
||||
@@ -0,0 +1,201 @@
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Form, Path, Query, Request, Response
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic_validation_decorator import ValidateFields
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.annotation.cache_annotation import ApiCache, ApiCacheEvict
|
||||
from common.annotation.log_annotation import Log
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
|
||||
from common.aspect.db_seesion import DBSessionDependency
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
|
||||
from common.constant import ApiGroup, ApiNamespace
|
||||
from common.enums import BusinessType
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel
|
||||
from module_admin.entity.vo.config_vo import ConfigModel, ConfigPageQueryModel, DeleteConfigModel
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel
|
||||
from module_admin.service.config_service import ConfigService
|
||||
from utils.common_util import bytes2file_response
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
config_controller = APIRouterPro(
|
||||
prefix='/system/config', order_num=9, tags=['系统管理-参数管理'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@config_controller.get(
|
||||
'/list',
|
||||
summary='获取参数分页列表接口',
|
||||
description='用于获取参数分页列表',
|
||||
response_model=PageResponseModel[ConfigModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:config:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_CONFIG_LIST)
|
||||
async def get_system_config_list(
|
||||
request: Request,
|
||||
config_page_query: Annotated[ConfigPageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取分页数据
|
||||
config_page_query_result = await ConfigService.get_config_list_services(query_db, config_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=config_page_query_result)
|
||||
|
||||
|
||||
@config_controller.post(
|
||||
'',
|
||||
summary='新增参数接口',
|
||||
description='用于新增参数',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:config:add')],
|
||||
)
|
||||
@ValidateFields(validate_model='add_config')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.CONFIG_MUTATION)
|
||||
@Log(title='参数管理', business_type=BusinessType.INSERT)
|
||||
async def add_system_config(
|
||||
request: Request,
|
||||
add_config: ConfigModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
add_config.create_by = current_user.user.user_name
|
||||
add_config.create_time = datetime.now()
|
||||
add_config.update_by = current_user.user.user_name
|
||||
add_config.update_time = datetime.now()
|
||||
add_config_result = await ConfigService.add_config_services(request, query_db, add_config)
|
||||
logger.info(add_config_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=add_config_result.message)
|
||||
|
||||
|
||||
@config_controller.put(
|
||||
'',
|
||||
summary='编辑参数接口',
|
||||
description='用于编辑参数',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:config:edit')],
|
||||
)
|
||||
@ValidateFields(validate_model='edit_config')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.CONFIG_MUTATION)
|
||||
@Log(title='参数管理', business_type=BusinessType.UPDATE)
|
||||
async def edit_system_config(
|
||||
request: Request,
|
||||
edit_config: ConfigModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
edit_config.update_by = current_user.user.user_name
|
||||
edit_config.update_time = datetime.now()
|
||||
edit_config_result = await ConfigService.edit_config_services(request, query_db, edit_config)
|
||||
logger.info(edit_config_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_config_result.message)
|
||||
|
||||
|
||||
@config_controller.delete(
|
||||
'/refreshCache',
|
||||
summary='刷新参数缓存接口',
|
||||
description='用于刷新参数缓存',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:config:remove')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_CONFIG_REFRESH_CACHE, preset=ApiRateLimitPreset.USER_COMMON_MUTATION)
|
||||
@Log(title='参数管理', business_type=BusinessType.UPDATE)
|
||||
async def refresh_system_config(
|
||||
request: Request,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
refresh_config_result = await ConfigService.refresh_sys_config_services(request, query_db)
|
||||
logger.info(refresh_config_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=refresh_config_result.message)
|
||||
|
||||
|
||||
@config_controller.delete(
|
||||
'/{config_ids}',
|
||||
summary='删除参数接口',
|
||||
description='用于删除参数',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:config:remove')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.CONFIG_MUTATION)
|
||||
@Log(title='参数管理', business_type=BusinessType.DELETE)
|
||||
async def delete_system_config(
|
||||
request: Request,
|
||||
config_ids: Annotated[str, Path(description='需要删除的参数主键')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
delete_config = DeleteConfigModel(configIds=config_ids)
|
||||
delete_config_result = await ConfigService.delete_config_services(request, query_db, delete_config)
|
||||
logger.info(delete_config_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_config_result.message)
|
||||
|
||||
|
||||
@config_controller.get(
|
||||
'/{config_id}',
|
||||
summary='获取参数详情接口',
|
||||
description='用于获取指定参数的详细信息',
|
||||
response_model=DataResponseModel[ConfigModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:config:query')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_CONFIG_DETAIL)
|
||||
async def query_detail_system_config(
|
||||
request: Request,
|
||||
config_id: Annotated[int, Path(description='参数主键')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
config_detail_result = await ConfigService.config_detail_services(query_db, config_id)
|
||||
logger.info(f'获取config_id为{config_id}的信息成功')
|
||||
|
||||
return ResponseUtil.success(data=config_detail_result)
|
||||
|
||||
|
||||
@config_controller.get(
|
||||
'/configKey/{config_key}',
|
||||
summary='根据参数键查询参数值接口',
|
||||
description='用于根据参数键从缓存中查询参数值',
|
||||
response_model=ResponseBaseModel,
|
||||
)
|
||||
async def query_system_config(request: Request, config_key: str) -> Response:
|
||||
# 获取全量数据
|
||||
config_query_result = await ConfigService.query_config_list_from_cache_services(request.app.state.redis, config_key)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(msg=config_query_result)
|
||||
|
||||
|
||||
@config_controller.post(
|
||||
'/export',
|
||||
summary='导出参数列表接口',
|
||||
description='用于导出当前符合查询条件的参数列表数据',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回参数列表excel文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
dependencies=[UserInterfaceAuthDependency('system:config:export')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_CONFIG_EXPORT, preset=ApiRateLimitPreset.USER_RESOURCE_EXPORT)
|
||||
@Log(title='参数管理', business_type=BusinessType.EXPORT)
|
||||
async def export_system_config_list(
|
||||
request: Request,
|
||||
config_page_query: Annotated[ConfigPageQueryModel, Form()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
config_query_result = await ConfigService.get_config_list_services(query_db, config_page_query, is_page=False)
|
||||
config_export_result = await ConfigService.export_config_list_services(config_query_result)
|
||||
logger.info('导出成功')
|
||||
|
||||
return ResponseUtil.streaming(data=bytes2file_response(config_export_result))
|
||||
@@ -0,0 +1,175 @@
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Path, Query, Request, Response
|
||||
from pydantic_validation_decorator import ValidateFields
|
||||
from sqlalchemy import ColumnElement
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.annotation.cache_annotation import ApiCache, ApiCacheEvict
|
||||
from common.annotation.log_annotation import Log
|
||||
from common.aspect.data_scope import DataScopeDependency
|
||||
from common.aspect.db_seesion import DBSessionDependency
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
|
||||
from common.constant import ApiGroup, ApiNamespace
|
||||
from common.enums import BusinessType
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DataResponseModel, ResponseBaseModel
|
||||
from module_admin.entity.do.dept_do import SysDept
|
||||
from module_admin.entity.vo.dept_vo import DeleteDeptModel, DeptModel, DeptQueryModel
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel
|
||||
from module_admin.service.dept_service import DeptService
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
dept_controller = APIRouterPro(
|
||||
prefix='/system/dept', order_num=6, tags=['系统管理-部门管理'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@dept_controller.get(
|
||||
'/list/exclude/{dept_id}',
|
||||
summary='获取编辑部门的下拉树接口',
|
||||
description='用于获取部门下拉树,不包含指定部门及其子部门',
|
||||
response_model=DataResponseModel[list[DeptModel]],
|
||||
dependencies=[UserInterfaceAuthDependency('system:dept:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_DEPT_EDIT_TREE)
|
||||
async def get_system_dept_tree_for_edit_option(
|
||||
request: Request,
|
||||
dept_id: Annotated[int, Path(description='部门id')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
dept_query = DeptModel(deptId=dept_id)
|
||||
dept_query_result = await DeptService.get_dept_for_edit_option_services(query_db, dept_query, data_scope_sql)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=dept_query_result)
|
||||
|
||||
|
||||
@dept_controller.get(
|
||||
'/list',
|
||||
summary='获取部门列表接口',
|
||||
description='用于获取部门列表',
|
||||
response_model=DataResponseModel[list[DeptModel]],
|
||||
dependencies=[UserInterfaceAuthDependency('system:dept:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_DEPT_LIST)
|
||||
async def get_system_dept_list(
|
||||
request: Request,
|
||||
dept_query: Annotated[DeptQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
dept_query_result = await DeptService.get_dept_list_services(query_db, dept_query, data_scope_sql)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=dept_query_result)
|
||||
|
||||
|
||||
@dept_controller.post(
|
||||
'',
|
||||
summary='新增部门接口',
|
||||
description='用于新增部门',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:dept:add')],
|
||||
)
|
||||
@ValidateFields(validate_model='add_dept')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.DATA_SCOPE_MUTATION)
|
||||
@Log(title='部门管理', business_type=BusinessType.INSERT)
|
||||
async def add_system_dept(
|
||||
request: Request,
|
||||
add_dept: DeptModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
add_dept.create_by = current_user.user.user_name
|
||||
add_dept.create_time = datetime.now()
|
||||
add_dept.update_by = current_user.user.user_name
|
||||
add_dept.update_time = datetime.now()
|
||||
add_dept_result = await DeptService.add_dept_services(query_db, add_dept)
|
||||
logger.info(add_dept_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=add_dept_result.message)
|
||||
|
||||
|
||||
@dept_controller.put(
|
||||
'',
|
||||
summary='编辑部门接口',
|
||||
description='用于编辑部门',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:dept:edit')],
|
||||
)
|
||||
@ValidateFields(validate_model='edit_dept')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.DATA_SCOPE_MUTATION)
|
||||
@Log(title='部门管理', business_type=BusinessType.UPDATE)
|
||||
async def edit_system_dept(
|
||||
request: Request,
|
||||
edit_dept: DeptModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
if not current_user.user.admin:
|
||||
await DeptService.check_dept_data_scope_services(query_db, edit_dept.dept_id, data_scope_sql)
|
||||
edit_dept.update_by = current_user.user.user_name
|
||||
edit_dept.update_time = datetime.now()
|
||||
edit_dept_result = await DeptService.edit_dept_services(query_db, edit_dept)
|
||||
logger.info(edit_dept_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_dept_result.message)
|
||||
|
||||
|
||||
@dept_controller.delete(
|
||||
'/{dept_ids}',
|
||||
summary='删除部门接口',
|
||||
description='用于删除部门',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:dept:remove')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.DATA_SCOPE_MUTATION)
|
||||
@Log(title='部门管理', business_type=BusinessType.DELETE)
|
||||
async def delete_system_dept(
|
||||
request: Request,
|
||||
dept_ids: Annotated[str, Path(description='需要删除的部门id')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
dept_id_list = dept_ids.split(',') if dept_ids else []
|
||||
if dept_id_list:
|
||||
for dept_id in dept_id_list:
|
||||
if not current_user.user.admin:
|
||||
await DeptService.check_dept_data_scope_services(query_db, int(dept_id), data_scope_sql)
|
||||
delete_dept = DeleteDeptModel(deptIds=dept_ids)
|
||||
delete_dept.update_by = current_user.user.user_name
|
||||
delete_dept.update_time = datetime.now()
|
||||
delete_dept_result = await DeptService.delete_dept_services(query_db, delete_dept)
|
||||
logger.info(delete_dept_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_dept_result.message)
|
||||
|
||||
|
||||
@dept_controller.get(
|
||||
'/{dept_id}',
|
||||
summary='获取部门详情接口',
|
||||
description='用于获取指定部门的详情信息',
|
||||
response_model=DataResponseModel[DeptModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:dept:query')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_DEPT_DETAIL)
|
||||
async def query_detail_system_dept(
|
||||
request: Request,
|
||||
dept_id: Annotated[int, Path(description='部门id')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
if not current_user.user.admin:
|
||||
await DeptService.check_dept_data_scope_services(query_db, dept_id, data_scope_sql)
|
||||
detail_dept_result = await DeptService.dept_detail_services(query_db, dept_id)
|
||||
logger.info(f'获取dept_id为{dept_id}的信息成功')
|
||||
|
||||
return ResponseUtil.success(data=detail_dept_result)
|
||||
@@ -0,0 +1,377 @@
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Form, Path, Query, Request, Response
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic_validation_decorator import ValidateFields
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.annotation.cache_annotation import ApiCache, ApiCacheEvict
|
||||
from common.annotation.log_annotation import Log
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
|
||||
from common.aspect.db_seesion import DBSessionDependency
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
|
||||
from common.constant import ApiGroup, ApiNamespace
|
||||
from common.enums import BusinessType
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel
|
||||
from module_admin.entity.vo.dict_vo import (
|
||||
DeleteDictDataModel,
|
||||
DeleteDictTypeModel,
|
||||
DictDataModel,
|
||||
DictDataPageQueryModel,
|
||||
DictTypeModel,
|
||||
DictTypePageQueryModel,
|
||||
)
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel
|
||||
from module_admin.service.dict_service import DictDataService, DictTypeService
|
||||
from utils.common_util import bytes2file_response
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
dict_controller = APIRouterPro(
|
||||
prefix='/system/dict', order_num=8, tags=['系统管理-字典管理'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@dict_controller.get(
|
||||
'/type/list',
|
||||
summary='获取字典类型分页列表接口',
|
||||
description='用于获取字典类型分页列表',
|
||||
response_model=PageResponseModel[DictTypeModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_DICT_TYPE_LIST)
|
||||
async def get_system_dict_type_list(
|
||||
request: Request,
|
||||
dict_type_page_query: Annotated[DictTypePageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取分页数据
|
||||
dict_type_page_query_result = await DictTypeService.get_dict_type_list_services(
|
||||
query_db, dict_type_page_query, is_page=True
|
||||
)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=dict_type_page_query_result)
|
||||
|
||||
|
||||
@dict_controller.post(
|
||||
'/type',
|
||||
summary='新增字典类型接口',
|
||||
description='用于新增字典类型',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:add')],
|
||||
)
|
||||
@ValidateFields(validate_model='add_dict_type')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.DICT_TYPE_MUTATION)
|
||||
@Log(title='字典类型', business_type=BusinessType.INSERT)
|
||||
async def add_system_dict_type(
|
||||
request: Request,
|
||||
add_dict_type: DictTypeModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
add_dict_type.create_by = current_user.user.user_name
|
||||
add_dict_type.create_time = datetime.now()
|
||||
add_dict_type.update_by = current_user.user.user_name
|
||||
add_dict_type.update_time = datetime.now()
|
||||
add_dict_type_result = await DictTypeService.add_dict_type_services(request, query_db, add_dict_type)
|
||||
logger.info(add_dict_type_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=add_dict_type_result.message)
|
||||
|
||||
|
||||
@dict_controller.put(
|
||||
'/type',
|
||||
summary='编辑字典类型接口',
|
||||
description='用于编辑字典类型',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:edit')],
|
||||
)
|
||||
@ValidateFields(validate_model='edit_dict_type')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.DICT_TYPE_MUTATION)
|
||||
@Log(title='字典类型', business_type=BusinessType.UPDATE)
|
||||
async def edit_system_dict_type(
|
||||
request: Request,
|
||||
edit_dict_type: DictTypeModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
edit_dict_type.update_by = current_user.user.user_name
|
||||
edit_dict_type.update_time = datetime.now()
|
||||
edit_dict_type_result = await DictTypeService.edit_dict_type_services(request, query_db, edit_dict_type)
|
||||
logger.info(edit_dict_type_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_dict_type_result.message)
|
||||
|
||||
|
||||
@dict_controller.delete(
|
||||
'/type/refreshCache',
|
||||
summary='刷新字典缓存接口',
|
||||
description='用于刷新字典缓存',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:remove')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_DICT_REFRESH_CACHE, preset=ApiRateLimitPreset.USER_COMMON_MUTATION)
|
||||
@Log(title='字典类型', business_type=BusinessType.UPDATE)
|
||||
async def refresh_system_dict(request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()]) -> Response:
|
||||
refresh_dict_result = await DictTypeService.refresh_sys_dict_services(request, query_db)
|
||||
logger.info(refresh_dict_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=refresh_dict_result.message)
|
||||
|
||||
|
||||
@dict_controller.delete(
|
||||
'/type/{dict_ids}',
|
||||
summary='删除字典类型接口',
|
||||
description='用于删除字典类型',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:remove')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.DICT_TYPE_MUTATION)
|
||||
@Log(title='字典类型', business_type=BusinessType.DELETE)
|
||||
async def delete_system_dict_type(
|
||||
request: Request,
|
||||
dict_ids: Annotated[str, Path(description='需要删除的字典主键')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
delete_dict_type = DeleteDictTypeModel(dictIds=dict_ids)
|
||||
delete_dict_type_result = await DictTypeService.delete_dict_type_services(request, query_db, delete_dict_type)
|
||||
logger.info(delete_dict_type_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_dict_type_result.message)
|
||||
|
||||
|
||||
@dict_controller.get(
|
||||
'/type/optionselect',
|
||||
summary='获取字典类型下拉列表接口',
|
||||
description='用于获取字典类型下拉列表',
|
||||
response_model=DataResponseModel[list[DictTypeModel]],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_DICT_TYPE_OPTIONS)
|
||||
async def query_system_dict_type_options(
|
||||
request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()]
|
||||
) -> Response:
|
||||
dict_type_query_result = await DictTypeService.get_dict_type_list_services(
|
||||
query_db, DictTypePageQueryModel(), is_page=False
|
||||
)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=dict_type_query_result)
|
||||
|
||||
|
||||
@dict_controller.get(
|
||||
'/type/{dict_id}',
|
||||
summary='获取字典类型详情接口',
|
||||
description='用于获取指定字典类型的详细信息',
|
||||
response_model=DataResponseModel[DictTypeModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:query')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_DICT_TYPE_DETAIL)
|
||||
async def query_detail_system_dict_type(
|
||||
request: Request,
|
||||
dict_id: Annotated[int, Path(description='字典主键')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
dict_type_detail_result = await DictTypeService.dict_type_detail_services(query_db, dict_id)
|
||||
logger.info(f'获取dict_id为{dict_id}的信息成功')
|
||||
|
||||
return ResponseUtil.success(data=dict_type_detail_result)
|
||||
|
||||
|
||||
@dict_controller.post(
|
||||
'/type/export',
|
||||
summary='导出字典类型列表接口',
|
||||
description='用于导出当前符合查询条件的字典类型列表数据',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回字典类型列表excel文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:export')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_DICT_TYPE_EXPORT, preset=ApiRateLimitPreset.USER_RESOURCE_EXPORT)
|
||||
@Log(title='字典类型', business_type=BusinessType.EXPORT)
|
||||
async def export_system_dict_type_list(
|
||||
request: Request,
|
||||
dict_type_page_query: Annotated[DictTypePageQueryModel, Form()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
dict_type_query_result = await DictTypeService.get_dict_type_list_services(
|
||||
query_db, dict_type_page_query, is_page=False
|
||||
)
|
||||
dict_type_export_result = await DictTypeService.export_dict_type_list_services(dict_type_query_result)
|
||||
logger.info('导出成功')
|
||||
|
||||
return ResponseUtil.streaming(data=bytes2file_response(dict_type_export_result))
|
||||
|
||||
|
||||
@dict_controller.get(
|
||||
'/data/type/{dict_type}',
|
||||
summary='获取指定字典类型的数据列表接口',
|
||||
description='用于从缓存中获取指定字典类型的所有数据项',
|
||||
response_model=DataResponseModel[list[DictDataModel]],
|
||||
)
|
||||
async def query_system_dict_type_data(
|
||||
request: Request,
|
||||
dict_type: Annotated[str, Path(description='字典类型')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
dict_data_query_result = await DictDataService.query_dict_data_list_from_cache_services(
|
||||
request.app.state.redis, dict_type
|
||||
)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=dict_data_query_result)
|
||||
|
||||
|
||||
@dict_controller.get(
|
||||
'/data/list',
|
||||
summary='获取字典数据分页列表接口',
|
||||
description='用于获取字典数据分页列表',
|
||||
response_model=PageResponseModel[DictDataModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_DICT_DATA_LIST)
|
||||
async def get_system_dict_data_list(
|
||||
request: Request,
|
||||
dict_data_page_query: Annotated[DictDataPageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取分页数据
|
||||
dict_data_page_query_result = await DictDataService.get_dict_data_list_services(
|
||||
query_db, dict_data_page_query, is_page=True
|
||||
)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=dict_data_page_query_result)
|
||||
|
||||
|
||||
@dict_controller.post(
|
||||
'/data',
|
||||
summary='新增字典数据接口',
|
||||
description='用于新增字典数据',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:add')],
|
||||
)
|
||||
@ValidateFields(validate_model='add_dict_data')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.DICT_DATA_MUTATION)
|
||||
@Log(title='字典数据', business_type=BusinessType.INSERT)
|
||||
async def add_system_dict_data(
|
||||
request: Request,
|
||||
add_dict_data: DictDataModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
add_dict_data.create_by = current_user.user.user_name
|
||||
add_dict_data.create_time = datetime.now()
|
||||
add_dict_data.update_by = current_user.user.user_name
|
||||
add_dict_data.update_time = datetime.now()
|
||||
add_dict_data_result = await DictDataService.add_dict_data_services(request, query_db, add_dict_data)
|
||||
logger.info(add_dict_data_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=add_dict_data_result.message)
|
||||
|
||||
|
||||
@dict_controller.put(
|
||||
'/data',
|
||||
summary='编辑字典数据接口',
|
||||
description='用于编辑字典数据',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:edit')],
|
||||
)
|
||||
@ValidateFields(validate_model='edit_dict_data')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.DICT_DATA_MUTATION)
|
||||
@Log(title='字典数据', business_type=BusinessType.UPDATE)
|
||||
async def edit_system_dict_data(
|
||||
request: Request,
|
||||
edit_dict_data: DictDataModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
edit_dict_data.update_by = current_user.user.user_name
|
||||
edit_dict_data.update_time = datetime.now()
|
||||
edit_dict_data_result = await DictDataService.edit_dict_data_services(request, query_db, edit_dict_data)
|
||||
logger.info(edit_dict_data_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_dict_data_result.message)
|
||||
|
||||
|
||||
@dict_controller.delete(
|
||||
'/data/{dict_codes}',
|
||||
summary='删除字典数据接口',
|
||||
description='用于删除字典数据',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:remove')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.DICT_DATA_MUTATION)
|
||||
@Log(title='字典数据', business_type=BusinessType.DELETE)
|
||||
async def delete_system_dict_data(
|
||||
request: Request,
|
||||
dict_codes: Annotated[str, Path(description='需要删除的字典编码')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
delete_dict_data = DeleteDictDataModel(dictCodes=dict_codes)
|
||||
delete_dict_data_result = await DictDataService.delete_dict_data_services(request, query_db, delete_dict_data)
|
||||
logger.info(delete_dict_data_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_dict_data_result.message)
|
||||
|
||||
|
||||
@dict_controller.get(
|
||||
'/data/{dict_code}',
|
||||
summary='获取字典数据详情接口',
|
||||
description='用于获取指定字典数据的详细信息',
|
||||
response_model=DataResponseModel[DictDataModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:query')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_DICT_DATA_DETAIL)
|
||||
async def query_detail_system_dict_data(
|
||||
request: Request,
|
||||
dict_code: Annotated[int, Path(description='字典编码')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
detail_dict_data_result = await DictDataService.dict_data_detail_services(query_db, dict_code)
|
||||
logger.info(f'获取dict_code为{dict_code}的信息成功')
|
||||
|
||||
return ResponseUtil.success(data=detail_dict_data_result)
|
||||
|
||||
|
||||
@dict_controller.post(
|
||||
'/data/export',
|
||||
summary='导出字典数据列表接口',
|
||||
description='用于导出当前符合查询条件的字典数据列表数据',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回字典数据列表excel文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
dependencies=[UserInterfaceAuthDependency('system:dict:export')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_DICT_DATA_EXPORT, preset=ApiRateLimitPreset.USER_RESOURCE_EXPORT)
|
||||
@Log(title='字典数据', business_type=BusinessType.EXPORT)
|
||||
async def export_system_dict_data_list(
|
||||
request: Request,
|
||||
dict_data_page_query: Annotated[DictDataPageQueryModel, Form()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
dict_data_query_result = await DictDataService.get_dict_data_list_services(
|
||||
query_db, dict_data_page_query, is_page=False
|
||||
)
|
||||
dict_data_export_result = await DictDataService.export_dict_data_list_services(dict_data_query_result)
|
||||
logger.info('导出成功')
|
||||
|
||||
return ResponseUtil.streaming(data=bytes2file_response(dict_data_export_result))
|
||||
@@ -0,0 +1,318 @@
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Form, Path, Query, Request, Response
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic_validation_decorator import ValidateFields
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.annotation.cache_annotation import ApiCache, ApiCacheEvict
|
||||
from common.annotation.log_annotation import Log
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
|
||||
from common.aspect.db_seesion import DBSessionDependency
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
|
||||
from common.constant import ApiGroup, ApiNamespace
|
||||
from common.enums import BusinessType
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel
|
||||
from module_admin.entity.vo.job_vo import (
|
||||
DeleteJobLogModel,
|
||||
DeleteJobModel,
|
||||
EditJobModel,
|
||||
JobLogModel,
|
||||
JobLogPageQueryModel,
|
||||
JobModel,
|
||||
JobPageQueryModel,
|
||||
)
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel
|
||||
from module_admin.service.job_log_service import JobLogService
|
||||
from module_admin.service.job_service import JobService
|
||||
from utils.common_util import bytes2file_response
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
job_controller = APIRouterPro(
|
||||
prefix='/monitor', order_num=13, tags=['系统监控-定时任务'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@job_controller.get(
|
||||
'/job/list',
|
||||
summary='获取定时任务分页列表接口',
|
||||
description='用于获取定时任务分页列表',
|
||||
response_model=PageResponseModel[JobModel],
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:job:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.MONITOR_JOB_LIST)
|
||||
async def get_system_job_list(
|
||||
request: Request,
|
||||
job_page_query: Annotated[JobPageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取分页数据
|
||||
job_page_query_result = await JobService.get_job_list_services(query_db, job_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=job_page_query_result)
|
||||
|
||||
|
||||
@job_controller.post(
|
||||
'/job',
|
||||
summary='新增定时任务接口',
|
||||
description='用于新增定时任务',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:job:add')],
|
||||
)
|
||||
@ValidateFields(validate_model='add_job')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.JOB_MUTATION)
|
||||
@Log(title='定时任务', business_type=BusinessType.INSERT)
|
||||
async def add_system_job(
|
||||
request: Request,
|
||||
add_job: JobModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
add_job.create_by = current_user.user.user_name
|
||||
add_job.create_time = datetime.now()
|
||||
add_job.update_by = current_user.user.user_name
|
||||
add_job.update_time = datetime.now()
|
||||
add_job_result = await JobService.add_job_services(query_db, add_job)
|
||||
logger.info(add_job_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=add_job_result.message)
|
||||
|
||||
|
||||
@job_controller.put(
|
||||
'/job',
|
||||
summary='编辑定时任务接口',
|
||||
description='用于编辑定时任务',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:job:edit')],
|
||||
)
|
||||
@ValidateFields(validate_model='edit_job')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.JOB_MUTATION)
|
||||
@Log(title='定时任务', business_type=BusinessType.UPDATE)
|
||||
async def edit_system_job(
|
||||
request: Request,
|
||||
edit_job: EditJobModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
edit_job.update_by = current_user.user.user_name
|
||||
edit_job.update_time = datetime.now()
|
||||
edit_job_result = await JobService.edit_job_services(query_db, edit_job)
|
||||
logger.info(edit_job_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_job_result.message)
|
||||
|
||||
|
||||
@job_controller.put(
|
||||
'/job/changeStatus',
|
||||
summary='修改定时任务状态接口',
|
||||
description='用于修改定时任务状态',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:job:changeStatus')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.JOB_MUTATION)
|
||||
@Log(title='定时任务', business_type=BusinessType.UPDATE)
|
||||
async def change_system_job_status(
|
||||
request: Request,
|
||||
change_job: EditJobModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
edit_job = EditJobModel(
|
||||
jobId=change_job.job_id,
|
||||
status=change_job.status,
|
||||
updateBy=current_user.user.user_name,
|
||||
updateTime=datetime.now(),
|
||||
type='status',
|
||||
)
|
||||
edit_job_result = await JobService.edit_job_services(query_db, edit_job)
|
||||
logger.info(edit_job_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_job_result.message)
|
||||
|
||||
|
||||
@job_controller.put(
|
||||
'/job/run',
|
||||
summary='执行定时任务接口',
|
||||
description='用于执行指定的定时任务',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:job:changeStatus')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_JOB_RUN, preset=ApiRateLimitPreset.USER_RESOURCE_EXECUTION)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.JOB_MUTATION)
|
||||
@Log(title='定时任务', business_type=BusinessType.UPDATE)
|
||||
async def execute_system_job(
|
||||
request: Request,
|
||||
execute_job: JobModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
execute_job_result = await JobService.execute_job_once_services(query_db, execute_job)
|
||||
logger.info(execute_job_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=execute_job_result.message)
|
||||
|
||||
|
||||
@job_controller.delete(
|
||||
'/job/{job_ids}',
|
||||
summary='删除定时任务接口',
|
||||
description='用于删除定时任务',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:job:remove')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_JOB_DELETE, preset=ApiRateLimitPreset.USER_DESTRUCTIVE_MUTATION)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.JOB_MUTATION)
|
||||
@Log(title='定时任务', business_type=BusinessType.DELETE)
|
||||
async def delete_system_job(
|
||||
request: Request,
|
||||
job_ids: Annotated[str, Path(description='需要删除的定时任务ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
delete_job = DeleteJobModel(jobIds=job_ids)
|
||||
delete_job_result = await JobService.delete_job_services(query_db, delete_job)
|
||||
logger.info(delete_job_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_job_result.message)
|
||||
|
||||
|
||||
@job_controller.get(
|
||||
'/job/{job_id}',
|
||||
summary='获取定时任务详情接口',
|
||||
description='用于获取指定定时任务的详情信息',
|
||||
response_model=DataResponseModel[JobModel],
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:job:query')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.MONITOR_JOB_DETAIL)
|
||||
async def query_detail_system_job(
|
||||
request: Request,
|
||||
job_id: Annotated[int, Path(description='任务ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
job_detail_result = await JobService.job_detail_services(query_db, job_id)
|
||||
logger.info(f'获取job_id为{job_id}的信息成功')
|
||||
|
||||
return ResponseUtil.success(data=job_detail_result)
|
||||
|
||||
|
||||
@job_controller.post(
|
||||
'/job/export',
|
||||
summary='导出定时任务列表接口',
|
||||
description='用于导出当前符合查询条件的定时任务列表数据',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回定时任务列表excel文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:job:export')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_JOB_EXPORT, preset=ApiRateLimitPreset.USER_RESOURCE_EXPORT)
|
||||
@Log(title='定时任务', business_type=BusinessType.EXPORT)
|
||||
async def export_system_job_list(
|
||||
request: Request,
|
||||
job_page_query: Annotated[JobPageQueryModel, Form()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
job_query_result = await JobService.get_job_list_services(query_db, job_page_query, is_page=False)
|
||||
job_export_result = await JobService.export_job_list_services(request, job_query_result)
|
||||
logger.info('导出成功')
|
||||
|
||||
return ResponseUtil.streaming(data=bytes2file_response(job_export_result))
|
||||
|
||||
|
||||
@job_controller.get(
|
||||
'/jobLog/list',
|
||||
summary='获取定时任务调度日志分页列表接口',
|
||||
description='用于获取定时任务调度日志分页列表',
|
||||
response_model=PageResponseModel[JobLogModel],
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:job:list')],
|
||||
)
|
||||
async def get_system_job_log_list(
|
||||
request: Request,
|
||||
job_log_page_query: Annotated[JobLogPageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取分页数据
|
||||
job_log_page_query_result = await JobLogService.get_job_log_list_services(
|
||||
query_db, job_log_page_query, is_page=True
|
||||
)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=job_log_page_query_result)
|
||||
|
||||
|
||||
@job_controller.delete(
|
||||
'/jobLog/clean',
|
||||
summary='清空定时任务调度日志接口',
|
||||
description='用于清空所有定时任务调度日志',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:job:remove')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_JOB_LOG_CLEAN, preset=ApiRateLimitPreset.USER_DESTRUCTIVE_MUTATION)
|
||||
@Log(title='定时任务调度日志', business_type=BusinessType.CLEAN)
|
||||
async def clear_system_job_log(
|
||||
request: Request,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
clear_job_log_result = await JobLogService.clear_job_log_services(query_db)
|
||||
logger.info(clear_job_log_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=clear_job_log_result.message)
|
||||
|
||||
|
||||
@job_controller.delete(
|
||||
'/jobLog/{job_log_ids}',
|
||||
summary='删除定时任务调度日志接口',
|
||||
description='用于删除定时任务调度日志',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:job:remove')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_JOB_LOG_DELETE, preset=ApiRateLimitPreset.USER_DESTRUCTIVE_MUTATION)
|
||||
@Log(title='定时任务调度日志', business_type=BusinessType.DELETE)
|
||||
async def delete_system_job_log(
|
||||
request: Request,
|
||||
job_log_ids: Annotated[str, Path(description='需要删除的定时任务日志ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
delete_job_log = DeleteJobLogModel(jobLogIds=job_log_ids)
|
||||
delete_job_log_result = await JobLogService.delete_job_log_services(query_db, delete_job_log)
|
||||
logger.info(delete_job_log_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_job_log_result.message)
|
||||
|
||||
|
||||
@job_controller.post(
|
||||
'/jobLog/export',
|
||||
summary='导出定时任务调度日志列表接口',
|
||||
description='用于导出当前符合查询条件的定时任务调度日志列表数据',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回定时任务日志列表excel文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:job:export')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_JOB_LOG_EXPORT, preset=ApiRateLimitPreset.USER_RESOURCE_EXPORT)
|
||||
@Log(title='定时任务调度日志', business_type=BusinessType.EXPORT)
|
||||
async def export_system_job_log_list(
|
||||
request: Request,
|
||||
job_log_page_query: Annotated[JobLogPageQueryModel, Form()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
job_log_query_result = await JobLogService.get_job_log_list_services(query_db, job_log_page_query, is_page=False)
|
||||
job_log_export_result = await JobLogService.export_job_log_list_services(request, job_log_query_result)
|
||||
logger.info('导出成功')
|
||||
|
||||
return ResponseUtil.streaming(data=bytes2file_response(job_log_export_result))
|
||||
@@ -0,0 +1,241 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Form, Path, Query, Request, Response
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.annotation.log_annotation import Log
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
|
||||
from common.aspect.db_seesion import DBSessionDependency
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import PreAuthDependency
|
||||
from common.constant import ApiNamespace
|
||||
from common.enums import BusinessType
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import PageResponseModel, ResponseBaseModel
|
||||
from module_admin.entity.vo.log_vo import (
|
||||
DeleteLoginLogModel,
|
||||
DeleteOperLogModel,
|
||||
LogininforModel,
|
||||
LoginLogPageQueryModel,
|
||||
OperLogModel,
|
||||
OperLogPageQueryModel,
|
||||
UnlockUser,
|
||||
)
|
||||
from module_admin.service.log_service import LoginLogService, OperationLogService
|
||||
from utils.common_util import bytes2file_response
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
log_controller = APIRouterPro(
|
||||
prefix='/monitor', order_num=11, tags=['系统管理-日志管理'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@log_controller.get(
|
||||
'/operlog/list',
|
||||
summary='获取操作日志分页列表接口',
|
||||
description='用于获取操作日志分页列表',
|
||||
response_model=PageResponseModel[OperLogModel],
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:operlog:list')],
|
||||
)
|
||||
async def get_system_operation_log_list(
|
||||
request: Request,
|
||||
operation_log_page_query: Annotated[OperLogPageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取分页数据
|
||||
operation_log_page_query_result = await OperationLogService.get_operation_log_list_services(
|
||||
query_db, operation_log_page_query, is_page=True
|
||||
)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=operation_log_page_query_result)
|
||||
|
||||
|
||||
@log_controller.delete(
|
||||
'/operlog/clean',
|
||||
summary='清空操作日志接口',
|
||||
description='用于清空所有操作日志',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:operlog:remove')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_OPERLOG_CLEAN, preset=ApiRateLimitPreset.USER_DESTRUCTIVE_MUTATION)
|
||||
@Log(title='操作日志', business_type=BusinessType.CLEAN)
|
||||
async def clear_system_operation_log(
|
||||
request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()]
|
||||
) -> Response:
|
||||
clear_operation_log_result = await OperationLogService.clear_operation_log_services(query_db)
|
||||
logger.info(clear_operation_log_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=clear_operation_log_result.message)
|
||||
|
||||
|
||||
@log_controller.delete(
|
||||
'/operlog/{oper_ids}',
|
||||
summary='删除操作日志接口',
|
||||
description='用于删除操作日志',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:operlog:remove')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_OPERLOG_DELETE, preset=ApiRateLimitPreset.USER_DESTRUCTIVE_MUTATION)
|
||||
@Log(title='操作日志', business_type=BusinessType.DELETE)
|
||||
async def delete_system_operation_log(
|
||||
request: Request,
|
||||
oper_ids: Annotated[str, Path(description='需要删除的日志主键')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
delete_operation_log = DeleteOperLogModel(operIds=oper_ids)
|
||||
delete_operation_log_result = await OperationLogService.delete_operation_log_services(
|
||||
query_db, delete_operation_log
|
||||
)
|
||||
logger.info(delete_operation_log_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_operation_log_result.message)
|
||||
|
||||
|
||||
@log_controller.post(
|
||||
'/operlog/export',
|
||||
summary='导出操作日志接口',
|
||||
description='用于导出当前符合查询条件的操作日志数据',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回操作日志列表excel文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:operlog:export')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_OPERLOG_EXPORT, preset=ApiRateLimitPreset.USER_RESOURCE_EXPORT)
|
||||
@Log(title='操作日志', business_type=BusinessType.EXPORT)
|
||||
async def export_system_operation_log_list(
|
||||
request: Request,
|
||||
operation_log_page_query: Annotated[OperLogPageQueryModel, Form()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
operation_log_query_result = await OperationLogService.get_operation_log_list_services(
|
||||
query_db, operation_log_page_query, is_page=False
|
||||
)
|
||||
operation_log_export_result = await OperationLogService.export_operation_log_list_services(
|
||||
request, operation_log_query_result
|
||||
)
|
||||
logger.info('导出成功')
|
||||
|
||||
return ResponseUtil.streaming(data=bytes2file_response(operation_log_export_result))
|
||||
|
||||
|
||||
@log_controller.get(
|
||||
'/logininfor/list',
|
||||
summary='获取登录日志分页列表接口',
|
||||
description='用于获取登录日志分页列表',
|
||||
response_model=PageResponseModel[LogininforModel],
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:logininfor:list')],
|
||||
)
|
||||
async def get_system_login_log_list(
|
||||
request: Request,
|
||||
login_log_page_query: Annotated[LoginLogPageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取分页数据
|
||||
login_log_page_query_result = await LoginLogService.get_login_log_list_services(
|
||||
query_db, login_log_page_query, is_page=True
|
||||
)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=login_log_page_query_result)
|
||||
|
||||
|
||||
@log_controller.delete(
|
||||
'/logininfor/clean',
|
||||
summary='清空登录日志接口',
|
||||
description='用于清空所有登录日志',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:logininfor:remove')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_LOGININFO_CLEAN, preset=ApiRateLimitPreset.USER_DESTRUCTIVE_MUTATION)
|
||||
@Log(title='登录日志', business_type=BusinessType.CLEAN)
|
||||
async def clear_system_login_log(
|
||||
request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()]
|
||||
) -> Response:
|
||||
clear_login_log_result = await LoginLogService.clear_login_log_services(query_db)
|
||||
logger.info(clear_login_log_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=clear_login_log_result.message)
|
||||
|
||||
|
||||
@log_controller.delete(
|
||||
'/logininfor/{info_ids}',
|
||||
summary='删除登录日志接口',
|
||||
description='用于删除登录日志',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:logininfor:remove')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_LOGININFO_DELETE, preset=ApiRateLimitPreset.USER_DESTRUCTIVE_MUTATION)
|
||||
@Log(title='登录日志', business_type=BusinessType.DELETE)
|
||||
async def delete_system_login_log(
|
||||
request: Request,
|
||||
info_ids: Annotated[str, Path(description='需要删除的访问ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
delete_login_log = DeleteLoginLogModel(infoIds=info_ids)
|
||||
delete_login_log_result = await LoginLogService.delete_login_log_services(query_db, delete_login_log)
|
||||
logger.info(delete_login_log_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_login_log_result.message)
|
||||
|
||||
|
||||
@log_controller.get(
|
||||
'/logininfor/unlock/{user_name}',
|
||||
summary='解锁账户接口',
|
||||
description='用于解锁指定用户账户',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:logininfor:unlock')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_LOGININFO_UNLOCK, preset=ApiRateLimitPreset.USER_SECURITY_MUTATION)
|
||||
@Log(title='账户解锁', business_type=BusinessType.OTHER)
|
||||
async def unlock_system_user(
|
||||
request: Request,
|
||||
user_name: Annotated[str, Path(description='用户名称')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
unlock_user = UnlockUser(userName=user_name)
|
||||
unlock_user_result = await LoginLogService.unlock_user_services(request, unlock_user)
|
||||
logger.info(unlock_user_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=unlock_user_result.message)
|
||||
|
||||
|
||||
@log_controller.post(
|
||||
'/logininfor/export',
|
||||
summary='导出登录日志接口',
|
||||
description='用于导出当前符合查询条件的登录日志数据',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回登录日志列表excel文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:logininfor:export')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_LOGININFO_EXPORT, preset=ApiRateLimitPreset.USER_RESOURCE_EXPORT)
|
||||
@Log(title='登录日志', business_type=BusinessType.EXPORT)
|
||||
async def export_system_login_log_list(
|
||||
request: Request,
|
||||
login_log_page_query: Annotated[LoginLogPageQueryModel, Form()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
login_log_query_result = await LoginLogService.get_login_log_list_services(
|
||||
query_db, login_log_page_query, is_page=False
|
||||
)
|
||||
login_log_export_result = await LoginLogService.export_login_log_list_services(login_log_query_result)
|
||||
logger.info('导出成功')
|
||||
|
||||
return ResponseUtil.streaming(data=bytes2file_response(login_log_export_result))
|
||||
@@ -0,0 +1,192 @@
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Annotated
|
||||
|
||||
import jwt
|
||||
from fastapi import Depends, Request, Response
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.annotation.cache_annotation import ApiCache, ApiCacheEvict
|
||||
from common.annotation.log_annotation import Log
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
|
||||
from common.aspect.db_seesion import DBSessionDependency
|
||||
from common.aspect.pre_auth import CurrentUserDependency
|
||||
from common.constant import ApiGroup, ApiNamespace
|
||||
from common.enums import BusinessType, RedisInitKeyConfig
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import CrudResponseModel, DataResponseModel, DynamicResponseModel, ResponseBaseModel
|
||||
from config.env import AppConfig, JwtConfig
|
||||
from module_admin.entity.vo.login_vo import LoginToken, RouterModel, Token, UserLogin, UserRegister
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel, EditUserModel
|
||||
from module_admin.service.login_service import CustomOAuth2PasswordRequestForm, LoginService, oauth2_scheme
|
||||
from module_admin.service.user_service import UserService
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
login_controller = APIRouterPro(order_num=1, tags=['登录模块'])
|
||||
|
||||
|
||||
@login_controller.post(
|
||||
'/login',
|
||||
summary='登录接口',
|
||||
description='用于用户登录',
|
||||
response_model=DynamicResponseModel[LoginToken] | Token,
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.LOGIN, preset=ApiRateLimitPreset.ANON_AUTH_LOGIN)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.LOGIN_SUCCESS_MUTATION)
|
||||
@Log(title='用户登录', business_type=BusinessType.OTHER, log_type='login')
|
||||
async def login(
|
||||
request: Request,
|
||||
form_data: Annotated[CustomOAuth2PasswordRequestForm, Depends()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
captcha_enabled = (
|
||||
await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.captchaEnabled') == 'true'
|
||||
)
|
||||
user = UserLogin(
|
||||
userName=form_data.username,
|
||||
password=form_data.password,
|
||||
code=form_data.code,
|
||||
uuid=form_data.uuid,
|
||||
loginInfo=form_data.login_info,
|
||||
captchaEnabled=captcha_enabled,
|
||||
)
|
||||
result = await LoginService.authenticate_user(request, query_db, user)
|
||||
access_token_expires = timedelta(minutes=JwtConfig.jwt_expire_minutes)
|
||||
session_id = str(uuid.uuid4())
|
||||
access_token = await LoginService.create_access_token(
|
||||
data={
|
||||
'user_id': str(result[0].user_id),
|
||||
'user_name': result[0].user_name,
|
||||
'dept_name': result[1].dept_name if result[1] else None,
|
||||
'session_id': session_id,
|
||||
'login_info': user.login_info,
|
||||
},
|
||||
expires_delta=access_token_expires,
|
||||
)
|
||||
if AppConfig.app_same_time_login:
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{session_id}',
|
||||
access_token,
|
||||
ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes),
|
||||
)
|
||||
else:
|
||||
# 此方法可实现同一账号同一时间只能登录一次
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{result[0].user_id}',
|
||||
access_token,
|
||||
ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes),
|
||||
)
|
||||
await UserService.edit_user_services(
|
||||
query_db, EditUserModel(userId=result[0].user_id, loginDate=datetime.now(), type='status')
|
||||
)
|
||||
logger.info('登录成功')
|
||||
# 判断请求是否来自于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': access_token, 'token_type': 'Bearer'}
|
||||
return ResponseUtil.success(msg='登录成功', dict_content={'token': access_token})
|
||||
|
||||
|
||||
@login_controller.get(
|
||||
'/getInfo',
|
||||
summary='获取用户信息接口',
|
||||
description='用于获取当前登录用户的信息',
|
||||
response_model=DynamicResponseModel[CurrentUserModel],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.LOGIN_USER_INFO)
|
||||
async def get_login_user_info(
|
||||
request: Request, current_user: Annotated[CurrentUserModel, CurrentUserDependency()]
|
||||
) -> Response:
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=current_user)
|
||||
|
||||
|
||||
@login_controller.get(
|
||||
'/getRouters',
|
||||
summary='获取用户路由接口',
|
||||
description='用于获取当前登录用户的路由信息',
|
||||
response_model=DataResponseModel[list[RouterModel]],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.LOGIN_USER_ROUTERS)
|
||||
async def get_login_user_routers(
|
||||
request: Request,
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
logger.info('获取成功')
|
||||
user_routers = await LoginService.get_current_user_routers(current_user.user.user_id, query_db)
|
||||
|
||||
return ResponseUtil.success(data=user_routers)
|
||||
|
||||
|
||||
@login_controller.post(
|
||||
'/register',
|
||||
summary='注册接口',
|
||||
description='用于用户注册',
|
||||
response_model=DataResponseModel[CrudResponseModel],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.REGISTER, preset=ApiRateLimitPreset.ANON_AUTH_REGISTER)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.USER_ENTITY_MUTATION)
|
||||
async def register_user(
|
||||
request: Request,
|
||||
user_register: UserRegister,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
user_register_result = await LoginService.register_user_services(request, query_db, user_register)
|
||||
logger.info(user_register_result.message)
|
||||
|
||||
return ResponseUtil.success(data=user_register_result, msg=user_register_result.message)
|
||||
|
||||
|
||||
# @login_controller.post("/getSmsCode", response_model=SmsCode)
|
||||
# async def get_sms_code(request: Request, user: ResetUserModel, query_db: AsyncSession = DBSessionDependency()):
|
||||
# try:
|
||||
# sms_result = await LoginService.get_sms_code_services(request, query_db, user)
|
||||
# if sms_result.is_success:
|
||||
# logger.info('获取成功')
|
||||
# return ResponseUtil.success(data=sms_result)
|
||||
# else:
|
||||
# logger.warning(sms_result.message)
|
||||
# return ResponseUtil.failure(msg=sms_result.message)
|
||||
# except Exception as e:
|
||||
# logger.exception(e)
|
||||
# return ResponseUtil.error(msg=str(e))
|
||||
#
|
||||
#
|
||||
# @login_controller.post("/forgetPwd", response_model=CrudResponseModel)
|
||||
# async def forget_user_pwd(request: Request, forget_user: ResetUserModel, query_db: AsyncSession = DBSessionDependency()):
|
||||
# try:
|
||||
# forget_user_result = await LoginService.forget_user_services(request, query_db, forget_user)
|
||||
# if forget_user_result.is_success:
|
||||
# logger.info(forget_user_result.message)
|
||||
# return ResponseUtil.success(data=forget_user_result, msg=forget_user_result.message)
|
||||
# else:
|
||||
# logger.warning(forget_user_result.message)
|
||||
# return ResponseUtil.failure(msg=forget_user_result.message)
|
||||
# except Exception as e:
|
||||
# logger.exception(e)
|
||||
# return ResponseUtil.error(msg=str(e))
|
||||
|
||||
|
||||
@login_controller.post(
|
||||
'/logout',
|
||||
summary='退出登录接口',
|
||||
description='用于用户退出登录',
|
||||
response_model=ResponseBaseModel,
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.LOGOUT_MUTATION)
|
||||
async def logout(request: Request, token: Annotated[str | None, Depends(oauth2_scheme)]) -> Response:
|
||||
payload = jwt.decode(
|
||||
token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm], options={'verify_exp': False}
|
||||
)
|
||||
if AppConfig.app_same_time_login:
|
||||
token_id: str = payload.get('session_id')
|
||||
else:
|
||||
token_id: str = payload.get('user_id')
|
||||
await LoginService.logout_services(request, token_id)
|
||||
logger.info('退出成功')
|
||||
|
||||
return ResponseUtil.success(msg='退出成功')
|
||||
@@ -0,0 +1,173 @@
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Path, Query, Request, Response
|
||||
from pydantic_validation_decorator import ValidateFields
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.annotation.cache_annotation import ApiCache, ApiCacheEvict
|
||||
from common.annotation.log_annotation import Log
|
||||
from common.aspect.db_seesion import DBSessionDependency
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
|
||||
from common.constant import ApiGroup, ApiNamespace
|
||||
from common.enums import BusinessType
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DataResponseModel, DynamicResponseModel, ResponseBaseModel
|
||||
from module_admin.entity.vo.menu_vo import DeleteMenuModel, MenuModel, MenuQueryModel, MenuTreeModel
|
||||
from module_admin.entity.vo.role_vo import RoleMenuQueryModel
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel
|
||||
from module_admin.service.menu_service import MenuService
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
menu_controller = APIRouterPro(
|
||||
prefix='/system/menu', order_num=5, tags=['系统管理-菜单管理'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@menu_controller.get(
|
||||
'/treeselect',
|
||||
summary='获取菜单树接口',
|
||||
description='用于获取当前用户可见的菜单树',
|
||||
response_model=DataResponseModel[list[MenuTreeModel]],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_MENU_TREE)
|
||||
async def get_system_menu_tree(
|
||||
request: Request,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
menu_query_result = await MenuService.get_menu_tree_services(query_db, current_user)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=menu_query_result)
|
||||
|
||||
|
||||
@menu_controller.get(
|
||||
'/roleMenuTreeselect/{role_id}',
|
||||
summary='获取角色菜单树接口',
|
||||
description='用于获取指定角色可见的菜单树',
|
||||
response_model=DynamicResponseModel[RoleMenuQueryModel],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_MENU_ROLE_TREE)
|
||||
async def get_system_role_menu_tree(
|
||||
request: Request,
|
||||
role_id: Annotated[int, Path(description='角色ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
role_menu_query_result = await MenuService.get_role_menu_tree_services(query_db, role_id, current_user)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=role_menu_query_result)
|
||||
|
||||
|
||||
@menu_controller.get(
|
||||
'/list',
|
||||
summary='获取菜单列表接口',
|
||||
description='用于获取当前用户可见的菜单列表',
|
||||
response_model=DataResponseModel[list[MenuModel]],
|
||||
dependencies=[UserInterfaceAuthDependency('system:menu:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_MENU_LIST)
|
||||
async def get_system_menu_list(
|
||||
request: Request,
|
||||
menu_query: Annotated[MenuQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
menu_query_result = await MenuService.get_menu_list_services(query_db, menu_query, current_user)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=menu_query_result)
|
||||
|
||||
|
||||
@menu_controller.post(
|
||||
'',
|
||||
summary='新增菜单接口',
|
||||
description='用于新增菜单',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:menu:add')],
|
||||
)
|
||||
@ValidateFields(validate_model='add_menu')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.MENU_MUTATION)
|
||||
@Log(title='菜单管理', business_type=BusinessType.INSERT)
|
||||
async def add_system_menu(
|
||||
request: Request,
|
||||
add_menu: MenuModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
add_menu.create_by = current_user.user.user_name
|
||||
add_menu.create_time = datetime.now()
|
||||
add_menu.update_by = current_user.user.user_name
|
||||
add_menu.update_time = datetime.now()
|
||||
add_menu_result = await MenuService.add_menu_services(query_db, add_menu)
|
||||
logger.info(add_menu_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=add_menu_result.message)
|
||||
|
||||
|
||||
@menu_controller.put(
|
||||
'',
|
||||
summary='编辑菜单接口',
|
||||
description='用于编辑菜单',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:menu:edit')],
|
||||
)
|
||||
@ValidateFields(validate_model='edit_menu')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.MENU_MUTATION)
|
||||
@Log(title='菜单管理', business_type=BusinessType.UPDATE)
|
||||
async def edit_system_menu(
|
||||
request: Request,
|
||||
edit_menu: MenuModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
edit_menu.update_by = current_user.user.user_name
|
||||
edit_menu.update_time = datetime.now()
|
||||
edit_menu_result = await MenuService.edit_menu_services(query_db, edit_menu)
|
||||
logger.info(edit_menu_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_menu_result.message)
|
||||
|
||||
|
||||
@menu_controller.delete(
|
||||
'/{menu_ids}',
|
||||
summary='删除菜单接口',
|
||||
description='用于删除菜单',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:menu:remove')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.MENU_MUTATION)
|
||||
@Log(title='菜单管理', business_type=BusinessType.DELETE)
|
||||
async def delete_system_menu(
|
||||
request: Request,
|
||||
menu_ids: Annotated[str, Path(description='需要删除的菜单ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
delete_menu = DeleteMenuModel(menuIds=menu_ids)
|
||||
delete_menu_result = await MenuService.delete_menu_services(query_db, delete_menu)
|
||||
logger.info(delete_menu_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_menu_result.message)
|
||||
|
||||
|
||||
@menu_controller.get(
|
||||
'/{menu_id}',
|
||||
summary='获取菜单详情接口',
|
||||
description='用于获取指定菜单的详情信息',
|
||||
response_model=DataResponseModel[MenuModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:menu:query')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_MENU_DETAIL)
|
||||
async def query_detail_system_menu(
|
||||
request: Request,
|
||||
menu_id: Annotated[int, Path(description='菜单ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
menu_detail_result = await MenuService.menu_detail_services(query_db, menu_id)
|
||||
logger.info(f'获取menu_id为{menu_id}的信息成功')
|
||||
|
||||
return ResponseUtil.success(data=menu_detail_result)
|
||||
@@ -0,0 +1,135 @@
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Path, Query, Request, Response
|
||||
from pydantic_validation_decorator import ValidateFields
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.annotation.cache_annotation import ApiCache, ApiCacheEvict
|
||||
from common.annotation.log_annotation import Log
|
||||
from common.aspect.db_seesion import DBSessionDependency
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
|
||||
from common.constant import ApiGroup, ApiNamespace
|
||||
from common.enums import BusinessType
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel
|
||||
from module_admin.entity.vo.notice_vo import DeleteNoticeModel, NoticeModel, NoticePageQueryModel
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel
|
||||
from module_admin.service.notice_service import NoticeService
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
notice_controller = APIRouterPro(
|
||||
prefix='/system/notice', order_num=10, tags=['系统管理-通知公告管理'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@notice_controller.get(
|
||||
'/list',
|
||||
summary='获取通知公告分页列表接口',
|
||||
description='用于获取通知公告分页列表',
|
||||
response_model=PageResponseModel[NoticeModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:notice:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_NOTICE_LIST)
|
||||
async def get_system_notice_list(
|
||||
request: Request,
|
||||
notice_page_query: Annotated[NoticePageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取分页数据
|
||||
notice_page_query_result = await NoticeService.get_notice_list_services(query_db, notice_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=notice_page_query_result)
|
||||
|
||||
|
||||
@notice_controller.post(
|
||||
'',
|
||||
summary='新增通知公告接口',
|
||||
description='用于新增通知公告',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:notice:add')],
|
||||
)
|
||||
@ValidateFields(validate_model='add_notice')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.NOTICE_MUTATION)
|
||||
@Log(title='通知公告', business_type=BusinessType.INSERT)
|
||||
async def add_system_notice(
|
||||
request: Request,
|
||||
add_notice: NoticeModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
add_notice.create_by = current_user.user.user_name
|
||||
add_notice.create_time = datetime.now()
|
||||
add_notice.update_by = current_user.user.user_name
|
||||
add_notice.update_time = datetime.now()
|
||||
add_notice_result = await NoticeService.add_notice_services(query_db, add_notice)
|
||||
logger.info(add_notice_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=add_notice_result.message)
|
||||
|
||||
|
||||
@notice_controller.put(
|
||||
'',
|
||||
summary='编辑通知公告接口',
|
||||
description='用于编辑通知公告',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:notice:edit')],
|
||||
)
|
||||
@ValidateFields(validate_model='edit_notice')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.NOTICE_MUTATION)
|
||||
@Log(title='通知公告', business_type=BusinessType.UPDATE)
|
||||
async def edit_system_notice(
|
||||
request: Request,
|
||||
edit_notice: NoticeModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
edit_notice.update_by = current_user.user.user_name
|
||||
edit_notice.update_time = datetime.now()
|
||||
edit_notice_result = await NoticeService.edit_notice_services(query_db, edit_notice)
|
||||
logger.info(edit_notice_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_notice_result.message)
|
||||
|
||||
|
||||
@notice_controller.delete(
|
||||
'/{notice_ids}',
|
||||
summary='删除通知公告接口',
|
||||
description='用于删除通知公告',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:notice:remove')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.NOTICE_MUTATION)
|
||||
@Log(title='通知公告', business_type=BusinessType.DELETE)
|
||||
async def delete_system_notice(
|
||||
request: Request,
|
||||
notice_ids: Annotated[str, Path(description='需要删除的公告ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
delete_notice = DeleteNoticeModel(noticeIds=notice_ids)
|
||||
delete_notice_result = await NoticeService.delete_notice_services(query_db, delete_notice)
|
||||
logger.info(delete_notice_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_notice_result.message)
|
||||
|
||||
|
||||
@notice_controller.get(
|
||||
'/{notice_id}',
|
||||
summary='获取通知公告详情接口',
|
||||
description='用于获取指定通知公告的详细信息',
|
||||
response_model=DataResponseModel[NoticeModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:notice:query')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_NOTICE_DETAIL)
|
||||
async def query_detail_system_post(
|
||||
request: Request,
|
||||
notice_id: Annotated[int, Path(description='公告ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
notice_detail_result = await NoticeService.notice_detail_services(query_db, notice_id)
|
||||
logger.info(f'获取notice_id为{notice_id}的信息成功')
|
||||
|
||||
return ResponseUtil.success(data=notice_detail_result)
|
||||
@@ -0,0 +1,63 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Path, Query, Request, Response
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.annotation.log_annotation import Log
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
|
||||
from common.aspect.db_seesion import DBSessionDependency
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import PreAuthDependency
|
||||
from common.constant import ApiNamespace
|
||||
from common.enums import BusinessType
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import ResponseBaseModel
|
||||
from module_admin.entity.vo.online_vo import DeleteOnlineModel, OnlinePageResponseModel, OnlineQueryModel
|
||||
from module_admin.service.online_service import OnlineService
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
online_controller = APIRouterPro(
|
||||
prefix='/monitor/online', order_num=12, tags=['系统监控-在线用户'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@online_controller.get(
|
||||
'/list',
|
||||
summary='获取在线用户分页列表接口',
|
||||
description='用于获取在线用户分页列表',
|
||||
response_model=OnlinePageResponseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:online:list')],
|
||||
)
|
||||
async def get_monitor_online_list(
|
||||
request: Request,
|
||||
online_page_query: Annotated[OnlineQueryModel, Query()],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
online_query_result = await OnlineService.get_online_list_services(request, online_page_query)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(
|
||||
model_content=OnlinePageResponseModel(rows=online_query_result, total=len(online_query_result))
|
||||
)
|
||||
|
||||
|
||||
@online_controller.delete(
|
||||
'/{token_ids}',
|
||||
summary='强退在线用户接口',
|
||||
description='用于强退指定会话编号的在线用户',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:online:forceLogout')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.MONITOR_ONLINE_FORCE_LOGOUT, preset=ApiRateLimitPreset.USER_SECURITY_MUTATION)
|
||||
@Log(title='在线用户', business_type=BusinessType.FORCE)
|
||||
async def delete_monitor_online(
|
||||
request: Request,
|
||||
token_ids: Annotated[str, Path(description='需要强退的会话编号')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
delete_online = DeleteOnlineModel(tokenIds=token_ids)
|
||||
delete_online_result = await OnlineService.delete_online_services(request, delete_online)
|
||||
logger.info(delete_online_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_online_result.message)
|
||||
@@ -0,0 +1,168 @@
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Form, Path, Query, Request, Response
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic_validation_decorator import ValidateFields
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.annotation.cache_annotation import ApiCache, ApiCacheEvict
|
||||
from common.annotation.log_annotation import Log
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
|
||||
from common.aspect.db_seesion import DBSessionDependency
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
|
||||
from common.constant import ApiGroup, ApiNamespace
|
||||
from common.enums import BusinessType
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel
|
||||
from module_admin.entity.vo.post_vo import DeletePostModel, PostModel, PostPageQueryModel
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel
|
||||
from module_admin.service.post_service import PostService
|
||||
from utils.common_util import bytes2file_response
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
post_controller = APIRouterPro(
|
||||
prefix='/system/post', order_num=7, tags=['系统管理-岗位管理'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@post_controller.get(
|
||||
'/list',
|
||||
summary='获取岗位分页列表接口',
|
||||
description='用于获取岗位分页列表',
|
||||
response_model=PageResponseModel[PostModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:post:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_POST_LIST)
|
||||
async def get_system_post_list(
|
||||
request: Request,
|
||||
post_page_query: Annotated[PostPageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取分页数据
|
||||
post_page_query_result = await PostService.get_post_list_services(query_db, post_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=post_page_query_result)
|
||||
|
||||
|
||||
@post_controller.post(
|
||||
'',
|
||||
summary='新增岗位接口',
|
||||
description='用于新增岗位',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:post:add')],
|
||||
)
|
||||
@ValidateFields(validate_model='add_post')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.POST_MUTATION)
|
||||
@Log(title='岗位管理', business_type=BusinessType.INSERT)
|
||||
async def add_system_post(
|
||||
request: Request,
|
||||
add_post: PostModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
add_post.create_by = current_user.user.user_name
|
||||
add_post.create_time = datetime.now()
|
||||
add_post.update_by = current_user.user.user_name
|
||||
add_post.update_time = datetime.now()
|
||||
add_post_result = await PostService.add_post_services(query_db, add_post)
|
||||
logger.info(add_post_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=add_post_result.message)
|
||||
|
||||
|
||||
@post_controller.put(
|
||||
'',
|
||||
summary='编辑岗位接口',
|
||||
description='用于编辑岗位',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:post:edit')],
|
||||
)
|
||||
@ValidateFields(validate_model='edit_post')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.POST_MUTATION)
|
||||
@Log(title='岗位管理', business_type=BusinessType.UPDATE)
|
||||
async def edit_system_post(
|
||||
request: Request,
|
||||
edit_post: PostModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
edit_post.update_by = current_user.user.user_name
|
||||
edit_post.update_time = datetime.now()
|
||||
edit_post_result = await PostService.edit_post_services(query_db, edit_post)
|
||||
logger.info(edit_post_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_post_result.message)
|
||||
|
||||
|
||||
@post_controller.delete(
|
||||
'/{post_ids}',
|
||||
summary='删除岗位接口',
|
||||
description='用于删除岗位',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:post:remove')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.POST_MUTATION)
|
||||
@Log(title='岗位管理', business_type=BusinessType.DELETE)
|
||||
async def delete_system_post(
|
||||
request: Request,
|
||||
post_ids: Annotated[str, Path(description='需要删除的岗位ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
delete_post = DeletePostModel(postIds=post_ids)
|
||||
delete_post_result = await PostService.delete_post_services(query_db, delete_post)
|
||||
logger.info(delete_post_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_post_result.message)
|
||||
|
||||
|
||||
@post_controller.get(
|
||||
'/{post_id}',
|
||||
summary='获取岗位详情接口',
|
||||
description='用于获取指定岗位的详细信息',
|
||||
response_model=DataResponseModel[PostModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:post:query')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_POST_DETAIL)
|
||||
async def query_detail_system_post(
|
||||
request: Request,
|
||||
post_id: Annotated[int, Path(description='岗位ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
post_detail_result = await PostService.post_detail_services(query_db, post_id)
|
||||
logger.info(f'获取post_id为{post_id}的信息成功')
|
||||
|
||||
return ResponseUtil.success(data=post_detail_result)
|
||||
|
||||
|
||||
@post_controller.post(
|
||||
'/export',
|
||||
summary='导出岗位列表接口',
|
||||
description='用于导出当前符合查询条件的岗位列表数据',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回岗位列表excel文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
dependencies=[UserInterfaceAuthDependency('system:post:export')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_POST_EXPORT, preset=ApiRateLimitPreset.USER_RESOURCE_EXPORT)
|
||||
@Log(title='岗位管理', business_type=BusinessType.EXPORT)
|
||||
async def export_system_post_list(
|
||||
request: Request,
|
||||
post_page_query: Annotated[PostPageQueryModel, Form()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
post_query_result = await PostService.get_post_list_services(query_db, post_page_query, is_page=False)
|
||||
post_export_result = await PostService.export_post_list_services(post_query_result)
|
||||
logger.info('导出成功')
|
||||
|
||||
return ResponseUtil.streaming(data=bytes2file_response(post_export_result))
|
||||
@@ -0,0 +1,400 @@
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Form, Path, Query, Request, Response
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic_validation_decorator import ValidateFields
|
||||
from sqlalchemy import ColumnElement
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.annotation.cache_annotation import ApiCache, ApiCacheEvict
|
||||
from common.annotation.log_annotation import Log
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
|
||||
from common.aspect.data_scope import DataScopeDependency
|
||||
from common.aspect.db_seesion import DBSessionDependency
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
|
||||
from common.constant import ApiGroup, ApiNamespace
|
||||
from common.enums import BusinessType
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DataResponseModel, DynamicResponseModel, PageResponseModel, ResponseBaseModel
|
||||
from module_admin.entity.do.dept_do import SysDept
|
||||
from module_admin.entity.do.user_do import SysUser
|
||||
from module_admin.entity.vo.dept_vo import DeptModel
|
||||
from module_admin.entity.vo.role_vo import (
|
||||
AddRoleModel,
|
||||
DeleteRoleModel,
|
||||
RoleDeptQueryModel,
|
||||
RoleModel,
|
||||
RolePageQueryModel,
|
||||
)
|
||||
from module_admin.entity.vo.user_vo import CrudUserRoleModel, CurrentUserModel, UserInfoModel, UserRolePageQueryModel
|
||||
from module_admin.service.dept_service import DeptService
|
||||
from module_admin.service.role_service import RoleService
|
||||
from module_admin.service.user_service import UserService
|
||||
from utils.common_util import bytes2file_response
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
role_controller = APIRouterPro(
|
||||
prefix='/system/role', order_num=4, tags=['系统管理-角色管理'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@role_controller.get(
|
||||
'/deptTree/{role_id}',
|
||||
summary='获取自定义数据权限时可见的部门树接口',
|
||||
description='用于自定义数据权限时获取当前用户可见的部门树',
|
||||
response_model=DynamicResponseModel[RoleDeptQueryModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:query')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_ROLE_DEPT_TREE)
|
||||
async def get_system_role_dept_tree(
|
||||
request: Request,
|
||||
role_id: Annotated[int, Path(description='角色ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
dept_query_result = await DeptService.get_dept_tree_services(query_db, DeptModel(), data_scope_sql)
|
||||
role_dept_query_result = await RoleService.get_role_dept_tree_services(query_db, role_id)
|
||||
role_dept_query_result.depts = dept_query_result
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=role_dept_query_result)
|
||||
|
||||
|
||||
@role_controller.get(
|
||||
'/list',
|
||||
summary='获取角色分页列表接口',
|
||||
description='用于获取角色分页列表',
|
||||
response_model=PageResponseModel[RoleModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_ROLE_LIST)
|
||||
async def get_system_role_list(
|
||||
request: Request,
|
||||
role_page_query: Annotated[RolePageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
role_page_query_result = await RoleService.get_role_list_services(
|
||||
query_db, role_page_query, data_scope_sql, is_page=True
|
||||
)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=role_page_query_result)
|
||||
|
||||
|
||||
@role_controller.post(
|
||||
'',
|
||||
summary='新增角色接口',
|
||||
description='用于新增角色',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:add')],
|
||||
)
|
||||
@ValidateFields(validate_model='add_role')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.ROLE_ENTITY_MUTATION)
|
||||
@Log(title='角色管理', business_type=BusinessType.INSERT)
|
||||
async def add_system_role(
|
||||
request: Request,
|
||||
add_role: AddRoleModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
add_role.create_by = current_user.user.user_name
|
||||
add_role.create_time = datetime.now()
|
||||
add_role.update_by = current_user.user.user_name
|
||||
add_role.update_time = datetime.now()
|
||||
add_role_result = await RoleService.add_role_services(query_db, add_role)
|
||||
logger.info(add_role_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=add_role_result.message)
|
||||
|
||||
|
||||
@role_controller.put(
|
||||
'',
|
||||
summary='编辑角色接口',
|
||||
description='用于编辑角色',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:edit')],
|
||||
)
|
||||
@ValidateFields(validate_model='edit_role')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.ROLE_PERMISSION_MUTATION)
|
||||
@Log(title='角色管理', business_type=BusinessType.UPDATE)
|
||||
async def edit_system_role(
|
||||
request: Request,
|
||||
edit_role: AddRoleModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
await RoleService.check_role_allowed_services(edit_role)
|
||||
if not current_user.user.admin:
|
||||
await RoleService.check_role_data_scope_services(query_db, str(edit_role.role_id), data_scope_sql)
|
||||
edit_role.update_by = current_user.user.user_name
|
||||
edit_role.update_time = datetime.now()
|
||||
edit_role_result = await RoleService.edit_role_services(query_db, edit_role)
|
||||
logger.info(edit_role_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_role_result.message)
|
||||
|
||||
|
||||
@role_controller.put(
|
||||
'/dataScope',
|
||||
summary='编辑角色数据权限接口',
|
||||
description='用于编辑角色数据权限',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:edit')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.DATA_SCOPE_MUTATION)
|
||||
@Log(title='角色管理', business_type=BusinessType.GRANT)
|
||||
async def edit_system_role_datascope(
|
||||
request: Request,
|
||||
role_data_scope: AddRoleModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
await RoleService.check_role_allowed_services(role_data_scope)
|
||||
if not current_user.user.admin:
|
||||
await RoleService.check_role_data_scope_services(query_db, str(role_data_scope.role_id), data_scope_sql)
|
||||
edit_role = AddRoleModel(
|
||||
roleId=role_data_scope.role_id,
|
||||
dataScope=role_data_scope.data_scope,
|
||||
deptIds=role_data_scope.dept_ids,
|
||||
deptCheckStrictly=role_data_scope.dept_check_strictly,
|
||||
updateBy=current_user.user.user_name,
|
||||
updateTime=datetime.now(),
|
||||
)
|
||||
role_data_scope_result = await RoleService.role_datascope_services(query_db, edit_role)
|
||||
logger.info(role_data_scope_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=role_data_scope_result.message)
|
||||
|
||||
|
||||
@role_controller.delete(
|
||||
'/{role_ids}',
|
||||
summary='删除角色接口',
|
||||
description='用于删除角色',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:remove')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.ROLE_ENTITY_MUTATION)
|
||||
@Log(title='角色管理', business_type=BusinessType.DELETE)
|
||||
async def delete_system_role(
|
||||
request: Request,
|
||||
role_ids: Annotated[str, Path(description='需要删除的角色ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
role_id_list = role_ids.split(',') if role_ids else []
|
||||
if role_id_list:
|
||||
for role_id in role_id_list:
|
||||
await RoleService.check_role_allowed_services(RoleModel(roleId=int(role_id)))
|
||||
if not current_user.user.admin:
|
||||
await RoleService.check_role_data_scope_services(query_db, role_id, data_scope_sql)
|
||||
delete_role = DeleteRoleModel(roleIds=role_ids, updateBy=current_user.user.user_name, updateTime=datetime.now())
|
||||
delete_role_result = await RoleService.delete_role_services(query_db, delete_role)
|
||||
logger.info(delete_role_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_role_result.message)
|
||||
|
||||
|
||||
@role_controller.get(
|
||||
'/{role_id}',
|
||||
summary='获取角色详情接口',
|
||||
description='用于获取指定角色的详细信息',
|
||||
response_model=DataResponseModel[RoleModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:query')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_ROLE_DETAIL)
|
||||
async def query_detail_system_role(
|
||||
request: Request,
|
||||
role_id: Annotated[int, Path(description='角色ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
if not current_user.user.admin:
|
||||
await RoleService.check_role_data_scope_services(query_db, str(role_id), data_scope_sql)
|
||||
role_detail_result = await RoleService.role_detail_services(query_db, role_id)
|
||||
logger.info(f'获取role_id为{role_id}的信息成功')
|
||||
|
||||
return ResponseUtil.success(data=role_detail_result.model_dump(by_alias=True))
|
||||
|
||||
|
||||
@role_controller.post(
|
||||
'/export',
|
||||
summary='导出角色列表接口',
|
||||
description='用于导出当前符合查询条件的角色列表数据',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回角色列表excel文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:export')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_ROLE_EXPORT, preset=ApiRateLimitPreset.USER_RESOURCE_EXPORT)
|
||||
@Log(title='角色管理', business_type=BusinessType.EXPORT)
|
||||
async def export_system_role_list(
|
||||
request: Request,
|
||||
role_page_query: Annotated[RolePageQueryModel, Form()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
role_query_result = await RoleService.get_role_list_services(
|
||||
query_db, role_page_query, data_scope_sql, is_page=False
|
||||
)
|
||||
role_export_result = await RoleService.export_role_list_services(role_query_result)
|
||||
logger.info('导出成功')
|
||||
|
||||
return ResponseUtil.streaming(data=bytes2file_response(role_export_result))
|
||||
|
||||
|
||||
@role_controller.put(
|
||||
'/changeStatus',
|
||||
summary='修改角色状态接口',
|
||||
description='用于修改角色状态',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:edit')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.ROLE_MUTATION)
|
||||
@Log(title='角色管理', business_type=BusinessType.UPDATE)
|
||||
async def reset_system_role_status(
|
||||
request: Request,
|
||||
change_role: AddRoleModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
await RoleService.check_role_allowed_services(change_role)
|
||||
if not current_user.user.admin:
|
||||
await RoleService.check_role_data_scope_services(query_db, str(change_role.role_id), data_scope_sql)
|
||||
edit_role = AddRoleModel(
|
||||
roleId=change_role.role_id,
|
||||
status=change_role.status,
|
||||
updateBy=current_user.user.user_name,
|
||||
updateTime=datetime.now(),
|
||||
type='status',
|
||||
)
|
||||
edit_role_result = await RoleService.edit_role_services(query_db, edit_role)
|
||||
logger.info(edit_role_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_role_result.message)
|
||||
|
||||
|
||||
@role_controller.get(
|
||||
'/authUser/allocatedList',
|
||||
summary='获取已分配用户分页列表接口',
|
||||
description='用于获取指定角色已分配的用户分页列表',
|
||||
response_model=PageResponseModel[UserInfoModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_ROLE_ALLOCATED_USER_LIST)
|
||||
async def get_system_allocated_user_list(
|
||||
request: Request,
|
||||
user_role: Annotated[UserRolePageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysUser)],
|
||||
) -> Response:
|
||||
role_user_allocated_page_query_result = await RoleService.get_role_user_allocated_list_services(
|
||||
query_db, user_role, data_scope_sql, is_page=True
|
||||
)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=role_user_allocated_page_query_result)
|
||||
|
||||
|
||||
@role_controller.get(
|
||||
'/authUser/unallocatedList',
|
||||
summary='获取未分配用户分页列表接口',
|
||||
description='用于获取指定角色未分配的用户分页列表',
|
||||
response_model=PageResponseModel[UserInfoModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_ROLE_UNALLOCATED_USER_LIST)
|
||||
async def get_system_unallocated_user_list(
|
||||
request: Request,
|
||||
user_role: Annotated[UserRolePageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysUser)],
|
||||
) -> Response:
|
||||
role_user_unallocated_page_query_result = await RoleService.get_role_user_unallocated_list_services(
|
||||
query_db, user_role, data_scope_sql, is_page=True
|
||||
)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=role_user_unallocated_page_query_result)
|
||||
|
||||
|
||||
@role_controller.put(
|
||||
'/authUser/selectAll',
|
||||
summary='分配用户给角色接口',
|
||||
description='用于给指定角色分配用户',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:edit')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_ROLE_AUTH_USER_SELECT_ALL, preset=ApiRateLimitPreset.USER_SECURITY_MUTATION)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.ROLE_MUTATION)
|
||||
@Log(title='角色管理', business_type=BusinessType.GRANT)
|
||||
async def add_system_role_user(
|
||||
request: Request,
|
||||
add_role_user: Annotated[CrudUserRoleModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
if not current_user.user.admin:
|
||||
await RoleService.check_role_data_scope_services(query_db, str(add_role_user.role_id), data_scope_sql)
|
||||
add_role_user_result = await UserService.add_user_role_services(query_db, add_role_user)
|
||||
logger.info(add_role_user_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=add_role_user_result.message)
|
||||
|
||||
|
||||
@role_controller.put(
|
||||
'/authUser/cancel',
|
||||
summary='取消分配用户给角色接口',
|
||||
description='用于取消指定用户分配给角色',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:edit')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_ROLE_AUTH_USER_CANCEL, preset=ApiRateLimitPreset.USER_SECURITY_MUTATION)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.ROLE_MUTATION)
|
||||
@Log(title='角色管理', business_type=BusinessType.GRANT)
|
||||
async def cancel_system_role_user(
|
||||
request: Request,
|
||||
cancel_user_role: CrudUserRoleModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
cancel_user_role_result = await UserService.delete_user_role_services(query_db, cancel_user_role)
|
||||
logger.info(cancel_user_role_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=cancel_user_role_result.message)
|
||||
|
||||
|
||||
@role_controller.put(
|
||||
'/authUser/cancelAll',
|
||||
summary='批量取消分配用户给角色接口',
|
||||
description='用于批量取消用户分配给角色',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:role:edit')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_ROLE_AUTH_USER_CANCEL_ALL, preset=ApiRateLimitPreset.USER_SECURITY_MUTATION)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.ROLE_MUTATION)
|
||||
@Log(title='角色管理', business_type=BusinessType.GRANT)
|
||||
async def batch_cancel_system_role_user(
|
||||
request: Request,
|
||||
batch_cancel_user_role: Annotated[CrudUserRoleModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
batch_cancel_user_role_result = await UserService.delete_user_role_services(query_db, batch_cancel_user_role)
|
||||
logger.info(batch_cancel_user_role_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=batch_cancel_user_role_result.message)
|
||||
@@ -0,0 +1,32 @@
|
||||
from fastapi import Request, Response
|
||||
|
||||
from common.annotation.cache_annotation import ApiCache
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import PreAuthDependency
|
||||
from common.constant import ApiNamespace
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DataResponseModel
|
||||
from module_admin.entity.vo.server_vo import ServerMonitorModel
|
||||
from module_admin.service.server_service import ServerService
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
server_controller = APIRouterPro(
|
||||
prefix='/monitor/server', order_num=14, tags=['系统监控-服务监控'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@server_controller.get(
|
||||
'',
|
||||
summary='获取服务器监控信息接口',
|
||||
description='用于获取当前服务器的监控信息',
|
||||
response_model=DataResponseModel[ServerMonitorModel],
|
||||
dependencies=[UserInterfaceAuthDependency('monitor:server:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.MONITOR_SERVER_INFO)
|
||||
async def get_monitor_server_info(request: Request) -> Response:
|
||||
# 获取全量数据
|
||||
server_info_query_result = await ServerService.get_server_monitor_info()
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=server_info_query_result)
|
||||
@@ -0,0 +1,78 @@
|
||||
from fastapi import Request, Response
|
||||
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import PreAuthDependency
|
||||
from common.constant import ApiNamespace
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DataResponseModel
|
||||
from module_admin.entity.vo.transport_crypto_vo import (
|
||||
TransportCryptoFrontendConfigModel,
|
||||
TransportCryptoMonitorModel,
|
||||
TransportCryptoPublicKeyModel,
|
||||
)
|
||||
from module_admin.service.transport_crypto_service import TransportCryptoService
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
|
||||
transport_crypto_controller = APIRouterPro(prefix='/transport/crypto', order_num=15, tags=['传输加密模块'])
|
||||
|
||||
|
||||
@transport_crypto_controller.get(
|
||||
'/frontend-config',
|
||||
summary='获取前端传输加密配置接口',
|
||||
description='公开接口,用于向前端下发当前传输层加解密启用状态与运行模式,供前端统一跟随后端策略',
|
||||
response_model=DataResponseModel[TransportCryptoFrontendConfigModel],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.TRANSPORT_CRYPTO_FRONTEND_CONFIG, preset=ApiRateLimitPreset.ANON_PUBLIC_METADATA)
|
||||
async def get_transport_frontend_config(request: Request) -> Response:
|
||||
"""
|
||||
获取当前前端传输层加解密运行配置
|
||||
|
||||
:param request: 当前请求对象
|
||||
:return: 前端传输层加解密运行配置响应
|
||||
"""
|
||||
transport_frontend_config = await TransportCryptoService.get_transport_frontend_config_services()
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=transport_frontend_config)
|
||||
|
||||
|
||||
@transport_crypto_controller.get(
|
||||
'/public-key',
|
||||
summary='获取传输加密公钥接口',
|
||||
description='公开接口,用于向前端下发当前可用的传输层加密公钥,已配置匿名限流保护',
|
||||
response_model=DataResponseModel[TransportCryptoPublicKeyModel],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.TRANSPORT_CRYPTO_PUBLIC_KEY, preset=ApiRateLimitPreset.ANON_PUBLIC_METADATA)
|
||||
async def get_transport_public_key(request: Request) -> Response:
|
||||
"""
|
||||
获取当前传输层加密公钥
|
||||
|
||||
:param request: 当前请求对象
|
||||
:return: 公钥下发响应
|
||||
"""
|
||||
transport_public_key = await TransportCryptoService.get_transport_public_key_services()
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=transport_public_key)
|
||||
|
||||
|
||||
@transport_crypto_controller.get(
|
||||
'/monitor',
|
||||
summary='获取传输层加解密监控信息接口',
|
||||
description='用于获取基于Redis聚合的传输层加解密运行状态与统计信息',
|
||||
response_model=DataResponseModel[TransportCryptoMonitorModel],
|
||||
dependencies=[PreAuthDependency(), UserInterfaceAuthDependency('monitor:transportCrypto:list')],
|
||||
)
|
||||
async def get_transport_crypto_monitor_info(request: Request) -> Response:
|
||||
"""
|
||||
获取基于Redis聚合的传输层加解密监控信息
|
||||
|
||||
:param request: 当前请求对象
|
||||
:return: 传输层加解密监控信息响应
|
||||
"""
|
||||
transport_crypto_monitor_info = await TransportCryptoService.get_transport_crypto_monitor_info_services(request)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=transport_crypto_monitor_info)
|
||||
@@ -0,0 +1,550 @@
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import Annotated, Literal
|
||||
|
||||
import aiofiles
|
||||
from fastapi import File, Form, Path, Query, Request, Response, UploadFile
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic_validation_decorator import ValidateFields
|
||||
from sqlalchemy import ColumnElement
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.annotation.cache_annotation import ApiCache, ApiCacheEvict
|
||||
from common.annotation.log_annotation import Log
|
||||
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitBypassConfig, ApiRateLimitPreset
|
||||
from common.aspect.data_scope import DataScopeDependency
|
||||
from common.aspect.db_seesion import DBSessionDependency
|
||||
from common.aspect.interface_auth import UserInterfaceAuthDependency
|
||||
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
|
||||
from common.constant import ApiGroup, ApiNamespace
|
||||
from common.enums import BusinessType
|
||||
from common.router import APIRouterPro
|
||||
from common.vo import DataResponseModel, DynamicResponseModel, PageResponseModel, ResponseBaseModel
|
||||
from config.env import UploadConfig
|
||||
from module_admin.entity.do.dept_do import SysDept
|
||||
from module_admin.entity.do.user_do import SysUser
|
||||
from module_admin.entity.vo.dept_vo import DeptModel, DeptTreeModel
|
||||
from module_admin.entity.vo.user_vo import (
|
||||
AddUserModel,
|
||||
AvatarModel,
|
||||
CrudUserRoleModel,
|
||||
CurrentUserModel,
|
||||
DeleteUserModel,
|
||||
EditUserModel,
|
||||
ResetPasswordModel,
|
||||
ResetUserModel,
|
||||
UserDetailModel,
|
||||
UserInfoModel,
|
||||
UserModel,
|
||||
UserPageQueryModel,
|
||||
UserProfileModel,
|
||||
UserRoleQueryModel,
|
||||
UserRoleResponseModel,
|
||||
UserRowModel,
|
||||
)
|
||||
from module_admin.service.dept_service import DeptService
|
||||
from module_admin.service.role_service import RoleService
|
||||
from module_admin.service.user_service import UserService
|
||||
from utils.common_util import bytes2file_response
|
||||
from utils.log_util import logger
|
||||
from utils.pwd_util import PwdUtil
|
||||
from utils.response_util import ResponseUtil
|
||||
from utils.upload_util import UploadUtil
|
||||
|
||||
user_controller = APIRouterPro(
|
||||
prefix='/system/user', order_num=3, tags=['系统管理-用户管理'], dependencies=[PreAuthDependency()]
|
||||
)
|
||||
|
||||
|
||||
@user_controller.get(
|
||||
'/deptTree',
|
||||
summary='获取部门树接口',
|
||||
description='用于获取当前登录用户可见的部门树',
|
||||
response_model=DataResponseModel[list[DeptTreeModel]],
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_USER_DEPT_TREE)
|
||||
async def get_system_dept_tree(
|
||||
request: Request,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
dept_query_result = await DeptService.get_dept_tree_services(query_db, DeptModel(), data_scope_sql)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(data=dept_query_result)
|
||||
|
||||
|
||||
@user_controller.get(
|
||||
'/list',
|
||||
summary='获取用户分页列表接口',
|
||||
description='用于获取用户分页列表',
|
||||
response_model=PageResponseModel[UserRowModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:list')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_USER_LIST)
|
||||
async def get_system_user_list(
|
||||
request: Request,
|
||||
user_page_query: Annotated[UserPageQueryModel, Query()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysUser)],
|
||||
) -> Response:
|
||||
# 获取分页数据
|
||||
user_page_query_result = await UserService.get_user_list_services(
|
||||
query_db, user_page_query, data_scope_sql, is_page=True
|
||||
)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=user_page_query_result)
|
||||
|
||||
|
||||
@user_controller.post(
|
||||
'',
|
||||
summary='新增用户接口',
|
||||
description='用于新增用户',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:add')],
|
||||
)
|
||||
@ValidateFields(validate_model='add_user')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.USER_ENTITY_MUTATION)
|
||||
@Log(title='用户管理', business_type=BusinessType.INSERT)
|
||||
async def add_system_user(
|
||||
request: Request,
|
||||
add_user: AddUserModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
dept_data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
role_data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
if not current_user.user.admin:
|
||||
await DeptService.check_dept_data_scope_services(query_db, add_user.dept_id, dept_data_scope_sql)
|
||||
await RoleService.check_role_data_scope_services(
|
||||
query_db, ','.join([str(item) for item in add_user.role_ids]), role_data_scope_sql
|
||||
)
|
||||
add_user.password = PwdUtil.get_password_hash(add_user.password)
|
||||
add_user.create_by = current_user.user.user_name
|
||||
add_user.create_time = datetime.now()
|
||||
add_user.update_by = current_user.user.user_name
|
||||
add_user.update_time = datetime.now()
|
||||
add_user_result = await UserService.add_user_services(query_db, add_user)
|
||||
logger.info(add_user_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=add_user_result.message)
|
||||
|
||||
|
||||
@user_controller.put(
|
||||
'',
|
||||
summary='编辑用户接口',
|
||||
description='用于编辑用户',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:edit')],
|
||||
)
|
||||
@ValidateFields(validate_model='edit_user')
|
||||
@ApiCacheEvict(namespaces=ApiGroup.USER_PERMISSION_MUTATION)
|
||||
@Log(title='用户管理', business_type=BusinessType.UPDATE)
|
||||
async def edit_system_user(
|
||||
request: Request,
|
||||
edit_user: EditUserModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
user_data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysUser)],
|
||||
dept_data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
role_data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
await UserService.check_user_allowed_services(edit_user)
|
||||
if not current_user.user.admin:
|
||||
await UserService.check_user_data_scope_services(query_db, edit_user.user_id, user_data_scope_sql)
|
||||
await DeptService.check_dept_data_scope_services(query_db, edit_user.dept_id, dept_data_scope_sql)
|
||||
await RoleService.check_role_data_scope_services(
|
||||
query_db, ','.join([str(item) for item in edit_user.role_ids]), role_data_scope_sql
|
||||
)
|
||||
edit_user.update_by = current_user.user.user_name
|
||||
edit_user.update_time = datetime.now()
|
||||
edit_user_result = await UserService.edit_user_services(query_db, edit_user)
|
||||
logger.info(edit_user_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_user_result.message)
|
||||
|
||||
|
||||
@user_controller.delete(
|
||||
'/{user_ids}',
|
||||
summary='删除用户接口',
|
||||
description='用于删除用户',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:remove')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.USER_ENTITY_MUTATION)
|
||||
@Log(title='用户管理', business_type=BusinessType.DELETE)
|
||||
async def delete_system_user(
|
||||
request: Request,
|
||||
user_ids: Annotated[str, Path(description='需要删除的用户ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysUser)],
|
||||
) -> Response:
|
||||
user_id_list = user_ids.split(',') if user_ids else []
|
||||
if user_id_list:
|
||||
if current_user.user.user_id in list(map(int, user_id_list)):
|
||||
logger.warning('当前登录用户不能删除')
|
||||
|
||||
return ResponseUtil.failure(msg='当前登录用户不能删除')
|
||||
for user_id in user_id_list:
|
||||
await UserService.check_user_allowed_services(UserModel(userId=int(user_id)))
|
||||
if not current_user.user.admin:
|
||||
await UserService.check_user_data_scope_services(query_db, int(user_id), data_scope_sql)
|
||||
delete_user = DeleteUserModel(userIds=user_ids, updateBy=current_user.user.user_name, updateTime=datetime.now())
|
||||
delete_user_result = await UserService.delete_user_services(query_db, delete_user)
|
||||
logger.info(delete_user_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=delete_user_result.message)
|
||||
|
||||
|
||||
@user_controller.put(
|
||||
'/resetPwd',
|
||||
summary='重置用户密码接口',
|
||||
description='用于重置用户密码',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:resetPwd')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.USER_INFO_MUTATION)
|
||||
@Log(title='用户管理', business_type=BusinessType.UPDATE)
|
||||
async def reset_system_user_pwd(
|
||||
request: Request,
|
||||
reset_user: EditUserModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysUser)],
|
||||
) -> Response:
|
||||
await UserService.check_user_allowed_services(reset_user)
|
||||
if not current_user.user.admin:
|
||||
await UserService.check_user_data_scope_services(query_db, reset_user.user_id, data_scope_sql)
|
||||
edit_user = EditUserModel(
|
||||
userId=reset_user.user_id,
|
||||
password=PwdUtil.get_password_hash(reset_user.password),
|
||||
pwdUpdateDate=datetime.now(),
|
||||
updateBy=current_user.user.user_name,
|
||||
updateTime=datetime.now(),
|
||||
type='pwd',
|
||||
)
|
||||
edit_user_result = await UserService.edit_user_services(query_db, edit_user)
|
||||
logger.info(edit_user_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_user_result.message)
|
||||
|
||||
|
||||
@user_controller.put(
|
||||
'/changeStatus',
|
||||
summary='修改用户状态接口',
|
||||
description='用于修改用户状态',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:edit')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.USER_INFO_MUTATION)
|
||||
@Log(title='用户管理', business_type=BusinessType.UPDATE)
|
||||
async def change_system_user_status(
|
||||
request: Request,
|
||||
change_user: EditUserModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysUser)],
|
||||
) -> Response:
|
||||
await UserService.check_user_allowed_services(change_user)
|
||||
if not current_user.user.admin:
|
||||
await UserService.check_user_data_scope_services(query_db, change_user.user_id, data_scope_sql)
|
||||
edit_user = EditUserModel(
|
||||
userId=change_user.user_id,
|
||||
status=change_user.status,
|
||||
updateBy=current_user.user.user_name,
|
||||
updateTime=datetime.now(),
|
||||
type='status',
|
||||
)
|
||||
edit_user_result = await UserService.edit_user_services(query_db, edit_user)
|
||||
logger.info(edit_user_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_user_result.message)
|
||||
|
||||
|
||||
@user_controller.get(
|
||||
'/profile',
|
||||
summary='获取用户个人信息接口',
|
||||
description='用于获取当前登录用户的个人信息',
|
||||
response_model=DynamicResponseModel[UserProfileModel],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_USER_PROFILE)
|
||||
async def query_detail_system_user_profile(
|
||||
request: Request,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
profile_user_result = await UserService.user_profile_services(query_db, current_user.user.user_id)
|
||||
logger.info(f'获取user_id为{current_user.user.user_id}的信息成功')
|
||||
|
||||
return ResponseUtil.success(model_content=profile_user_result)
|
||||
|
||||
|
||||
@user_controller.get(
|
||||
'/{user_id}',
|
||||
summary='获取用户详情接口',
|
||||
description='用于获取指定用户的详情信息',
|
||||
response_model=DynamicResponseModel[UserDetailModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:query')],
|
||||
)
|
||||
@user_controller.get(
|
||||
'/',
|
||||
summary='获取用户岗位和角色列表接口',
|
||||
description='用于获取当前登录用户可见的岗位和角色列表',
|
||||
response_model=DynamicResponseModel[UserDetailModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:query')],
|
||||
)
|
||||
@ApiCache(namespace=ApiNamespace.SYSTEM_USER_DETAIL)
|
||||
async def query_detail_system_user(
|
||||
request: Request,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysUser)],
|
||||
user_id: int | Literal[''] | None = '',
|
||||
) -> Response:
|
||||
if user_id and not current_user.user.admin:
|
||||
await UserService.check_user_data_scope_services(query_db, user_id, data_scope_sql)
|
||||
detail_user_result = await UserService.user_detail_services(query_db, user_id)
|
||||
logger.info(f'获取user_id为{user_id}的信息成功')
|
||||
|
||||
return ResponseUtil.success(model_content=detail_user_result)
|
||||
|
||||
|
||||
@user_controller.post(
|
||||
'/profile/avatar',
|
||||
summary='修改用户头像接口',
|
||||
description='用于修改当前登录用户的头像',
|
||||
response_model=DynamicResponseModel[AvatarModel],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_USER_PROFILE_AVATAR, preset=ApiRateLimitPreset.USER_RESOURCE_UPLOAD)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.USER_INFO_MUTATION)
|
||||
@Log(title='个人信息', business_type=BusinessType.UPDATE)
|
||||
async def change_system_user_profile_avatar(
|
||||
request: Request,
|
||||
avatarfile: Annotated[bytes, File()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
if avatarfile:
|
||||
relative_path = (
|
||||
f'avatar/{datetime.now().strftime("%Y")}/{datetime.now().strftime("%m")}/{datetime.now().strftime("%d")}'
|
||||
)
|
||||
dir_path = os.path.join(UploadConfig.UPLOAD_PATH, relative_path)
|
||||
try:
|
||||
os.makedirs(dir_path)
|
||||
except FileExistsError:
|
||||
pass
|
||||
avatar_name = f'avatar_{datetime.now().strftime("%Y%m%d%H%M%S")}{UploadConfig.UPLOAD_MACHINE}{UploadUtil.generate_random_number()}.png'
|
||||
avatar_path = os.path.join(dir_path, avatar_name)
|
||||
async with aiofiles.open(avatar_path, 'wb') as f:
|
||||
await f.write(avatarfile)
|
||||
edit_user = EditUserModel(
|
||||
userId=current_user.user.user_id,
|
||||
avatar=f'{UploadConfig.UPLOAD_PREFIX}/{relative_path}/{avatar_name}',
|
||||
updateBy=current_user.user.user_name,
|
||||
updateTime=datetime.now(),
|
||||
type='avatar',
|
||||
)
|
||||
edit_user_result = await UserService.edit_user_services(query_db, edit_user)
|
||||
logger.info(edit_user_result.message)
|
||||
|
||||
return ResponseUtil.success(model_content=AvatarModel(imgUrl=edit_user.avatar), msg=edit_user_result.message)
|
||||
return ResponseUtil.failure(msg='上传图片异常,请联系管理员')
|
||||
|
||||
|
||||
@user_controller.put(
|
||||
'/profile',
|
||||
summary='修改用户个人信息接口',
|
||||
description='用于修改当前登录用户的个人信息',
|
||||
response_model=ResponseBaseModel,
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.USER_INFO_MUTATION)
|
||||
@Log(title='个人信息', business_type=BusinessType.UPDATE)
|
||||
async def change_system_user_profile_info(
|
||||
request: Request,
|
||||
user_info: UserInfoModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
edit_user = EditUserModel(
|
||||
**user_info.model_dump(exclude_unset=True, by_alias=True, exclude={'role_ids', 'post_ids'}),
|
||||
userId=current_user.user.user_id,
|
||||
userName=current_user.user.user_name,
|
||||
updateBy=current_user.user.user_name,
|
||||
updateTime=datetime.now(),
|
||||
roleIds=current_user.user.role_ids.split(',') if current_user.user.role_ids else [],
|
||||
postIds=current_user.user.post_ids.split(',') if current_user.user.post_ids else [],
|
||||
role=current_user.user.role,
|
||||
)
|
||||
edit_user_result = await UserService.edit_user_services(query_db, edit_user)
|
||||
logger.info(edit_user_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=edit_user_result.message)
|
||||
|
||||
|
||||
@user_controller.put(
|
||||
'/profile/updatePwd',
|
||||
summary='修改用户密码接口',
|
||||
description='用于修改当前登录用户的密码',
|
||||
response_model=ResponseBaseModel,
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.USER_INFO_MUTATION)
|
||||
@Log(title='个人信息', business_type=BusinessType.UPDATE)
|
||||
async def reset_system_user_password(
|
||||
request: Request,
|
||||
reset_password: ResetPasswordModel,
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
) -> Response:
|
||||
reset_user = ResetUserModel(
|
||||
userId=current_user.user.user_id,
|
||||
oldPassword=reset_password.old_password,
|
||||
password=reset_password.new_password,
|
||||
pwdUpdateDate=datetime.now(),
|
||||
updateBy=current_user.user.user_name,
|
||||
updateTime=datetime.now(),
|
||||
)
|
||||
reset_user_result = await UserService.reset_user_services(query_db, reset_user)
|
||||
logger.info(reset_user_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=reset_user_result.message)
|
||||
|
||||
|
||||
@user_controller.post(
|
||||
'/importData',
|
||||
summary='批量导入用户接口',
|
||||
description='用于批量导入用户数据',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:import')],
|
||||
)
|
||||
@ApiRateLimit(
|
||||
namespace=ApiNamespace.SYSTEM_USER_IMPORT,
|
||||
preset=ApiRateLimitPreset.USER_RESOURCE_IMPORT,
|
||||
bypass=ApiRateLimitBypassConfig(roles=('admin',)),
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.DATA_SCOPE_MUTATION)
|
||||
@Log(title='用户管理', business_type=BusinessType.IMPORT)
|
||||
async def batch_import_system_user(
|
||||
request: Request,
|
||||
file: Annotated[UploadFile, File(...)],
|
||||
update_support: Annotated[bool, Query(alias='updateSupport')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
user_data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysUser)],
|
||||
dept_data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
batch_import_result = await UserService.batch_import_user_services(
|
||||
request, query_db, file, update_support, current_user, user_data_scope_sql, dept_data_scope_sql
|
||||
)
|
||||
logger.info(batch_import_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=batch_import_result.message)
|
||||
|
||||
|
||||
@user_controller.post(
|
||||
'/importTemplate',
|
||||
summary='获取用户导入模板接口',
|
||||
description='用于获取用户导入模板excel文件',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回导入用户模板excel文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:import')],
|
||||
)
|
||||
async def export_system_user_template(
|
||||
request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()]
|
||||
) -> Response:
|
||||
user_import_template_result = await UserService.get_user_import_template_services()
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.streaming(data=bytes2file_response(user_import_template_result))
|
||||
|
||||
|
||||
@user_controller.post(
|
||||
'/export',
|
||||
summary='导出用户列表接口',
|
||||
description='用于导出当前符合查询条件的用户列表数据',
|
||||
response_class=StreamingResponse,
|
||||
responses={
|
||||
200: {
|
||||
'description': '流式返回用户列表excel文件',
|
||||
'content': {
|
||||
'application/octet-stream': {},
|
||||
},
|
||||
}
|
||||
},
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:export')],
|
||||
)
|
||||
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_USER_EXPORT, preset=ApiRateLimitPreset.USER_RESOURCE_EXPORT)
|
||||
@Log(title='用户管理', business_type=BusinessType.EXPORT)
|
||||
async def export_system_user_list(
|
||||
request: Request,
|
||||
user_page_query: Annotated[UserPageQueryModel, Form()],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysUser)],
|
||||
) -> Response:
|
||||
# 获取全量数据
|
||||
user_query_result = await UserService.get_user_list_services(
|
||||
query_db, user_page_query, data_scope_sql, is_page=False
|
||||
)
|
||||
user_export_result = await UserService.export_user_list_services(user_query_result)
|
||||
logger.info('导出成功')
|
||||
|
||||
return ResponseUtil.streaming(data=bytes2file_response(user_export_result))
|
||||
|
||||
|
||||
@user_controller.get(
|
||||
'/authRole/{user_id}',
|
||||
summary='获取用户已分配角色列表接口',
|
||||
description='用于获取指定用户已分配的角色列表',
|
||||
response_model=DynamicResponseModel[UserRoleResponseModel],
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:query')],
|
||||
)
|
||||
async def get_system_allocated_role_list(
|
||||
request: Request,
|
||||
user_id: Annotated[int, Path(description='用户ID')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
) -> Response:
|
||||
user_role_query = UserRoleQueryModel(userId=user_id)
|
||||
user_role_allocated_query_result = await UserService.get_user_role_allocated_list_services(
|
||||
query_db, user_role_query
|
||||
)
|
||||
logger.info('获取成功')
|
||||
|
||||
return ResponseUtil.success(model_content=user_role_allocated_query_result)
|
||||
|
||||
|
||||
@user_controller.put(
|
||||
'/authRole',
|
||||
summary='给用户分配角色接口',
|
||||
description='用于给指定用户分配角色',
|
||||
response_model=ResponseBaseModel,
|
||||
dependencies=[UserInterfaceAuthDependency('system:user:edit')],
|
||||
)
|
||||
@ApiCacheEvict(namespaces=ApiGroup.USER_PERMISSION_MUTATION)
|
||||
@Log(title='用户管理', business_type=BusinessType.GRANT)
|
||||
async def update_system_role_user(
|
||||
request: Request,
|
||||
user_id: Annotated[int, Query(alias='userId')],
|
||||
role_ids: Annotated[str, Query(alias='roleIds')],
|
||||
query_db: Annotated[AsyncSession, DBSessionDependency()],
|
||||
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
|
||||
user_data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysUser)],
|
||||
role_data_scope_sql: Annotated[ColumnElement, DataScopeDependency(SysDept)],
|
||||
) -> Response:
|
||||
if not current_user.user.admin:
|
||||
await UserService.check_user_data_scope_services(query_db, user_id, user_data_scope_sql)
|
||||
await RoleService.check_role_data_scope_services(query_db, role_ids, role_data_scope_sql)
|
||||
add_user_role_result = await UserService.add_user_role_services(
|
||||
query_db, CrudUserRoleModel(userId=user_id, roleIds=role_ids)
|
||||
)
|
||||
logger.info(add_user_role_result.message)
|
||||
|
||||
return ResponseUtil.success(msg=add_user_role_result.message)
|
||||
@@ -0,0 +1,124 @@
|
||||
from datetime import datetime, time
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import delete, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.vo import PageModel
|
||||
from module_admin.entity.do.config_do import SysConfig
|
||||
from module_admin.entity.vo.config_vo import ConfigModel, ConfigPageQueryModel
|
||||
from utils.page_util import PageUtil
|
||||
|
||||
|
||||
class ConfigDao:
|
||||
"""
|
||||
参数配置管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_config_detail_by_id(cls, db: AsyncSession, config_id: int) -> SysConfig | None:
|
||||
"""
|
||||
根据参数配置id获取参数配置详细信息
|
||||
|
||||
:param db: orm对象
|
||||
:param config_id: 参数配置id
|
||||
:return: 参数配置信息对象
|
||||
"""
|
||||
config_info = (await db.execute(select(SysConfig).where(SysConfig.config_id == config_id))).scalars().first()
|
||||
|
||||
return config_info
|
||||
|
||||
@classmethod
|
||||
async def get_config_detail_by_info(cls, db: AsyncSession, config: ConfigModel) -> SysConfig | None:
|
||||
"""
|
||||
根据参数配置参数获取参数配置信息
|
||||
|
||||
:param db: orm对象
|
||||
:param config: 参数配置参数对象
|
||||
:return: 参数配置信息对象
|
||||
"""
|
||||
config_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysConfig).where(
|
||||
SysConfig.config_key == config.config_key if config.config_key else True,
|
||||
SysConfig.config_value == config.config_value if config.config_value else True,
|
||||
)
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return config_info
|
||||
|
||||
@classmethod
|
||||
async def get_config_list(
|
||||
cls, db: AsyncSession, query_object: ConfigPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
根据查询参数获取参数配置列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 参数配置列表信息对象
|
||||
"""
|
||||
query = (
|
||||
select(SysConfig)
|
||||
.where(
|
||||
SysConfig.config_name.like(f'%{query_object.config_name}%') if query_object.config_name else True,
|
||||
SysConfig.config_key.like(f'%{query_object.config_key}%') if query_object.config_key else True,
|
||||
SysConfig.config_type == query_object.config_type if query_object.config_type else True,
|
||||
SysConfig.create_time.between(
|
||||
datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
|
||||
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
|
||||
)
|
||||
if query_object.begin_time and query_object.end_time
|
||||
else True,
|
||||
)
|
||||
.order_by(SysConfig.config_id)
|
||||
.distinct()
|
||||
)
|
||||
config_list: PageModel | list[dict[str, Any]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return config_list
|
||||
|
||||
@classmethod
|
||||
async def add_config_dao(cls, db: AsyncSession, config: ConfigModel) -> SysConfig:
|
||||
"""
|
||||
新增参数配置数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param config: 参数配置对象
|
||||
:return:
|
||||
"""
|
||||
db_config = SysConfig(**config.model_dump())
|
||||
db.add(db_config)
|
||||
await db.flush()
|
||||
|
||||
return db_config
|
||||
|
||||
@classmethod
|
||||
async def edit_config_dao(cls, db: AsyncSession, config: dict) -> None:
|
||||
"""
|
||||
编辑参数配置数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param config: 需要更新的参数配置字典
|
||||
:return:
|
||||
"""
|
||||
await db.execute(update(SysConfig), [config])
|
||||
|
||||
@classmethod
|
||||
async def delete_config_dao(cls, db: AsyncSession, config: ConfigModel) -> None:
|
||||
"""
|
||||
删除参数配置数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param config: 参数配置对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysConfig).where(SysConfig.config_id.in_([config.config_id])))
|
||||
@@ -0,0 +1,315 @@
|
||||
from collections.abc import Sequence
|
||||
|
||||
from sqlalchemy import ColumnElement, bindparam, func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.util import immutabledict
|
||||
|
||||
from module_admin.entity.do.dept_do import SysDept
|
||||
from module_admin.entity.do.user_do import SysUser
|
||||
from module_admin.entity.vo.dept_vo import DeptModel
|
||||
|
||||
|
||||
class DeptDao:
|
||||
"""
|
||||
部门管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_dept_by_id(cls, db: AsyncSession, dept_id: int) -> SysDept | None:
|
||||
"""
|
||||
根据部门id获取在用部门信息
|
||||
|
||||
:param db: orm对象
|
||||
:param dept_id: 部门id
|
||||
:return: 在用部门信息对象
|
||||
"""
|
||||
dept_info = (await db.execute(select(SysDept).where(SysDept.dept_id == dept_id))).scalars().first()
|
||||
|
||||
return dept_info
|
||||
|
||||
@classmethod
|
||||
async def get_dept_detail_by_id(cls, db: AsyncSession, dept_id: int) -> SysDept | None:
|
||||
"""
|
||||
根据部门id获取部门详细信息
|
||||
|
||||
:param db: orm对象
|
||||
:param dept_id: 部门id
|
||||
:return: 部门信息对象
|
||||
"""
|
||||
dept_info = (
|
||||
(await db.execute(select(SysDept).where(SysDept.dept_id == dept_id, SysDept.del_flag == '0')))
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return dept_info
|
||||
|
||||
@classmethod
|
||||
async def get_dept_detail_by_info(cls, db: AsyncSession, dept: DeptModel) -> SysDept | None:
|
||||
"""
|
||||
根据部门参数获取部门信息
|
||||
|
||||
:param db: orm对象
|
||||
:param dept: 部门参数对象
|
||||
:return: 部门信息对象
|
||||
"""
|
||||
dept_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysDept).where(
|
||||
SysDept.parent_id == dept.parent_id if dept.parent_id else True,
|
||||
SysDept.dept_name == dept.dept_name if dept.dept_name else True,
|
||||
SysDept.del_flag == '0',
|
||||
)
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return dept_info
|
||||
|
||||
@classmethod
|
||||
async def get_dept_info_for_edit_option(
|
||||
cls, db: AsyncSession, dept_info: DeptModel, data_scope_sql: ColumnElement
|
||||
) -> Sequence[SysDept]:
|
||||
"""
|
||||
获取部门编辑对应的在用部门列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param dept_info: 部门对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:return: 部门列表信息
|
||||
"""
|
||||
dept_result = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysDept)
|
||||
.where(
|
||||
SysDept.dept_id != dept_info.dept_id,
|
||||
~SysDept.dept_id.in_(
|
||||
select(SysDept.dept_id).where(func.find_in_set(dept_info.dept_id, SysDept.ancestors))
|
||||
),
|
||||
SysDept.del_flag == '0',
|
||||
SysDept.status == '0',
|
||||
data_scope_sql,
|
||||
)
|
||||
.order_by(SysDept.order_num)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
return dept_result
|
||||
|
||||
@classmethod
|
||||
async def get_children_dept_dao(cls, db: AsyncSession, dept_id: int) -> Sequence[SysDept]:
|
||||
"""
|
||||
根据部门id查询当前部门的子部门列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param dept_id: 部门id
|
||||
:return: 子部门信息列表
|
||||
"""
|
||||
dept_result = (
|
||||
(await db.execute(select(SysDept).where(func.find_in_set(dept_id, SysDept.ancestors)))).scalars().all()
|
||||
)
|
||||
|
||||
return dept_result
|
||||
|
||||
@classmethod
|
||||
async def get_dept_list_for_tree(
|
||||
cls, db: AsyncSession, dept_info: DeptModel, data_scope_sql: ColumnElement
|
||||
) -> Sequence[SysDept]:
|
||||
"""
|
||||
获取所有在用部门列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param dept_info: 部门对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:return: 在用部门列表信息
|
||||
"""
|
||||
dept_result = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysDept)
|
||||
.where(
|
||||
SysDept.status == '0',
|
||||
SysDept.del_flag == '0',
|
||||
SysDept.dept_name.like(f'%{dept_info.dept_name}%') if dept_info.dept_name else True,
|
||||
data_scope_sql,
|
||||
)
|
||||
.order_by(SysDept.order_num)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
return dept_result
|
||||
|
||||
@classmethod
|
||||
async def get_dept_list(
|
||||
cls, db: AsyncSession, page_object: DeptModel, data_scope_sql: ColumnElement
|
||||
) -> Sequence[SysDept]:
|
||||
"""
|
||||
根据查询参数获取部门列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param page_object: 不分页查询参数对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:return: 部门列表信息对象
|
||||
"""
|
||||
dept_result = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysDept)
|
||||
.where(
|
||||
SysDept.del_flag == '0',
|
||||
SysDept.dept_id == page_object.dept_id if page_object.dept_id is not None else True,
|
||||
SysDept.status == page_object.status if page_object.status else True,
|
||||
SysDept.dept_name.like(f'%{page_object.dept_name}%') if page_object.dept_name else True,
|
||||
data_scope_sql,
|
||||
)
|
||||
.order_by(SysDept.order_num)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
return dept_result
|
||||
|
||||
@classmethod
|
||||
async def add_dept_dao(cls, db: AsyncSession, dept: DeptModel) -> SysDept:
|
||||
"""
|
||||
新增部门数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param dept: 部门对象
|
||||
:return: 新增校验结果
|
||||
"""
|
||||
db_dept = SysDept(**dept.model_dump())
|
||||
db.add(db_dept)
|
||||
await db.flush()
|
||||
|
||||
return db_dept
|
||||
|
||||
@classmethod
|
||||
async def edit_dept_dao(cls, db: AsyncSession, dept: dict) -> None:
|
||||
"""
|
||||
编辑部门数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param dept: 需要更新的部门字典
|
||||
:return: 编辑校验结果
|
||||
"""
|
||||
await db.execute(update(SysDept), [dept])
|
||||
|
||||
@classmethod
|
||||
async def update_dept_children_dao(cls, db: AsyncSession, update_dept: list) -> None:
|
||||
"""
|
||||
更新子部门信息
|
||||
|
||||
:param db: orm对象
|
||||
:param update_dept: 需要更新的部门列表
|
||||
:return:
|
||||
"""
|
||||
await db.execute(
|
||||
update(SysDept)
|
||||
.where(SysDept.dept_id == bindparam('dept_id'))
|
||||
.values(
|
||||
{
|
||||
'dept_id': bindparam('dept_id'),
|
||||
'ancestors': bindparam('ancestors'),
|
||||
}
|
||||
),
|
||||
update_dept,
|
||||
execution_options=immutabledict({'synchronize_session': None}),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def update_dept_status_normal_dao(cls, db: AsyncSession, dept_id_list: list) -> None:
|
||||
"""
|
||||
批量更新部门状态为正常
|
||||
|
||||
:param db: orm对象
|
||||
:param dept_id_list: 部门id列表
|
||||
:return:
|
||||
"""
|
||||
await db.execute(update(SysDept).where(SysDept.dept_id.in_(dept_id_list)).values(status='0'))
|
||||
|
||||
@classmethod
|
||||
async def delete_dept_dao(cls, db: AsyncSession, dept: DeptModel) -> None:
|
||||
"""
|
||||
删除部门数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param dept: 部门对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(
|
||||
update(SysDept)
|
||||
.where(SysDept.dept_id == dept.dept_id)
|
||||
.values(del_flag='2', update_by=dept.update_by, update_time=dept.update_time)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def count_normal_children_dept_dao(cls, db: AsyncSession, dept_id: int) -> int | None:
|
||||
"""
|
||||
根据部门id查询查询所有子部门(正常状态)的数量
|
||||
|
||||
:param db: orm对象
|
||||
:param dept_id: 部门id
|
||||
:return: 所有子部门(正常状态)的数量
|
||||
"""
|
||||
normal_children_dept_count = (
|
||||
await db.execute(
|
||||
select(func.count('*'))
|
||||
.select_from(SysDept)
|
||||
.where(SysDept.status == '0', SysDept.del_flag == '0', func.find_in_set(dept_id, SysDept.ancestors))
|
||||
)
|
||||
).scalar()
|
||||
|
||||
return normal_children_dept_count
|
||||
|
||||
@classmethod
|
||||
async def count_children_dept_dao(cls, db: AsyncSession, dept_id: int) -> int | None:
|
||||
"""
|
||||
根据部门id查询查询所有子部门(所有状态)的数量
|
||||
|
||||
:param db: orm对象
|
||||
:param dept_id: 部门id
|
||||
:return: 所有子部门(所有状态)的数量
|
||||
"""
|
||||
children_dept_count = (
|
||||
await db.execute(
|
||||
select(func.count('*'))
|
||||
.select_from(SysDept)
|
||||
.where(SysDept.del_flag == '0', SysDept.parent_id == dept_id)
|
||||
.limit(1)
|
||||
)
|
||||
).scalar()
|
||||
|
||||
return children_dept_count
|
||||
|
||||
@classmethod
|
||||
async def count_dept_user_dao(cls, db: AsyncSession, dept_id: int) -> int | None:
|
||||
"""
|
||||
根据部门id查询查询部门下的用户数量
|
||||
|
||||
:param db: orm对象
|
||||
:param dept_id: 部门id
|
||||
:return: 部门下的用户数量
|
||||
"""
|
||||
dept_user_count = (
|
||||
await db.execute(
|
||||
select(func.count('*')).select_from(SysUser).where(SysUser.dept_id == dept_id, SysUser.del_flag == '0')
|
||||
)
|
||||
).scalar()
|
||||
|
||||
return dept_user_count
|
||||
@@ -0,0 +1,294 @@
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime, time
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import and_, delete, func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.vo import PageModel
|
||||
from module_admin.entity.do.dict_do import SysDictData, SysDictType
|
||||
from module_admin.entity.vo.dict_vo import DictDataModel, DictDataPageQueryModel, DictTypeModel, DictTypePageQueryModel
|
||||
from utils.page_util import PageUtil
|
||||
from utils.time_format_util import list_format_datetime
|
||||
|
||||
|
||||
class DictTypeDao:
|
||||
"""
|
||||
字典类型管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_dict_type_detail_by_id(cls, db: AsyncSession, dict_id: int) -> SysDictType | None:
|
||||
"""
|
||||
根据字典类型id获取字典类型详细信息
|
||||
|
||||
:param db: orm对象
|
||||
:param dict_id: 字典类型id
|
||||
:return: 字典类型信息对象
|
||||
"""
|
||||
dict_type_info = (await db.execute(select(SysDictType).where(SysDictType.dict_id == dict_id))).scalars().first()
|
||||
|
||||
return dict_type_info
|
||||
|
||||
@classmethod
|
||||
async def get_dict_type_detail_by_info(cls, db: AsyncSession, dict_type: DictTypeModel) -> SysDictType | None:
|
||||
"""
|
||||
根据字典类型参数获取字典类型信息
|
||||
|
||||
:param db: orm对象
|
||||
:param dict_type: 字典类型参数对象
|
||||
:return: 字典类型信息对象
|
||||
"""
|
||||
dict_type_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysDictType).where(
|
||||
SysDictType.dict_type == dict_type.dict_type if dict_type.dict_type else True,
|
||||
SysDictType.dict_name == dict_type.dict_name if dict_type.dict_name else True,
|
||||
)
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return dict_type_info
|
||||
|
||||
@classmethod
|
||||
async def get_all_dict_type(cls, db: AsyncSession) -> list[Any]:
|
||||
"""
|
||||
获取所有的字典类型信息
|
||||
|
||||
:param db: orm对象
|
||||
:return: 字典类型信息列表对象
|
||||
"""
|
||||
dict_type_info = (await db.execute(select(SysDictType))).scalars().all()
|
||||
|
||||
return list_format_datetime(dict_type_info)
|
||||
|
||||
@classmethod
|
||||
async def get_dict_type_list(
|
||||
cls, db: AsyncSession, query_object: DictTypePageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
根据查询参数获取字典类型列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 字典类型列表信息对象
|
||||
"""
|
||||
query = (
|
||||
select(SysDictType)
|
||||
.where(
|
||||
SysDictType.dict_name.like(f'%{query_object.dict_name}%') if query_object.dict_name else True,
|
||||
SysDictType.dict_type.like(f'%{query_object.dict_type}%') if query_object.dict_type else True,
|
||||
SysDictType.status == query_object.status if query_object.status else True,
|
||||
SysDictType.create_time.between(
|
||||
datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
|
||||
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
|
||||
)
|
||||
if query_object.begin_time and query_object.end_time
|
||||
else True,
|
||||
)
|
||||
.order_by(SysDictType.dict_id)
|
||||
.distinct()
|
||||
)
|
||||
dict_type_list: PageModel | list[dict[str, Any]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return dict_type_list
|
||||
|
||||
@classmethod
|
||||
async def add_dict_type_dao(cls, db: AsyncSession, dict_type: DictTypeModel) -> SysDictType:
|
||||
"""
|
||||
新增字典类型数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param dict_type: 字典类型对象
|
||||
:return:
|
||||
"""
|
||||
db_dict_type = SysDictType(**dict_type.model_dump())
|
||||
db.add(db_dict_type)
|
||||
await db.flush()
|
||||
|
||||
return db_dict_type
|
||||
|
||||
@classmethod
|
||||
async def edit_dict_type_dao(cls, db: AsyncSession, dict_type: dict) -> None:
|
||||
"""
|
||||
编辑字典类型数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param dict_type: 需要更新的字典类型字典
|
||||
:return:
|
||||
"""
|
||||
await db.execute(update(SysDictType), [dict_type])
|
||||
|
||||
@classmethod
|
||||
async def delete_dict_type_dao(cls, db: AsyncSession, dict_type: DictTypeModel) -> None:
|
||||
"""
|
||||
删除字典类型数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param dict_type: 字典类型对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysDictType).where(SysDictType.dict_id.in_([dict_type.dict_id])))
|
||||
|
||||
|
||||
class DictDataDao:
|
||||
"""
|
||||
字典数据管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_dict_data_detail_by_id(cls, db: AsyncSession, dict_code: int) -> SysDictData | None:
|
||||
"""
|
||||
根据字典数据id获取字典数据详细信息
|
||||
|
||||
:param db: orm对象
|
||||
:param dict_code: 字典数据id
|
||||
:return: 字典数据信息对象
|
||||
"""
|
||||
dict_data_info = (
|
||||
(await db.execute(select(SysDictData).where(SysDictData.dict_code == dict_code))).scalars().first()
|
||||
)
|
||||
|
||||
return dict_data_info
|
||||
|
||||
@classmethod
|
||||
async def get_dict_data_detail_by_info(cls, db: AsyncSession, dict_data: DictDataModel) -> SysDictData | None:
|
||||
"""
|
||||
根据字典数据参数获取字典数据信息
|
||||
|
||||
:param db: orm对象
|
||||
:param dict_data: 字典数据参数对象
|
||||
:return: 字典数据信息对象
|
||||
"""
|
||||
dict_data_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysDictData).where(
|
||||
SysDictData.dict_type == dict_data.dict_type,
|
||||
SysDictData.dict_label == dict_data.dict_label,
|
||||
SysDictData.dict_value == dict_data.dict_value,
|
||||
)
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return dict_data_info
|
||||
|
||||
@classmethod
|
||||
async def get_dict_data_list(
|
||||
cls, db: AsyncSession, query_object: DictDataPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
根据查询参数获取字典数据列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 字典数据列表信息对象
|
||||
"""
|
||||
query = (
|
||||
select(SysDictData)
|
||||
.where(
|
||||
SysDictData.dict_type == query_object.dict_type if query_object.dict_type else True,
|
||||
SysDictData.dict_label.like(f'%{query_object.dict_label}%') if query_object.dict_label else True,
|
||||
SysDictData.status == query_object.status if query_object.status else True,
|
||||
)
|
||||
.order_by(SysDictData.dict_sort)
|
||||
.distinct()
|
||||
)
|
||||
dict_data_list: PageModel | list[dict[str, Any]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return dict_data_list
|
||||
|
||||
@classmethod
|
||||
async def query_dict_data_list(cls, db: AsyncSession, dict_type: str) -> Sequence[SysDictData]:
|
||||
"""
|
||||
根据查询参数获取字典数据列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param dict_type: 字典类型
|
||||
:return: 字典数据列表信息对象
|
||||
"""
|
||||
dict_data_list = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysDictData)
|
||||
.select_from(SysDictType)
|
||||
.where(SysDictType.dict_type == dict_type if dict_type else True, SysDictType.status == '0')
|
||||
.join(
|
||||
SysDictData,
|
||||
and_(SysDictType.dict_type == SysDictData.dict_type, SysDictData.status == '0'),
|
||||
isouter=True,
|
||||
)
|
||||
.order_by(SysDictData.dict_sort)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
return dict_data_list
|
||||
|
||||
@classmethod
|
||||
async def add_dict_data_dao(cls, db: AsyncSession, dict_data: DictDataModel) -> SysDictData:
|
||||
"""
|
||||
新增字典数据数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param dict_data: 字典数据对象
|
||||
:return:
|
||||
"""
|
||||
db_data_type = SysDictData(**dict_data.model_dump())
|
||||
db.add(db_data_type)
|
||||
await db.flush()
|
||||
|
||||
return db_data_type
|
||||
|
||||
@classmethod
|
||||
async def edit_dict_data_dao(cls, db: AsyncSession, dict_data: dict) -> None:
|
||||
"""
|
||||
编辑字典数据数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param dict_data: 需要更新的字典数据字典
|
||||
:return:
|
||||
"""
|
||||
await db.execute(update(SysDictData), [dict_data])
|
||||
|
||||
@classmethod
|
||||
async def delete_dict_data_dao(cls, db: AsyncSession, dict_data: DictDataModel) -> None:
|
||||
"""
|
||||
删除字典数据数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param dict_data: 字典数据对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysDictData).where(SysDictData.dict_code.in_([dict_data.dict_code])))
|
||||
|
||||
@classmethod
|
||||
async def count_dict_data_dao(cls, db: AsyncSession, dict_type: str) -> int | None:
|
||||
"""
|
||||
根据字典类型查询字典类型关联的字典数据数量
|
||||
|
||||
:param db: orm对象
|
||||
:param dict_type: 字典类型
|
||||
:return: 字典类型关联的字典数据数量
|
||||
"""
|
||||
dict_data_count = (
|
||||
await db.execute(select(func.count('*')).select_from(SysDictData).where(SysDictData.dict_type == dict_type))
|
||||
).scalar()
|
||||
|
||||
return dict_data_count
|
||||
@@ -0,0 +1,156 @@
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import delete, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.vo import PageModel
|
||||
from module_admin.entity.do.job_do import SysJob
|
||||
from module_admin.entity.vo.job_vo import JobModel, JobPageQueryModel
|
||||
from utils.page_util import PageUtil
|
||||
|
||||
|
||||
class JobDao:
|
||||
"""
|
||||
定时任务管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_job_detail_by_id(cls, db: AsyncSession, job_id: int) -> SysJob | None:
|
||||
"""
|
||||
根据定时任务id获取定时任务详细信息
|
||||
|
||||
:param db: orm对象
|
||||
:param job_id: 定时任务id
|
||||
:return: 定时任务信息对象
|
||||
"""
|
||||
job_info = (await db.execute(select(SysJob).where(SysJob.job_id == job_id))).scalars().first()
|
||||
|
||||
return job_info
|
||||
|
||||
@classmethod
|
||||
async def get_job_detail_by_info(cls, db: AsyncSession, job: JobModel) -> SysJob | None:
|
||||
"""
|
||||
根据定时任务参数获取定时任务信息
|
||||
|
||||
:param db: orm对象
|
||||
:param job: 定时任务参数对象
|
||||
:return: 定时任务信息对象
|
||||
"""
|
||||
job_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysJob).where(
|
||||
SysJob.job_name == job.job_name,
|
||||
SysJob.job_group == job.job_group,
|
||||
SysJob.job_executor == job.job_executor,
|
||||
SysJob.invoke_target == job.invoke_target,
|
||||
SysJob.job_args == job.job_args,
|
||||
SysJob.job_kwargs == job.job_kwargs,
|
||||
SysJob.cron_expression == job.cron_expression,
|
||||
)
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return job_info
|
||||
|
||||
@classmethod
|
||||
async def get_job_list(
|
||||
cls, db: AsyncSession, query_object: JobPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
根据查询参数获取定时任务列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 定时任务列表信息对象
|
||||
"""
|
||||
query = (
|
||||
select(SysJob)
|
||||
.where(
|
||||
SysJob.job_name.like(f'%{query_object.job_name}%') if query_object.job_name else True,
|
||||
SysJob.job_group == query_object.job_group if query_object.job_group else True,
|
||||
SysJob.status == query_object.status if query_object.status else True,
|
||||
)
|
||||
.order_by(SysJob.job_id)
|
||||
.distinct()
|
||||
)
|
||||
job_list: PageModel | list[dict[str, Any]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return job_list
|
||||
|
||||
@classmethod
|
||||
async def get_job_list_for_scheduler(cls, db: AsyncSession) -> Sequence[SysJob]:
|
||||
"""
|
||||
获取定时任务列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:return: 定时任务列表信息对象
|
||||
"""
|
||||
job_list = (await db.execute(select(SysJob).where(SysJob.status == '0').distinct())).scalars().all()
|
||||
|
||||
return job_list
|
||||
|
||||
@classmethod
|
||||
async def get_all_job_list_for_scheduler(cls, db: AsyncSession) -> Sequence[SysJob]:
|
||||
"""
|
||||
获取全部定时任务列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:return: 定时任务列表信息对象
|
||||
"""
|
||||
job_list = (await db.execute(select(SysJob).distinct())).scalars().all()
|
||||
|
||||
return job_list
|
||||
|
||||
@classmethod
|
||||
async def add_job_dao(cls, db: AsyncSession, job: JobModel) -> SysJob:
|
||||
"""
|
||||
新增定时任务数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param job: 定时任务对象
|
||||
:return:
|
||||
"""
|
||||
db_job = SysJob(**job.model_dump())
|
||||
db.add(db_job)
|
||||
await db.flush()
|
||||
|
||||
return db_job
|
||||
|
||||
@classmethod
|
||||
async def edit_job_dao(cls, db: AsyncSession, job: dict, old_job: JobModel) -> None:
|
||||
"""
|
||||
编辑定时任务数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param job: 需要更新的定时任务字典
|
||||
:param old_job: 原定时任务对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(
|
||||
update(SysJob)
|
||||
.where(
|
||||
SysJob.job_id == old_job.job_id,
|
||||
SysJob.job_name == old_job.job_name,
|
||||
SysJob.job_group == old_job.job_group,
|
||||
)
|
||||
.values(**job)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def delete_job_dao(cls, db: AsyncSession, job: JobModel) -> None:
|
||||
"""
|
||||
删除定时任务数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param job: 定时任务对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysJob).where(SysJob.job_id.in_([job.job_id])))
|
||||
@@ -0,0 +1,87 @@
|
||||
from datetime import datetime, time
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import delete, desc, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from common.vo import PageModel
|
||||
from module_admin.entity.do.job_do import SysJobLog
|
||||
from module_admin.entity.vo.job_vo import JobLogModel, JobLogPageQueryModel
|
||||
from utils.page_util import PageUtil
|
||||
|
||||
|
||||
class JobLogDao:
|
||||
"""
|
||||
定时任务日志管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_job_log_list(
|
||||
cls, db: AsyncSession, query_object: JobLogPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
根据查询参数获取定时任务日志列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 定时任务日志列表信息对象
|
||||
"""
|
||||
query = (
|
||||
select(SysJobLog)
|
||||
.where(
|
||||
SysJobLog.job_name.like(f'%{query_object.job_name}%') if query_object.job_name else True,
|
||||
SysJobLog.job_group == query_object.job_group if query_object.job_group else True,
|
||||
SysJobLog.status == query_object.status if query_object.status else True,
|
||||
SysJobLog.create_time.between(
|
||||
datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
|
||||
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
|
||||
)
|
||||
if query_object.begin_time and query_object.end_time
|
||||
else True,
|
||||
)
|
||||
.order_by(desc(SysJobLog.create_time))
|
||||
.distinct()
|
||||
)
|
||||
job_log_list: PageModel | list[dict[str, Any]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return job_log_list
|
||||
|
||||
@classmethod
|
||||
def add_job_log_dao(cls, db: Session, job_log: JobLogModel) -> SysJobLog:
|
||||
"""
|
||||
新增定时任务日志数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param job_log: 定时任务日志对象
|
||||
:return:
|
||||
"""
|
||||
db_job_log = SysJobLog(**job_log.model_dump())
|
||||
db.add(db_job_log)
|
||||
db.flush()
|
||||
|
||||
return db_job_log
|
||||
|
||||
@classmethod
|
||||
async def delete_job_log_dao(cls, db: AsyncSession, job_log: JobLogModel) -> None:
|
||||
"""
|
||||
删除定时任务日志数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param job_log: 定时任务日志对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysJobLog).where(SysJobLog.job_log_id.in_([job_log.job_log_id])))
|
||||
|
||||
@classmethod
|
||||
async def clear_job_log_dao(cls, db: AsyncSession) -> None:
|
||||
"""
|
||||
清除定时任务日志数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysJobLog))
|
||||
@@ -0,0 +1,183 @@
|
||||
from datetime import datetime, time
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import asc, delete, desc, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.vo import PageModel
|
||||
from module_admin.entity.do.log_do import SysLogininfor, SysOperLog
|
||||
from module_admin.entity.vo.log_vo import LogininforModel, LoginLogPageQueryModel, OperLogModel, OperLogPageQueryModel
|
||||
from utils.common_util import SnakeCaseUtil
|
||||
from utils.page_util import PageUtil
|
||||
from utils.time_format_util import TimeFormatUtil
|
||||
|
||||
|
||||
class OperationLogDao:
|
||||
"""
|
||||
操作日志管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_operation_log_list(
|
||||
cls, db: AsyncSession, query_object: OperLogPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
根据查询参数获取操作日志列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 操作日志列表信息对象
|
||||
"""
|
||||
if query_object.is_asc == 'ascending':
|
||||
order_by_column = asc(getattr(SysOperLog, SnakeCaseUtil.camel_to_snake(query_object.order_by_column), None))
|
||||
elif query_object.is_asc == 'descending':
|
||||
order_by_column = desc(
|
||||
getattr(SysOperLog, SnakeCaseUtil.camel_to_snake(query_object.order_by_column), None)
|
||||
)
|
||||
else:
|
||||
order_by_column = desc(SysOperLog.oper_time)
|
||||
query = (
|
||||
select(SysOperLog)
|
||||
.where(
|
||||
SysOperLog.title.like(f'%{query_object.title}%') if query_object.title else True,
|
||||
SysOperLog.oper_name.like(f'%{query_object.oper_name}%') if query_object.oper_name else True,
|
||||
SysOperLog.business_type == query_object.business_type if query_object.business_type else True,
|
||||
SysOperLog.status == query_object.status if query_object.status else True,
|
||||
SysOperLog.oper_time.between(
|
||||
datetime.combine(TimeFormatUtil.parse_date(query_object.begin_time), time(00, 00, 00)),
|
||||
datetime.combine(TimeFormatUtil.parse_date(query_object.end_time), time(23, 59, 59)),
|
||||
)
|
||||
if query_object.begin_time and query_object.end_time
|
||||
else True,
|
||||
)
|
||||
.distinct()
|
||||
.order_by(order_by_column)
|
||||
)
|
||||
operation_log_list: PageModel | list[dict[str, Any]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return operation_log_list
|
||||
|
||||
@classmethod
|
||||
async def add_operation_log_dao(cls, db: AsyncSession, operation_log: OperLogModel) -> SysOperLog:
|
||||
"""
|
||||
新增操作日志数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param operation_log: 操作日志对象
|
||||
:return: 新增校验结果
|
||||
"""
|
||||
db_operation_log = SysOperLog(**operation_log.model_dump())
|
||||
db.add(db_operation_log)
|
||||
await db.flush()
|
||||
|
||||
return db_operation_log
|
||||
|
||||
@classmethod
|
||||
async def delete_operation_log_dao(cls, db: AsyncSession, operation_log: OperLogModel) -> None:
|
||||
"""
|
||||
删除操作日志数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param operation_log: 操作日志对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysOperLog).where(SysOperLog.oper_id.in_([operation_log.oper_id])))
|
||||
|
||||
@classmethod
|
||||
async def clear_operation_log_dao(cls, db: AsyncSession) -> None:
|
||||
"""
|
||||
清除操作日志数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysOperLog))
|
||||
|
||||
|
||||
class LoginLogDao:
|
||||
"""
|
||||
登录日志管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_login_log_list(
|
||||
cls, db: AsyncSession, query_object: LoginLogPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
根据查询参数获取登录日志列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 登录日志列表信息对象
|
||||
"""
|
||||
if query_object.is_asc == 'ascending':
|
||||
order_by_column = asc(
|
||||
getattr(SysLogininfor, SnakeCaseUtil.camel_to_snake(query_object.order_by_column), None)
|
||||
)
|
||||
elif query_object.is_asc == 'descending':
|
||||
order_by_column = desc(
|
||||
getattr(SysLogininfor, SnakeCaseUtil.camel_to_snake(query_object.order_by_column), None)
|
||||
)
|
||||
else:
|
||||
order_by_column = desc(SysLogininfor.login_time)
|
||||
query = (
|
||||
select(SysLogininfor)
|
||||
.where(
|
||||
SysLogininfor.ipaddr.like(f'%{query_object.ipaddr}%') if query_object.ipaddr else True,
|
||||
SysLogininfor.user_name.like(f'%{query_object.user_name}%') if query_object.user_name else True,
|
||||
SysLogininfor.status == query_object.status if query_object.status else True,
|
||||
SysLogininfor.login_time.between(
|
||||
datetime.combine(TimeFormatUtil.parse_date(query_object.begin_time), time(00, 00, 00)),
|
||||
datetime.combine(TimeFormatUtil.parse_date(query_object.end_time), time(23, 59, 59)),
|
||||
)
|
||||
if query_object.begin_time and query_object.end_time
|
||||
else True,
|
||||
)
|
||||
.distinct()
|
||||
.order_by(order_by_column)
|
||||
)
|
||||
login_log_list: PageModel | list[dict[str, Any]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return login_log_list
|
||||
|
||||
@classmethod
|
||||
async def add_login_log_dao(cls, db: AsyncSession, login_log: LogininforModel) -> SysLogininfor:
|
||||
"""
|
||||
新增登录日志数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param login_log: 登录日志对象
|
||||
:return: 新增校验结果
|
||||
"""
|
||||
db_login_log = SysLogininfor(**login_log.model_dump())
|
||||
db.add(db_login_log)
|
||||
await db.flush()
|
||||
|
||||
return db_login_log
|
||||
|
||||
@classmethod
|
||||
async def delete_login_log_dao(cls, db: AsyncSession, login_log: LogininforModel) -> None:
|
||||
"""
|
||||
删除登录日志数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param login_log: 登录日志对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysLogininfor).where(SysLogininfor.info_id.in_([login_log.info_id])))
|
||||
|
||||
@classmethod
|
||||
async def clear_login_log_dao(cls, db: AsyncSession) -> None:
|
||||
"""
|
||||
清除登录日志数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysLogininfor))
|
||||
@@ -0,0 +1,29 @@
|
||||
from sqlalchemy import Row, and_, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from module_admin.entity.do.dept_do import SysDept
|
||||
from module_admin.entity.do.user_do import SysUser
|
||||
|
||||
|
||||
async def login_by_account(db: AsyncSession, user_name: str) -> Row[tuple[SysUser, SysDept]] | None:
|
||||
"""
|
||||
根据用户名查询用户信息
|
||||
|
||||
:param db: orm对象
|
||||
:param user_name: 用户名
|
||||
:return: 用户对象
|
||||
"""
|
||||
user = (
|
||||
await db.execute(
|
||||
select(SysUser, SysDept)
|
||||
.where(SysUser.user_name == user_name, SysUser.del_flag == '0')
|
||||
.join(
|
||||
SysDept,
|
||||
and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == '0', SysDept.del_flag == '0'),
|
||||
isouter=True,
|
||||
)
|
||||
.distinct()
|
||||
)
|
||||
).first()
|
||||
|
||||
return user
|
||||
@@ -0,0 +1,228 @@
|
||||
from collections.abc import Sequence
|
||||
|
||||
from sqlalchemy import and_, delete, func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from module_admin.entity.do.menu_do import SysMenu
|
||||
from module_admin.entity.do.role_do import SysRole, SysRoleMenu
|
||||
from module_admin.entity.do.user_do import SysUser, SysUserRole
|
||||
from module_admin.entity.vo.menu_vo import MenuModel, MenuQueryModel
|
||||
|
||||
|
||||
class MenuDao:
|
||||
"""
|
||||
菜单管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_menu_detail_by_id(cls, db: AsyncSession, menu_id: int) -> SysMenu | None:
|
||||
"""
|
||||
根据菜单id获取菜单详细信息
|
||||
|
||||
:param db: orm对象
|
||||
:param menu_id: 菜单id
|
||||
:return: 菜单信息对象
|
||||
"""
|
||||
menu_info = (await db.execute(select(SysMenu).where(SysMenu.menu_id == menu_id))).scalars().first()
|
||||
|
||||
return menu_info
|
||||
|
||||
@classmethod
|
||||
async def get_menu_detail_by_info(cls, db: AsyncSession, menu: MenuModel) -> SysMenu | None:
|
||||
"""
|
||||
根据菜单参数获取菜单信息
|
||||
|
||||
:param db: orm对象
|
||||
:param menu: 菜单参数对象
|
||||
:return: 菜单信息对象
|
||||
"""
|
||||
menu_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysMenu).where(
|
||||
SysMenu.parent_id == menu.parent_id if menu.parent_id else True,
|
||||
SysMenu.menu_name == menu.menu_name if menu.menu_name else True,
|
||||
SysMenu.menu_type == menu.menu_type if menu.menu_type else True,
|
||||
)
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return menu_info
|
||||
|
||||
@classmethod
|
||||
async def get_menu_list_for_tree(cls, db: AsyncSession, user_id: int, role: list) -> Sequence[SysMenu]:
|
||||
"""
|
||||
根据角色信息获取所有在用菜单列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param user_id: 用户id
|
||||
:param role: 用户角色列表信息
|
||||
:return: 菜单列表信息
|
||||
"""
|
||||
role_id_list = [item.role_id for item in role]
|
||||
if 1 in role_id_list:
|
||||
menu_query_all = (
|
||||
(await db.execute(select(SysMenu).where(SysMenu.status == '0').order_by(SysMenu.order_num).distinct()))
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
else:
|
||||
menu_query_all = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysMenu)
|
||||
.select_from(SysUser)
|
||||
.where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
|
||||
.join(SysUserRole, SysUser.user_id == SysUserRole.user_id, isouter=True)
|
||||
.join(
|
||||
SysRole,
|
||||
and_(
|
||||
SysUserRole.role_id == SysRole.role_id, SysRole.status == '0', SysRole.del_flag == '0'
|
||||
),
|
||||
isouter=True,
|
||||
)
|
||||
.join(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id, isouter=True)
|
||||
.join(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == '0'))
|
||||
.order_by(SysMenu.order_num)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
return menu_query_all
|
||||
|
||||
@classmethod
|
||||
async def get_menu_list(
|
||||
cls, db: AsyncSession, page_object: MenuQueryModel, user_id: int, role: list
|
||||
) -> Sequence[SysMenu]:
|
||||
"""
|
||||
根据查询参数获取菜单列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param page_object: 不分页查询参数对象
|
||||
:param user_id: 用户id
|
||||
:param role: 用户角色列表
|
||||
:return: 菜单列表信息对象
|
||||
"""
|
||||
role_id_list = [item.role_id for item in role]
|
||||
if 1 in role_id_list:
|
||||
menu_query_all = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysMenu)
|
||||
.where(
|
||||
SysMenu.status == page_object.status if page_object.status else True,
|
||||
SysMenu.menu_name.like(f'%{page_object.menu_name}%') if page_object.menu_name else True,
|
||||
)
|
||||
.order_by(SysMenu.order_num)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
else:
|
||||
menu_query_all = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysMenu)
|
||||
.select_from(SysUser)
|
||||
.where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
|
||||
.join(SysUserRole, SysUser.user_id == SysUserRole.user_id, isouter=True)
|
||||
.join(
|
||||
SysRole,
|
||||
and_(
|
||||
SysUserRole.role_id == SysRole.role_id, SysRole.status == '0', SysRole.del_flag == '0'
|
||||
),
|
||||
isouter=True,
|
||||
)
|
||||
.join(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id, isouter=True)
|
||||
.join(
|
||||
SysMenu,
|
||||
and_(
|
||||
SysRoleMenu.menu_id == SysMenu.menu_id,
|
||||
SysMenu.status == page_object.status if page_object.status else True,
|
||||
SysMenu.menu_name.like(f'%{page_object.menu_name}%') if page_object.menu_name else True,
|
||||
),
|
||||
)
|
||||
.order_by(SysMenu.order_num)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
return menu_query_all
|
||||
|
||||
@classmethod
|
||||
async def add_menu_dao(cls, db: AsyncSession, menu: MenuModel) -> SysMenu:
|
||||
"""
|
||||
新增菜单数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param menu: 菜单对象
|
||||
:return:
|
||||
"""
|
||||
db_menu = SysMenu(**menu.model_dump())
|
||||
db.add(db_menu)
|
||||
await db.flush()
|
||||
|
||||
return db_menu
|
||||
|
||||
@classmethod
|
||||
async def edit_menu_dao(cls, db: AsyncSession, menu: dict) -> None:
|
||||
"""
|
||||
编辑菜单数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param menu: 需要更新的菜单字典
|
||||
:return:
|
||||
"""
|
||||
await db.execute(update(SysMenu), [menu])
|
||||
|
||||
@classmethod
|
||||
async def delete_menu_dao(cls, db: AsyncSession, menu: MenuModel) -> None:
|
||||
"""
|
||||
删除菜单数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param menu: 菜单对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysMenu).where(SysMenu.menu_id.in_([menu.menu_id])))
|
||||
|
||||
@classmethod
|
||||
async def has_child_by_menu_id_dao(cls, db: AsyncSession, menu_id: int) -> int | None:
|
||||
"""
|
||||
根据菜单id查询菜单关联子菜单的数量
|
||||
|
||||
:param db: orm对象
|
||||
:param menu_id: 菜单id
|
||||
:return: 菜单关联子菜单的数量
|
||||
"""
|
||||
menu_count = (
|
||||
await db.execute(select(func.count('*')).select_from(SysMenu).where(SysMenu.parent_id == menu_id))
|
||||
).scalar()
|
||||
|
||||
return menu_count
|
||||
|
||||
@classmethod
|
||||
async def check_menu_exist_role_dao(cls, db: AsyncSession, menu_id: int) -> int | None:
|
||||
"""
|
||||
根据菜单id查询菜单关联角色数量
|
||||
|
||||
:param db: orm对象
|
||||
:param menu_id: 菜单id
|
||||
:return: 菜单关联角色数量
|
||||
"""
|
||||
role_count = (
|
||||
await db.execute(select(func.count('*')).select_from(SysRoleMenu).where(SysRoleMenu.menu_id == menu_id))
|
||||
).scalar()
|
||||
|
||||
return role_count
|
||||
@@ -0,0 +1,125 @@
|
||||
from datetime import datetime, time
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import delete, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.vo import PageModel
|
||||
from module_admin.entity.do.notice_do import SysNotice
|
||||
from module_admin.entity.vo.notice_vo import NoticeModel, NoticePageQueryModel
|
||||
from utils.page_util import PageUtil
|
||||
|
||||
|
||||
class NoticeDao:
|
||||
"""
|
||||
通知公告管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_notice_detail_by_id(cls, db: AsyncSession, notice_id: int) -> SysNotice | None:
|
||||
"""
|
||||
根据通知公告id获取通知公告详细信息
|
||||
|
||||
:param db: orm对象
|
||||
:param notice_id: 通知公告id
|
||||
:return: 通知公告信息对象
|
||||
"""
|
||||
notice_info = (await db.execute(select(SysNotice).where(SysNotice.notice_id == notice_id))).scalars().first()
|
||||
|
||||
return notice_info
|
||||
|
||||
@classmethod
|
||||
async def get_notice_detail_by_info(cls, db: AsyncSession, notice: NoticeModel) -> SysNotice | None:
|
||||
"""
|
||||
根据通知公告参数获取通知公告信息
|
||||
|
||||
:param db: orm对象
|
||||
:param notice: 通知公告参数对象
|
||||
:return: 通知公告信息对象
|
||||
"""
|
||||
notice_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysNotice).where(
|
||||
SysNotice.notice_title == notice.notice_title,
|
||||
SysNotice.notice_type == notice.notice_type,
|
||||
SysNotice.notice_content == notice.notice_content,
|
||||
)
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return notice_info
|
||||
|
||||
@classmethod
|
||||
async def get_notice_list(
|
||||
cls, db: AsyncSession, query_object: NoticePageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
根据查询参数获取通知公告列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 通知公告列表信息对象
|
||||
"""
|
||||
query = (
|
||||
select(SysNotice)
|
||||
.where(
|
||||
SysNotice.notice_title.like(f'%{query_object.notice_title}%') if query_object.notice_title else True,
|
||||
SysNotice.create_by.like(f'%{query_object.create_by}%') if query_object.create_by else True,
|
||||
SysNotice.notice_type == query_object.notice_type if query_object.notice_type else True,
|
||||
SysNotice.create_time.between(
|
||||
datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
|
||||
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
|
||||
)
|
||||
if query_object.begin_time and query_object.end_time
|
||||
else True,
|
||||
)
|
||||
.order_by(SysNotice.notice_id)
|
||||
.distinct()
|
||||
)
|
||||
notice_list: PageModel | list[dict[str, Any]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return notice_list
|
||||
|
||||
@classmethod
|
||||
async def add_notice_dao(cls, db: AsyncSession, notice: NoticeModel) -> SysNotice:
|
||||
"""
|
||||
新增通知公告数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param notice: 通知公告对象
|
||||
:return:
|
||||
"""
|
||||
db_notice = SysNotice(**notice.model_dump())
|
||||
db.add(db_notice)
|
||||
await db.flush()
|
||||
|
||||
return db_notice
|
||||
|
||||
@classmethod
|
||||
async def edit_notice_dao(cls, db: AsyncSession, notice: dict) -> None:
|
||||
"""
|
||||
编辑通知公告数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param notice: 需要更新的通知公告字典
|
||||
:return:
|
||||
"""
|
||||
await db.execute(update(SysNotice), [notice])
|
||||
|
||||
@classmethod
|
||||
async def delete_notice_dao(cls, db: AsyncSession, notice: NoticeModel) -> None:
|
||||
"""
|
||||
删除通知公告数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param notice: 通知公告对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysNotice).where(SysNotice.notice_id.in_([notice.notice_id])))
|
||||
@@ -0,0 +1,151 @@
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import delete, func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.vo import PageModel
|
||||
from module_admin.entity.do.post_do import SysPost
|
||||
from module_admin.entity.do.user_do import SysUserPost
|
||||
from module_admin.entity.vo.post_vo import PostModel, PostPageQueryModel
|
||||
from utils.page_util import PageUtil
|
||||
|
||||
|
||||
class PostDao:
|
||||
"""
|
||||
岗位管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_post_by_id(cls, db: AsyncSession, post_id: int) -> SysPost | None:
|
||||
"""
|
||||
根据岗位id获取在用岗位详细信息
|
||||
|
||||
:param db: orm对象
|
||||
:param post_id: 岗位id
|
||||
:return: 在用岗位信息对象
|
||||
"""
|
||||
post_info = (
|
||||
(await db.execute(select(SysPost).where(SysPost.post_id == post_id, SysPost.status == '0')))
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return post_info
|
||||
|
||||
@classmethod
|
||||
async def get_post_detail_by_id(cls, db: AsyncSession, post_id: int) -> SysPost | None:
|
||||
"""
|
||||
根据岗位id获取岗位详细信息
|
||||
|
||||
:param db: orm对象
|
||||
:param post_id: 岗位id
|
||||
:return: 岗位信息对象
|
||||
"""
|
||||
post_info = (await db.execute(select(SysPost).where(SysPost.post_id == post_id))).scalars().first()
|
||||
|
||||
return post_info
|
||||
|
||||
@classmethod
|
||||
async def get_post_detail_by_info(cls, db: AsyncSession, post: PostModel) -> SysPost | None:
|
||||
"""
|
||||
根据岗位参数获取岗位信息
|
||||
|
||||
:param db: orm对象
|
||||
:param post: 岗位参数对象
|
||||
:return: 岗位信息对象
|
||||
"""
|
||||
post_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysPost).where(
|
||||
SysPost.post_name == post.post_name if post.post_name else True,
|
||||
SysPost.post_code == post.post_code if post.post_code else True,
|
||||
SysPost.post_sort == post.post_sort if post.post_sort else True,
|
||||
)
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return post_info
|
||||
|
||||
@classmethod
|
||||
async def get_post_list(
|
||||
cls, db: AsyncSession, query_object: PostPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
根据查询参数获取岗位列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 岗位列表信息对象
|
||||
"""
|
||||
query = (
|
||||
select(SysPost)
|
||||
.where(
|
||||
SysPost.post_code.like(f'%{query_object.post_code}%') if query_object.post_code else True,
|
||||
SysPost.post_name.like(f'%{query_object.post_name}%') if query_object.post_name else True,
|
||||
SysPost.status == query_object.status if query_object.status else True,
|
||||
)
|
||||
.order_by(SysPost.post_sort)
|
||||
.distinct()
|
||||
)
|
||||
post_list: PageModel | list[dict[str, Any]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return post_list
|
||||
|
||||
@classmethod
|
||||
async def add_post_dao(cls, db: AsyncSession, post: PostModel) -> SysPost:
|
||||
"""
|
||||
新增岗位数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param post: 岗位对象
|
||||
:return:
|
||||
"""
|
||||
db_post = SysPost(**post.model_dump())
|
||||
db.add(db_post)
|
||||
await db.flush()
|
||||
|
||||
return db_post
|
||||
|
||||
@classmethod
|
||||
async def edit_post_dao(cls, db: AsyncSession, post: dict) -> None:
|
||||
"""
|
||||
编辑岗位数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param post: 需要更新的岗位字典
|
||||
:return:
|
||||
"""
|
||||
await db.execute(update(SysPost), [post])
|
||||
|
||||
@classmethod
|
||||
async def delete_post_dao(cls, db: AsyncSession, post: PostModel) -> None:
|
||||
"""
|
||||
删除岗位数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param post: 岗位对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysPost).where(SysPost.post_id.in_([post.post_id])))
|
||||
|
||||
@classmethod
|
||||
async def count_user_post_dao(cls, db: AsyncSession, post_id: int) -> int | None:
|
||||
"""
|
||||
根据岗位id查询岗位关联的用户数量
|
||||
|
||||
:param db: orm对象
|
||||
:param post_id: 岗位id
|
||||
:return: 岗位关联的用户数量
|
||||
"""
|
||||
user_post_count = (
|
||||
await db.execute(select(func.count('*')).select_from(SysUserPost).where(SysUserPost.post_id == post_id))
|
||||
).scalar()
|
||||
|
||||
return user_post_count
|
||||
@@ -0,0 +1,345 @@
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime, time
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import ColumnElement, and_, delete, desc, func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.vo import PageModel
|
||||
from module_admin.entity.do.dept_do import SysDept
|
||||
from module_admin.entity.do.menu_do import SysMenu
|
||||
from module_admin.entity.do.role_do import SysRole, SysRoleDept, SysRoleMenu
|
||||
from module_admin.entity.do.user_do import SysUser, SysUserRole
|
||||
from module_admin.entity.vo.role_vo import RoleDeptModel, RoleMenuModel, RoleModel, RolePageQueryModel
|
||||
from utils.page_util import PageUtil
|
||||
|
||||
|
||||
class RoleDao:
|
||||
"""
|
||||
角色管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_role_by_name(cls, db: AsyncSession, role_name: str) -> SysRole | None:
|
||||
"""
|
||||
根据角色名获取在用角色信息
|
||||
|
||||
:param db: orm对象
|
||||
:param role_name: 角色名
|
||||
:return: 当前角色名的角色信息对象
|
||||
"""
|
||||
query_role_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysRole)
|
||||
.where(SysRole.status == '0', SysRole.del_flag == '0', SysRole.role_name == role_name)
|
||||
.order_by(desc(SysRole.create_time))
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return query_role_info
|
||||
|
||||
@classmethod
|
||||
async def get_role_by_info(cls, db: AsyncSession, role: RoleModel) -> SysRole | None:
|
||||
"""
|
||||
根据角色参数获取角色信息
|
||||
|
||||
:param db: orm对象
|
||||
:param role: 角色参数
|
||||
:return: 当前角色参数的角色信息对象
|
||||
"""
|
||||
query_role_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysRole)
|
||||
.where(
|
||||
SysRole.del_flag == '0',
|
||||
SysRole.role_name == role.role_name if role.role_name else True,
|
||||
SysRole.role_key == role.role_key if role.role_key else True,
|
||||
)
|
||||
.order_by(desc(SysRole.create_time))
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return query_role_info
|
||||
|
||||
@classmethod
|
||||
async def get_role_by_id(cls, db: AsyncSession, role_id: int) -> SysRole | None:
|
||||
"""
|
||||
根据角色id获取在用角色信息
|
||||
|
||||
:param db: orm对象
|
||||
:param role_id: 角色id
|
||||
:return: 当前角色id的角色信息对象
|
||||
"""
|
||||
role_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysRole).where(SysRole.role_id == role_id, SysRole.status == '0', SysRole.del_flag == '0')
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return role_info
|
||||
|
||||
@classmethod
|
||||
async def get_role_detail_by_id(cls, db: AsyncSession, role_id: int) -> SysRole | None:
|
||||
"""
|
||||
根据role_id获取角色详细信息
|
||||
|
||||
:param db: orm对象
|
||||
:param role_id: 角色id
|
||||
:return: 当前role_id的角色信息对象
|
||||
"""
|
||||
query_role_info = (
|
||||
(await db.execute(select(SysRole).where(SysRole.del_flag == '0', SysRole.role_id == role_id).distinct()))
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return query_role_info
|
||||
|
||||
@classmethod
|
||||
async def get_role_select_option_dao(cls, db: AsyncSession) -> Sequence[SysRole]:
|
||||
"""
|
||||
获取编辑页面对应的在用角色列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:return: 角色列表信息
|
||||
"""
|
||||
role_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysRole).where(SysRole.role_id != 1, SysRole.status == '0', SysRole.del_flag == '0')
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
return role_info
|
||||
|
||||
@classmethod
|
||||
async def get_role_list(
|
||||
cls, db: AsyncSession, query_object: RolePageQueryModel, data_scope_sql: ColumnElement, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
根据查询参数获取角色列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:param is_page: 是否开启分页
|
||||
:return: 角色列表信息对象
|
||||
"""
|
||||
query = (
|
||||
select(SysRole)
|
||||
.join(SysUserRole, SysUserRole.role_id == SysRole.role_id, isouter=True)
|
||||
.join(SysUser, SysUser.user_id == SysUserRole.user_id, isouter=True)
|
||||
.join(SysDept, SysDept.dept_id == SysUser.dept_id, isouter=True)
|
||||
.where(
|
||||
SysRole.del_flag == '0',
|
||||
SysRole.role_id == query_object.role_id if query_object.role_id is not None else True,
|
||||
SysRole.role_name.like(f'%{query_object.role_name}%') if query_object.role_name else True,
|
||||
SysRole.role_key.like(f'%{query_object.role_key}%') if query_object.role_key else True,
|
||||
SysRole.status == query_object.status if query_object.status else True,
|
||||
SysRole.create_time.between(
|
||||
datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
|
||||
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
|
||||
)
|
||||
if query_object.begin_time and query_object.end_time
|
||||
else True,
|
||||
data_scope_sql,
|
||||
)
|
||||
.order_by(SysRole.role_sort)
|
||||
.distinct()
|
||||
)
|
||||
role_list: PageModel | list[dict[str, Any]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return role_list
|
||||
|
||||
@classmethod
|
||||
async def add_role_dao(cls, db: AsyncSession, role: RoleModel) -> SysRole:
|
||||
"""
|
||||
新增角色数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param role: 角色对象
|
||||
:return:
|
||||
"""
|
||||
db_role = SysRole(**role.model_dump(exclude={'admin'}))
|
||||
db.add(db_role)
|
||||
await db.flush()
|
||||
|
||||
return db_role
|
||||
|
||||
@classmethod
|
||||
async def edit_role_dao(cls, db: AsyncSession, role: dict) -> None:
|
||||
"""
|
||||
编辑角色数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param role: 需要更新的角色字典
|
||||
:return:
|
||||
"""
|
||||
await db.execute(update(SysRole), [role])
|
||||
|
||||
@classmethod
|
||||
async def delete_role_dao(cls, db: AsyncSession, role: RoleModel) -> None:
|
||||
"""
|
||||
删除角色数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param role: 角色对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(
|
||||
update(SysRole)
|
||||
.where(SysRole.role_id == role.role_id)
|
||||
.values(del_flag='2', update_by=role.update_by, update_time=role.update_time)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_role_menu_dao(cls, db: AsyncSession, role: RoleModel) -> Sequence[SysMenu]:
|
||||
"""
|
||||
根据角色id获取角色菜单关联列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param role: 角色对象
|
||||
:return: 角色菜单关联列表信息
|
||||
"""
|
||||
role_menu_query_all = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysMenu)
|
||||
.join(SysRoleMenu, SysRoleMenu.menu_id == SysMenu.menu_id)
|
||||
.where(
|
||||
SysRoleMenu.role_id == role.role_id,
|
||||
~SysMenu.menu_id.in_(
|
||||
select(SysMenu.parent_id)
|
||||
.select_from(SysMenu)
|
||||
.join(
|
||||
SysRoleMenu,
|
||||
and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysRoleMenu.role_id == role.role_id),
|
||||
)
|
||||
)
|
||||
if role.menu_check_strictly
|
||||
else True,
|
||||
)
|
||||
.order_by(SysMenu.parent_id, SysMenu.order_num)
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
return role_menu_query_all
|
||||
|
||||
@classmethod
|
||||
async def add_role_menu_dao(cls, db: AsyncSession, role_menu: RoleMenuModel) -> None:
|
||||
"""
|
||||
新增角色菜单关联信息数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param role_menu: 用户角色菜单关联对象
|
||||
:return:
|
||||
"""
|
||||
db_role_menu = SysRoleMenu(**role_menu.model_dump())
|
||||
db.add(db_role_menu)
|
||||
|
||||
@classmethod
|
||||
async def delete_role_menu_dao(cls, db: AsyncSession, role_menu: RoleMenuModel) -> None:
|
||||
"""
|
||||
删除角色菜单关联信息数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param role_menu: 角色菜单关联对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysRoleMenu).where(SysRoleMenu.role_id.in_([role_menu.role_id])))
|
||||
|
||||
@classmethod
|
||||
async def get_role_dept_dao(cls, db: AsyncSession, role: RoleModel) -> Sequence[SysDept]:
|
||||
"""
|
||||
根据角色id获取角色部门关联列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param role: 角色对象
|
||||
:return: 角色部门关联列表信息
|
||||
"""
|
||||
role_dept_query_all = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysDept)
|
||||
.join(SysRoleDept, SysRoleDept.dept_id == SysDept.dept_id)
|
||||
.where(
|
||||
SysRoleDept.role_id == role.role_id,
|
||||
~SysDept.dept_id.in_(
|
||||
select(SysDept.parent_id)
|
||||
.select_from(SysDept)
|
||||
.join(
|
||||
SysRoleDept,
|
||||
and_(SysRoleDept.dept_id == SysDept.dept_id, SysRoleDept.role_id == role.role_id),
|
||||
)
|
||||
)
|
||||
if role.dept_check_strictly
|
||||
else True,
|
||||
)
|
||||
.order_by(SysDept.parent_id, SysDept.order_num)
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
return role_dept_query_all
|
||||
|
||||
@classmethod
|
||||
async def add_role_dept_dao(cls, db: AsyncSession, role_dept: RoleDeptModel) -> None:
|
||||
"""
|
||||
新增角色部门关联信息数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param role_dept: 用户角色部门关联对象
|
||||
:return:
|
||||
"""
|
||||
db_role_dept = SysRoleDept(**role_dept.dict())
|
||||
db.add(db_role_dept)
|
||||
|
||||
@classmethod
|
||||
async def delete_role_dept_dao(cls, db: AsyncSession, role_dept: RoleDeptModel) -> None:
|
||||
"""
|
||||
删除角色部门关联信息数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param role_dept: 角色部门关联对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysRoleDept).where(SysRoleDept.role_id.in_([role_dept.role_id])))
|
||||
|
||||
@classmethod
|
||||
async def count_user_role_dao(cls, db: AsyncSession, role_id: int) -> int | None:
|
||||
"""
|
||||
根据角色id查询角色关联用户数量
|
||||
|
||||
:param db: orm对象
|
||||
:param role_id: 角色id
|
||||
:return: 角色关联用户数量
|
||||
"""
|
||||
user_count = (
|
||||
await db.execute(select(func.count('*')).select_from(SysUserRole).where(SysUserRole.role_id == role_id))
|
||||
).scalar()
|
||||
|
||||
return user_count
|
||||
@@ -0,0 +1,587 @@
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime, time
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import ColumnElement, and_, delete, desc, func, or_, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.vo import PageModel
|
||||
from module_admin.entity.do.dept_do import SysDept
|
||||
from module_admin.entity.do.menu_do import SysMenu
|
||||
from module_admin.entity.do.post_do import SysPost
|
||||
from module_admin.entity.do.role_do import SysRole, SysRoleMenu
|
||||
from module_admin.entity.do.user_do import SysUser, SysUserPost, SysUserRole
|
||||
from module_admin.entity.vo.user_vo import (
|
||||
UserModel,
|
||||
UserPageQueryModel,
|
||||
UserPostModel,
|
||||
UserRoleModel,
|
||||
UserRolePageQueryModel,
|
||||
UserRoleQueryModel,
|
||||
)
|
||||
from utils.page_util import PageUtil
|
||||
|
||||
|
||||
class UserDao:
|
||||
"""
|
||||
用户管理模块数据库操作层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_user_by_name(cls, db: AsyncSession, user_name: str) -> SysUser | None:
|
||||
"""
|
||||
根据用户名获取用户信息
|
||||
|
||||
:param db: orm对象
|
||||
:param user_name: 用户名
|
||||
:return: 当前用户名的用户信息对象
|
||||
"""
|
||||
query_user_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysUser)
|
||||
.where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_name == user_name)
|
||||
.order_by(desc(SysUser.create_time))
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return query_user_info
|
||||
|
||||
@classmethod
|
||||
async def get_user_by_info(cls, db: AsyncSession, user: UserModel) -> SysUser | None:
|
||||
"""
|
||||
根据用户参数获取用户信息
|
||||
|
||||
:param db: orm对象
|
||||
:param user: 用户参数
|
||||
:return: 当前用户参数的用户信息对象
|
||||
"""
|
||||
query_user_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysUser)
|
||||
.where(
|
||||
SysUser.del_flag == '0',
|
||||
SysUser.user_name == user.user_name if user.user_name else True,
|
||||
SysUser.phonenumber == user.phonenumber if user.phonenumber else True,
|
||||
SysUser.email == user.email if user.email else True,
|
||||
)
|
||||
.order_by(desc(SysUser.create_time))
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return query_user_info
|
||||
|
||||
@classmethod
|
||||
async def get_user_by_id(cls, db: AsyncSession, user_id: int) -> dict[str, Any]:
|
||||
"""
|
||||
根据user_id获取用户信息
|
||||
|
||||
:param db: orm对象
|
||||
:param user_id: 用户id
|
||||
:return: 当前user_id的用户信息对象
|
||||
"""
|
||||
query_user_basic_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysUser)
|
||||
.where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
query_user_dept_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysDept)
|
||||
.select_from(SysUser)
|
||||
.where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
|
||||
.join(
|
||||
SysDept,
|
||||
and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == '0', SysDept.del_flag == '0'),
|
||||
)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
query_user_role_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysRole)
|
||||
.select_from(SysUser)
|
||||
.where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
|
||||
.join(SysUserRole, SysUser.user_id == SysUserRole.user_id, isouter=True)
|
||||
.join(
|
||||
SysRole,
|
||||
and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == '0', SysRole.del_flag == '0'),
|
||||
)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
query_user_post_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysPost)
|
||||
.select_from(SysUser)
|
||||
.where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
|
||||
.join(SysUserPost, SysUser.user_id == SysUserPost.user_id, isouter=True)
|
||||
.join(SysPost, and_(SysUserPost.post_id == SysPost.post_id, SysPost.status == '0'))
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
role_id_list = [item.role_id for item in query_user_role_info]
|
||||
if 1 in role_id_list:
|
||||
query_user_menu_info = (
|
||||
(await db.execute(select(SysMenu).where(SysMenu.status == '0').distinct())).scalars().all()
|
||||
)
|
||||
else:
|
||||
query_user_menu_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysMenu)
|
||||
.select_from(SysUser)
|
||||
.where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
|
||||
.join(SysUserRole, SysUser.user_id == SysUserRole.user_id, isouter=True)
|
||||
.join(
|
||||
SysRole,
|
||||
and_(
|
||||
SysUserRole.role_id == SysRole.role_id, SysRole.status == '0', SysRole.del_flag == '0'
|
||||
),
|
||||
isouter=True,
|
||||
)
|
||||
.join(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id, isouter=True)
|
||||
.join(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == '0'))
|
||||
.order_by(SysMenu.order_num)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
results = {
|
||||
'user_basic_info': query_user_basic_info,
|
||||
'user_dept_info': query_user_dept_info,
|
||||
'user_role_info': query_user_role_info,
|
||||
'user_post_info': query_user_post_info,
|
||||
'user_menu_info': query_user_menu_info,
|
||||
}
|
||||
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
async def get_user_detail_by_id(cls, db: AsyncSession, user_id: int) -> dict[str, Any]:
|
||||
"""
|
||||
根据user_id获取用户详细信息
|
||||
|
||||
:param db: orm对象
|
||||
:param user_id: 用户id
|
||||
:return: 当前user_id的用户信息对象
|
||||
"""
|
||||
query_user_basic_info = (
|
||||
(await db.execute(select(SysUser).where(SysUser.del_flag == '0', SysUser.user_id == user_id).distinct()))
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
query_user_dept_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysDept)
|
||||
.select_from(SysUser)
|
||||
.where(SysUser.del_flag == '0', SysUser.user_id == user_id)
|
||||
.join(
|
||||
SysDept,
|
||||
and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == '0', SysDept.del_flag == '0'),
|
||||
)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
query_user_role_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysRole)
|
||||
.select_from(SysUser)
|
||||
.where(SysUser.del_flag == '0', SysUser.user_id == user_id)
|
||||
.join(SysUserRole, SysUser.user_id == SysUserRole.user_id, isouter=True)
|
||||
.join(
|
||||
SysRole,
|
||||
and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == '0', SysRole.del_flag == '0'),
|
||||
)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
query_user_post_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysPost)
|
||||
.select_from(SysUser)
|
||||
.where(SysUser.del_flag == '0', SysUser.user_id == user_id)
|
||||
.join(SysUserPost, SysUser.user_id == SysUserPost.user_id, isouter=True)
|
||||
.join(SysPost, and_(SysUserPost.post_id == SysPost.post_id, SysPost.status == '0'))
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
query_user_menu_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysMenu)
|
||||
.select_from(SysUser)
|
||||
.where(SysUser.del_flag == '0', SysUser.user_id == user_id)
|
||||
.join(SysUserRole, SysUser.user_id == SysUserRole.user_id, isouter=True)
|
||||
.join(
|
||||
SysRole,
|
||||
and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == '0', SysRole.del_flag == '0'),
|
||||
isouter=True,
|
||||
)
|
||||
.join(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id, isouter=True)
|
||||
.join(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == '0'))
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
results = {
|
||||
'user_basic_info': query_user_basic_info,
|
||||
'user_dept_info': query_user_dept_info,
|
||||
'user_role_info': query_user_role_info,
|
||||
'user_post_info': query_user_post_info,
|
||||
'user_menu_info': query_user_menu_info,
|
||||
}
|
||||
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
async def get_user_list(
|
||||
cls, db: AsyncSession, query_object: UserPageQueryModel, data_scope_sql: ColumnElement, is_page: bool = False
|
||||
) -> PageModel | list[list[dict[str, Any]]]:
|
||||
"""
|
||||
根据查询参数获取用户列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:param is_page: 是否开启分页
|
||||
:return: 用户列表信息对象
|
||||
"""
|
||||
query = (
|
||||
select(SysUser, SysDept)
|
||||
.where(
|
||||
SysUser.del_flag == '0',
|
||||
or_(
|
||||
SysUser.dept_id == query_object.dept_id,
|
||||
SysUser.dept_id.in_(
|
||||
select(SysDept.dept_id).where(func.find_in_set(query_object.dept_id, SysDept.ancestors))
|
||||
),
|
||||
)
|
||||
if query_object.dept_id
|
||||
else True,
|
||||
SysUser.user_id == query_object.user_id if query_object.user_id is not None else True,
|
||||
SysUser.user_name.like(f'%{query_object.user_name}%') if query_object.user_name else True,
|
||||
SysUser.nick_name.like(f'%{query_object.nick_name}%') if query_object.nick_name else True,
|
||||
SysUser.email.like(f'%{query_object.email}%') if query_object.email else True,
|
||||
SysUser.phonenumber.like(f'%{query_object.phonenumber}%') if query_object.phonenumber else True,
|
||||
SysUser.status == query_object.status if query_object.status else True,
|
||||
SysUser.sex == query_object.sex if query_object.sex else True,
|
||||
SysUser.create_time.between(
|
||||
datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
|
||||
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
|
||||
)
|
||||
if query_object.begin_time and query_object.end_time
|
||||
else True,
|
||||
data_scope_sql,
|
||||
)
|
||||
.join(
|
||||
SysDept,
|
||||
and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == '0', SysDept.del_flag == '0'),
|
||||
isouter=True,
|
||||
)
|
||||
.order_by(SysUser.user_id)
|
||||
.distinct()
|
||||
)
|
||||
user_list: PageModel | list[list[dict[str, Any]]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return user_list
|
||||
|
||||
@classmethod
|
||||
async def add_user_dao(cls, db: AsyncSession, user: UserModel) -> SysUser:
|
||||
"""
|
||||
新增用户数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param user: 用户对象
|
||||
:return: 新增校验结果
|
||||
"""
|
||||
db_user = SysUser(**user.model_dump(exclude={'admin'}))
|
||||
db.add(db_user)
|
||||
await db.flush()
|
||||
|
||||
return db_user
|
||||
|
||||
@classmethod
|
||||
async def edit_user_dao(cls, db: AsyncSession, user: dict) -> None:
|
||||
"""
|
||||
编辑用户数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param user: 需要更新的用户字典
|
||||
:return: 编辑校验结果
|
||||
"""
|
||||
await db.execute(update(SysUser), [user])
|
||||
|
||||
@classmethod
|
||||
async def delete_user_dao(cls, db: AsyncSession, user: UserModel) -> None:
|
||||
"""
|
||||
删除用户数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param user: 用户对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(
|
||||
update(SysUser)
|
||||
.where(SysUser.user_id == user.user_id)
|
||||
.values(del_flag='2', update_by=user.update_by, update_time=user.update_time)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_user_role_allocated_list_by_user_id(
|
||||
cls, db: AsyncSession, query_object: UserRoleQueryModel
|
||||
) -> Sequence[SysRole]:
|
||||
"""
|
||||
根据用户id获取用户已分配的角色列表信息数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 用户角色查询对象
|
||||
:return: 用户已分配的角色列表信息
|
||||
"""
|
||||
allocated_role_list = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysRole)
|
||||
.where(
|
||||
SysRole.del_flag == '0',
|
||||
SysRole.role_id != 1,
|
||||
SysRole.role_name == query_object.role_name if query_object.role_name else True,
|
||||
SysRole.role_key == query_object.role_key if query_object.role_key else True,
|
||||
SysRole.role_id.in_(
|
||||
select(SysUserRole.role_id).where(SysUserRole.user_id == query_object.user_id)
|
||||
),
|
||||
)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
return allocated_role_list
|
||||
|
||||
@classmethod
|
||||
async def get_user_role_allocated_list_by_role_id(
|
||||
cls,
|
||||
db: AsyncSession,
|
||||
query_object: UserRolePageQueryModel,
|
||||
data_scope_sql: ColumnElement,
|
||||
is_page: bool = False,
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
根据角色id获取已分配的用户列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 用户角色查询对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:param is_page: 是否开启分页
|
||||
:return: 角色已分配的用户列表信息
|
||||
"""
|
||||
query = (
|
||||
select(SysUser)
|
||||
.join(SysDept, SysDept.dept_id == SysUser.dept_id, isouter=True)
|
||||
.join(SysUserRole, SysUserRole.user_id == SysUser.user_id, isouter=True)
|
||||
.join(SysRole, SysRole.role_id == SysUserRole.role_id, isouter=True)
|
||||
.where(
|
||||
SysUser.del_flag == '0',
|
||||
SysUser.user_name == query_object.user_name if query_object.user_name else True,
|
||||
SysUser.phonenumber == query_object.phonenumber if query_object.phonenumber else True,
|
||||
SysRole.role_id == query_object.role_id,
|
||||
data_scope_sql,
|
||||
)
|
||||
.distinct()
|
||||
)
|
||||
allocated_user_list: PageModel | list[dict[str, Any]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return allocated_user_list
|
||||
|
||||
@classmethod
|
||||
async def get_user_role_unallocated_list_by_role_id(
|
||||
cls,
|
||||
db: AsyncSession,
|
||||
query_object: UserRolePageQueryModel,
|
||||
data_scope_sql: ColumnElement,
|
||||
is_page: bool = False,
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
根据角色id获取未分配的用户列表信息
|
||||
|
||||
:param db: orm对象
|
||||
:param query_object: 用户角色查询对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:param is_page: 是否开启分页
|
||||
:return: 角色未分配的用户列表信息
|
||||
"""
|
||||
query = (
|
||||
select(SysUser)
|
||||
.join(SysDept, SysDept.dept_id == SysUser.dept_id, isouter=True)
|
||||
.join(SysUserRole, SysUserRole.user_id == SysUser.user_id, isouter=True)
|
||||
.join(SysRole, SysRole.role_id == SysUserRole.role_id, isouter=True)
|
||||
.where(
|
||||
SysUser.del_flag == '0',
|
||||
SysUser.user_name == query_object.user_name if query_object.user_name else True,
|
||||
SysUser.phonenumber == query_object.phonenumber if query_object.phonenumber else True,
|
||||
or_(SysRole.role_id != query_object.role_id, SysRole.role_id.is_(None)),
|
||||
~SysUser.user_id.in_(
|
||||
select(SysUser.user_id)
|
||||
.select_from(SysUser)
|
||||
.join(
|
||||
SysUserRole,
|
||||
and_(SysUserRole.user_id == SysUser.user_id, SysUserRole.role_id == query_object.role_id),
|
||||
)
|
||||
),
|
||||
data_scope_sql,
|
||||
)
|
||||
.distinct()
|
||||
)
|
||||
unallocated_user_list: PageModel | list[dict[str, Any]] = await PageUtil.paginate(
|
||||
db, query, query_object.page_num, query_object.page_size, is_page
|
||||
)
|
||||
|
||||
return unallocated_user_list
|
||||
|
||||
@classmethod
|
||||
async def add_user_role_dao(cls, db: AsyncSession, user_role: UserRoleModel) -> None:
|
||||
"""
|
||||
新增用户角色关联信息数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param user_role: 用户角色关联对象
|
||||
:return:
|
||||
"""
|
||||
db_user_role = SysUserRole(**user_role.model_dump())
|
||||
db.add(db_user_role)
|
||||
|
||||
@classmethod
|
||||
async def delete_user_role_dao(cls, db: AsyncSession, user_role: UserRoleModel) -> None:
|
||||
"""
|
||||
删除用户角色关联信息数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param user_role: 用户角色关联对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysUserRole).where(SysUserRole.user_id.in_([user_role.user_id])))
|
||||
|
||||
@classmethod
|
||||
async def delete_user_role_by_user_and_role_dao(cls, db: AsyncSession, user_role: UserRoleModel) -> None:
|
||||
"""
|
||||
根据用户id及角色id删除用户角色关联信息数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param user_role: 用户角色关联对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(
|
||||
delete(SysUserRole).where(
|
||||
SysUserRole.user_id == user_role.user_id if user_role.user_id else True,
|
||||
SysUserRole.role_id == user_role.role_id if user_role.role_id else True,
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_user_role_detail(cls, db: AsyncSession, user_role: UserRoleModel) -> SysUserRole | None:
|
||||
"""
|
||||
根据用户角色关联获取用户角色关联详细信息
|
||||
|
||||
:param db: orm对象
|
||||
:param user_role: 用户角色关联对象
|
||||
:return: 用户角色关联信息
|
||||
"""
|
||||
user_role_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysUserRole)
|
||||
.where(SysUserRole.user_id == user_role.user_id, SysUserRole.role_id == user_role.role_id)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
return user_role_info
|
||||
|
||||
@classmethod
|
||||
async def add_user_post_dao(cls, db: AsyncSession, user_post: UserPostModel) -> None:
|
||||
"""
|
||||
新增用户岗位关联信息数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param user_post: 用户岗位关联对象
|
||||
:return:
|
||||
"""
|
||||
db_user_post = SysUserPost(**user_post.model_dump())
|
||||
db.add(db_user_post)
|
||||
|
||||
@classmethod
|
||||
async def delete_user_post_dao(cls, db: AsyncSession, user_post: UserPostModel) -> None:
|
||||
"""
|
||||
删除用户岗位关联信息数据库操作
|
||||
|
||||
:param db: orm对象
|
||||
:param user_post: 用户岗位关联对象
|
||||
:return:
|
||||
"""
|
||||
await db.execute(delete(SysUserPost).where(SysUserPost.user_id.in_([user_post.user_id])))
|
||||
|
||||
@classmethod
|
||||
async def get_user_dept_info(cls, db: AsyncSession, dept_id: int) -> SysDept | None:
|
||||
dept_basic_info = (
|
||||
(
|
||||
await db.execute(
|
||||
select(SysDept).where(SysDept.dept_id == dept_id, SysDept.status == '0', SysDept.del_flag == '0')
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
return dept_basic_info
|
||||
@@ -0,0 +1,32 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CHAR, Column, DateTime, Integer, String
|
||||
|
||||
from config.database import Base
|
||||
from config.env import DataBaseConfig
|
||||
from utils.common_util import SqlalchemyUtil
|
||||
|
||||
|
||||
class SysConfig(Base):
|
||||
"""
|
||||
参数配置表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_config'
|
||||
__table_args__ = {'comment': '参数配置表'}
|
||||
|
||||
config_id = Column(Integer, primary_key=True, nullable=False, autoincrement=True, comment='参数主键')
|
||||
config_name = Column(String(100), nullable=True, server_default="''", comment='参数名称')
|
||||
config_key = Column(String(100), nullable=True, server_default="''", comment='参数键名')
|
||||
config_value = Column(String(500), nullable=True, server_default="''", comment='参数键值')
|
||||
config_type = Column(CHAR(1), nullable=True, server_default='N', comment='系统内置(Y是 N否)')
|
||||
create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
|
||||
create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
|
||||
update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
|
||||
update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
|
||||
remark = Column(
|
||||
String(500),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='备注',
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CHAR, BigInteger, Column, DateTime, Integer, String
|
||||
|
||||
from config.database import Base
|
||||
from config.env import DataBaseConfig
|
||||
from utils.common_util import SqlalchemyUtil
|
||||
|
||||
|
||||
class SysDept(Base):
|
||||
"""
|
||||
部门表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_dept'
|
||||
__table_args__ = {'comment': '部门表'}
|
||||
|
||||
dept_id = Column(BigInteger, primary_key=True, autoincrement=True, comment='部门id')
|
||||
parent_id = Column(BigInteger, server_default='0', comment='父部门id')
|
||||
ancestors = Column(String(50), nullable=True, server_default="''", comment='祖级列表')
|
||||
dept_name = Column(String(30), nullable=True, server_default="''", comment='部门名称')
|
||||
order_num = Column(Integer, server_default='0', comment='显示顺序')
|
||||
leader = Column(
|
||||
String(20),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='负责人',
|
||||
)
|
||||
phone = Column(
|
||||
String(11),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='联系电话',
|
||||
)
|
||||
email = Column(
|
||||
String(50),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='邮箱',
|
||||
)
|
||||
status = Column(CHAR(1), nullable=True, server_default='0', comment='部门状态(0正常 1停用)')
|
||||
del_flag = Column(CHAR(1), nullable=True, server_default='0', comment='删除标志(0代表存在 2代表删除)')
|
||||
create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
|
||||
create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
|
||||
update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
|
||||
update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
|
||||
@@ -0,0 +1,70 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CHAR, BigInteger, Column, DateTime, Integer, String
|
||||
|
||||
from config.database import Base
|
||||
from config.env import DataBaseConfig
|
||||
from utils.common_util import SqlalchemyUtil
|
||||
|
||||
|
||||
class SysDictType(Base):
|
||||
"""
|
||||
字典类型表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_dict_type'
|
||||
__table_args__ = {'comment': '字典类型表'}
|
||||
|
||||
dict_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='字典主键')
|
||||
dict_name = Column(String(100), nullable=True, server_default="''", comment='字典名称')
|
||||
dict_type = Column(String(100), unique=True, nullable=True, server_default="''", comment='字典类型')
|
||||
status = Column(CHAR(1), nullable=True, server_default='0', comment='状态(0正常 1停用)')
|
||||
create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
|
||||
create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
|
||||
update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
|
||||
update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
|
||||
remark = Column(
|
||||
String(500),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='备注',
|
||||
)
|
||||
|
||||
|
||||
class SysDictData(Base):
|
||||
"""
|
||||
字典数据表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_dict_data'
|
||||
__table_args__ = {'comment': '字典数据表'}
|
||||
|
||||
dict_code = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='字典编码')
|
||||
dict_sort = Column(Integer, nullable=True, server_default='0', comment='字典排序')
|
||||
dict_label = Column(String(100), nullable=True, server_default="''", comment='字典标签')
|
||||
dict_value = Column(String(100), nullable=True, server_default="''", comment='字典键值')
|
||||
dict_type = Column(String(100), nullable=True, server_default="''", comment='字典类型')
|
||||
css_class = Column(
|
||||
String(100),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='样式属性(其他样式扩展)',
|
||||
)
|
||||
list_class = Column(
|
||||
String(100),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='表格回显样式',
|
||||
)
|
||||
is_default = Column(CHAR(1), nullable=True, server_default='N', comment='是否默认(Y是 N否)')
|
||||
status = Column(CHAR(1), nullable=True, server_default='0', comment='状态(0正常 1停用)')
|
||||
create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
|
||||
create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
|
||||
update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
|
||||
update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
|
||||
remark = Column(
|
||||
String(500),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='备注',
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CHAR, BigInteger, Column, DateTime, String
|
||||
|
||||
from config.database import Base
|
||||
|
||||
|
||||
class SysJob(Base):
|
||||
"""
|
||||
定时任务调度表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_job'
|
||||
__table_args__ = {'comment': '定时任务调度表'}
|
||||
|
||||
job_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='任务ID')
|
||||
job_name = Column(String(64), primary_key=True, nullable=False, server_default="''", comment='任务名称')
|
||||
job_group = Column(String(64), primary_key=True, nullable=False, server_default='default', comment='任务组名')
|
||||
job_executor = Column(String(64), nullable=True, server_default='default', comment='任务执行器')
|
||||
invoke_target = Column(String(500), nullable=False, comment='调用目标字符串')
|
||||
job_args = Column(String(255), nullable=True, server_default="''", comment='位置参数')
|
||||
job_kwargs = Column(String(255), nullable=True, server_default="''", comment='关键字参数')
|
||||
cron_expression = Column(String(255), nullable=True, server_default="''", comment='cron执行表达式')
|
||||
misfire_policy = Column(
|
||||
String(20),
|
||||
nullable=True,
|
||||
server_default='3',
|
||||
comment='计划执行错误策略(1立即执行 2执行一次 3放弃执行)',
|
||||
)
|
||||
concurrent = Column(CHAR(1), nullable=True, server_default='1', comment='是否并发执行(0允许 1禁止)')
|
||||
status = Column(CHAR(1), nullable=True, server_default='0', comment='状态(0正常 1暂停)')
|
||||
create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
|
||||
create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
|
||||
update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
|
||||
update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
|
||||
remark = Column(String(500), nullable=True, server_default="''", comment='备注信息')
|
||||
|
||||
|
||||
class SysJobLog(Base):
|
||||
"""
|
||||
定时任务调度日志表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_job_log'
|
||||
__table_args__ = {'comment': '定时任务调度日志表'}
|
||||
|
||||
job_log_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='任务日志ID')
|
||||
job_name = Column(String(64), nullable=False, comment='任务名称')
|
||||
job_group = Column(String(64), nullable=False, comment='任务组名')
|
||||
job_executor = Column(String(64), nullable=False, comment='任务执行器')
|
||||
invoke_target = Column(String(500), nullable=False, comment='调用目标字符串')
|
||||
job_args = Column(String(255), nullable=True, server_default="''", comment='位置参数')
|
||||
job_kwargs = Column(String(255), nullable=True, server_default="''", comment='关键字参数')
|
||||
job_trigger = Column(String(255), nullable=True, server_default="''", comment='任务触发器')
|
||||
job_message = Column(String(500), nullable=True, comment='日志信息')
|
||||
status = Column(CHAR(1), nullable=True, server_default='0', comment='执行状态(0正常 1失败)')
|
||||
exception_info = Column(String(2000), nullable=True, server_default="''", comment='异常信息')
|
||||
create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
|
||||
@@ -0,0 +1,60 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CHAR, BigInteger, Column, DateTime, Index, Integer, String
|
||||
|
||||
from config.database import Base
|
||||
|
||||
|
||||
class SysLogininfor(Base):
|
||||
"""
|
||||
系统访问记录
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_logininfor'
|
||||
__table_args__ = {'comment': '系统访问记录'}
|
||||
|
||||
info_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='访问ID')
|
||||
user_name = Column(String(50), nullable=True, server_default="''", comment='用户账号')
|
||||
ipaddr = Column(String(128), nullable=True, server_default="''", comment='登录IP地址')
|
||||
login_location = Column(String(255), nullable=True, server_default="''", comment='登录地点')
|
||||
browser = Column(String(50), nullable=True, server_default="''", comment='浏览器类型')
|
||||
os = Column(String(50), nullable=True, server_default="''", comment='操作系统')
|
||||
status = Column(CHAR(1), nullable=True, server_default='0', comment='登录状态(0成功 1失败)')
|
||||
msg = Column(String(255), nullable=True, server_default="''", comment='提示消息')
|
||||
login_time = Column(DateTime, nullable=True, default=datetime.now(), comment='访问时间')
|
||||
|
||||
idx_sys_logininfor_s = Index('idx_sys_logininfor_s', status)
|
||||
idx_sys_logininfor_lt = Index('idx_sys_logininfor_lt', login_time)
|
||||
|
||||
|
||||
class SysOperLog(Base):
|
||||
"""
|
||||
操作日志记录
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_oper_log'
|
||||
__table_args__ = {'comment': '操作日志记录'}
|
||||
|
||||
oper_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='日志主键')
|
||||
title = Column(String(50), nullable=True, server_default="''", comment='模块标题')
|
||||
business_type = Column(Integer, nullable=True, server_default='0', comment='业务类型(0其它 1新增 2修改 3删除)')
|
||||
method = Column(String(100), nullable=True, server_default="''", comment='方法名称')
|
||||
request_method = Column(String(10), nullable=True, server_default="''", comment='请求方式')
|
||||
operator_type = Column(
|
||||
Integer, nullable=True, server_default='0', comment='操作类别(0其它 1后台用户 2手机端用户)'
|
||||
)
|
||||
oper_name = Column(String(50), nullable=True, server_default="''", comment='操作人员')
|
||||
dept_name = Column(String(50), nullable=True, server_default="''", comment='部门名称')
|
||||
oper_url = Column(String(255), nullable=True, server_default="''", comment='请求URL')
|
||||
oper_ip = Column(String(128), nullable=True, server_default="''", comment='主机地址')
|
||||
oper_location = Column(String(255), nullable=True, server_default="''", comment='操作地点')
|
||||
oper_param = Column(String(2000), nullable=True, server_default="''", comment='请求参数')
|
||||
json_result = Column(String(2000), nullable=True, server_default="''", comment='返回参数')
|
||||
status = Column(Integer, nullable=True, server_default='0', comment='操作状态(0正常 1异常)')
|
||||
error_msg = Column(String(2000), nullable=True, server_default="''", comment='错误消息')
|
||||
oper_time = Column(DateTime, nullable=True, default=datetime.now(), comment='操作时间')
|
||||
cost_time = Column(BigInteger, nullable=True, server_default='0', comment='消耗时间')
|
||||
|
||||
idx_sys_oper_log_bt = Index('idx_sys_oper_log_bt', business_type)
|
||||
idx_sys_oper_log_s = Index('idx_sys_oper_log_s', status)
|
||||
idx_sys_oper_log_ot = Index('idx_sys_oper_log_ot', oper_time)
|
||||
@@ -0,0 +1,52 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CHAR, BigInteger, Column, DateTime, Integer, String
|
||||
|
||||
from config.database import Base
|
||||
from config.env import DataBaseConfig
|
||||
from utils.common_util import SqlalchemyUtil
|
||||
|
||||
|
||||
class SysMenu(Base):
|
||||
"""
|
||||
菜单权限表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_menu'
|
||||
__table_args__ = {'comment': '菜单权限表'}
|
||||
|
||||
menu_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='菜单ID')
|
||||
menu_name = Column(String(50), nullable=False, comment='菜单名称')
|
||||
parent_id = Column(BigInteger, nullable=True, server_default='0', comment='父菜单ID')
|
||||
order_num = Column(Integer, server_default='0', comment='显示顺序')
|
||||
path = Column(String(200), nullable=True, server_default="''", comment='路由地址')
|
||||
component = Column(
|
||||
String(255),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='组件路径',
|
||||
)
|
||||
query = Column(
|
||||
String(255),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='路由参数',
|
||||
)
|
||||
route_name = Column(String(50), nullable=True, server_default="''", comment='路由名称')
|
||||
is_frame = Column(Integer, nullable=True, server_default='1', comment='是否为外链(0是 1否)')
|
||||
is_cache = Column(Integer, nullable=True, server_default='0', comment='是否缓存(0缓存 1不缓存)')
|
||||
menu_type = Column(CHAR(1), nullable=True, server_default="''", comment='菜单类型(M目录 C菜单 F按钮)')
|
||||
visible = Column(CHAR(1), nullable=True, server_default='0', comment='菜单状态(0显示 1隐藏)')
|
||||
status = Column(CHAR(1), nullable=True, server_default='0', comment='菜单状态(0正常 1停用)')
|
||||
perms = Column(
|
||||
String(100),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='权限标识',
|
||||
)
|
||||
icon = Column(String(100), nullable=True, server_default='#', comment='菜单图标')
|
||||
create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
|
||||
create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
|
||||
update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
|
||||
update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
|
||||
remark = Column(String(500), nullable=True, server_default="''", comment='备注')
|
||||
@@ -0,0 +1,38 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CHAR, Column, DateTime, Integer, LargeBinary, String
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
from config.database import Base
|
||||
from config.env import DataBaseConfig
|
||||
from utils.common_util import SqlalchemyUtil
|
||||
|
||||
|
||||
class SysNotice(Base):
|
||||
"""
|
||||
通知公告表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_notice'
|
||||
__table_args__ = {'comment': '通知公告表'}
|
||||
|
||||
notice_id = Column(Integer, primary_key=True, nullable=False, autoincrement=True, comment='公告ID')
|
||||
notice_title = Column(String(50), nullable=False, comment='公告标题')
|
||||
notice_type = Column(CHAR(1), nullable=False, comment='公告类型(1通知 2公告)')
|
||||
notice_content = Column(
|
||||
mysql.LONGBLOB if DataBaseConfig.db_type == 'mysql' else LargeBinary,
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type, False),
|
||||
comment='公告内容',
|
||||
)
|
||||
status = Column(CHAR(1), nullable=True, server_default='0', comment='公告状态(0正常 1关闭)')
|
||||
create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
|
||||
create_time = Column(DateTime, nullable=True, comment='创建时间', default=datetime.now())
|
||||
update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
|
||||
update_time = Column(DateTime, nullable=True, comment='更新时间', default=datetime.now())
|
||||
remark = Column(
|
||||
String(255),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='备注',
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CHAR, BigInteger, Column, DateTime, Integer, String
|
||||
|
||||
from config.database import Base
|
||||
from config.env import DataBaseConfig
|
||||
from utils.common_util import SqlalchemyUtil
|
||||
|
||||
|
||||
class SysPost(Base):
|
||||
"""
|
||||
岗位信息表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_post'
|
||||
__table_args__ = {'comment': '岗位信息表'}
|
||||
|
||||
post_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='岗位ID')
|
||||
post_code = Column(String(64), nullable=False, comment='岗位编码')
|
||||
post_name = Column(String(50), nullable=False, comment='岗位名称')
|
||||
post_sort = Column(Integer, nullable=False, comment='显示顺序')
|
||||
status = Column(CHAR(1), nullable=False, comment='状态(0正常 1停用)')
|
||||
create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
|
||||
create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
|
||||
update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
|
||||
update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
|
||||
remark = Column(
|
||||
String(500),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='备注',
|
||||
)
|
||||
@@ -0,0 +1,76 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CHAR, BigInteger, Column, DateTime, Integer, SmallInteger, String
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
from config.database import Base
|
||||
from config.env import DataBaseConfig
|
||||
from utils.common_util import SqlalchemyUtil
|
||||
|
||||
|
||||
class SysRole(Base):
|
||||
"""
|
||||
角色信息表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_role'
|
||||
__table_args__ = {'comment': '角色信息表'}
|
||||
|
||||
role_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='角色ID')
|
||||
role_name = Column(String(30), nullable=False, comment='角色名称')
|
||||
role_key = Column(String(100), nullable=False, comment='角色权限字符串')
|
||||
role_sort = Column(Integer, nullable=False, comment='显示顺序')
|
||||
data_scope = Column(
|
||||
CHAR(1),
|
||||
nullable=True,
|
||||
server_default='1',
|
||||
comment='数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)',
|
||||
)
|
||||
menu_check_strictly = Column(
|
||||
mysql.TINYINT(display_width=1) if DataBaseConfig.db_type == 'mysql' else SmallInteger,
|
||||
nullable=True,
|
||||
server_default='1',
|
||||
comment='菜单树选择项是否关联显示',
|
||||
)
|
||||
dept_check_strictly = Column(
|
||||
mysql.TINYINT(display_width=1) if DataBaseConfig.db_type == 'mysql' else SmallInteger,
|
||||
nullable=True,
|
||||
server_default='1',
|
||||
comment='部门树选择项是否关联显示',
|
||||
)
|
||||
status = Column(CHAR(1), nullable=False, comment='角色状态(0正常 1停用)')
|
||||
del_flag = Column(CHAR(1), nullable=True, server_default='0', comment='删除标志(0代表存在 2代表删除)')
|
||||
create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
|
||||
create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
|
||||
update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
|
||||
update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
|
||||
remark = Column(
|
||||
String(500),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='备注',
|
||||
)
|
||||
|
||||
|
||||
class SysRoleDept(Base):
|
||||
"""
|
||||
角色和部门关联表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_role_dept'
|
||||
__table_args__ = {'comment': '角色和部门关联表'}
|
||||
|
||||
role_id = Column(BigInteger, primary_key=True, nullable=False, comment='角色ID')
|
||||
dept_id = Column(BigInteger, primary_key=True, nullable=False, comment='部门ID')
|
||||
|
||||
|
||||
class SysRoleMenu(Base):
|
||||
"""
|
||||
角色和菜单关联表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_role_menu'
|
||||
__table_args__ = {'comment': '角色和菜单关联表'}
|
||||
|
||||
role_id = Column(BigInteger, primary_key=True, nullable=False, comment='角色ID')
|
||||
menu_id = Column(BigInteger, primary_key=True, nullable=False, comment='菜单ID')
|
||||
@@ -0,0 +1,71 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CHAR, BigInteger, Column, DateTime, String
|
||||
|
||||
from config.database import Base
|
||||
from config.env import DataBaseConfig
|
||||
from utils.common_util import SqlalchemyUtil
|
||||
|
||||
|
||||
class SysUser(Base):
|
||||
"""
|
||||
用户信息表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_user'
|
||||
__table_args__ = {'comment': '用户信息表'}
|
||||
|
||||
user_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='用户ID')
|
||||
dept_id = Column(
|
||||
BigInteger,
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type, False),
|
||||
comment='部门ID',
|
||||
)
|
||||
user_name = Column(String(30), nullable=False, comment='用户账号')
|
||||
nick_name = Column(String(30), nullable=False, comment='用户昵称')
|
||||
user_type = Column(String(2), nullable=True, server_default='00', comment='用户类型(00系统用户)')
|
||||
email = Column(String(50), nullable=True, server_default="''", comment='用户邮箱')
|
||||
phonenumber = Column(String(11), nullable=True, server_default="''", comment='手机号码')
|
||||
sex = Column(CHAR(1), nullable=True, server_default='0', comment='用户性别(0男 1女 2未知)')
|
||||
avatar = Column(String(100), nullable=True, server_default="''", comment='头像地址')
|
||||
password = Column(String(100), nullable=True, server_default="''", comment='密码')
|
||||
status = Column(CHAR(1), nullable=True, server_default='0', comment='帐号状态(0正常 1停用)')
|
||||
del_flag = Column(CHAR(1), nullable=True, server_default='0', comment='删除标志(0代表存在 2代表删除)')
|
||||
login_ip = Column(String(128), nullable=True, server_default="''", comment='最后登录IP')
|
||||
login_date = Column(DateTime, nullable=True, comment='最后登录时间')
|
||||
pwd_update_date = Column(DateTime, nullable=True, comment='密码最后更新时间')
|
||||
create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
|
||||
create_time = Column(DateTime, nullable=True, comment='创建时间', default=datetime.now())
|
||||
update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
|
||||
update_time = Column(DateTime, nullable=True, comment='更新时间', default=datetime.now())
|
||||
remark = Column(
|
||||
String(500),
|
||||
nullable=True,
|
||||
server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
|
||||
comment='备注',
|
||||
)
|
||||
|
||||
|
||||
class SysUserRole(Base):
|
||||
"""
|
||||
用户和角色关联表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_user_role'
|
||||
__table_args__ = {'comment': '用户和角色关联表'}
|
||||
|
||||
user_id = Column(BigInteger, primary_key=True, nullable=False, comment='用户ID')
|
||||
role_id = Column(BigInteger, primary_key=True, nullable=False, comment='角色ID')
|
||||
|
||||
|
||||
class SysUserPost(Base):
|
||||
"""
|
||||
用户与岗位关联表
|
||||
"""
|
||||
|
||||
__tablename__ = 'sys_user_post'
|
||||
__table_args__ = {'comment': '用户与岗位关联表'}
|
||||
|
||||
user_id = Column(BigInteger, primary_key=True, nullable=False, comment='用户ID')
|
||||
post_id = Column(BigInteger, primary_key=True, nullable=False, comment='岗位ID')
|
||||
@@ -0,0 +1,29 @@
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
|
||||
|
||||
class CacheMonitorModel(BaseModel):
|
||||
"""
|
||||
缓存监控信息对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
command_stats: list | None = Field(default=[], description='命令统计')
|
||||
db_size: int | None = Field(default=None, description='Key数量')
|
||||
info: dict | None = Field(default={}, description='Redis信息')
|
||||
|
||||
|
||||
class CacheInfoModel(BaseModel):
|
||||
"""
|
||||
缓存监控对象对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
cache_key: str | None = Field(default=None, description='缓存键名')
|
||||
cache_name: str | None = Field(default=None, description='缓存名称')
|
||||
cache_value: Any | None = Field(default=None, description='缓存内容')
|
||||
remark: str | None = Field(default=None, description='备注')
|
||||
@@ -0,0 +1,15 @@
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
|
||||
|
||||
class UploadResponseModel(BaseModel):
|
||||
"""
|
||||
上传响应模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
file_name: str | None = Field(default=None, description='新文件映射路径')
|
||||
new_file_name: str | None = Field(default=None, description='新文件名称')
|
||||
original_filename: str | None = Field(default=None, description='原文件名称')
|
||||
url: str | None = Field(default=None, description='新文件url')
|
||||
@@ -0,0 +1,73 @@
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
from pydantic_validation_decorator import NotBlank, Size
|
||||
|
||||
|
||||
class ConfigModel(BaseModel):
|
||||
"""
|
||||
参数配置表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
config_id: int | None = Field(default=None, description='参数主键')
|
||||
config_name: str | None = Field(default=None, description='参数名称')
|
||||
config_key: str | None = Field(default=None, description='参数键名')
|
||||
config_value: str | None = Field(default=None, description='参数键值')
|
||||
config_type: Literal['Y', 'N'] | None = Field(default=None, description='系统内置(Y是 N否)')
|
||||
create_by: str | None = Field(default=None, description='创建者')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: datetime | None = Field(default=None, description='更新时间')
|
||||
remark: str | None = Field(default=None, description='备注')
|
||||
|
||||
@NotBlank(field_name='config_key', message='参数名称不能为空')
|
||||
@Size(field_name='config_key', min_length=0, max_length=100, message='参数名称长度不能超过100个字符')
|
||||
def get_config_key(self) -> str | None:
|
||||
return self.config_key
|
||||
|
||||
@NotBlank(field_name='config_name', message='参数键名不能为空')
|
||||
@Size(field_name='config_name', min_length=0, max_length=100, message='参数键名长度不能超过100个字符')
|
||||
def get_config_name(self) -> str | None:
|
||||
return self.config_name
|
||||
|
||||
@NotBlank(field_name='config_value', message='参数键值不能为空')
|
||||
@Size(field_name='config_value', min_length=0, max_length=500, message='参数键值长度不能超过500个字符')
|
||||
def get_config_value(self) -> str | None:
|
||||
return self.config_value
|
||||
|
||||
def validate_fields(self) -> None:
|
||||
self.get_config_key()
|
||||
self.get_config_name()
|
||||
self.get_config_value()
|
||||
|
||||
|
||||
class ConfigQueryModel(ConfigModel):
|
||||
"""
|
||||
参数配置管理不分页查询模型
|
||||
"""
|
||||
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class ConfigPageQueryModel(ConfigQueryModel):
|
||||
"""
|
||||
参数配置管理分页查询模型
|
||||
"""
|
||||
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
class DeleteConfigModel(BaseModel):
|
||||
"""
|
||||
删除参数配置模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
config_ids: str = Field(description='需要删除的参数主键')
|
||||
@@ -0,0 +1,87 @@
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
from pydantic_validation_decorator import Network, NotBlank, Size
|
||||
|
||||
|
||||
class DeptModel(BaseModel):
|
||||
"""
|
||||
部门表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
dept_id: int | None = Field(default=None, description='部门id')
|
||||
parent_id: int | None = Field(default=None, description='父部门id')
|
||||
ancestors: str | None = Field(default=None, description='祖级列表')
|
||||
dept_name: str | None = Field(default=None, description='部门名称')
|
||||
order_num: int | None = Field(default=None, description='显示顺序')
|
||||
leader: str | None = Field(default=None, description='负责人')
|
||||
phone: str | None = Field(default=None, description='联系电话')
|
||||
email: str | None = Field(default=None, description='邮箱')
|
||||
status: Literal['0', '1'] | None = Field(default=None, description='部门状态(0正常 1停用)')
|
||||
del_flag: Literal['0', '2'] | None = Field(default=None, description='删除标志(0代表存在 2代表删除)')
|
||||
create_by: str | None = Field(default=None, description='创建者')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: datetime | None = Field(default=None, description='更新时间')
|
||||
|
||||
@NotBlank(field_name='dept_name', message='部门名称不能为空')
|
||||
@Size(field_name='dept_name', min_length=0, max_length=30, message='部门名称长度不能超过30个字符')
|
||||
def get_dept_name(self) -> str | None:
|
||||
return self.dept_name
|
||||
|
||||
@NotBlank(field_name='order_num', message='显示顺序不能为空')
|
||||
def get_order_num(self) -> int | None:
|
||||
return self.order_num
|
||||
|
||||
@Size(field_name='phone', min_length=0, max_length=11, message='联系电话长度不能超过11个字符')
|
||||
def get_phone(self) -> str | None:
|
||||
return self.phone
|
||||
|
||||
@Network(field_name='email', field_type='EmailStr', message='邮箱格式不正确')
|
||||
@Size(field_name='email', min_length=0, max_length=50, message='邮箱长度不能超过50个字符')
|
||||
def get_email(self) -> str | None:
|
||||
return self.email
|
||||
|
||||
def validate_fields(self) -> None:
|
||||
self.get_dept_name()
|
||||
self.get_order_num()
|
||||
self.get_phone()
|
||||
self.get_email()
|
||||
|
||||
|
||||
class DeptQueryModel(DeptModel):
|
||||
"""
|
||||
部门管理不分页查询模型
|
||||
"""
|
||||
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class DeptTreeModel(BaseModel):
|
||||
"""
|
||||
部门树模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
id: int = Field(description='部门id')
|
||||
label: str = Field(description='部门名称')
|
||||
parent_id: int = Field(description='父部门id')
|
||||
children: list['DeptTreeModel'] | None = Field(default=None, description='子部门树')
|
||||
|
||||
|
||||
class DeleteDeptModel(BaseModel):
|
||||
"""
|
||||
删除部门模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
dept_ids: str = Field(default=None, description='需要删除的部门id')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: str | None = Field(default=None, description='更新时间')
|
||||
@@ -0,0 +1,147 @@
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
from pydantic_validation_decorator import NotBlank, Pattern, Size
|
||||
|
||||
|
||||
class DictTypeModel(BaseModel):
|
||||
"""
|
||||
字典类型表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
dict_id: int | None = Field(default=None, description='字典主键')
|
||||
dict_name: str | None = Field(default=None, description='字典名称')
|
||||
dict_type: str | None = Field(default=None, description='字典类型')
|
||||
status: Literal['0', '1'] | None = Field(default=None, description='状态(0正常 1停用)')
|
||||
create_by: str | None = Field(default=None, description='创建者')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: datetime | None = Field(default=None, description='更新时间')
|
||||
remark: str | None = Field(default=None, description='备注')
|
||||
|
||||
@NotBlank(field_name='dict_name', message='字典名称不能为空')
|
||||
@Size(field_name='dict_name', min_length=0, max_length=100, message='字典类型名称长度不能超过100个字符')
|
||||
def get_dict_name(self) -> str | None:
|
||||
return self.dict_name
|
||||
|
||||
@NotBlank(field_name='dict_type', message='字典类型不能为空')
|
||||
@Size(field_name='dict_type', min_length=0, max_length=100, message='字典类型类型长度不能超过100个字符')
|
||||
@Pattern(
|
||||
field_name='dict_type',
|
||||
regexp='^[a-z][a-z0-9_]*$',
|
||||
message='字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)',
|
||||
)
|
||||
def get_dict_type(self) -> str | None:
|
||||
return self.dict_type
|
||||
|
||||
def validate_fields(self) -> None:
|
||||
self.get_dict_name()
|
||||
self.get_dict_type()
|
||||
|
||||
|
||||
class DictDataModel(BaseModel):
|
||||
"""
|
||||
字典数据表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
dict_code: int | None = Field(default=None, description='字典编码')
|
||||
dict_sort: int | None = Field(default=None, description='字典排序')
|
||||
dict_label: str | None = Field(default=None, description='字典标签')
|
||||
dict_value: str | None = Field(default=None, description='字典键值')
|
||||
dict_type: str | None = Field(default=None, description='字典类型')
|
||||
css_class: str | None = Field(default=None, description='样式属性(其他样式扩展)')
|
||||
list_class: str | None = Field(default=None, description='表格回显样式')
|
||||
is_default: Literal['Y', 'N'] | None = Field(default=None, description='是否默认(Y是 N否)')
|
||||
status: Literal['0', '1'] | None = Field(default=None, description='状态(0正常 1停用)')
|
||||
create_by: str | None = Field(default=None, description='创建者')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: datetime | None = Field(default=None, description='更新时间')
|
||||
remark: str | None = Field(default=None, description='备注')
|
||||
|
||||
@NotBlank(field_name='dict_label', message='字典标签不能为空')
|
||||
@Size(field_name='dict_label', min_length=0, max_length=100, message='字典标签长度不能超过100个字符')
|
||||
def get_dict_label(self) -> str | None:
|
||||
return self.dict_label
|
||||
|
||||
@NotBlank(field_name='dict_value', message='字典键值不能为空')
|
||||
@Size(field_name='dict_value', min_length=0, max_length=100, message='字典键值长度不能超过100个字符')
|
||||
def get_dict_value(self) -> str | None:
|
||||
return self.dict_value
|
||||
|
||||
@NotBlank(field_name='dict_type', message='字典类型不能为空')
|
||||
@Size(field_name='dict_type', min_length=0, max_length=100, message='字典类型长度不能超过100个字符')
|
||||
def get_dict_type(self) -> str | None:
|
||||
return self.dict_type
|
||||
|
||||
@Size(field_name='css_class', min_length=0, max_length=100, message='样式属性长度不能超过100个字符')
|
||||
def get_css_class(self) -> str | None:
|
||||
return self.css_class
|
||||
|
||||
def validate_fields(self) -> None:
|
||||
self.get_dict_label()
|
||||
self.get_dict_value()
|
||||
self.get_dict_type()
|
||||
self.get_css_class()
|
||||
|
||||
|
||||
class DictTypeQueryModel(DictTypeModel):
|
||||
"""
|
||||
字典类型管理不分页查询模型
|
||||
"""
|
||||
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class DictTypePageQueryModel(DictTypeQueryModel):
|
||||
"""
|
||||
字典类型管理分页查询模型
|
||||
"""
|
||||
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
class DeleteDictTypeModel(BaseModel):
|
||||
"""
|
||||
删除字典类型模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
dict_ids: str = Field(description='需要删除的字典主键')
|
||||
|
||||
|
||||
class DictDataQueryModel(DictDataModel):
|
||||
"""
|
||||
字典数据管理不分页查询模型
|
||||
"""
|
||||
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class DictDataPageQueryModel(DictDataQueryModel):
|
||||
"""
|
||||
字典数据管理分页查询模型
|
||||
"""
|
||||
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
class DeleteDictDataModel(BaseModel):
|
||||
"""
|
||||
删除字典数据模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
dict_codes: str = Field(description='需要删除的字典编码')
|
||||
@@ -0,0 +1,132 @@
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
from pydantic_validation_decorator import NotBlank, Size
|
||||
|
||||
|
||||
class JobModel(BaseModel):
|
||||
"""
|
||||
定时任务调度表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
job_id: int | None = Field(default=None, description='任务ID')
|
||||
job_name: str | None = Field(default=None, description='任务名称')
|
||||
job_group: str | None = Field(default=None, description='任务组名')
|
||||
job_executor: str | None = Field(default=None, description='任务执行器')
|
||||
invoke_target: str | None = Field(default=None, description='调用目标字符串')
|
||||
job_args: str | None = Field(default=None, description='位置参数')
|
||||
job_kwargs: str | None = Field(default=None, description='关键字参数')
|
||||
cron_expression: str | None = Field(default=None, description='cron执行表达式')
|
||||
misfire_policy: Literal['1', '2', '3'] | None = Field(
|
||||
default=None, description='计划执行错误策略(1立即执行 2执行一次 3放弃执行)'
|
||||
)
|
||||
concurrent: Literal['0', '1'] | None = Field(default=None, description='是否并发执行(0允许 1禁止)')
|
||||
status: Literal['0', '1'] | None = Field(default=None, description='状态(0正常 1暂停)')
|
||||
create_by: str | None = Field(default=None, description='创建者')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: datetime | None = Field(default=None, description='更新时间')
|
||||
remark: str | None = Field(default=None, description='备注信息')
|
||||
|
||||
@NotBlank(field_name='invoke_target', message='调用目标字符串不能为空')
|
||||
@Size(field_name='invoke_target', min_length=0, max_length=500, message='调用目标字符串长度不能超过500个字符')
|
||||
def get_invoke_target(self) -> str | None:
|
||||
return self.invoke_target
|
||||
|
||||
@NotBlank(field_name='cron_expression', message='Cron执行表达式不能为空')
|
||||
@Size(field_name='cron_expression', min_length=0, max_length=255, message='Cron执行表达式不能超过255个字符')
|
||||
def get_cron_expression(self) -> str | None:
|
||||
return self.cron_expression
|
||||
|
||||
def validate_fields(self) -> None:
|
||||
self.get_invoke_target()
|
||||
self.get_cron_expression()
|
||||
|
||||
|
||||
class JobLogModel(BaseModel):
|
||||
"""
|
||||
定时任务调度日志表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
job_log_id: int | None = Field(default=None, description='任务日志ID')
|
||||
job_name: str | None = Field(default=None, description='任务名称')
|
||||
job_group: str | None = Field(default=None, description='任务组名')
|
||||
job_executor: str | None = Field(default=None, description='任务执行器')
|
||||
invoke_target: str | None = Field(default=None, description='调用目标字符串')
|
||||
job_args: str | None = Field(default=None, description='位置参数')
|
||||
job_kwargs: str | None = Field(default=None, description='关键字参数')
|
||||
job_trigger: str | None = Field(default=None, description='任务触发器')
|
||||
job_message: str | None = Field(default=None, description='日志信息')
|
||||
status: Literal['0', '1'] | None = Field(default=None, description='执行状态(0正常 1失败)')
|
||||
exception_info: str | None = Field(default=None, description='异常信息')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
|
||||
|
||||
class JobQueryModel(JobModel):
|
||||
"""
|
||||
定时任务管理不分页查询模型
|
||||
"""
|
||||
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class JobPageQueryModel(JobQueryModel):
|
||||
"""
|
||||
定时任务管理分页查询模型
|
||||
"""
|
||||
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
class EditJobModel(JobModel):
|
||||
"""
|
||||
编辑定时任务模型
|
||||
"""
|
||||
|
||||
type: str | None = Field(default=None, description='操作类型')
|
||||
|
||||
|
||||
class DeleteJobModel(BaseModel):
|
||||
"""
|
||||
删除定时任务模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
job_ids: str = Field(description='需要删除的定时任务ID')
|
||||
|
||||
|
||||
class JobLogQueryModel(JobLogModel):
|
||||
"""
|
||||
定时任务日志不分页查询模型
|
||||
"""
|
||||
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class JobLogPageQueryModel(JobLogQueryModel):
|
||||
"""
|
||||
定时任务日志管理分页查询模型
|
||||
"""
|
||||
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
class DeleteJobLogModel(BaseModel):
|
||||
"""
|
||||
删除定时任务日志模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
job_log_ids: str = Field(description='需要删除的定时任务日志ID')
|
||||
@@ -0,0 +1,127 @@
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
|
||||
|
||||
class OperLogModel(BaseModel):
|
||||
"""
|
||||
操作日志表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
oper_id: int | None = Field(default=None, description='日志主键')
|
||||
title: str | None = Field(default=None, description='模块标题')
|
||||
business_type: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] | None = (
|
||||
Field(
|
||||
default=None, description='业务类型(0其它 1新增 2修改 3删除 4授权 5导出 6导入 7强退 8生成代码 9清空数据)'
|
||||
)
|
||||
)
|
||||
method: str | None = Field(default=None, description='方法名称')
|
||||
request_method: str | None = Field(default=None, description='请求方式')
|
||||
operator_type: Literal[0, 1, 2] | None = Field(default=None, description='操作类别(0其它 1后台用户 2手机端用户)')
|
||||
oper_name: str | None = Field(default=None, description='操作人员')
|
||||
dept_name: str | None = Field(default=None, description='部门名称')
|
||||
oper_url: str | None = Field(default=None, description='请求URL')
|
||||
oper_ip: str | None = Field(default=None, description='主机地址')
|
||||
oper_location: str | None = Field(default=None, description='操作地点')
|
||||
oper_param: str | None = Field(default=None, description='请求参数')
|
||||
json_result: str | None = Field(default=None, description='返回参数')
|
||||
status: Literal[0, 1, '0', '1'] | None = Field(default=None, description='操作状态(0正常 1异常)')
|
||||
error_msg: str | None = Field(default=None, description='错误消息')
|
||||
oper_time: datetime | None = Field(default=None, description='操作时间')
|
||||
cost_time: int | None = Field(default=None, description='消耗时间')
|
||||
|
||||
|
||||
class LogininforModel(BaseModel):
|
||||
"""
|
||||
登录日志表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
info_id: int | None = Field(default=None, description='访问ID')
|
||||
user_name: str | None = Field(default=None, description='用户账号')
|
||||
ipaddr: str | None = Field(default=None, description='登录IP地址')
|
||||
login_location: str | None = Field(default=None, description='登录地点')
|
||||
browser: str | None = Field(default=None, description='浏览器类型')
|
||||
os: str | None = Field(default=None, description='操作系统')
|
||||
status: Literal['0', '1'] | None = Field(default=None, description='登录状态(0成功 1失败)')
|
||||
msg: str | None = Field(default=None, description='提示消息')
|
||||
login_time: datetime | None = Field(default=None, description='访问时间')
|
||||
|
||||
|
||||
class OperLogQueryModel(OperLogModel):
|
||||
"""
|
||||
操作日志管理不分页查询模型
|
||||
"""
|
||||
|
||||
order_by_column: str | None = Field(default=None, description='排序的字段名称')
|
||||
is_asc: Literal['ascending', 'descending'] | None = Field(
|
||||
default=None, description='排序方式(ascending升序 descending降序)'
|
||||
)
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class OperLogPageQueryModel(OperLogQueryModel):
|
||||
"""
|
||||
操作日志管理分页查询模型
|
||||
"""
|
||||
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
class DeleteOperLogModel(BaseModel):
|
||||
"""
|
||||
删除操作日志模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
oper_ids: str = Field(description='需要删除的日志主键')
|
||||
|
||||
|
||||
class LoginLogQueryModel(LogininforModel):
|
||||
"""
|
||||
登录日志管理不分页查询模型
|
||||
"""
|
||||
|
||||
order_by_column: str | None = Field(default=None, description='排序的字段名称')
|
||||
is_asc: Literal['ascending', 'descending'] | None = Field(
|
||||
default=None, description='排序方式(ascending升序 descending降序)'
|
||||
)
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class LoginLogPageQueryModel(LoginLogQueryModel):
|
||||
"""
|
||||
登录日志管理分页查询模型
|
||||
"""
|
||||
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
class DeleteLoginLogModel(BaseModel):
|
||||
"""
|
||||
删除登录日志模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
info_ids: str = Field(description='需要删除的访问ID')
|
||||
|
||||
|
||||
class UnlockUser(BaseModel):
|
||||
"""
|
||||
解锁用户模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
user_name: str = Field(description='用户名称')
|
||||
@@ -0,0 +1,93 @@
|
||||
import re
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
from pydantic.alias_generators import to_camel
|
||||
|
||||
from exceptions.exception import ModelValidatorException
|
||||
from module_admin.entity.vo.menu_vo import MenuModel
|
||||
|
||||
|
||||
class UserLogin(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
user_name: str = Field(description='用户名称')
|
||||
password: str = Field(description='用户密码')
|
||||
code: str | None = Field(default=None, description='验证码')
|
||||
uuid: str | None = Field(default=None, description='会话编号')
|
||||
login_info: dict | None = Field(default=None, description='登录信息,前端无需传递')
|
||||
captcha_enabled: bool | None = Field(default=None, description='是否启用验证码,前端无需传递')
|
||||
|
||||
|
||||
class UserRegister(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
username: str = Field(description='用户名称')
|
||||
password: str = Field(description='用户密码')
|
||||
confirm_password: str = Field(description='用户二次确认密码')
|
||||
code: str | None = Field(default=None, description='验证码')
|
||||
uuid: str | None = Field(default=None, description='会话编号')
|
||||
|
||||
@model_validator(mode='after')
|
||||
def check_password(self) -> 'UserRegister':
|
||||
pattern = r"""^[^<>"'|\\]+$"""
|
||||
if self.password is None or re.match(pattern, self.password):
|
||||
return self
|
||||
raise ModelValidatorException(message='密码不能包含非法字符:< > " \' \\ |')
|
||||
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str = Field(description='token信息')
|
||||
token_type: str = Field(description='token类型')
|
||||
|
||||
|
||||
class LoginToken(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
token: str = Field(description='token信息')
|
||||
|
||||
|
||||
class CaptchaCode(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
captcha_enabled: bool = Field(description='是否启用验证码')
|
||||
register_enabled: bool = Field(description='是否启用注册')
|
||||
img: str = Field(description='验证码图片')
|
||||
uuid: str = Field(description='会话编号')
|
||||
|
||||
|
||||
class SmsCode(BaseModel):
|
||||
is_success: bool | None = Field(default=None, description='操作是否成功')
|
||||
sms_code: str = Field(description='短信验证码')
|
||||
session_id: str = Field(description='会话编号')
|
||||
message: str | None = Field(default=None, description='响应信息')
|
||||
|
||||
|
||||
class MenuTreeModel(MenuModel):
|
||||
children: list['MenuTreeModel'] | None | None = Field(default=None, description='子菜单')
|
||||
|
||||
|
||||
class MetaModel(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
title: str | None = Field(default=None, description='设置路由在侧边栏和面包屑中展示的名字')
|
||||
icon: str | None = Field(default=None, description='设置路由的图标')
|
||||
no_cache: bool | None = Field(default=None, description='设置为true,则不会被 <keep-alive>缓存')
|
||||
link: str | None = Field(default=None, description='内链地址(http(s)://开头)')
|
||||
|
||||
|
||||
class RouterModel(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
name: str | None = Field(default=None, description='路由名称')
|
||||
path: str | None = Field(default=None, description='路由地址')
|
||||
hidden: bool | None = Field(default=None, description='是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现')
|
||||
redirect: str | None = Field(
|
||||
default=None, description='重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击'
|
||||
)
|
||||
component: str | None = Field(default=None, description='组件地址')
|
||||
query: str | None = Field(default=None, description='路由参数:如 {"id": 1, "name": "ry"}')
|
||||
always_show: bool | None = Field(
|
||||
default=None, description='当你一个路由下面的children声明的路由大于1个时,自动会变成嵌套的模式--如组件页面'
|
||||
)
|
||||
meta: MetaModel | None = Field(default=None, description='其他元素')
|
||||
children: list['RouterModel'] | None | None = Field(default=None, description='子路由')
|
||||
@@ -0,0 +1,100 @@
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
from pydantic_validation_decorator import NotBlank, Size
|
||||
|
||||
|
||||
class MenuModel(BaseModel):
|
||||
"""
|
||||
菜单表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
menu_id: int | None = Field(default=None, description='菜单ID')
|
||||
menu_name: str | None = Field(default=None, description='菜单名称')
|
||||
parent_id: int | None = Field(default=None, description='父菜单ID')
|
||||
order_num: int | None = Field(default=None, description='显示顺序')
|
||||
path: str | None = Field(default=None, description='路由地址')
|
||||
component: str | None = Field(default=None, description='组件路径')
|
||||
query: str | None = Field(default=None, description='路由参数')
|
||||
route_name: str | None = Field(default=None, description='路由名称')
|
||||
is_frame: Literal[0, 1] | None = Field(default=None, description='是否为外链(0是 1否)')
|
||||
is_cache: Literal[0, 1] | None = Field(default=None, description='是否缓存(0缓存 1不缓存)')
|
||||
menu_type: Literal['M', 'C', 'F'] | None = Field(default=None, description='菜单类型(M目录 C菜单 F按钮)')
|
||||
visible: Literal['0', '1'] | None = Field(default=None, description='菜单状态(0显示 1隐藏)')
|
||||
status: Literal['0', '1'] | None = Field(default=None, description='菜单状态(0正常 1停用)')
|
||||
perms: str | None = Field(default=None, description='权限标识')
|
||||
icon: str | None = Field(default=None, description='菜单图标')
|
||||
create_by: str | None = Field(default=None, description='创建者')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: datetime | None = Field(default=None, description='更新时间')
|
||||
remark: str | None = Field(default=None, description='备注')
|
||||
|
||||
@NotBlank(field_name='menu_name', message='菜单名称不能为空')
|
||||
@Size(field_name='menu_name', min_length=0, max_length=50, message='菜单名称长度不能超过50个字符')
|
||||
def get_menu_name(self) -> str | None:
|
||||
return self.menu_name
|
||||
|
||||
@NotBlank(field_name='order_num', message='显示顺序不能为空')
|
||||
def get_order_num(self) -> int | None:
|
||||
return self.order_num
|
||||
|
||||
@Size(field_name='path', min_length=0, max_length=200, message='路由地址长度不能超过200个字符')
|
||||
def get_path(self) -> str | None:
|
||||
return self.path
|
||||
|
||||
@Size(field_name='component', min_length=0, max_length=255, message='组件路径长度不能超过255个字符')
|
||||
def get_component(self) -> str | None:
|
||||
return self.component
|
||||
|
||||
@NotBlank(field_name='menu_type', message='菜单类型不能为空')
|
||||
def get_menu_type(self) -> Literal['M', 'C', 'F'] | None:
|
||||
return self.menu_type
|
||||
|
||||
@Size(field_name='perms', min_length=0, max_length=100, message='权限标识长度不能超过100个字符')
|
||||
def get_perms(self) -> str | None:
|
||||
return self.perms
|
||||
|
||||
def validate_fields(self) -> None:
|
||||
self.get_menu_name()
|
||||
self.get_order_num()
|
||||
self.get_path()
|
||||
self.get_component()
|
||||
self.get_menu_type()
|
||||
self.get_perms()
|
||||
|
||||
|
||||
class MenuQueryModel(MenuModel):
|
||||
"""
|
||||
菜单管理不分页查询模型
|
||||
"""
|
||||
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class MenuTreeModel(BaseModel):
|
||||
"""
|
||||
菜单树模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
id: int = Field(description='菜单id')
|
||||
label: str = Field(description='菜单名称')
|
||||
parent_id: int = Field(description='父菜单id')
|
||||
children: list['MenuTreeModel'] | None = Field(default=None, description='子菜单树')
|
||||
|
||||
|
||||
class DeleteMenuModel(BaseModel):
|
||||
"""
|
||||
删除菜单模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
menu_ids: str = Field(description='需要删除的菜单ID')
|
||||
@@ -0,0 +1,62 @@
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
from pydantic_validation_decorator import NotBlank, Size, Xss
|
||||
|
||||
|
||||
class NoticeModel(BaseModel):
|
||||
"""
|
||||
通知公告表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
notice_id: int | None = Field(default=None, description='公告ID')
|
||||
notice_title: str | None = Field(default=None, description='公告标题')
|
||||
notice_type: Literal['1', '2'] | None = Field(default=None, description='公告类型(1通知 2公告)')
|
||||
notice_content: bytes | None = Field(default=None, description='公告内容')
|
||||
status: Literal['0', '1'] | None = Field(default=None, description='公告状态(0正常 1关闭)')
|
||||
create_by: str | None = Field(default=None, description='创建者')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: datetime | None = Field(default=None, description='更新时间')
|
||||
remark: str | None = Field(default=None, description='备注')
|
||||
|
||||
@Xss(field_name='notice_title', message='公告标题不能包含脚本字符')
|
||||
@NotBlank(field_name='notice_title', message='公告标题不能为空')
|
||||
@Size(field_name='notice_title', min_length=0, max_length=50, message='公告标题不能超过50个字符')
|
||||
def get_notice_title(self) -> str | None:
|
||||
return self.notice_title
|
||||
|
||||
def validate_fields(self) -> None:
|
||||
self.get_notice_title()
|
||||
|
||||
|
||||
class NoticeQueryModel(NoticeModel):
|
||||
"""
|
||||
通知公告管理不分页查询模型
|
||||
"""
|
||||
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class NoticePageQueryModel(NoticeQueryModel):
|
||||
"""
|
||||
通知公告管理分页查询模型
|
||||
"""
|
||||
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
class DeleteNoticeModel(BaseModel):
|
||||
"""
|
||||
删除通知公告模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
notice_ids: str = Field(description='需要删除的公告ID')
|
||||
@@ -0,0 +1,51 @@
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
|
||||
|
||||
class OnlineModel(BaseModel):
|
||||
"""
|
||||
在线用户对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
token_id: str | None = Field(default=None, description='会话编号')
|
||||
user_name: str | None = Field(default=None, description='登录名称')
|
||||
dept_name: str | None = Field(default=None, description='所属部门')
|
||||
ipaddr: str | None = Field(default=None, description='主机')
|
||||
login_location: str | None = Field(default=None, description='登录地点')
|
||||
browser: str | None = Field(default=None, description='浏览器类型')
|
||||
os: str | None = Field(default=None, description='操作系统')
|
||||
login_time: datetime | None = Field(default=None, description='登录时间')
|
||||
|
||||
|
||||
class OnlineQueryModel(OnlineModel):
|
||||
"""
|
||||
岗位管理不分页查询模型
|
||||
"""
|
||||
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class OnlinePageResponseModel(BaseModel):
|
||||
"""
|
||||
在线用户分页响应模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
rows: list[OnlineModel] = Field(description='在线用户记录列表')
|
||||
total: int = Field(description='总记录数')
|
||||
|
||||
|
||||
class DeleteOnlineModel(BaseModel):
|
||||
"""
|
||||
强退在线用户模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
token_ids: str = Field(description='需要强退的会话编号')
|
||||
@@ -0,0 +1,72 @@
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
from pydantic_validation_decorator import NotBlank, Size
|
||||
|
||||
|
||||
class PostModel(BaseModel):
|
||||
"""
|
||||
岗位信息表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
post_id: int | None = Field(default=None, description='岗位ID')
|
||||
post_code: str | None = Field(default=None, description='岗位编码')
|
||||
post_name: str | None = Field(default=None, description='岗位名称')
|
||||
post_sort: int | None = Field(default=None, description='显示顺序')
|
||||
status: Literal['0', '1'] | None = Field(default=None, description='状态(0正常 1停用)')
|
||||
create_by: str | None = Field(default=None, description='创建者')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: datetime | None = Field(default=None, description='更新时间')
|
||||
remark: str | None = Field(default=None, description='备注')
|
||||
|
||||
@NotBlank(field_name='post_code', message='岗位编码不能为空')
|
||||
@Size(field_name='post_code', min_length=0, max_length=64, message='岗位编码长度不能超过64个字符')
|
||||
def get_post_code(self) -> str | None:
|
||||
return self.post_code
|
||||
|
||||
@NotBlank(field_name='post_name', message='岗位名称不能为空')
|
||||
@Size(field_name='post_name', min_length=0, max_length=50, message='岗位名称长度不能超过50个字符')
|
||||
def get_post_name(self) -> str | None:
|
||||
return self.post_name
|
||||
|
||||
@NotBlank(field_name='post_sort', message='显示顺序不能为空')
|
||||
def get_post_sort(self) -> int | None:
|
||||
return self.post_sort
|
||||
|
||||
def validate_fields(self) -> None:
|
||||
self.get_post_code()
|
||||
self.get_post_name()
|
||||
self.get_post_sort()
|
||||
|
||||
|
||||
class PostQueryModel(PostModel):
|
||||
"""
|
||||
岗位管理不分页查询模型
|
||||
"""
|
||||
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class PostPageQueryModel(PostQueryModel):
|
||||
"""
|
||||
岗位管理分页查询模型
|
||||
"""
|
||||
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
class DeletePostModel(BaseModel):
|
||||
"""
|
||||
删除岗位模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
post_ids: str = Field(description='需要删除的岗位ID')
|
||||
@@ -0,0 +1,157 @@
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
||||
from pydantic.alias_generators import to_camel
|
||||
from pydantic_validation_decorator import NotBlank, Size
|
||||
|
||||
|
||||
class RoleModel(BaseModel):
|
||||
"""
|
||||
角色表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
role_id: int | None = Field(default=None, description='角色ID')
|
||||
role_name: str | None = Field(default=None, description='角色名称')
|
||||
role_key: str | None = Field(default=None, description='角色权限字符串')
|
||||
role_sort: int | None = Field(default=None, description='显示顺序')
|
||||
data_scope: Literal['1', '2', '3', '4', '5'] | None = Field(
|
||||
default=None,
|
||||
description='数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限)',
|
||||
)
|
||||
menu_check_strictly: int | bool | None = Field(default=None, description='菜单树选择项是否关联显示')
|
||||
dept_check_strictly: int | bool | None = Field(default=None, description='部门树选择项是否关联显示')
|
||||
status: Literal['0', '1'] | None = Field(default=None, description='角色状态(0正常 1停用)')
|
||||
del_flag: Literal['0', '2'] | None = Field(default=None, description='删除标志(0代表存在 2代表删除)')
|
||||
create_by: str | None = Field(default=None, description='创建者')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: datetime | None = Field(default=None, description='更新时间')
|
||||
remark: str | None = Field(default=None, description='备注')
|
||||
admin: bool | None = Field(default=False, description='是否为admin')
|
||||
|
||||
@field_validator('menu_check_strictly', 'dept_check_strictly')
|
||||
@classmethod
|
||||
def check_filed_mapping(cls, v: int | bool) -> int | bool:
|
||||
if v == 1:
|
||||
v = True
|
||||
elif v == 0:
|
||||
v = False
|
||||
elif v is True:
|
||||
v = 1
|
||||
elif v is False:
|
||||
v = 0
|
||||
return v
|
||||
|
||||
@model_validator(mode='after')
|
||||
def check_admin(self) -> 'RoleModel':
|
||||
if self.role_id == 1:
|
||||
self.admin = True
|
||||
else:
|
||||
self.admin = False
|
||||
return self
|
||||
|
||||
@NotBlank(field_name='role_name', message='角色名称不能为空')
|
||||
@Size(field_name='role_name', min_length=0, max_length=30, message='角色名称长度不能超过30个字符')
|
||||
def get_role_name(self) -> str | None:
|
||||
return self.role_name
|
||||
|
||||
@NotBlank(field_name='role_key', message='权限字符不能为空')
|
||||
@Size(field_name='role_key', min_length=0, max_length=100, message='权限字符长度不能超过100个字符')
|
||||
def get_role_key(self) -> str | None:
|
||||
return self.role_key
|
||||
|
||||
@NotBlank(field_name='role_sort', message='显示顺序不能为空')
|
||||
def get_role_sort(self) -> int | None:
|
||||
return self.role_sort
|
||||
|
||||
def validate_fields(self) -> None:
|
||||
self.get_role_name()
|
||||
self.get_role_key()
|
||||
self.get_role_sort()
|
||||
|
||||
|
||||
class RoleMenuModel(BaseModel):
|
||||
"""
|
||||
角色和菜单关联表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
role_id: int | None = Field(default=None, description='角色ID')
|
||||
menu_id: int | None = Field(default=None, description='菜单ID')
|
||||
|
||||
|
||||
class RoleDeptModel(BaseModel):
|
||||
"""
|
||||
角色和部门关联表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
role_id: int | None = Field(default=None, description='角色ID')
|
||||
dept_id: int | None = Field(default=None, description='部门ID')
|
||||
|
||||
|
||||
class RoleQueryModel(RoleModel):
|
||||
"""
|
||||
角色管理不分页查询模型
|
||||
"""
|
||||
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class RolePageQueryModel(RoleQueryModel):
|
||||
"""
|
||||
角色管理分页查询模型
|
||||
"""
|
||||
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
class RoleMenuQueryModel(BaseModel):
|
||||
"""
|
||||
角色菜单查询模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
menus: list = Field(default=[], description='菜单信息')
|
||||
checked_keys: list[int] = Field(default=[], description='已选择的菜单ID信息')
|
||||
|
||||
|
||||
class RoleDeptQueryModel(BaseModel):
|
||||
"""
|
||||
角色部门查询模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
depts: list = Field(default=[], description='部门信息')
|
||||
checked_keys: list[int] = Field(default=[], description='已选择的部门ID信息')
|
||||
|
||||
|
||||
class AddRoleModel(RoleModel):
|
||||
"""
|
||||
新增角色模型
|
||||
"""
|
||||
|
||||
dept_ids: list = Field(default=[], description='部门ID信息')
|
||||
menu_ids: list = Field(default=[], description='菜单ID信息')
|
||||
type: str | None = Field(default=None, description='操作类型')
|
||||
|
||||
|
||||
class DeleteRoleModel(BaseModel):
|
||||
"""
|
||||
删除角色模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
role_ids: str = Field(description='需要删除的角色ID')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: datetime | None = Field(default=None, description='更新时间')
|
||||
@@ -0,0 +1,66 @@
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
|
||||
|
||||
class CpuInfo(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
cpu_num: int | None = Field(default=None, description='核心数')
|
||||
used: float | None = Field(default=None, description='CPU用户使用率')
|
||||
sys: float | None = Field(default=None, description='CPU系统使用率')
|
||||
free: float | None = Field(default=None, description='CPU当前空闲率')
|
||||
|
||||
|
||||
class MemoryInfo(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
total: str | None = Field(default=None, description='内存总量')
|
||||
used: str | None = Field(default=None, description='已用内存')
|
||||
free: str | None = Field(default=None, description='剩余内存')
|
||||
usage: float | None = Field(default=None, description='使用率')
|
||||
|
||||
|
||||
class SysInfo(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
computer_ip: str | None = Field(default=None, description='服务器IP')
|
||||
computer_name: str | None = Field(default=None, description='服务器名称')
|
||||
os_arch: str | None = Field(default=None, description='系统架构')
|
||||
os_name: str | None = Field(default=None, description='操作系统')
|
||||
user_dir: str | None = Field(default=None, description='项目路径')
|
||||
|
||||
|
||||
class PyInfo(MemoryInfo):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
name: str | None = Field(default=None, description='Python名称')
|
||||
version: str | None = Field(default=None, description='Python版本')
|
||||
start_time: str | None = Field(default=None, description='启动时间')
|
||||
run_time: str | None = Field(default=None, description='运行时长')
|
||||
home: str | None = Field(default=None, description='安装路径')
|
||||
|
||||
|
||||
class SysFiles(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
dir_name: str | None = Field(default=None, description='盘符路径')
|
||||
sys_type_name: str | None = Field(default=None, description='盘符类型')
|
||||
type_name: str | None = Field(default=None, description='文件类型')
|
||||
total: str | None = Field(default=None, description='总大小')
|
||||
used: str | None = Field(default=None, description='已经使用量')
|
||||
free: str | None = Field(default=None, description='剩余大小')
|
||||
usage: str | None = Field(default=None, description='资源的使用率')
|
||||
|
||||
|
||||
class ServerMonitorModel(BaseModel):
|
||||
"""
|
||||
服务监控对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
cpu: CpuInfo | None = Field(description='CPU相关信息')
|
||||
py: PyInfo | None = Field(description='Python相关信息')
|
||||
mem: MemoryInfo | None = Field(description='內存相关信息')
|
||||
sys: SysInfo | None = Field(description='服务器相关信息')
|
||||
sys_files: list[SysFiles] | None = Field(description='磁盘相关信息')
|
||||
@@ -0,0 +1,99 @@
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
|
||||
|
||||
class TransportCryptoFrontendConfigModel(BaseModel):
|
||||
"""
|
||||
传输层加解密前端运行配置模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
transport_crypto_enabled: bool = Field(description='后端是否启用传输层加解密')
|
||||
transport_crypto_mode: str = Field(description='当前传输层加解密模式')
|
||||
transport_crypto_active: bool = Field(description='前端当前是否应启用传输层加解密')
|
||||
envelope_version: str = Field(description='当前传输层加密信封协议版本')
|
||||
public_key_url: str = Field(description='传输层公钥接口路径')
|
||||
request_envelope_algorithm: str = Field(description='前端请求信封算法标识')
|
||||
response_envelope_algorithm: str = Field(description='前端响应信封算法标识')
|
||||
enabled_paths: list[str] = Field(description='启用传输层加解密的路径列表')
|
||||
required_paths: list[str] = Field(description='强制要求加密传输的路径列表')
|
||||
exclude_paths: list[str] = Field(description='排除传输层加解密的路径列表')
|
||||
max_encrypted_get_url_length: int = Field(description='前端执行加密GET/DELETE请求时允许的最大URL长度')
|
||||
config_expire_at: int = Field(description='前端配置建议刷新时间戳')
|
||||
|
||||
|
||||
class TransportCryptoPublicKeyModel(BaseModel):
|
||||
"""
|
||||
传输层加解密公钥下发模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
kid: str = Field(description='当前启用的密钥版本标识')
|
||||
envelope_version: str = Field(description='当前传输层加密信封协议版本')
|
||||
alg: str = Field(description='当前传输层加密算法标识')
|
||||
public_key: str = Field(description='当前可用的传输层公钥')
|
||||
supported_kids: list[str] = Field(description='当前支持解密的密钥版本列表')
|
||||
expire_at: int = Field(description='当前公钥建议刷新时间戳')
|
||||
|
||||
|
||||
class TransportCryptoKidStatModel(BaseModel):
|
||||
"""
|
||||
传输层加解密按密钥版本统计模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
kid: str | None = Field(default=None, description='密钥版本标识')
|
||||
encrypted_requests: int | None = Field(default=0, description='加密请求次数')
|
||||
decrypt_success: int | None = Field(default=0, description='请求解密成功次数')
|
||||
decrypt_failure: int | None = Field(default=0, description='请求解密失败次数')
|
||||
encrypted_responses: int | None = Field(default=0, description='加密响应次数')
|
||||
|
||||
|
||||
class TransportCryptoFailureRecordModel(BaseModel):
|
||||
"""
|
||||
传输层加解密最近失败记录模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
time: datetime | None = Field(default=None, description='失败时间')
|
||||
method: str | None = Field(default=None, description='请求方法')
|
||||
path: str | None = Field(default=None, description='请求路径')
|
||||
reason: str | None = Field(default=None, description='失败原因分类')
|
||||
kid: str | None = Field(default=None, description='密钥版本标识')
|
||||
|
||||
|
||||
class TransportCryptoMonitorModel(BaseModel):
|
||||
"""
|
||||
传输层加解密监控模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
monitor_scope: str | None = Field(default=None, description='监控统计范围,默认基于Redis聚合')
|
||||
started_at: datetime | None = Field(default=None, description='当前监控统计起始时间')
|
||||
app_env: str | None = Field(default=None, description='当前应用环境')
|
||||
transport_crypto_enabled: bool | None = Field(default=None, description='是否启用传输层加解密')
|
||||
transport_crypto_mode: str | None = Field(default=None, description='当前传输层加解密模式')
|
||||
current_kid: str | None = Field(default=None, description='当前启用的密钥版本')
|
||||
supported_kids: list[str] | None = Field(default=[], description='当前支持的密钥版本列表')
|
||||
enabled_paths: list[str] | None = Field(default=[], description='启用传输层加解密的路径列表')
|
||||
required_paths: list[str] | None = Field(default=[], description='强制要求加密传输的路径列表')
|
||||
exclude_paths: list[str] | None = Field(default=[], description='排除传输层加解密的路径列表')
|
||||
requests_total: int | None = Field(default=0, description='命中传输层加解密规则的请求总数')
|
||||
plain_requests_total: int | None = Field(default=0, description='明文请求总数')
|
||||
encrypted_requests_total: int | None = Field(default=0, description='加密请求总数')
|
||||
required_rejected_total: int | None = Field(default=0, description='强制加密接口被拒绝的次数')
|
||||
decrypt_success_total: int | None = Field(default=0, description='请求解密成功次数')
|
||||
decrypt_failure_total: int | None = Field(default=0, description='请求解密失败次数')
|
||||
plain_responses_total: int | None = Field(default=0, description='明文响应次数')
|
||||
encrypted_responses_total: int | None = Field(default=0, description='加密响应次数')
|
||||
encrypted_error_responses_total: int | None = Field(default=0, description='加密错误响应次数')
|
||||
failure_reasons: dict[str, int] | None = Field(default={}, description='按失败原因归类的次数统计')
|
||||
kid_stats: list[TransportCryptoKidStatModel] | None = Field(default=[], description='按密钥版本归类的统计信息')
|
||||
recent_failures: list[TransportCryptoFailureRecordModel] | None = Field(default=[], description='最近失败事件列表')
|
||||
@@ -0,0 +1,299 @@
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
from pydantic.alias_generators import to_camel
|
||||
from pydantic_validation_decorator import Network, NotBlank, Size, Xss
|
||||
|
||||
from exceptions.exception import ModelValidatorException
|
||||
from module_admin.entity.vo.dept_vo import DeptModel
|
||||
from module_admin.entity.vo.post_vo import PostModel
|
||||
from module_admin.entity.vo.role_vo import RoleModel
|
||||
|
||||
|
||||
class TokenData(BaseModel):
|
||||
"""
|
||||
token解析结果
|
||||
"""
|
||||
|
||||
user_id: int | None = Field(default=None, description='用户ID')
|
||||
|
||||
|
||||
class UserModel(BaseModel):
|
||||
"""
|
||||
用户表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
user_id: int | None = Field(default=None, description='用户ID')
|
||||
dept_id: int | None = Field(default=None, description='部门ID')
|
||||
user_name: str | None = Field(default=None, description='用户账号')
|
||||
nick_name: str | None = Field(default=None, description='用户昵称')
|
||||
user_type: str | None = Field(default=None, description='用户类型(00系统用户)')
|
||||
email: str | None = Field(default=None, description='用户邮箱')
|
||||
phonenumber: str | None = Field(default=None, description='手机号码')
|
||||
sex: Literal['0', '1', '2'] | None = Field(default=None, description='用户性别(0男 1女 2未知)')
|
||||
avatar: str | None = Field(default=None, description='头像地址')
|
||||
password: str | None = Field(default=None, description='密码')
|
||||
status: Literal['0', '1'] | None = Field(default=None, description='帐号状态(0正常 1停用)')
|
||||
del_flag: Literal['0', '2'] | None = Field(default=None, description='删除标志(0代表存在 2代表删除)')
|
||||
login_ip: str | None = Field(default=None, description='最后登录IP')
|
||||
login_date: datetime | None = Field(default=None, description='最后登录时间')
|
||||
pwd_update_date: datetime | None = Field(default=None, description='密码最后更新时间')
|
||||
create_by: str | None = Field(default=None, description='创建者')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: datetime | None = Field(default=None, description='更新时间')
|
||||
remark: str | None = Field(default=None, description='备注')
|
||||
admin: bool | None = Field(default=False, description='是否为admin')
|
||||
|
||||
@model_validator(mode='after')
|
||||
def check_password(self) -> 'UserModel':
|
||||
pattern = r"""^[^<>"'|\\]+$"""
|
||||
if self.password is None or re.match(pattern, self.password):
|
||||
return self
|
||||
raise ModelValidatorException(message='密码不能包含非法字符:< > " \' \\ |')
|
||||
|
||||
@model_validator(mode='after')
|
||||
def check_admin(self) -> 'UserModel':
|
||||
if self.user_id == 1:
|
||||
self.admin = True
|
||||
else:
|
||||
self.admin = False
|
||||
return self
|
||||
|
||||
@Xss(field_name='user_name', message='用户账号不能包含脚本字符')
|
||||
@NotBlank(field_name='user_name', message='用户账号不能为空')
|
||||
@Size(field_name='user_name', min_length=0, max_length=30, message='用户账号长度不能超过30个字符')
|
||||
def get_user_name(self) -> str | None:
|
||||
return self.user_name
|
||||
|
||||
@Xss(field_name='nick_name', message='用户昵称不能包含脚本字符')
|
||||
@Size(field_name='nick_name', min_length=0, max_length=30, message='用户昵称长度不能超过30个字符')
|
||||
def get_nick_name(self) -> str | None:
|
||||
return self.nick_name
|
||||
|
||||
@Network(field_name='email', field_type='EmailStr', message='邮箱格式不正确')
|
||||
@Size(field_name='email', min_length=0, max_length=50, message='邮箱长度不能超过50个字符')
|
||||
def get_email(self) -> str | None:
|
||||
return self.email
|
||||
|
||||
@Size(field_name='phonenumber', min_length=0, max_length=11, message='手机号码长度不能超过11个字符')
|
||||
def get_phonenumber(self) -> str | None:
|
||||
return self.phonenumber
|
||||
|
||||
def validate_fields(self) -> None:
|
||||
self.get_user_name()
|
||||
self.get_nick_name()
|
||||
self.get_email()
|
||||
self.get_phonenumber()
|
||||
|
||||
|
||||
class UserRowModel(UserModel):
|
||||
"""
|
||||
用户列表行数据模型
|
||||
"""
|
||||
|
||||
dept: DeptModel | None = Field(default=None, description='部门信息')
|
||||
|
||||
|
||||
class UserRoleModel(BaseModel):
|
||||
"""
|
||||
用户和角色关联表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
user_id: int | None = Field(default=None, description='用户ID')
|
||||
role_id: int | None = Field(default=None, description='角色ID')
|
||||
|
||||
|
||||
class UserPostModel(BaseModel):
|
||||
"""
|
||||
用户与岗位关联表对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
|
||||
|
||||
user_id: int | None = Field(default=None, description='用户ID')
|
||||
post_id: int | None = Field(default=None, description='岗位ID')
|
||||
|
||||
|
||||
class UserInfoModel(UserModel):
|
||||
post_ids: str | None | None = Field(default=None, description='岗位ID信息')
|
||||
role_ids: str | None | None = Field(default=None, description='角色ID信息')
|
||||
dept: DeptModel | None | None = Field(default=None, description='部门信息')
|
||||
role: list[RoleModel | None] | None = Field(default=[], description='角色信息')
|
||||
|
||||
|
||||
class CurrentUserModel(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
permissions: list = Field(description='权限信息')
|
||||
roles: list = Field(description='角色信息')
|
||||
user: UserInfoModel | None = Field(description='用户信息')
|
||||
is_default_modify_pwd: bool = Field(default=False, description='是否初始密码修改提醒')
|
||||
is_password_expired: bool = Field(default=False, description='密码是否过期提醒')
|
||||
|
||||
|
||||
class UserDetailModel(BaseModel):
|
||||
"""
|
||||
获取用户详情信息响应模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
data: UserInfoModel | None | None = Field(default=None, description='用户信息')
|
||||
post_ids: list | None = Field(default=None, description='岗位ID信息')
|
||||
posts: list[PostModel | None] = Field(description='岗位信息')
|
||||
role_ids: list | None = Field(default=None, description='角色ID信息')
|
||||
roles: list[RoleModel | None] = Field(description='角色信息')
|
||||
|
||||
|
||||
class UserProfileModel(BaseModel):
|
||||
"""
|
||||
获取个人信息响应模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
data: UserInfoModel | None = Field(description='用户信息')
|
||||
post_group: str | None = Field(description='岗位信息')
|
||||
role_group: str | None = Field(description='角色信息')
|
||||
|
||||
|
||||
class AvatarModel(BaseModel):
|
||||
"""
|
||||
上传头像响应模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
img_url: str = Field(description='头像地址')
|
||||
|
||||
|
||||
class UserQueryModel(UserModel):
|
||||
"""
|
||||
用户管理不分页查询模型
|
||||
"""
|
||||
|
||||
begin_time: str | None = Field(default=None, description='开始时间')
|
||||
end_time: str | None = Field(default=None, description='结束时间')
|
||||
|
||||
|
||||
class UserPageQueryModel(UserQueryModel):
|
||||
"""
|
||||
用户管理分页查询模型
|
||||
"""
|
||||
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
class AddUserModel(UserModel):
|
||||
"""
|
||||
新增用户模型
|
||||
"""
|
||||
|
||||
role_ids: list | None = Field(default=[], description='角色ID信息')
|
||||
post_ids: list | None = Field(default=[], description='岗位ID信息')
|
||||
type: str | None = Field(default=None, description='操作类型')
|
||||
|
||||
|
||||
class EditUserModel(AddUserModel):
|
||||
"""
|
||||
编辑用户模型
|
||||
"""
|
||||
|
||||
role: list | None = Field(default=[], description='角色信息')
|
||||
|
||||
|
||||
class ResetPasswordModel(BaseModel):
|
||||
"""
|
||||
重置密码模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
old_password: str | None = Field(default=None, description='旧密码')
|
||||
new_password: str | None = Field(default=None, description='新密码')
|
||||
|
||||
@model_validator(mode='after')
|
||||
def check_new_password(self) -> 'ResetPasswordModel':
|
||||
pattern = r"""^[^<>"'|\\]+$"""
|
||||
if self.new_password is None or re.match(pattern, self.new_password):
|
||||
return self
|
||||
raise ModelValidatorException(message='密码不能包含非法字符:< > " \' \\ |')
|
||||
|
||||
|
||||
class ResetUserModel(UserModel):
|
||||
"""
|
||||
重置用户密码模型
|
||||
"""
|
||||
|
||||
old_password: str | None = Field(default=None, description='旧密码')
|
||||
sms_code: str | None = Field(default=None, description='验证码')
|
||||
session_id: str | None = Field(default=None, description='会话id')
|
||||
|
||||
|
||||
class DeleteUserModel(BaseModel):
|
||||
"""
|
||||
删除用户模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
user_ids: str = Field(description='需要删除的用户ID')
|
||||
update_by: str | None = Field(default=None, description='更新者')
|
||||
update_time: datetime | None = Field(default=None, description='更新时间')
|
||||
|
||||
|
||||
class UserRoleQueryModel(UserModel):
|
||||
"""
|
||||
用户角色关联管理不分页查询模型
|
||||
"""
|
||||
|
||||
role_id: int | None = Field(default=None, description='角色ID')
|
||||
|
||||
|
||||
class UserRolePageQueryModel(UserRoleQueryModel):
|
||||
"""
|
||||
用户角色关联管理分页查询模型
|
||||
"""
|
||||
|
||||
page_num: int = Field(default=1, description='当前页码')
|
||||
page_size: int = Field(default=10, description='每页记录数')
|
||||
|
||||
|
||||
class SelectedRoleModel(RoleModel):
|
||||
"""
|
||||
是否选择角色模型
|
||||
"""
|
||||
|
||||
flag: bool | None = Field(default=False, description='选择标识')
|
||||
|
||||
|
||||
class UserRoleResponseModel(BaseModel):
|
||||
"""
|
||||
用户角色关联管理列表返回模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
roles: list[SelectedRoleModel | None] = Field(default=[], description='角色信息')
|
||||
user: UserInfoModel = Field(description='用户信息')
|
||||
|
||||
|
||||
class CrudUserRoleModel(BaseModel):
|
||||
"""
|
||||
新增、删除用户关联角色及角色关联用户模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
user_id: int | None = Field(default=None, description='用户ID')
|
||||
user_ids: str | None = Field(default=None, description='用户ID信息')
|
||||
role_id: int | None = Field(default=None, description='角色ID')
|
||||
role_ids: str | None = Field(default=None, description='角色ID信息')
|
||||
@@ -0,0 +1,126 @@
|
||||
from fastapi import Request
|
||||
|
||||
from common.enums import RedisInitKeyConfig
|
||||
from common.vo import CrudResponseModel
|
||||
from config.get_redis import RedisUtil
|
||||
from module_admin.entity.vo.cache_vo import CacheInfoModel, CacheMonitorModel
|
||||
|
||||
|
||||
class CacheService:
|
||||
"""
|
||||
缓存监控模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_cache_monitor_statistical_info_services(cls, request: Request) -> CacheMonitorModel:
|
||||
"""
|
||||
获取缓存监控信息service
|
||||
|
||||
:param request: Request对象
|
||||
:return: 缓存监控信息
|
||||
"""
|
||||
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 = [
|
||||
{'name': key.split('_')[1], 'value': str(value.get('calls'))} for key, value in command_stats_dict.items()
|
||||
]
|
||||
result = CacheMonitorModel(commandStats=command_stats, dbSize=db_size, info=info)
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def get_cache_monitor_cache_name_services(cls) -> list[CacheInfoModel]:
|
||||
"""
|
||||
获取缓存名称列表信息service
|
||||
|
||||
:return: 缓存名称列表信息
|
||||
"""
|
||||
name_list = [
|
||||
CacheInfoModel(
|
||||
cacheKey='',
|
||||
cacheName=key_config.key,
|
||||
cacheValue='',
|
||||
remark=key_config.remark,
|
||||
)
|
||||
for key_config in RedisInitKeyConfig
|
||||
]
|
||||
|
||||
return name_list
|
||||
|
||||
@classmethod
|
||||
async def get_cache_monitor_cache_key_services(cls, request: Request, cache_name: str) -> list[str]:
|
||||
"""
|
||||
获取缓存键名列表信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param cache_name: 缓存名称
|
||||
:return: 缓存键名列表信息
|
||||
"""
|
||||
cache_keys: list[str] = await request.app.state.redis.keys(f'{cache_name}*')
|
||||
cache_key_list = [key.split(':', 1)[1] for key in cache_keys if key.startswith(f'{cache_name}:')]
|
||||
|
||||
return cache_key_list
|
||||
|
||||
@classmethod
|
||||
async def get_cache_monitor_cache_value_services(
|
||||
cls, request: Request, cache_name: str, cache_key: str
|
||||
) -> CacheInfoModel:
|
||||
"""
|
||||
获取缓存内容信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param cache_name: 缓存名称
|
||||
:param cache_key: 缓存键名
|
||||
:return: 缓存内容信息
|
||||
"""
|
||||
cache_value = await request.app.state.redis.get(f'{cache_name}:{cache_key}')
|
||||
|
||||
return CacheInfoModel(cacheKey=cache_key, cacheName=cache_name, cacheValue=cache_value, remark='')
|
||||
|
||||
@classmethod
|
||||
async def clear_cache_monitor_cache_name_services(cls, request: Request, cache_name: str) -> CrudResponseModel:
|
||||
"""
|
||||
清除缓存名称对应所有键值service
|
||||
|
||||
:param request: Request对象
|
||||
:param cache_name: 缓存名称
|
||||
:return: 操作缓存响应信息
|
||||
"""
|
||||
cache_keys = await request.app.state.redis.keys(f'{cache_name}*')
|
||||
if cache_keys:
|
||||
await request.app.state.redis.delete(*cache_keys)
|
||||
|
||||
return CrudResponseModel(is_success=True, message=f'{cache_name}对应键值清除成功')
|
||||
|
||||
@classmethod
|
||||
async def clear_cache_monitor_cache_key_services(cls, request: Request, cache_key: str) -> CrudResponseModel:
|
||||
"""
|
||||
清除缓存名称对应所有键值service
|
||||
|
||||
:param request: Request对象
|
||||
:param cache_key: 缓存键名
|
||||
:return: 操作缓存响应信息
|
||||
"""
|
||||
cache_keys = await request.app.state.redis.keys(f'*{cache_key}')
|
||||
if cache_keys:
|
||||
await request.app.state.redis.delete(*cache_keys)
|
||||
|
||||
return CrudResponseModel(is_success=True, message=f'{cache_key}清除成功')
|
||||
|
||||
@classmethod
|
||||
async def clear_cache_monitor_all_services(cls, request: Request) -> CrudResponseModel:
|
||||
"""
|
||||
清除所有缓存service
|
||||
|
||||
:param request: Request对象
|
||||
:return: 操作缓存响应信息
|
||||
"""
|
||||
cache_keys = await request.app.state.redis.keys()
|
||||
if cache_keys:
|
||||
await request.app.state.redis.delete(*cache_keys)
|
||||
|
||||
await RedisUtil.init_sys_dict(request.app.state.redis)
|
||||
await RedisUtil.init_sys_config(request.app.state.redis)
|
||||
|
||||
return CrudResponseModel(is_success=True, message='所有缓存清除成功')
|
||||
@@ -0,0 +1,54 @@
|
||||
import base64
|
||||
import io
|
||||
import random
|
||||
|
||||
import anyio
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
|
||||
class CaptchaService:
|
||||
"""
|
||||
验证码模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def create_captcha_image_service(cls) -> list[str, int]:
|
||||
# 创建空白图像
|
||||
image = Image.new('RGB', (160, 60), color='#EAEAEA')
|
||||
|
||||
# 创建绘图对象
|
||||
draw = ImageDraw.Draw(image)
|
||||
|
||||
# 设置字体
|
||||
font_path = (await anyio.Path.cwd()) / 'assets' / 'font' / 'Arial.ttf'
|
||||
font = ImageFont.truetype(font_path, size=30)
|
||||
|
||||
# 生成两个0-9之间的随机整数
|
||||
num1 = random.randint(0, 9)
|
||||
num2 = random.randint(0, 9)
|
||||
# 从运算符列表中随机选择一个
|
||||
operational_character_list = ['+', '-', '*']
|
||||
operational_character = random.choice(operational_character_list)
|
||||
# 根据选择的运算符进行计算
|
||||
if operational_character == '+':
|
||||
result = num1 + num2
|
||||
elif operational_character == '-':
|
||||
result = num1 - num2
|
||||
# 非负数结果的验证码更友好一些
|
||||
if result < 0:
|
||||
num1, num2 = num2, num1
|
||||
result = num1 - num2
|
||||
else:
|
||||
result = num1 * num2
|
||||
# 绘制文本
|
||||
text = f'{num1} {operational_character} {num2} = ?'
|
||||
draw.text((25, 15), text, fill='blue', font=font)
|
||||
|
||||
# 将图像数据保存到内存中
|
||||
buffer = io.BytesIO()
|
||||
image.save(buffer, format='PNG')
|
||||
|
||||
# 将图像数据转换为base64字符串
|
||||
base64_string = base64.b64encode(buffer.getvalue()).decode()
|
||||
|
||||
return [base64_string, result]
|
||||
@@ -0,0 +1,99 @@
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
import aiofiles
|
||||
from fastapi import BackgroundTasks, Request, UploadFile
|
||||
|
||||
from common.vo import CrudResponseModel
|
||||
from config.env import UploadConfig
|
||||
from exceptions.exception import ServiceException
|
||||
from module_admin.entity.vo.common_vo import UploadResponseModel
|
||||
from utils.upload_util import UploadUtil
|
||||
|
||||
|
||||
class CommonService:
|
||||
"""
|
||||
通用模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def upload_service(cls, request: Request, file: UploadFile) -> CrudResponseModel:
|
||||
"""
|
||||
通用上传service
|
||||
|
||||
:param request: Request对象
|
||||
:param file: 上传文件对象
|
||||
:return: 上传结果
|
||||
"""
|
||||
if not UploadUtil.check_file_extension(file):
|
||||
raise ServiceException(message='文件类型不合法')
|
||||
relative_path = (
|
||||
f'upload/{datetime.now().strftime("%Y")}/{datetime.now().strftime("%m")}/{datetime.now().strftime("%d")}'
|
||||
)
|
||||
dir_path = os.path.join(UploadConfig.UPLOAD_PATH, relative_path)
|
||||
try:
|
||||
os.makedirs(dir_path)
|
||||
except FileExistsError:
|
||||
pass
|
||||
filename = f'{file.filename.rsplit(".", 1)[0]}_{datetime.now().strftime("%Y%m%d%H%M%S")}{UploadConfig.UPLOAD_MACHINE}{UploadUtil.generate_random_number()}.{file.filename.rsplit(".")[-1]}'
|
||||
filepath = os.path.join(dir_path, filename)
|
||||
async with aiofiles.open(filepath, 'wb') as f:
|
||||
# 流式写出大型文件,这里的10代表10MB
|
||||
while True:
|
||||
chunk = await file.read(1024 * 1024 * 10)
|
||||
if not chunk:
|
||||
break
|
||||
await f.write(chunk)
|
||||
|
||||
return CrudResponseModel(
|
||||
is_success=True,
|
||||
result=UploadResponseModel(
|
||||
fileName=f'{UploadConfig.UPLOAD_PREFIX}/{relative_path}/{filename}',
|
||||
newFileName=filename,
|
||||
originalFilename=file.filename,
|
||||
url=f'{request.base_url}{UploadConfig.UPLOAD_PREFIX[1:]}/{relative_path}/{filename}',
|
||||
),
|
||||
message='上传成功',
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def download_services(
|
||||
cls, background_tasks: BackgroundTasks, file_name: str, delete: bool
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
下载下载目录文件service
|
||||
|
||||
:param background_tasks: 后台任务对象
|
||||
:param file_name: 下载的文件名称
|
||||
:param delete: 是否在下载完成后删除文件
|
||||
:return: 上传结果
|
||||
"""
|
||||
filepath = os.path.join(UploadConfig.DOWNLOAD_PATH, file_name)
|
||||
if '..' in file_name:
|
||||
raise ServiceException(message='文件名称不合法')
|
||||
if not UploadUtil.check_file_exists(filepath):
|
||||
raise ServiceException(message='文件不存在')
|
||||
if delete:
|
||||
background_tasks.add_task(UploadUtil.delete_file, filepath)
|
||||
return CrudResponseModel(is_success=True, result=UploadUtil.generate_file(filepath), message='下载成功')
|
||||
|
||||
@classmethod
|
||||
async def download_resource_services(cls, resource: str) -> CrudResponseModel:
|
||||
"""
|
||||
下载上传目录文件service
|
||||
|
||||
:param resource: 下载的文件名称
|
||||
:return: 上传结果
|
||||
"""
|
||||
filepath = os.path.join(resource.replace(UploadConfig.UPLOAD_PREFIX, UploadConfig.UPLOAD_PATH))
|
||||
filename = resource.rsplit('/', 1)[-1]
|
||||
if (
|
||||
'..' in filename
|
||||
or not UploadUtil.check_file_timestamp(filename)
|
||||
or not UploadUtil.check_file_machine(filename)
|
||||
or not UploadUtil.check_file_random_code(filename)
|
||||
):
|
||||
raise ServiceException(message='文件名称不合法')
|
||||
if not UploadUtil.check_file_exists(filepath):
|
||||
raise ServiceException(message='文件不存在')
|
||||
return CrudResponseModel(is_success=True, result=UploadUtil.generate_file(filepath), message='下载成功')
|
||||
@@ -0,0 +1,234 @@
|
||||
from typing import Any
|
||||
|
||||
from fastapi import Request
|
||||
from redis import asyncio as aioredis
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.constant import CommonConstant
|
||||
from common.enums import RedisInitKeyConfig
|
||||
from common.vo import CrudResponseModel, PageModel
|
||||
from exceptions.exception import ServiceException
|
||||
from module_admin.dao.config_dao import ConfigDao
|
||||
from module_admin.entity.vo.config_vo import ConfigModel, ConfigPageQueryModel, DeleteConfigModel
|
||||
from utils.common_util import CamelCaseUtil
|
||||
from utils.excel_util import ExcelUtil
|
||||
|
||||
|
||||
class ConfigService:
|
||||
"""
|
||||
参数配置管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_config_list_services(
|
||||
cls, query_db: AsyncSession, query_object: ConfigPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
获取参数配置列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 参数配置列表信息对象
|
||||
"""
|
||||
config_list_result = await ConfigDao.get_config_list(query_db, query_object, is_page)
|
||||
|
||||
return config_list_result
|
||||
|
||||
@classmethod
|
||||
async def init_cache_sys_config_services(cls, query_db: AsyncSession, redis: aioredis.Redis) -> None:
|
||||
"""
|
||||
应用初始化:获取所有参数配置对应的键值对信息并缓存service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param redis: redis对象
|
||||
:return:
|
||||
"""
|
||||
# 获取以sys_config:开头的键列表
|
||||
keys = await redis.keys(f'{RedisInitKeyConfig.SYS_CONFIG.key}:*')
|
||||
# 删除匹配的键
|
||||
if keys:
|
||||
await redis.delete(*keys)
|
||||
config_all = await ConfigDao.get_config_list(query_db, ConfigPageQueryModel(), is_page=False)
|
||||
for config_obj in config_all:
|
||||
await redis.set(
|
||||
f'{RedisInitKeyConfig.SYS_CONFIG.key}:{config_obj.get("configKey")}',
|
||||
config_obj.get('configValue'),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def query_config_list_from_cache_services(cls, redis: aioredis.Redis, config_key: str) -> Any:
|
||||
"""
|
||||
从缓存获取参数键名对应值service
|
||||
|
||||
:param redis: redis对象
|
||||
:param config_key: 参数键名
|
||||
:return: 参数键名对应值
|
||||
"""
|
||||
result = await redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:{config_key}')
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def check_config_key_unique_services(cls, query_db: AsyncSession, page_object: ConfigModel) -> bool:
|
||||
"""
|
||||
校验参数键名是否唯一service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 参数配置对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
config_id = -1 if page_object.config_id is None else page_object.config_id
|
||||
config = await ConfigDao.get_config_detail_by_info(query_db, ConfigModel(configKey=page_object.config_key))
|
||||
if config and config.config_id != config_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def add_config_services(
|
||||
cls, request: Request, query_db: AsyncSession, page_object: ConfigModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
新增参数配置信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增参数配置对象
|
||||
:return: 新增参数配置校验结果
|
||||
"""
|
||||
if not await cls.check_config_key_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增参数{page_object.config_name}失败,参数键名已存在')
|
||||
try:
|
||||
await ConfigDao.add_config_dao(query_db, page_object)
|
||||
await query_db.commit()
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.SYS_CONFIG.key}:{page_object.config_key}', page_object.config_value
|
||||
)
|
||||
return CrudResponseModel(is_success=True, message='新增成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def edit_config_services(
|
||||
cls, request: Request, query_db: AsyncSession, page_object: ConfigModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
编辑参数配置信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param page_object: 编辑参数配置对象
|
||||
:return: 编辑参数配置校验结果
|
||||
"""
|
||||
edit_config = page_object.model_dump(exclude_unset=True)
|
||||
config_info = await cls.config_detail_services(query_db, page_object.config_id)
|
||||
if config_info.config_id:
|
||||
if not await cls.check_config_key_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改参数{page_object.config_name}失败,参数键名已存在')
|
||||
try:
|
||||
await ConfigDao.edit_config_dao(query_db, edit_config)
|
||||
await query_db.commit()
|
||||
if config_info.config_key != page_object.config_key:
|
||||
await request.app.state.redis.delete(
|
||||
f'{RedisInitKeyConfig.SYS_CONFIG.key}:{config_info.config_key}'
|
||||
)
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.SYS_CONFIG.key}:{page_object.config_key}', page_object.config_value
|
||||
)
|
||||
return CrudResponseModel(is_success=True, message='更新成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='参数配置不存在')
|
||||
|
||||
@classmethod
|
||||
async def delete_config_services(
|
||||
cls, request: Request, query_db: AsyncSession, page_object: DeleteConfigModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
删除参数配置信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除参数配置对象
|
||||
:return: 删除参数配置校验结果
|
||||
"""
|
||||
if page_object.config_ids:
|
||||
config_id_list = page_object.config_ids.split(',')
|
||||
try:
|
||||
delete_config_key_list = []
|
||||
for config_id in config_id_list:
|
||||
config_info = await cls.config_detail_services(query_db, int(config_id))
|
||||
if config_info.config_type == CommonConstant.YES:
|
||||
raise ServiceException(message=f'内置参数{config_info.config_key}不能删除')
|
||||
await ConfigDao.delete_config_dao(query_db, ConfigModel(configId=int(config_id)))
|
||||
delete_config_key_list.append(f'{RedisInitKeyConfig.SYS_CONFIG.key}:{config_info.config_key}')
|
||||
await query_db.commit()
|
||||
if delete_config_key_list:
|
||||
await request.app.state.redis.delete(*delete_config_key_list)
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='传入参数配置id为空')
|
||||
|
||||
@classmethod
|
||||
async def config_detail_services(cls, query_db: AsyncSession, config_id: int) -> ConfigModel:
|
||||
"""
|
||||
获取参数配置详细信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param config_id: 参数配置id
|
||||
:return: 参数配置id对应的信息
|
||||
"""
|
||||
config = await ConfigDao.get_config_detail_by_id(query_db, config_id=config_id)
|
||||
result = ConfigModel(**CamelCaseUtil.transform_result(config)) if config else ConfigModel()
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
async def export_config_list_services(config_list: list) -> bytes:
|
||||
"""
|
||||
导出参数配置信息service
|
||||
|
||||
:param config_list: 参数配置信息列表
|
||||
:return: 参数配置信息对应excel的二进制数据
|
||||
"""
|
||||
# 创建一个映射字典,将英文键映射到中文键
|
||||
mapping_dict = {
|
||||
'configId': '参数主键',
|
||||
'configName': '参数名称',
|
||||
'configKey': '参数键名',
|
||||
'configValue': '参数键值',
|
||||
'configType': '系统内置',
|
||||
'createBy': '创建者',
|
||||
'createTime': '创建时间',
|
||||
'updateBy': '更新者',
|
||||
'updateTime': '更新时间',
|
||||
'remark': '备注',
|
||||
}
|
||||
|
||||
for item in config_list:
|
||||
if item.get('configType') == 'Y':
|
||||
item['configType'] = '是'
|
||||
else:
|
||||
item['configType'] = '否'
|
||||
binary_data = ExcelUtil.export_list2excel(config_list, mapping_dict)
|
||||
|
||||
return binary_data
|
||||
|
||||
@classmethod
|
||||
async def refresh_sys_config_services(cls, request: Request, query_db: AsyncSession) -> CrudResponseModel:
|
||||
"""
|
||||
刷新字典缓存信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:return: 刷新字典缓存校验结果
|
||||
"""
|
||||
await cls.init_cache_sys_config_services(query_db, request.app.state.redis)
|
||||
|
||||
return CrudResponseModel(is_success=True, message='刷新成功')
|
||||
@@ -0,0 +1,285 @@
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import ColumnElement
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.constant import CommonConstant
|
||||
from common.vo import CrudResponseModel
|
||||
from exceptions.exception import ServiceException, ServiceWarning
|
||||
from module_admin.dao.dept_dao import DeptDao
|
||||
from module_admin.entity.do.dept_do import SysDept
|
||||
from module_admin.entity.vo.dept_vo import DeleteDeptModel, DeptModel, DeptTreeModel
|
||||
from utils.common_util import CamelCaseUtil
|
||||
|
||||
|
||||
class DeptService:
|
||||
"""
|
||||
部门管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_dept_tree_services(
|
||||
cls, query_db: AsyncSession, page_object: DeptModel, data_scope_sql: ColumnElement
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
获取部门树信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 查询参数对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:return: 部门树信息对象
|
||||
"""
|
||||
dept_list_result = await DeptDao.get_dept_list_for_tree(query_db, page_object, data_scope_sql)
|
||||
dept_tree_model_result = cls.list_to_tree(dept_list_result)
|
||||
dept_tree_result = [dept.model_dump(exclude_unset=True, by_alias=True) for dept in dept_tree_model_result]
|
||||
|
||||
return dept_tree_result
|
||||
|
||||
@classmethod
|
||||
async def get_dept_for_edit_option_services(
|
||||
cls, query_db: AsyncSession, page_object: DeptModel, data_scope_sql: ColumnElement
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
获取部门编辑部门树信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 查询参数对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:return: 部门树信息对象
|
||||
"""
|
||||
dept_list_result = await DeptDao.get_dept_info_for_edit_option(query_db, page_object, data_scope_sql)
|
||||
|
||||
return CamelCaseUtil.transform_result(dept_list_result)
|
||||
|
||||
@classmethod
|
||||
async def get_dept_list_services(
|
||||
cls, query_db: AsyncSession, page_object: DeptModel, data_scope_sql: ColumnElement
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
获取部门列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 分页查询参数对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:return: 部门列表信息对象
|
||||
"""
|
||||
dept_list_result = await DeptDao.get_dept_list(query_db, page_object, data_scope_sql)
|
||||
|
||||
return CamelCaseUtil.transform_result(dept_list_result)
|
||||
|
||||
@classmethod
|
||||
async def check_dept_data_scope_services(
|
||||
cls, query_db: AsyncSession, dept_id: int, data_scope_sql: ColumnElement
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
校验部门是否有数据权限service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param dept_id: 部门id
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:return: 校验结果
|
||||
"""
|
||||
depts = await DeptDao.get_dept_list(query_db, DeptModel(deptId=dept_id), data_scope_sql)
|
||||
if depts:
|
||||
return CrudResponseModel(is_success=True, message='校验通过')
|
||||
raise ServiceException(message='没有权限访问部门数据')
|
||||
|
||||
@classmethod
|
||||
async def check_dept_name_unique_services(cls, query_db: AsyncSession, page_object: DeptModel) -> bool:
|
||||
"""
|
||||
校验部门名称是否唯一service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 部门对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
dept_id = -1 if page_object.dept_id is None else page_object.dept_id
|
||||
dept = await DeptDao.get_dept_detail_by_info(
|
||||
query_db, DeptModel(deptName=page_object.dept_name, parentId=page_object.parent_id)
|
||||
)
|
||||
if dept and dept.dept_id != dept_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def add_dept_services(cls, query_db: AsyncSession, page_object: DeptModel) -> CrudResponseModel:
|
||||
"""
|
||||
新增部门信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增部门对象
|
||||
:return: 新增部门校验结果
|
||||
"""
|
||||
if not await cls.check_dept_name_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增部门{page_object.dept_name}失败,部门名称已存在')
|
||||
parent_info = await DeptDao.get_dept_by_id(query_db, page_object.parent_id)
|
||||
if parent_info.status != CommonConstant.DEPT_NORMAL:
|
||||
raise ServiceException(message=f'部门{parent_info.dept_name}停用,不允许新增')
|
||||
page_object.ancestors = f'{parent_info.ancestors},{page_object.parent_id}'
|
||||
try:
|
||||
await DeptDao.add_dept_dao(query_db, page_object)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='新增成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def edit_dept_services(cls, query_db: AsyncSession, page_object: DeptModel) -> CrudResponseModel:
|
||||
"""
|
||||
编辑部门信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 编辑部门对象
|
||||
:return: 编辑部门校验结果
|
||||
"""
|
||||
if not await cls.check_dept_name_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改部门{page_object.dept_name}失败,部门名称已存在')
|
||||
if page_object.dept_id == page_object.parent_id:
|
||||
raise ServiceException(message=f'修改部门{page_object.dept_name}失败,上级部门不能是自己')
|
||||
if (
|
||||
page_object.status == CommonConstant.DEPT_DISABLE
|
||||
and (await DeptDao.count_normal_children_dept_dao(query_db, page_object.dept_id)) > 0
|
||||
):
|
||||
raise ServiceException(message=f'修改部门{page_object.dept_name}失败,该部门包含未停用的子部门')
|
||||
new_parent_dept = await DeptDao.get_dept_by_id(query_db, page_object.parent_id)
|
||||
old_dept = await DeptDao.get_dept_by_id(query_db, page_object.dept_id)
|
||||
try:
|
||||
if new_parent_dept and old_dept:
|
||||
new_ancestors = f'{new_parent_dept.ancestors},{new_parent_dept.dept_id}'
|
||||
old_ancestors = old_dept.ancestors
|
||||
page_object.ancestors = new_ancestors
|
||||
await cls.update_dept_children(query_db, page_object.dept_id, new_ancestors, old_ancestors)
|
||||
edit_dept = page_object.model_dump(exclude_unset=True)
|
||||
await DeptDao.edit_dept_dao(query_db, edit_dept)
|
||||
if (
|
||||
page_object.status == CommonConstant.DEPT_NORMAL
|
||||
and page_object.ancestors
|
||||
and page_object.ancestors != 0
|
||||
):
|
||||
await cls.update_parent_dept_status_normal(query_db, page_object)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='更新成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def delete_dept_services(cls, query_db: AsyncSession, page_object: DeleteDeptModel) -> CrudResponseModel:
|
||||
"""
|
||||
删除部门信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除部门对象
|
||||
:return: 删除部门校验结果
|
||||
"""
|
||||
if page_object.dept_ids:
|
||||
dept_id_list = page_object.dept_ids.split(',')
|
||||
try:
|
||||
for dept_id in dept_id_list:
|
||||
if (await DeptDao.count_children_dept_dao(query_db, int(dept_id))) > 0:
|
||||
raise ServiceWarning(message='存在下级部门,不允许删除')
|
||||
if (await DeptDao.count_dept_user_dao(query_db, int(dept_id))) > 0:
|
||||
raise ServiceWarning(message='部门存在用户,不允许删除')
|
||||
|
||||
await DeptDao.delete_dept_dao(query_db, DeptModel(deptId=dept_id))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='传入部门id为空')
|
||||
|
||||
@classmethod
|
||||
async def dept_detail_services(cls, query_db: AsyncSession, dept_id: int) -> DeptModel:
|
||||
"""
|
||||
获取部门详细信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param dept_id: 部门id
|
||||
:return: 部门id对应的信息
|
||||
"""
|
||||
dept = await DeptDao.get_dept_detail_by_id(query_db, dept_id=dept_id)
|
||||
result = DeptModel(**CamelCaseUtil.transform_result(dept)) if dept else DeptModel()
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def list_to_tree(cls, permission_list: Sequence[SysDept]) -> list[DeptTreeModel]:
|
||||
"""
|
||||
工具方法:根据部门列表信息生成树形嵌套数据
|
||||
|
||||
:param permission_list: 部门列表信息
|
||||
:return: 部门树形嵌套数据
|
||||
"""
|
||||
_permission_list = [
|
||||
DeptTreeModel(id=item.dept_id, label=item.dept_name, parentId=item.parent_id) for item in permission_list
|
||||
]
|
||||
# 转成id为key的字典
|
||||
mapping: dict[int, DeptTreeModel] = dict(zip([i.id for i in _permission_list], _permission_list, strict=False))
|
||||
|
||||
# 树容器
|
||||
container: list[DeptTreeModel] = []
|
||||
|
||||
for d in _permission_list:
|
||||
# 如果找不到父级项,则是根节点
|
||||
parent = mapping.get(d.parent_id)
|
||||
if parent is None:
|
||||
container.append(d)
|
||||
else:
|
||||
children: list[DeptTreeModel] = parent.children
|
||||
if not children:
|
||||
children = []
|
||||
children.append(d)
|
||||
parent.children = children
|
||||
|
||||
return container
|
||||
|
||||
@classmethod
|
||||
async def replace_first(cls, original_str: str, old_str: str, new_str: str) -> str:
|
||||
"""
|
||||
工具方法:替换字符串
|
||||
|
||||
:param original_str: 需要替换的原始字符串
|
||||
:param old_str: 用于匹配的字符串
|
||||
:param new_str: 替换的字符串
|
||||
:return: 替换后的字符串
|
||||
"""
|
||||
if original_str.startswith(old_str):
|
||||
return original_str.replace(old_str, new_str, 1)
|
||||
return original_str
|
||||
|
||||
@classmethod
|
||||
async def update_parent_dept_status_normal(cls, query_db: AsyncSession, dept: DeptModel) -> None:
|
||||
"""
|
||||
更新父部门状态为正常
|
||||
|
||||
:param query_db: orm对象
|
||||
:param dept: 部门对象
|
||||
:return:
|
||||
"""
|
||||
dept_id_list = dept.ancestors.split(',')
|
||||
await DeptDao.update_dept_status_normal_dao(query_db, list(map(int, dept_id_list)))
|
||||
|
||||
@classmethod
|
||||
async def update_dept_children(
|
||||
cls, query_db: AsyncSession, dept_id: int, new_ancestors: str, old_ancestors: str
|
||||
) -> None:
|
||||
"""
|
||||
更新子部门信息
|
||||
|
||||
:param query_db: orm对象
|
||||
:param dept_id: 部门id
|
||||
:param new_ancestors: 新的祖先
|
||||
:param old_ancestors: 旧的祖先
|
||||
:return:
|
||||
"""
|
||||
children = await DeptDao.get_children_dept_dao(query_db, dept_id)
|
||||
update_children = []
|
||||
for child in children:
|
||||
child_ancestors = await cls.replace_first(child.ancestors, old_ancestors, new_ancestors)
|
||||
update_children.append({'dept_id': child.dept_id, 'ancestors': child_ancestors})
|
||||
if children:
|
||||
await DeptDao.update_dept_children_dao(query_db, update_children)
|
||||
@@ -0,0 +1,464 @@
|
||||
import json
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from fastapi import Request
|
||||
from redis import asyncio as aioredis
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.constant import CommonConstant
|
||||
from common.enums import RedisInitKeyConfig
|
||||
from common.vo import CrudResponseModel, PageModel
|
||||
from exceptions.exception import ServiceException
|
||||
from module_admin.dao.dict_dao import DictDataDao, DictTypeDao
|
||||
from module_admin.entity.do.dict_do import SysDictData
|
||||
from module_admin.entity.vo.dict_vo import (
|
||||
DeleteDictDataModel,
|
||||
DeleteDictTypeModel,
|
||||
DictDataModel,
|
||||
DictDataPageQueryModel,
|
||||
DictTypeModel,
|
||||
DictTypePageQueryModel,
|
||||
)
|
||||
from utils.common_util import CamelCaseUtil
|
||||
from utils.excel_util import ExcelUtil
|
||||
|
||||
|
||||
class DictTypeService:
|
||||
"""
|
||||
字典类型管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_dict_type_list_services(
|
||||
cls, query_db: AsyncSession, query_object: DictTypePageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
获取字典类型列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 字典类型列表信息对象
|
||||
"""
|
||||
dict_type_list_result = await DictTypeDao.get_dict_type_list(query_db, query_object, is_page)
|
||||
|
||||
return dict_type_list_result
|
||||
|
||||
@classmethod
|
||||
async def check_dict_type_unique_services(cls, query_db: AsyncSession, page_object: DictTypeModel) -> bool:
|
||||
"""
|
||||
校验字典类型称是否唯一service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 字典类型对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
dict_id = -1 if page_object.dict_id is None else page_object.dict_id
|
||||
dict_type = await DictTypeDao.get_dict_type_detail_by_info(
|
||||
query_db, DictTypeModel(dictType=page_object.dict_type)
|
||||
)
|
||||
if dict_type and dict_type.dict_id != dict_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def add_dict_type_services(
|
||||
cls, request: Request, query_db: AsyncSession, page_object: DictTypeModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
新增字典类型信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增岗位对象
|
||||
:return: 新增字典类型校验结果
|
||||
"""
|
||||
if not await cls.check_dict_type_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增字典{page_object.dict_name}失败,字典类型已存在')
|
||||
try:
|
||||
await DictTypeDao.add_dict_type_dao(query_db, page_object)
|
||||
await query_db.commit()
|
||||
await request.app.state.redis.set(f'{RedisInitKeyConfig.SYS_DICT.key}:{page_object.dict_type}', '')
|
||||
result = {'is_success': True, 'message': '新增成功'}
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
return CrudResponseModel(**result)
|
||||
|
||||
@classmethod
|
||||
async def edit_dict_type_services(
|
||||
cls, request: Request, query_db: AsyncSession, page_object: DictTypeModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
编辑字典类型信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param page_object: 编辑字典类型对象
|
||||
:return: 编辑字典类型校验结果
|
||||
"""
|
||||
edit_dict_type = page_object.model_dump(exclude_unset=True)
|
||||
dict_type_info = await cls.dict_type_detail_services(query_db, page_object.dict_id)
|
||||
if dict_type_info.dict_id:
|
||||
if not await cls.check_dict_type_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改字典{page_object.dict_name}失败,字典类型已存在')
|
||||
try:
|
||||
query_dict_data = DictDataPageQueryModel(dictType=dict_type_info.dict_type)
|
||||
dict_data_list = await DictDataDao.get_dict_data_list(query_db, query_dict_data, is_page=False)
|
||||
if dict_type_info.dict_type != page_object.dict_type:
|
||||
for dict_data in dict_data_list:
|
||||
edit_dict_data = DictDataModel(
|
||||
dictCode=dict_data.get('dict_code'),
|
||||
dictType=page_object.dict_type,
|
||||
updateBy=page_object.update_by,
|
||||
updateTime=page_object.update_time,
|
||||
).model_dump(exclude_unset=True)
|
||||
await DictDataDao.edit_dict_data_dao(query_db, edit_dict_data)
|
||||
await DictTypeDao.edit_dict_type_dao(query_db, edit_dict_type)
|
||||
await query_db.commit()
|
||||
if dict_type_info.dict_type != page_object.dict_type:
|
||||
dict_data = [CamelCaseUtil.transform_result(row) for row in dict_data_list if row]
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.SYS_DICT.key}:{page_object.dict_type}',
|
||||
json.dumps(dict_data, ensure_ascii=False, default=str),
|
||||
)
|
||||
return CrudResponseModel(is_success=True, message='更新成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='字典类型不存在')
|
||||
|
||||
@classmethod
|
||||
async def delete_dict_type_services(
|
||||
cls, request: Request, query_db: AsyncSession, page_object: DeleteDictTypeModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
删除字典类型信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除字典类型对象
|
||||
:return: 删除字典类型校验结果
|
||||
"""
|
||||
if page_object.dict_ids:
|
||||
dict_id_list = page_object.dict_ids.split(',')
|
||||
try:
|
||||
delete_dict_type_list = []
|
||||
for dict_id in dict_id_list:
|
||||
dict_type_into = await cls.dict_type_detail_services(query_db, int(dict_id))
|
||||
if (await DictDataDao.count_dict_data_dao(query_db, dict_type_into.dict_type)) > 0:
|
||||
raise ServiceException(message=f'{dict_type_into.dict_name}已分配,不能删除')
|
||||
await DictTypeDao.delete_dict_type_dao(query_db, DictTypeModel(dictId=int(dict_id)))
|
||||
delete_dict_type_list.append(f'{RedisInitKeyConfig.SYS_DICT.key}:{dict_type_into.dict_type}')
|
||||
await query_db.commit()
|
||||
if delete_dict_type_list:
|
||||
await request.app.state.redis.delete(*delete_dict_type_list)
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='传入字典类型id为空')
|
||||
|
||||
@classmethod
|
||||
async def dict_type_detail_services(cls, query_db: AsyncSession, dict_id: int) -> DictTypeModel:
|
||||
"""
|
||||
获取字典类型详细信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param dict_id: 字典类型id
|
||||
:return: 字典类型id对应的信息
|
||||
"""
|
||||
dict_type = await DictTypeDao.get_dict_type_detail_by_id(query_db, dict_id=dict_id)
|
||||
result = DictTypeModel(**CamelCaseUtil.transform_result(dict_type)) if dict_type else DictTypeModel()
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
async def export_dict_type_list_services(dict_type_list: list) -> bytes:
|
||||
"""
|
||||
导出字典类型信息service
|
||||
|
||||
:param dict_type_list: 字典信息列表
|
||||
:return: 字典信息对应excel的二进制数据
|
||||
"""
|
||||
# 创建一个映射字典,将英文键映射到中文键
|
||||
mapping_dict = {
|
||||
'dictId': '字典编号',
|
||||
'dictName': '字典名称',
|
||||
'dictType': '字典类型',
|
||||
'status': '状态',
|
||||
'createBy': '创建者',
|
||||
'createTime': '创建时间',
|
||||
'updateBy': '更新者',
|
||||
'updateTime': '更新时间',
|
||||
'remark': '备注',
|
||||
}
|
||||
|
||||
for item in dict_type_list:
|
||||
if item.get('status') == '0':
|
||||
item['status'] = '正常'
|
||||
else:
|
||||
item['status'] = '停用'
|
||||
binary_data = ExcelUtil.export_list2excel(dict_type_list, mapping_dict)
|
||||
|
||||
return binary_data
|
||||
|
||||
@classmethod
|
||||
async def refresh_sys_dict_services(cls, request: Request, query_db: AsyncSession) -> CrudResponseModel:
|
||||
"""
|
||||
刷新字典缓存信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:return: 刷新字典缓存校验结果
|
||||
"""
|
||||
await DictDataService.init_cache_sys_dict_services(query_db, request.app.state.redis)
|
||||
result = {'is_success': True, 'message': '刷新成功'}
|
||||
|
||||
return CrudResponseModel(**result)
|
||||
|
||||
|
||||
class DictDataService:
|
||||
"""
|
||||
字典数据管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_dict_data_list_services(
|
||||
cls, query_db: AsyncSession, query_object: DictDataPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
获取字典数据列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 字典数据列表信息对象
|
||||
"""
|
||||
dict_data_list_result = await DictDataDao.get_dict_data_list(query_db, query_object, is_page)
|
||||
|
||||
return dict_data_list_result
|
||||
|
||||
@classmethod
|
||||
async def query_dict_data_list_services(cls, query_db: AsyncSession, dict_type: str) -> Sequence[SysDictData]:
|
||||
"""
|
||||
获取字典数据列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param dict_type: 字典类型
|
||||
:return: 字典数据列表信息对象
|
||||
"""
|
||||
dict_data_list_result = await DictDataDao.query_dict_data_list(query_db, dict_type)
|
||||
|
||||
return dict_data_list_result
|
||||
|
||||
@classmethod
|
||||
async def init_cache_sys_dict_services(cls, query_db: AsyncSession, redis: aioredis.Redis) -> None:
|
||||
"""
|
||||
应用初始化:获取所有字典类型对应的字典数据信息并缓存service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param redis: redis对象
|
||||
:return:
|
||||
"""
|
||||
# 获取以sys_dict:开头的键列表
|
||||
keys = await redis.keys(f'{RedisInitKeyConfig.SYS_DICT.key}:*')
|
||||
# 删除匹配的键
|
||||
if keys:
|
||||
await redis.delete(*keys)
|
||||
dict_type_all = await DictTypeDao.get_all_dict_type(query_db)
|
||||
for dict_type_obj in [item for item in dict_type_all if item.status == '0']:
|
||||
dict_type = dict_type_obj.dict_type
|
||||
dict_data_list = await DictDataDao.query_dict_data_list(query_db, dict_type)
|
||||
dict_data = [CamelCaseUtil.transform_result(row) for row in dict_data_list if row]
|
||||
await redis.set(
|
||||
f'{RedisInitKeyConfig.SYS_DICT.key}:{dict_type}',
|
||||
json.dumps(dict_data, ensure_ascii=False, default=str),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def query_dict_data_list_from_cache_services(
|
||||
cls, redis: aioredis.Redis, dict_type: str
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
从缓存获取字典数据列表信息service
|
||||
|
||||
:param redis: redis对象
|
||||
:param dict_type: 字典类型
|
||||
:return: 字典数据列表信息对象
|
||||
"""
|
||||
result = []
|
||||
dict_data_list_result = await redis.get(f'{RedisInitKeyConfig.SYS_DICT.key}:{dict_type}')
|
||||
if dict_data_list_result:
|
||||
result = json.loads(dict_data_list_result)
|
||||
|
||||
return CamelCaseUtil.transform_result(result)
|
||||
|
||||
@classmethod
|
||||
async def check_dict_data_unique_services(cls, query_db: AsyncSession, page_object: DictDataModel) -> bool:
|
||||
"""
|
||||
校验字典数据是否唯一service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 字典数据对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
dict_code = -1 if page_object.dict_code is None else page_object.dict_code
|
||||
dict_data = await DictDataDao.get_dict_data_detail_by_info(query_db, page_object)
|
||||
if dict_data and dict_data.dict_code != dict_code:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def add_dict_data_services(
|
||||
cls, request: Request, query_db: AsyncSession, page_object: DictDataModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
新增字典数据信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增岗位对象
|
||||
:return: 新增字典数据校验结果
|
||||
"""
|
||||
if not await cls.check_dict_data_unique_services(query_db, page_object):
|
||||
raise ServiceException(
|
||||
message=f'新增字典数据{page_object.dict_label}失败,{page_object.dict_type}下已存在该字典数据'
|
||||
)
|
||||
try:
|
||||
await DictDataDao.add_dict_data_dao(query_db, page_object)
|
||||
await query_db.commit()
|
||||
dict_data_list = await cls.query_dict_data_list_services(query_db, page_object.dict_type)
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.SYS_DICT.key}:{page_object.dict_type}',
|
||||
json.dumps(CamelCaseUtil.transform_result(dict_data_list), ensure_ascii=False, default=str),
|
||||
)
|
||||
return CrudResponseModel(is_success=True, message='新增成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def edit_dict_data_services(
|
||||
cls, request: Request, query_db: AsyncSession, page_object: DictDataModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
编辑字典数据信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param page_object: 编辑字典数据对象
|
||||
:return: 编辑字典数据校验结果
|
||||
"""
|
||||
edit_data_type = page_object.model_dump(exclude_unset=True)
|
||||
dict_data_info = await cls.dict_data_detail_services(query_db, page_object.dict_code)
|
||||
if dict_data_info.dict_code:
|
||||
if not await cls.check_dict_data_unique_services(query_db, page_object):
|
||||
raise ServiceException(
|
||||
message=f'新增字典数据{page_object.dict_label}失败,{page_object.dict_type}下已存在该字典数据'
|
||||
)
|
||||
try:
|
||||
await DictDataDao.edit_dict_data_dao(query_db, edit_data_type)
|
||||
await query_db.commit()
|
||||
dict_data_list = await cls.query_dict_data_list_services(query_db, page_object.dict_type)
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.SYS_DICT.key}:{page_object.dict_type}',
|
||||
json.dumps(CamelCaseUtil.transform_result(dict_data_list), ensure_ascii=False, default=str),
|
||||
)
|
||||
return CrudResponseModel(is_success=True, message='更新成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='字典数据不存在')
|
||||
|
||||
@classmethod
|
||||
async def delete_dict_data_services(
|
||||
cls, request: Request, query_db: AsyncSession, page_object: DeleteDictDataModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
删除字典数据信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除字典数据对象
|
||||
:return: 删除字典数据校验结果
|
||||
"""
|
||||
if page_object.dict_codes:
|
||||
dict_code_list = page_object.dict_codes.split(',')
|
||||
try:
|
||||
delete_dict_type_list = []
|
||||
for dict_code in dict_code_list:
|
||||
dict_data = await cls.dict_data_detail_services(query_db, int(dict_code))
|
||||
await DictDataDao.delete_dict_data_dao(query_db, DictDataModel(dictCode=dict_code))
|
||||
delete_dict_type_list.append(dict_data.dict_type)
|
||||
await query_db.commit()
|
||||
for dict_type in list(set(delete_dict_type_list)):
|
||||
dict_data_list = await cls.query_dict_data_list_services(query_db, dict_type)
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.SYS_DICT.key}:{dict_type}',
|
||||
json.dumps(CamelCaseUtil.transform_result(dict_data_list), ensure_ascii=False, default=str),
|
||||
)
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='传入字典数据id为空')
|
||||
|
||||
@classmethod
|
||||
async def dict_data_detail_services(cls, query_db: AsyncSession, dict_code: int) -> DictDataModel:
|
||||
"""
|
||||
获取字典数据详细信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param dict_code: 字典数据id
|
||||
:return: 字典数据id对应的信息
|
||||
"""
|
||||
dict_data = await DictDataDao.get_dict_data_detail_by_id(query_db, dict_code=dict_code)
|
||||
result = DictDataModel(**CamelCaseUtil.transform_result(dict_data)) if dict_data else DictDataModel()
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
async def export_dict_data_list_services(dict_data_list: list) -> bytes:
|
||||
"""
|
||||
导出字典数据信息service
|
||||
|
||||
:param dict_data_list: 字典数据信息列表
|
||||
:return: 字典数据信息对应excel的二进制数据
|
||||
"""
|
||||
# 创建一个映射字典,将英文键映射到中文键
|
||||
mapping_dict = {
|
||||
'dictCode': '字典编码',
|
||||
'dictSort': '字典标签',
|
||||
'dictLabel': '字典键值',
|
||||
'dictValue': '字典排序',
|
||||
'dictType': '字典类型',
|
||||
'cssClass': '样式属性',
|
||||
'listClass': '表格回显样式',
|
||||
'isDefault': '是否默认',
|
||||
'status': '状态',
|
||||
'createBy': '创建者',
|
||||
'createTime': '创建时间',
|
||||
'updateBy': '更新者',
|
||||
'updateTime': '更新时间',
|
||||
'remark': '备注',
|
||||
}
|
||||
|
||||
for item in dict_data_list:
|
||||
if item.get('status') == '0':
|
||||
item['status'] = '正常'
|
||||
else:
|
||||
item['status'] = '停用'
|
||||
if item.get('isDefault') == 'Y':
|
||||
item['isDefault'] = '是'
|
||||
else:
|
||||
item['isDefault'] = '否'
|
||||
binary_data = ExcelUtil.export_list2excel(dict_data_list, mapping_dict)
|
||||
|
||||
return binary_data
|
||||
@@ -0,0 +1,144 @@
|
||||
from typing import Any
|
||||
|
||||
from fastapi import Request
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from common.vo import CrudResponseModel, PageModel
|
||||
from module_admin.dao.job_log_dao import JobLogDao
|
||||
from module_admin.entity.vo.job_vo import DeleteJobLogModel, JobLogModel, JobLogPageQueryModel
|
||||
from module_admin.service.dict_service import DictDataService
|
||||
from utils.excel_util import ExcelUtil
|
||||
|
||||
|
||||
class JobLogService:
|
||||
"""
|
||||
定时任务日志管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_job_log_list_services(
|
||||
cls, query_db: AsyncSession, query_object: JobLogPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
获取定时任务日志列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 定时任务日志列表信息对象
|
||||
"""
|
||||
job_log_list_result = await JobLogDao.get_job_log_list(query_db, query_object, is_page)
|
||||
|
||||
return job_log_list_result
|
||||
|
||||
@classmethod
|
||||
def add_job_log_services(cls, query_db: Session, page_object: JobLogModel) -> CrudResponseModel:
|
||||
"""
|
||||
新增定时任务日志信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增定时任务日志对象
|
||||
:return: 新增定时任务日志校验结果
|
||||
"""
|
||||
try:
|
||||
JobLogDao.add_job_log_dao(query_db, page_object)
|
||||
query_db.commit()
|
||||
result = {'is_success': True, 'message': '新增成功'}
|
||||
except Exception as e:
|
||||
query_db.rollback()
|
||||
result = {'is_success': False, 'message': str(e)}
|
||||
|
||||
return CrudResponseModel(**result)
|
||||
|
||||
@classmethod
|
||||
async def delete_job_log_services(cls, query_db: AsyncSession, page_object: DeleteJobLogModel) -> CrudResponseModel:
|
||||
"""
|
||||
删除定时任务日志信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除定时任务日志对象
|
||||
:return: 删除定时任务日志校验结果
|
||||
"""
|
||||
if page_object.job_log_ids:
|
||||
job_log_id_list = page_object.job_log_ids.split(',')
|
||||
try:
|
||||
for job_log_id in job_log_id_list:
|
||||
await JobLogDao.delete_job_log_dao(query_db, JobLogModel(jobLogId=job_log_id))
|
||||
await query_db.commit()
|
||||
result = {'is_success': True, 'message': '删除成功'}
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
result = {'is_success': False, 'message': '传入定时任务日志id为空'}
|
||||
return CrudResponseModel(**result)
|
||||
|
||||
@classmethod
|
||||
async def clear_job_log_services(cls, query_db: AsyncSession) -> CrudResponseModel:
|
||||
"""
|
||||
清除定时任务日志信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:return: 清除定时任务日志校验结果
|
||||
"""
|
||||
try:
|
||||
await JobLogDao.clear_job_log_dao(query_db)
|
||||
await query_db.commit()
|
||||
result = {'is_success': True, 'message': '清除成功'}
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
return CrudResponseModel(**result)
|
||||
|
||||
@staticmethod
|
||||
async def export_job_log_list_services(request: Request, job_log_list: list) -> bytes:
|
||||
"""
|
||||
导出定时任务日志信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param job_log_list: 定时任务日志信息列表
|
||||
:return: 定时任务日志信息对应excel的二进制数据
|
||||
"""
|
||||
# 创建一个映射字典,将英文键映射到中文键
|
||||
mapping_dict = {
|
||||
'jobLogId': '任务日志编码',
|
||||
'jobName': '任务名称',
|
||||
'jobGroup': '任务组名',
|
||||
'jobExecutor': '任务执行器',
|
||||
'invokeTarget': '调用目标字符串',
|
||||
'jobArgs': '位置参数',
|
||||
'jobKwargs': '关键字参数',
|
||||
'jobTrigger': '任务触发器',
|
||||
'jobMessage': '日志信息',
|
||||
'status': '执行状态',
|
||||
'exceptionInfo': '异常信息',
|
||||
'createTime': '创建时间',
|
||||
}
|
||||
|
||||
job_group_list = await DictDataService.query_dict_data_list_from_cache_services(
|
||||
request.app.state.redis, dict_type='sys_job_group'
|
||||
)
|
||||
job_group_option = [{'label': item.get('dictLabel'), 'value': item.get('dictValue')} for item in job_group_list]
|
||||
job_group_option_dict = {item.get('value'): item for item in job_group_option}
|
||||
job_executor_list = await DictDataService.query_dict_data_list_from_cache_services(
|
||||
request.app.state.redis, dict_type='sys_job_executor'
|
||||
)
|
||||
job_executor_option = [
|
||||
{'label': item.get('dictLabel'), 'value': item.get('dictValue')} for item in job_executor_list
|
||||
]
|
||||
job_executor_option_dict = {item.get('value'): item for item in job_executor_option}
|
||||
|
||||
for item in job_log_list:
|
||||
if item.get('status') == '0':
|
||||
item['status'] = '正常'
|
||||
else:
|
||||
item['status'] = '暂停'
|
||||
if str(item.get('jobGroup')) in job_group_option_dict:
|
||||
item['jobGroup'] = job_group_option_dict.get(str(item.get('jobGroup'))).get('label')
|
||||
if str(item.get('jobExecutor')) in job_executor_option_dict:
|
||||
item['jobExecutor'] = job_executor_option_dict.get(str(item.get('jobExecutor'))).get('label')
|
||||
binary_data = ExcelUtil.export_list2excel(job_log_list, mapping_dict)
|
||||
|
||||
return binary_data
|
||||
@@ -0,0 +1,273 @@
|
||||
from typing import Any
|
||||
|
||||
from fastapi import Request
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.constant import CommonConstant, JobConstant
|
||||
from common.vo import CrudResponseModel, PageModel
|
||||
from config.get_scheduler import SchedulerUtil
|
||||
from exceptions.exception import ServiceException
|
||||
from module_admin.dao.job_dao import JobDao
|
||||
from module_admin.entity.vo.job_vo import DeleteJobModel, EditJobModel, JobModel, JobPageQueryModel
|
||||
from module_admin.service.dict_service import DictDataService
|
||||
from utils.common_util import CamelCaseUtil
|
||||
from utils.cron_util import CronUtil
|
||||
from utils.excel_util import ExcelUtil
|
||||
from utils.string_util import StringUtil
|
||||
|
||||
|
||||
class JobService:
|
||||
"""
|
||||
定时任务管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_job_list_services(
|
||||
cls, query_db: AsyncSession, query_object: JobPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
获取定时任务列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 定时任务列表信息对象
|
||||
"""
|
||||
job_list_result = await JobDao.get_job_list(query_db, query_object, is_page)
|
||||
|
||||
return job_list_result
|
||||
|
||||
@classmethod
|
||||
async def check_job_unique_services(cls, query_db: AsyncSession, page_object: JobModel) -> bool:
|
||||
"""
|
||||
校验定时任务是否存在service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 定时任务对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
job_id = -1 if page_object.job_id is None else page_object.job_id
|
||||
job = await JobDao.get_job_detail_by_info(query_db, page_object)
|
||||
if job and job.job_id != job_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def add_job_services(cls, query_db: AsyncSession, page_object: JobModel) -> CrudResponseModel:
|
||||
"""
|
||||
新增定时任务信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增定时任务对象
|
||||
:return: 新增定时任务校验结果
|
||||
"""
|
||||
if not CronUtil.validate_cron_expression(page_object.cron_expression):
|
||||
raise ServiceException(message=f'新增定时任务{page_object.job_name}失败,Cron表达式不正确')
|
||||
if StringUtil.contains_ignore_case(page_object.invoke_target, CommonConstant.LOOKUP_RMI):
|
||||
raise ServiceException(message=f'新增定时任务{page_object.job_name}失败,目标字符串不允许rmi调用')
|
||||
if StringUtil.contains_any_ignore_case(
|
||||
page_object.invoke_target, [CommonConstant.LOOKUP_LDAP, CommonConstant.LOOKUP_LDAPS]
|
||||
):
|
||||
raise ServiceException(message=f'新增定时任务{page_object.job_name}失败,目标字符串不允许ldap(s)调用')
|
||||
if StringUtil.contains_any_ignore_case(page_object.invoke_target, [CommonConstant.HTTP, CommonConstant.HTTPS]):
|
||||
raise ServiceException(message=f'新增定时任务{page_object.job_name}失败,目标字符串不允许http(s)调用')
|
||||
if StringUtil.startswith_any_case(page_object.invoke_target, JobConstant.JOB_ERROR_LIST):
|
||||
raise ServiceException(message=f'新增定时任务{page_object.job_name}失败,目标字符串存在违规')
|
||||
if not StringUtil.startswith_any_case(page_object.invoke_target, JobConstant.JOB_WHITE_LIST):
|
||||
raise ServiceException(message=f'新增定时任务{page_object.job_name}失败,目标字符串不在白名单内')
|
||||
if not await cls.check_job_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增定时任务{page_object.job_name}失败,定时任务已存在')
|
||||
try:
|
||||
add_job = await JobDao.add_job_dao(query_db, page_object)
|
||||
job_info = await cls.job_detail_services(query_db, add_job.job_id)
|
||||
if job_info.status == '0':
|
||||
SchedulerUtil.add_scheduler_job(job_info=job_info)
|
||||
await query_db.commit()
|
||||
await SchedulerUtil.request_scheduler_sync()
|
||||
result = {'is_success': True, 'message': '新增成功'}
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
return CrudResponseModel(**result)
|
||||
|
||||
@classmethod
|
||||
def _deal_edit_job(cls, page_object: EditJobModel, edit_job: dict[str, Any]) -> None:
|
||||
"""
|
||||
处理编辑定时任务字典
|
||||
|
||||
:param page_object: 编辑定时任务对象
|
||||
:param edit_job: 编辑定时任务字典
|
||||
"""
|
||||
if page_object.type == 'status':
|
||||
del edit_job['type']
|
||||
|
||||
@classmethod
|
||||
async def edit_job_services(cls, query_db: AsyncSession, page_object: EditJobModel) -> CrudResponseModel:
|
||||
"""
|
||||
编辑定时任务信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 编辑定时任务对象
|
||||
:return: 编辑定时任务校验结果
|
||||
"""
|
||||
edit_job = page_object.model_dump(exclude_unset=True)
|
||||
cls._deal_edit_job(page_object, edit_job)
|
||||
job_info = await cls.job_detail_services(query_db, page_object.job_id)
|
||||
if job_info:
|
||||
if page_object.type != 'status':
|
||||
if not CronUtil.validate_cron_expression(page_object.cron_expression):
|
||||
raise ServiceException(message=f'修改定时任务{page_object.job_name}失败,Cron表达式不正确')
|
||||
if StringUtil.contains_ignore_case(page_object.invoke_target, CommonConstant.LOOKUP_RMI):
|
||||
raise ServiceException(message=f'修改定时任务{page_object.job_name}失败,目标字符串不允许rmi调用')
|
||||
if StringUtil.contains_any_ignore_case(
|
||||
page_object.invoke_target, [CommonConstant.LOOKUP_LDAP, CommonConstant.LOOKUP_LDAPS]
|
||||
):
|
||||
raise ServiceException(
|
||||
message=f'修改定时任务{page_object.job_name}失败,目标字符串不允许ldap(s)调用'
|
||||
)
|
||||
if StringUtil.contains_any_ignore_case(
|
||||
page_object.invoke_target, [CommonConstant.HTTP, CommonConstant.HTTPS]
|
||||
):
|
||||
raise ServiceException(
|
||||
message=f'修改定时任务{page_object.job_name}失败,目标字符串不允许http(s)调用'
|
||||
)
|
||||
if StringUtil.startswith_any_case(page_object.invoke_target, JobConstant.JOB_ERROR_LIST):
|
||||
raise ServiceException(message=f'修改定时任务{page_object.job_name}失败,目标字符串存在违规')
|
||||
if not StringUtil.startswith_any_case(page_object.invoke_target, JobConstant.JOB_WHITE_LIST):
|
||||
raise ServiceException(message=f'修改定时任务{page_object.job_name}失败,目标字符串不在白名单内')
|
||||
if not await cls.check_job_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改定时任务{page_object.job_name}失败,定时任务已存在')
|
||||
try:
|
||||
await JobDao.edit_job_dao(query_db, edit_job, job_info)
|
||||
SchedulerUtil.remove_scheduler_job(job_id=edit_job.get('job_id'))
|
||||
if edit_job.get('status') == '0':
|
||||
job_info = await cls.job_detail_services(query_db, edit_job.get('job_id'))
|
||||
SchedulerUtil.add_scheduler_job(job_info=job_info)
|
||||
await query_db.commit()
|
||||
await SchedulerUtil.request_scheduler_sync()
|
||||
return CrudResponseModel(is_success=True, message='更新成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='定时任务不存在')
|
||||
|
||||
@classmethod
|
||||
async def execute_job_once_services(cls, query_db: AsyncSession, page_object: JobModel) -> CrudResponseModel:
|
||||
"""
|
||||
执行一次定时任务service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 定时任务对象
|
||||
:return: 执行一次定时任务结果
|
||||
"""
|
||||
SchedulerUtil.remove_scheduler_job(job_id=page_object.job_id)
|
||||
job_info = await cls.job_detail_services(query_db, page_object.job_id)
|
||||
if job_info:
|
||||
SchedulerUtil.execute_scheduler_job_once(job_info=job_info)
|
||||
return CrudResponseModel(is_success=True, message='执行成功')
|
||||
raise ServiceException(message='定时任务不存在')
|
||||
|
||||
@classmethod
|
||||
async def delete_job_services(cls, query_db: AsyncSession, page_object: DeleteJobModel) -> CrudResponseModel:
|
||||
"""
|
||||
删除定时任务信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除定时任务对象
|
||||
:return: 删除定时任务校验结果
|
||||
"""
|
||||
if page_object.job_ids:
|
||||
job_id_list = page_object.job_ids.split(',')
|
||||
try:
|
||||
for job_id in job_id_list:
|
||||
await JobDao.delete_job_dao(query_db, JobModel(jobId=job_id))
|
||||
SchedulerUtil.remove_scheduler_job(job_id=job_id)
|
||||
await query_db.commit()
|
||||
await SchedulerUtil.request_scheduler_sync()
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='传入定时任务id为空')
|
||||
|
||||
@classmethod
|
||||
async def job_detail_services(cls, query_db: AsyncSession, job_id: int) -> JobModel:
|
||||
"""
|
||||
获取定时任务详细信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param job_id: 定时任务id
|
||||
:return: 定时任务id对应的信息
|
||||
"""
|
||||
job = await JobDao.get_job_detail_by_id(query_db, job_id=job_id)
|
||||
result = JobModel(**CamelCaseUtil.transform_result(job)) if job else JobModel()
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
async def export_job_list_services(request: Request, job_list: list) -> bytes:
|
||||
"""
|
||||
导出定时任务信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param job_list: 定时任务信息列表
|
||||
:return: 定时任务信息对应excel的二进制数据
|
||||
"""
|
||||
# 创建一个映射字典,将英文键映射到中文键
|
||||
mapping_dict = {
|
||||
'jobId': '任务编码',
|
||||
'jobName': '任务名称',
|
||||
'jobGroup': '任务组名',
|
||||
'jobExecutor': '任务执行器',
|
||||
'invokeTarget': '调用目标字符串',
|
||||
'jobArgs': '位置参数',
|
||||
'jobKwargs': '关键字参数',
|
||||
'cronExpression': 'cron执行表达式',
|
||||
'misfirePolicy': '计划执行错误策略',
|
||||
'concurrent': '是否并发执行',
|
||||
'status': '状态',
|
||||
'createBy': '创建者',
|
||||
'createTime': '创建时间',
|
||||
'updateBy': '更新者',
|
||||
'updateTime': '更新时间',
|
||||
'remark': '备注',
|
||||
}
|
||||
|
||||
job_group_list = await DictDataService.query_dict_data_list_from_cache_services(
|
||||
request.app.state.redis, dict_type='sys_job_group'
|
||||
)
|
||||
job_group_option = [{'label': item.get('dictLabel'), 'value': item.get('dictValue')} for item in job_group_list]
|
||||
job_group_option_dict = {item.get('value'): item for item in job_group_option}
|
||||
job_executor_list = await DictDataService.query_dict_data_list_from_cache_services(
|
||||
request.app.state.redis, dict_type='sys_job_executor'
|
||||
)
|
||||
job_executor_option = [
|
||||
{'label': item.get('dictLabel'), 'value': item.get('dictValue')} for item in job_executor_list
|
||||
]
|
||||
job_executor_option_dict = {item.get('value'): item for item in job_executor_option}
|
||||
|
||||
for item in job_list:
|
||||
if item.get('status') == '0':
|
||||
item['status'] = '正常'
|
||||
else:
|
||||
item['status'] = '暂停'
|
||||
if str(item.get('jobGroup')) in job_group_option_dict:
|
||||
item['jobGroup'] = job_group_option_dict.get(str(item.get('jobGroup'))).get('label')
|
||||
if str(item.get('jobExecutor')) in job_executor_option_dict:
|
||||
item['jobExecutor'] = job_executor_option_dict.get(str(item.get('jobExecutor'))).get('label')
|
||||
if item.get('misfirePolicy') == '1':
|
||||
item['misfirePolicy'] = '立即执行'
|
||||
elif item.get('misfirePolicy') == '2':
|
||||
item['misfirePolicy'] = '执行一次'
|
||||
else:
|
||||
item['misfirePolicy'] = '放弃执行'
|
||||
if item.get('concurrent') == '0':
|
||||
item['concurrent'] = '允许'
|
||||
else:
|
||||
item['concurrent'] = '禁止'
|
||||
binary_data = ExcelUtil.export_list2excel(job_list, mapping_dict)
|
||||
|
||||
return binary_data
|
||||
@@ -0,0 +1,508 @@
|
||||
import asyncio
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from fastapi import Request
|
||||
from redis import asyncio as aioredis
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.vo import CrudResponseModel, PageModel
|
||||
from config.database import AsyncSessionLocal
|
||||
from config.env import LogConfig
|
||||
from exceptions.exception import ServiceException
|
||||
from middlewares.trace_middleware.ctx import TraceCtx
|
||||
from module_admin.dao.log_dao import LoginLogDao, OperationLogDao
|
||||
from module_admin.entity.vo.log_vo import (
|
||||
DeleteLoginLogModel,
|
||||
DeleteOperLogModel,
|
||||
LogininforModel,
|
||||
LoginLogPageQueryModel,
|
||||
OperLogModel,
|
||||
OperLogPageQueryModel,
|
||||
UnlockUser,
|
||||
)
|
||||
from module_admin.service.dict_service import DictDataService
|
||||
from utils.excel_util import ExcelUtil
|
||||
from utils.log_util import LogSanitizer, logger
|
||||
|
||||
|
||||
class OperationLogService:
|
||||
"""
|
||||
操作日志管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_operation_log_list_services(
|
||||
cls, query_db: AsyncSession, query_object: OperLogPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
获取操作日志列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 操作日志列表信息对象
|
||||
"""
|
||||
operation_log_list_result = await OperationLogDao.get_operation_log_list(query_db, query_object, is_page)
|
||||
|
||||
return operation_log_list_result
|
||||
|
||||
@classmethod
|
||||
async def add_operation_log_services(cls, query_db: AsyncSession, page_object: OperLogModel) -> CrudResponseModel:
|
||||
"""
|
||||
新增操作日志service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增操作日志对象
|
||||
:return: 新增操作日志校验结果
|
||||
"""
|
||||
try:
|
||||
await OperationLogDao.add_operation_log_dao(query_db, page_object)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='新增成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def delete_operation_log_services(
|
||||
cls, query_db: AsyncSession, page_object: DeleteOperLogModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
删除操作日志信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除操作日志对象
|
||||
:return: 删除操作日志校验结果
|
||||
"""
|
||||
if page_object.oper_ids:
|
||||
oper_id_list = page_object.oper_ids.split(',')
|
||||
try:
|
||||
for oper_id in oper_id_list:
|
||||
await OperationLogDao.delete_operation_log_dao(query_db, OperLogModel(operId=oper_id))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='传入操作日志id为空')
|
||||
|
||||
@classmethod
|
||||
async def clear_operation_log_services(cls, query_db: AsyncSession) -> CrudResponseModel:
|
||||
"""
|
||||
清除操作日志信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:return: 清除操作日志校验结果
|
||||
"""
|
||||
try:
|
||||
await OperationLogDao.clear_operation_log_dao(query_db)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='清除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def export_operation_log_list_services(cls, request: Request, operation_log_list: list) -> bytes:
|
||||
"""
|
||||
导出操作日志信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param operation_log_list: 操作日志信息列表
|
||||
:return: 操作日志信息对应excel的二进制数据
|
||||
"""
|
||||
# 创建一个映射字典,将英文键映射到中文键
|
||||
mapping_dict = {
|
||||
'operId': '日志编号',
|
||||
'title': '系统模块',
|
||||
'businessType': '操作类型',
|
||||
'method': '方法名称',
|
||||
'requestMethod': '请求方式',
|
||||
'operName': '操作人员',
|
||||
'deptName': '部门名称',
|
||||
'operUrl': '请求URL',
|
||||
'operIp': '操作地址',
|
||||
'operLocation': '操作地点',
|
||||
'operParam': '请求参数',
|
||||
'jsonResult': '返回参数',
|
||||
'status': '操作状态',
|
||||
'error_msg': '错误消息',
|
||||
'operTime': '操作日期',
|
||||
'costTime': '消耗时间(毫秒)',
|
||||
}
|
||||
|
||||
operation_type_list = await DictDataService.query_dict_data_list_from_cache_services(
|
||||
request.app.state.redis, dict_type='sys_oper_type'
|
||||
)
|
||||
operation_type_option = [
|
||||
{'label': item.get('dictLabel'), 'value': item.get('dictValue')} for item in operation_type_list
|
||||
]
|
||||
operation_type_option_dict = {item.get('value'): item for item in operation_type_option}
|
||||
|
||||
for item in operation_log_list:
|
||||
if item.get('status') == 0:
|
||||
item['status'] = '成功'
|
||||
else:
|
||||
item['status'] = '失败'
|
||||
if str(item.get('businessType')) in operation_type_option_dict:
|
||||
item['businessType'] = operation_type_option_dict.get(str(item.get('businessType'))).get('label')
|
||||
binary_data = ExcelUtil.export_list2excel(operation_log_list, mapping_dict)
|
||||
|
||||
return binary_data
|
||||
|
||||
|
||||
class LoginLogService:
|
||||
"""
|
||||
登录日志管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_login_log_list_services(
|
||||
cls, query_db: AsyncSession, query_object: LoginLogPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
获取登录日志列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 登录日志列表信息对象
|
||||
"""
|
||||
operation_log_list_result = await LoginLogDao.get_login_log_list(query_db, query_object, is_page)
|
||||
|
||||
return operation_log_list_result
|
||||
|
||||
@classmethod
|
||||
async def add_login_log_services(cls, query_db: AsyncSession, page_object: LogininforModel) -> CrudResponseModel:
|
||||
"""
|
||||
新增登录日志service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增登录日志对象
|
||||
:return: 新增登录日志校验结果
|
||||
"""
|
||||
try:
|
||||
await LoginLogDao.add_login_log_dao(query_db, page_object)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='新增成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def delete_login_log_services(
|
||||
cls, query_db: AsyncSession, page_object: DeleteLoginLogModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
删除操作日志信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除操作日志对象
|
||||
:return: 删除操作日志校验结果
|
||||
"""
|
||||
if page_object.info_ids:
|
||||
info_id_list = page_object.info_ids.split(',')
|
||||
try:
|
||||
for info_id in info_id_list:
|
||||
await LoginLogDao.delete_login_log_dao(query_db, LogininforModel(infoId=info_id))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='传入登录日志id为空')
|
||||
|
||||
@classmethod
|
||||
async def clear_login_log_services(cls, query_db: AsyncSession) -> CrudResponseModel:
|
||||
"""
|
||||
清除操作日志信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:return: 清除操作日志校验结果
|
||||
"""
|
||||
try:
|
||||
await LoginLogDao.clear_login_log_dao(query_db)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='清除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def unlock_user_services(cls, request: Request, unlock_user: UnlockUser) -> CrudResponseModel:
|
||||
locked_user = await request.app.state.redis.get(f'account_lock:{unlock_user.user_name}')
|
||||
if locked_user:
|
||||
await request.app.state.redis.delete(f'account_lock:{unlock_user.user_name}')
|
||||
return CrudResponseModel(is_success=True, message='解锁成功')
|
||||
raise ServiceException(message='该用户未锁定')
|
||||
|
||||
@staticmethod
|
||||
async def export_login_log_list_services(login_log_list: list) -> bytes:
|
||||
"""
|
||||
导出登录日志信息service
|
||||
|
||||
:param login_log_list: 登录日志信息列表
|
||||
:return: 登录日志信息对应excel的二进制数据
|
||||
"""
|
||||
# 创建一个映射字典,将英文键映射到中文键
|
||||
mapping_dict = {
|
||||
'infoId': '访问编号',
|
||||
'userName': '用户名称',
|
||||
'ipaddr': '登录地址',
|
||||
'loginLocation': '登录地点',
|
||||
'browser': '浏览器',
|
||||
'os': '操作系统',
|
||||
'status': '登录状态',
|
||||
'msg': '操作信息',
|
||||
'loginTime': '登录日期',
|
||||
}
|
||||
|
||||
for item in login_log_list:
|
||||
if item.get('status') == '0':
|
||||
item['status'] = '成功'
|
||||
else:
|
||||
item['status'] = '失败'
|
||||
binary_data = ExcelUtil.export_list2excel(login_log_list, mapping_dict)
|
||||
|
||||
return binary_data
|
||||
|
||||
|
||||
class LogQueueService:
|
||||
"""
|
||||
日志队列服务
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def _build_event_id(cls, request_id: str, log_type: str, source: str) -> str:
|
||||
"""
|
||||
生成日志事件唯一标识
|
||||
|
||||
:param request_id: 请求唯一标识
|
||||
:param log_type: 日志类型
|
||||
:param source: 日志来源
|
||||
:return: 事件唯一标识
|
||||
"""
|
||||
if not request_id:
|
||||
return uuid.uuid4().hex
|
||||
base = f'{request_id}:{log_type}:{source}'
|
||||
return hashlib.md5(base.encode('utf-8')).hexdigest()
|
||||
|
||||
@classmethod
|
||||
async def _xadd_event(cls, redis: aioredis.Redis, event_type: str, payload: dict, source: str) -> None:
|
||||
"""
|
||||
写入日志事件到Redis Streams
|
||||
|
||||
:param redis: Redis连接对象
|
||||
:param event_type: 事件类型
|
||||
:param payload: 事件负载
|
||||
:param source: 日志来源
|
||||
:return: None
|
||||
"""
|
||||
request_id = TraceCtx.get_request_id()
|
||||
trace_id = TraceCtx.get_trace_id()
|
||||
span_id = TraceCtx.get_span_id()
|
||||
event_id = cls._build_event_id(request_id, event_type, source)
|
||||
await redis.xadd(
|
||||
LogConfig.log_stream_key,
|
||||
{
|
||||
'event_id': event_id,
|
||||
'event_type': event_type,
|
||||
'request_id': request_id,
|
||||
'trace_id': trace_id,
|
||||
'span_id': span_id,
|
||||
'payload': json.dumps(payload, ensure_ascii=False, default=str),
|
||||
},
|
||||
maxlen=LogConfig.log_stream_maxlen,
|
||||
approximate=True,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def enqueue_login_log(cls, request: Request, login_log: LogininforModel, source: str) -> None:
|
||||
"""
|
||||
登录日志入队
|
||||
|
||||
:param request: Request对象
|
||||
:param login_log: 登录日志模型
|
||||
:param source: 日志来源
|
||||
:return: None
|
||||
"""
|
||||
payload = LogSanitizer.sanitize_data(login_log.model_dump(by_alias=True, exclude_none=True))
|
||||
await cls._xadd_event(request.app.state.redis, 'login', payload, source)
|
||||
|
||||
@classmethod
|
||||
async def enqueue_operation_log(cls, request: Request, operation_log: OperLogModel, source: str) -> None:
|
||||
"""
|
||||
操作日志入队
|
||||
|
||||
:param request: Request对象
|
||||
:param operation_log: 操作日志模型
|
||||
:param source: 日志来源
|
||||
:return: None
|
||||
"""
|
||||
payload = LogSanitizer.sanitize_data(operation_log.model_dump(by_alias=True, exclude_none=True))
|
||||
await cls._xadd_event(request.app.state.redis, 'operation', payload, source)
|
||||
|
||||
|
||||
class LogAggregatorService:
|
||||
"""
|
||||
日志聚合消费服务
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def _ensure_group(cls, redis: aioredis.Redis) -> None:
|
||||
"""
|
||||
初始化消费组
|
||||
|
||||
:param redis: Redis连接对象
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
await redis.xgroup_create(
|
||||
name=LogConfig.log_stream_key,
|
||||
groupname=LogConfig.log_stream_group,
|
||||
id='0-0',
|
||||
mkstream=True,
|
||||
)
|
||||
except Exception as exc:
|
||||
if 'BUSYGROUP' not in str(exc):
|
||||
raise
|
||||
|
||||
@classmethod
|
||||
async def _acquire_dedup(cls, redis: aioredis.Redis, event_id: str) -> bool:
|
||||
"""
|
||||
获取去重锁
|
||||
|
||||
:param redis: Redis连接对象
|
||||
:param event_id: 事件唯一标识
|
||||
:return: 是否获取成功
|
||||
"""
|
||||
if not event_id:
|
||||
return False
|
||||
key = f'{LogConfig.log_stream_dedup_prefix}:{event_id}'
|
||||
return await redis.set(key, '1', nx=True, ex=LogConfig.log_stream_dedup_ttl)
|
||||
|
||||
@classmethod
|
||||
async def _release_dedup(cls, redis: aioredis.Redis, event_id: str) -> None:
|
||||
"""
|
||||
释放去重锁
|
||||
|
||||
:param redis: Redis连接对象
|
||||
:param event_id: 事件唯一标识
|
||||
:return: None
|
||||
"""
|
||||
if not event_id:
|
||||
return
|
||||
await redis.delete(f'{LogConfig.log_stream_dedup_prefix}:{event_id}')
|
||||
|
||||
@classmethod
|
||||
async def _claim_pending(cls, redis: aioredis.Redis, consumer_name: str) -> None:
|
||||
"""
|
||||
认领并处理超时未确认的消息
|
||||
|
||||
:param redis: Redis连接对象
|
||||
:param consumer_name: 消费者名称
|
||||
:return: None
|
||||
"""
|
||||
if LogConfig.log_stream_claim_idle_ms <= 0:
|
||||
return
|
||||
start_id = '0-0'
|
||||
while True:
|
||||
result = await redis.xautoclaim(
|
||||
name=LogConfig.log_stream_key,
|
||||
groupname=LogConfig.log_stream_group,
|
||||
consumername=consumer_name,
|
||||
min_idle_time=LogConfig.log_stream_claim_idle_ms,
|
||||
start_id=start_id,
|
||||
count=LogConfig.log_stream_claim_batch_size,
|
||||
)
|
||||
if not result:
|
||||
return
|
||||
next_start_id, messages = result[0], result[1]
|
||||
if messages:
|
||||
await cls._process_messages(redis, LogConfig.log_stream_key, messages)
|
||||
if not messages or next_start_id == start_id:
|
||||
return
|
||||
start_id = next_start_id
|
||||
|
||||
@classmethod
|
||||
async def consume_stream(cls, redis: aioredis.Redis) -> None:
|
||||
"""
|
||||
消费日志队列
|
||||
|
||||
:param redis: Redis连接对象
|
||||
:return: None
|
||||
"""
|
||||
await cls._ensure_group(redis)
|
||||
consumer_name = f'{LogConfig.log_stream_consumer_prefix}-{os.getpid()}-{uuid.uuid4().hex[:6]}'
|
||||
last_claim_time = 0.0
|
||||
while True:
|
||||
try:
|
||||
now = asyncio.get_running_loop().time()
|
||||
if now - last_claim_time >= LogConfig.log_stream_claim_interval_ms / 1000:
|
||||
await cls._claim_pending(redis, consumer_name)
|
||||
last_claim_time = now
|
||||
result = await redis.xreadgroup(
|
||||
groupname=LogConfig.log_stream_group,
|
||||
consumername=consumer_name,
|
||||
streams={LogConfig.log_stream_key: '>'},
|
||||
count=LogConfig.log_stream_batch_size,
|
||||
block=LogConfig.log_stream_block_ms,
|
||||
)
|
||||
if not result:
|
||||
continue
|
||||
for stream_name, messages in result:
|
||||
await cls._process_messages(redis, stream_name, messages)
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except Exception as exc:
|
||||
logger.error(f'日志聚合消费异常: {exc}')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
@classmethod
|
||||
async def _process_messages(cls, redis: aioredis.Redis, stream_name: str, messages: list[tuple[str, dict]]) -> None:
|
||||
"""
|
||||
处理消息并落库
|
||||
|
||||
:param redis: Redis连接对象
|
||||
:param stream_name: Stream名称
|
||||
:param messages: 消息列表
|
||||
:return: None
|
||||
"""
|
||||
if not messages:
|
||||
return
|
||||
async with AsyncSessionLocal() as session:
|
||||
ack_ids: list[str] = []
|
||||
dedup_event_ids: list[str] = []
|
||||
try:
|
||||
for message_id, data in messages:
|
||||
event_type = data.get('event_type')
|
||||
event_id = data.get('event_id')
|
||||
payload_raw = data.get('payload') or '{}'
|
||||
if event_type not in {'login', 'operation'}:
|
||||
ack_ids.append(message_id)
|
||||
continue
|
||||
acquired = await cls._acquire_dedup(redis, event_id)
|
||||
if not acquired:
|
||||
ack_ids.append(message_id)
|
||||
continue
|
||||
dedup_event_ids.append(event_id)
|
||||
payload = json.loads(payload_raw)
|
||||
if event_type == 'login':
|
||||
await LoginLogDao.add_login_log_dao(session, LogininforModel(**payload))
|
||||
elif event_type == 'operation':
|
||||
await OperationLogDao.add_operation_log_dao(session, OperLogModel(**payload))
|
||||
ack_ids.append(message_id)
|
||||
if ack_ids:
|
||||
await session.commit()
|
||||
await redis.xack(stream_name, LogConfig.log_stream_group, *ack_ids)
|
||||
except Exception:
|
||||
await session.rollback()
|
||||
for event_id in dedup_event_ids:
|
||||
await cls._release_dedup(redis, event_id)
|
||||
raise
|
||||
@@ -0,0 +1,656 @@
|
||||
import random
|
||||
import uuid
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any
|
||||
|
||||
import jwt
|
||||
from fastapi import Depends, Form, Request
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from jwt.exceptions import InvalidTokenError
|
||||
from sqlalchemy import Row
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.constant import CommonConstant, MenuConstant
|
||||
from common.context import RequestContext
|
||||
from common.enums import RedisInitKeyConfig
|
||||
from common.vo import CrudResponseModel
|
||||
from config.env import AppConfig, JwtConfig
|
||||
from config.get_db import get_db
|
||||
from exceptions.exception import AuthException, LoginException, ServiceException
|
||||
from module_admin.dao.login_dao import login_by_account
|
||||
from module_admin.dao.user_dao import UserDao
|
||||
from module_admin.entity.do.dept_do import SysDept
|
||||
from module_admin.entity.do.menu_do import SysMenu
|
||||
from module_admin.entity.do.user_do import SysUser
|
||||
from module_admin.entity.vo.login_vo import MenuTreeModel, MetaModel, RouterModel, SmsCode, UserLogin, UserRegister
|
||||
from module_admin.entity.vo.user_vo import AddUserModel, CurrentUserModel, ResetUserModel, TokenData, UserInfoModel
|
||||
from module_admin.service.user_service import UserService
|
||||
from utils.client_ip_util import ClientIPUtil
|
||||
from utils.common_util import CamelCaseUtil
|
||||
from utils.log_util import logger
|
||||
from utils.message_util import message_service
|
||||
from utils.pwd_util import PwdUtil
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl='login')
|
||||
|
||||
|
||||
class CustomOAuth2PasswordRequestForm(OAuth2PasswordRequestForm):
|
||||
"""
|
||||
自定义OAuth2PasswordRequestForm类,增加验证码及会话编号参数
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
grant_type: str = Form(default=None, pattern='password'),
|
||||
username: str = Form(),
|
||||
password: str = Form(),
|
||||
scope: str = Form(default=''),
|
||||
client_id: str | None = Form(default=None),
|
||||
client_secret: str | None = Form(default=None),
|
||||
code: str | None = Form(default=''),
|
||||
uuid: str | None = Form(default=''),
|
||||
login_info: dict[str, str] | None = Form(default=None),
|
||||
) -> None:
|
||||
super().__init__(
|
||||
grant_type=grant_type,
|
||||
username=username,
|
||||
password=password,
|
||||
scope=scope,
|
||||
client_id=client_id,
|
||||
client_secret=client_secret,
|
||||
)
|
||||
self.code = code
|
||||
self.uuid = uuid
|
||||
self.login_info = login_info
|
||||
|
||||
|
||||
class LoginService:
|
||||
"""
|
||||
登录模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def authenticate_user(
|
||||
cls, request: Request, query_db: AsyncSession, login_user: UserLogin
|
||||
) -> Row[tuple[SysUser, SysDept]]:
|
||||
"""
|
||||
根据用户名密码校验用户登录
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param login_user: 登录用户对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
await cls.__check_login_ip(request)
|
||||
account_lock = await request.app.state.redis.get(
|
||||
f'{RedisInitKeyConfig.ACCOUNT_LOCK.key}:{login_user.user_name}'
|
||||
)
|
||||
if login_user.user_name == account_lock:
|
||||
logger.warning('账号已锁定,请稍后再试')
|
||||
raise LoginException(data='', message='账号已锁定,请稍后再试')
|
||||
# 判断请求是否来自于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
|
||||
)
|
||||
# 判断是否开启验证码,开启则验证,否则不验证(dev模式下来自API文档的登录请求不检验)
|
||||
if not login_user.captcha_enabled or (
|
||||
(request_from_swagger or request_from_redoc) and AppConfig.app_env == 'dev'
|
||||
):
|
||||
pass
|
||||
else:
|
||||
await cls.__check_login_captcha(request, login_user)
|
||||
user = await login_by_account(query_db, login_user.user_name)
|
||||
if not user:
|
||||
logger.warning('用户不存在')
|
||||
raise LoginException(data='', message='用户不存在')
|
||||
if not PwdUtil.verify_password(login_user.password, user[0].password):
|
||||
cache_password_error_count = await request.app.state.redis.get(
|
||||
f'{RedisInitKeyConfig.PASSWORD_ERROR_COUNT.key}:{login_user.user_name}'
|
||||
)
|
||||
password_error_counted = 0
|
||||
if cache_password_error_count:
|
||||
password_error_counted = cache_password_error_count
|
||||
password_error_count = int(password_error_counted) + 1
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.PASSWORD_ERROR_COUNT.key}:{login_user.user_name}',
|
||||
password_error_count,
|
||||
ex=timedelta(minutes=10),
|
||||
)
|
||||
if password_error_count > CommonConstant.PASSWORD_ERROR_COUNT:
|
||||
await request.app.state.redis.delete(
|
||||
f'{RedisInitKeyConfig.PASSWORD_ERROR_COUNT.key}:{login_user.user_name}'
|
||||
)
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.ACCOUNT_LOCK.key}:{login_user.user_name}',
|
||||
login_user.user_name,
|
||||
ex=timedelta(minutes=10),
|
||||
)
|
||||
logger.warning('10分钟内密码已输错超过5次,账号已锁定,请10分钟后再试')
|
||||
raise LoginException(data='', message='10分钟内密码已输错超过5次,账号已锁定,请10分钟后再试')
|
||||
logger.warning('密码错误')
|
||||
raise LoginException(data='', message='密码错误')
|
||||
if user[0].status == '1':
|
||||
logger.warning('用户已停用')
|
||||
raise LoginException(data='', message='用户已停用')
|
||||
await request.app.state.redis.delete(f'{RedisInitKeyConfig.PASSWORD_ERROR_COUNT.key}:{login_user.user_name}')
|
||||
return user
|
||||
|
||||
@classmethod
|
||||
async def __check_login_ip(cls, request: Request) -> bool:
|
||||
"""
|
||||
校验用户登录ip是否在黑名单内
|
||||
|
||||
:param request: Request对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
black_ip_value = await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.login.blackIPList')
|
||||
black_ip_list = black_ip_value.split(',') if black_ip_value else []
|
||||
if ClientIPUtil.get_client_ip(request) in black_ip_list:
|
||||
logger.warning('当前IP禁止登录')
|
||||
raise LoginException(data='', message='当前IP禁止登录')
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
async def __check_login_captcha(cls, request: Request, login_user: UserLogin) -> bool:
|
||||
"""
|
||||
校验用户登录验证码
|
||||
|
||||
:param request: Request对象
|
||||
:param login_user: 登录用户对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
captcha_value = await request.app.state.redis.get(f'{RedisInitKeyConfig.CAPTCHA_CODES.key}:{login_user.uuid}')
|
||||
if not captcha_value:
|
||||
logger.warning('验证码已失效')
|
||||
raise LoginException(data='', message='验证码已失效')
|
||||
if login_user.code != str(captcha_value):
|
||||
logger.warning('验证码错误')
|
||||
raise LoginException(data='', message='验证码错误')
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
async def create_access_token(cls, data: dict, expires_delta: timedelta | None = None) -> str:
|
||||
"""
|
||||
根据登录信息创建当前用户token
|
||||
|
||||
:param data: 登录信息
|
||||
:param expires_delta: token有效期
|
||||
:return: token
|
||||
"""
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.now(timezone.utc) + expires_delta
|
||||
else:
|
||||
expire = datetime.now(timezone.utc) + timedelta(minutes=30)
|
||||
to_encode.update({'exp': expire})
|
||||
encoded_jwt = jwt.encode(to_encode, JwtConfig.jwt_secret_key, algorithm=JwtConfig.jwt_algorithm)
|
||||
return encoded_jwt
|
||||
|
||||
@classmethod
|
||||
async def get_current_user(
|
||||
cls, request: Request = Request, token: str = Depends(oauth2_scheme), query_db: AsyncSession = Depends(get_db)
|
||||
) -> CurrentUserModel:
|
||||
"""
|
||||
根据token获取当前用户信息
|
||||
|
||||
:param request: Request对象
|
||||
:param token: 用户token
|
||||
:param query_db: orm对象
|
||||
:return: 当前用户信息对象
|
||||
:raise: 令牌异常AuthException
|
||||
"""
|
||||
# if token[:6] != 'Bearer':
|
||||
# logger.warning("用户token不合法")
|
||||
# raise AuthException(data="", message="用户token不合法")
|
||||
try:
|
||||
if token.startswith('Bearer'):
|
||||
token = token.split(' ')[1]
|
||||
payload = jwt.decode(token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm])
|
||||
user_id: str = payload.get('user_id')
|
||||
session_id: str = payload.get('session_id')
|
||||
if not user_id:
|
||||
logger.warning('用户token不合法')
|
||||
raise AuthException(data='', message='用户token不合法')
|
||||
token_data = TokenData(user_id=int(user_id))
|
||||
except InvalidTokenError as e:
|
||||
logger.warning('用户token已失效,请重新登录')
|
||||
raise AuthException(data='', message='用户token已失效,请重新登录') from e
|
||||
query_user = await UserDao.get_user_by_id(query_db, user_id=token_data.user_id)
|
||||
if query_user.get('user_basic_info') is None:
|
||||
logger.warning('用户token不合法')
|
||||
raise AuthException(data='', message='用户token不合法')
|
||||
if AppConfig.app_same_time_login:
|
||||
redis_token = await request.app.state.redis.get(f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{session_id}')
|
||||
else:
|
||||
# 此方法可实现同一账号同一时间只能登录一次
|
||||
redis_token = await request.app.state.redis.get(
|
||||
f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{query_user.get("user_basic_info").user_id}'
|
||||
)
|
||||
if token == redis_token:
|
||||
if AppConfig.app_same_time_login:
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{session_id}',
|
||||
redis_token,
|
||||
ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes),
|
||||
)
|
||||
else:
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{query_user.get("user_basic_info").user_id}',
|
||||
redis_token,
|
||||
ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes),
|
||||
)
|
||||
|
||||
role_id_list = [item.role_id for item in query_user.get('user_role_info')]
|
||||
if 1 in role_id_list: # noqa: SIM108
|
||||
permissions = ['*:*:*']
|
||||
else:
|
||||
permissions = [row.perms for row in query_user.get('user_menu_info')]
|
||||
post_ids = ','.join([str(row.post_id) for row in query_user.get('user_post_info')])
|
||||
role_ids = ','.join([str(row.role_id) for row in query_user.get('user_role_info')])
|
||||
roles = [row.role_key for row in query_user.get('user_role_info')]
|
||||
is_default_modify_pwd = await cls.__init_password_is_modify(
|
||||
request, query_user.get('user_basic_info').pwd_update_date
|
||||
)
|
||||
is_password_expired = await cls.__password_is_expired(
|
||||
request, query_user.get('user_basic_info').pwd_update_date
|
||||
)
|
||||
|
||||
current_user = CurrentUserModel(
|
||||
permissions=permissions,
|
||||
roles=roles,
|
||||
user=UserInfoModel(
|
||||
**CamelCaseUtil.transform_result(query_user.get('user_basic_info')),
|
||||
postIds=post_ids,
|
||||
roleIds=role_ids,
|
||||
dept=CamelCaseUtil.transform_result(query_user.get('user_dept_info')),
|
||||
role=CamelCaseUtil.transform_result(query_user.get('user_role_info')),
|
||||
),
|
||||
isDefaultModifyPwd=is_default_modify_pwd,
|
||||
isPasswordExpired=is_password_expired,
|
||||
)
|
||||
# 设置当前用户信息到上下文
|
||||
RequestContext.set_current_user(current_user)
|
||||
return current_user
|
||||
logger.warning('用户token已失效,请重新登录')
|
||||
raise AuthException(data='', message='用户token已失效,请重新登录')
|
||||
|
||||
@classmethod
|
||||
async def __init_password_is_modify(cls, request: Request, pwd_update_date: datetime) -> bool:
|
||||
"""
|
||||
判断当前用户是否初始密码登录
|
||||
|
||||
:param request: Request对象
|
||||
:param pwd_update_date: 密码最后更新时间
|
||||
:return: 是否初始密码登录
|
||||
"""
|
||||
init_password_is_modify = await request.app.state.redis.get(
|
||||
f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.initPasswordModify'
|
||||
)
|
||||
return init_password_is_modify == '1' and pwd_update_date is None
|
||||
|
||||
@classmethod
|
||||
async def __password_is_expired(cls, request: Request, pwd_update_date: datetime) -> bool:
|
||||
"""
|
||||
判断当前用户密码是否过期
|
||||
|
||||
:param request: Request对象
|
||||
:param pwd_update_date: 密码最后更新时间
|
||||
:return: 密码是否过期
|
||||
"""
|
||||
password_validate_days = await request.app.state.redis.get(
|
||||
f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.passwordValidateDays'
|
||||
)
|
||||
if password_validate_days and int(password_validate_days) > 0:
|
||||
if pwd_update_date is None:
|
||||
return True
|
||||
expire_date = pwd_update_date + timedelta(days=int(password_validate_days))
|
||||
if datetime.now() > expire_date:
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def get_current_user_routers(cls, user_id: int, query_db: AsyncSession) -> list[dict[str, Any]]:
|
||||
"""
|
||||
根据用户id获取当前用户路由信息
|
||||
|
||||
:param user_id: 用户id
|
||||
:param query_db: orm对象
|
||||
:return: 当前用户路由信息对象
|
||||
"""
|
||||
query_user = await UserDao.get_user_by_id(query_db, user_id=user_id)
|
||||
user_router_menu = sorted(
|
||||
[
|
||||
row
|
||||
for row in query_user.get('user_menu_info')
|
||||
if row.menu_type in [MenuConstant.TYPE_DIR, MenuConstant.TYPE_MENU]
|
||||
],
|
||||
key=lambda x: x.order_num,
|
||||
)
|
||||
menus = cls.__generate_menus(0, user_router_menu)
|
||||
user_router = cls.__generate_user_router_menu(menus)
|
||||
return [router.model_dump(exclude_unset=True, by_alias=True) for router in user_router]
|
||||
|
||||
@classmethod
|
||||
def __generate_menus(cls, pid: int, permission_list: list[SysMenu]) -> list[MenuTreeModel]:
|
||||
"""
|
||||
工具方法:根据菜单信息生成菜单信息树形嵌套数据
|
||||
|
||||
:param pid: 菜单id
|
||||
:param permission_list: 菜单列表信息
|
||||
:return: 菜单信息树形嵌套数据
|
||||
"""
|
||||
menu_list: list[MenuTreeModel] = []
|
||||
for permission in permission_list:
|
||||
if permission.parent_id == pid:
|
||||
children = cls.__generate_menus(permission.menu_id, permission_list)
|
||||
menu_list_data = MenuTreeModel(**CamelCaseUtil.transform_result(permission))
|
||||
if children:
|
||||
menu_list_data.children = children
|
||||
menu_list.append(menu_list_data)
|
||||
|
||||
return menu_list
|
||||
|
||||
@classmethod
|
||||
def __generate_user_router_menu(cls, permission_list: list[MenuTreeModel]) -> list[RouterModel]:
|
||||
"""
|
||||
工具方法:根据菜单树信息生成路由信息树形嵌套数据
|
||||
|
||||
:param permission_list: 菜单树列表信息
|
||||
:return: 路由信息树形嵌套数据
|
||||
"""
|
||||
router_list: list[RouterModel] = []
|
||||
for permission in permission_list:
|
||||
router = RouterModel(
|
||||
hidden=permission.visible == '1',
|
||||
name=RouterUtil.get_router_name(permission),
|
||||
path=RouterUtil.get_router_path(permission),
|
||||
component=RouterUtil.get_component(permission),
|
||||
query=permission.query,
|
||||
meta=MetaModel(
|
||||
title=permission.menu_name,
|
||||
icon=permission.icon,
|
||||
noCache=permission.is_cache == 1,
|
||||
link=permission.path if RouterUtil.is_http(permission.path) else None,
|
||||
),
|
||||
)
|
||||
c_menus = permission.children
|
||||
if c_menus and permission.menu_type == MenuConstant.TYPE_DIR:
|
||||
router.always_show = True
|
||||
router.redirect = 'noRedirect'
|
||||
router.children = cls.__generate_user_router_menu(c_menus)
|
||||
elif RouterUtil.is_menu_frame(permission):
|
||||
router.meta = None
|
||||
children_list: list[RouterModel] = []
|
||||
children = RouterModel(
|
||||
path=permission.path,
|
||||
component=permission.component,
|
||||
name=RouterUtil.get_route_name(permission.route_name, permission.path),
|
||||
meta=MetaModel(
|
||||
title=permission.menu_name,
|
||||
icon=permission.icon,
|
||||
noCache=permission.is_cache == 1,
|
||||
link=permission.path if RouterUtil.is_http(permission.path) else None,
|
||||
),
|
||||
query=permission.query,
|
||||
)
|
||||
children_list.append(children)
|
||||
router.children = children_list
|
||||
elif permission.parent_id == 0 and RouterUtil.is_inner_link(permission):
|
||||
router.meta = MetaModel(title=permission.menu_name, icon=permission.icon)
|
||||
router.path = '/'
|
||||
children_list: list[RouterModel] = []
|
||||
router_path = RouterUtil.inner_link_replace_each(permission.path)
|
||||
children = RouterModel(
|
||||
path=router_path,
|
||||
component=MenuConstant.INNER_LINK,
|
||||
name=RouterUtil.get_route_name(permission.route_name, permission.path),
|
||||
meta=MetaModel(
|
||||
title=permission.menu_name,
|
||||
icon=permission.icon,
|
||||
link=permission.path if RouterUtil.is_http(permission.path) else None,
|
||||
),
|
||||
)
|
||||
children_list.append(children)
|
||||
router.children = children_list
|
||||
|
||||
router_list.append(router)
|
||||
|
||||
return router_list
|
||||
|
||||
@classmethod
|
||||
async def register_user_services(
|
||||
cls, request: Request, query_db: AsyncSession, user_register: UserRegister
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
用户注册services
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param user_register: 注册用户对象
|
||||
:return: 注册结果
|
||||
"""
|
||||
register_enabled = (
|
||||
await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.registerUser') == 'true'
|
||||
)
|
||||
captcha_enabled = (
|
||||
await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.captchaEnabled')
|
||||
== 'true'
|
||||
)
|
||||
if user_register.password == user_register.confirm_password:
|
||||
if register_enabled:
|
||||
if captcha_enabled:
|
||||
captcha_value = await request.app.state.redis.get(
|
||||
f'{RedisInitKeyConfig.CAPTCHA_CODES.key}:{user_register.uuid}'
|
||||
)
|
||||
if not captcha_value:
|
||||
raise ServiceException(message='验证码已失效')
|
||||
if user_register.code != str(captcha_value):
|
||||
raise ServiceException(message='验证码错误')
|
||||
add_user = AddUserModel(
|
||||
userName=user_register.username,
|
||||
nickName=user_register.username,
|
||||
password=PwdUtil.get_password_hash(user_register.password),
|
||||
pwdUpdateDate=datetime.now(),
|
||||
)
|
||||
result = await UserService.add_user_services(query_db, add_user)
|
||||
return result
|
||||
raise ServiceException(message='注册程序已关闭,禁止注册')
|
||||
raise ServiceException(message='两次输入的密码不一致')
|
||||
|
||||
@classmethod
|
||||
async def get_sms_code_services(cls, request: Request, query_db: AsyncSession, user: ResetUserModel) -> SmsCode:
|
||||
"""
|
||||
获取短信验证码service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param user: 用户对象
|
||||
:return: 短信验证码对象
|
||||
"""
|
||||
redis_sms_result = await request.app.state.redis.get(f'{RedisInitKeyConfig.SMS_CODE.key}:{user.session_id}')
|
||||
if redis_sms_result:
|
||||
return SmsCode(is_success=False, sms_code='', session_id='', message='短信验证码仍在有效期内')
|
||||
is_user = await UserDao.get_user_by_name(query_db, user.user_name)
|
||||
if is_user:
|
||||
sms_code = str(random.randint(100000, 999999))
|
||||
session_id = str(uuid.uuid4())
|
||||
await request.app.state.redis.set(
|
||||
f'{RedisInitKeyConfig.SMS_CODE.key}:{session_id}', sms_code, ex=timedelta(minutes=2)
|
||||
)
|
||||
# 此处模拟调用短信服务
|
||||
message_service(sms_code)
|
||||
|
||||
return SmsCode(is_success=True, sms_code=sms_code, session_id=session_id, message='获取成功')
|
||||
|
||||
return SmsCode(is_success=False, sms_code='', session_id='', message='用户不存在')
|
||||
|
||||
@classmethod
|
||||
async def forget_user_services(
|
||||
cls, request: Request, query_db: AsyncSession, forget_user: ResetUserModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
用户忘记密码services
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param forget_user: 重置用户对象
|
||||
:return: 重置结果
|
||||
"""
|
||||
redis_sms_result = await request.app.state.redis.get(
|
||||
f'{RedisInitKeyConfig.SMS_CODE.key}:{forget_user.session_id}'
|
||||
)
|
||||
if forget_user.sms_code == redis_sms_result:
|
||||
forget_user.password = PwdUtil.get_password_hash(forget_user.password)
|
||||
forget_user.user_id = (await UserDao.get_user_by_name(query_db, forget_user.user_name)).user_id
|
||||
edit_result = await UserService.reset_user_services(query_db, forget_user)
|
||||
result = edit_result.dict()
|
||||
elif not redis_sms_result:
|
||||
result = {'is_success': False, 'message': '短信验证码已过期'}
|
||||
else:
|
||||
await request.app.state.redis.delete(f'{RedisInitKeyConfig.SMS_CODE.key}:{forget_user.session_id}')
|
||||
result = {'is_success': False, 'message': '短信验证码不正确'}
|
||||
|
||||
return CrudResponseModel(**result)
|
||||
|
||||
@classmethod
|
||||
async def logout_services(cls, request: Request, token_id: str) -> bool:
|
||||
"""
|
||||
退出登录services
|
||||
|
||||
:param request: Request对象
|
||||
:param token_id: 令牌编号
|
||||
:return: 退出登录结果
|
||||
"""
|
||||
await request.app.state.redis.delete(f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{token_id}')
|
||||
# await request.app.state.redis.delete(f'{current_user.user.user_id}_access_token')
|
||||
# await request.app.state.redis.delete(f'{current_user.user.user_id}_session_id')
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class RouterUtil:
|
||||
"""
|
||||
路由处理工具类
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_router_name(cls, menu: MenuTreeModel) -> str:
|
||||
"""
|
||||
获取路由名称
|
||||
|
||||
:param menu: 菜单数对象
|
||||
:return: 路由名称
|
||||
"""
|
||||
# 非外链并且是一级目录(类型为目录)
|
||||
if cls.is_menu_frame(menu):
|
||||
return ''
|
||||
|
||||
return cls.get_route_name(menu.route_name, menu.path)
|
||||
|
||||
@classmethod
|
||||
def get_route_name(cls, name: str, path: str) -> str:
|
||||
"""
|
||||
获取路由名称,如没有配置路由名称则取路由地址
|
||||
|
||||
:param name: 路由名称
|
||||
:param path: 路由地址
|
||||
:return: 路由名称(驼峰格式)
|
||||
"""
|
||||
router_name = name if name else path
|
||||
return router_name.capitalize()
|
||||
|
||||
@classmethod
|
||||
def get_router_path(cls, menu: MenuTreeModel) -> str | None:
|
||||
"""
|
||||
获取路由地址
|
||||
|
||||
:param menu: 菜单数对象
|
||||
:return: 路由地址
|
||||
"""
|
||||
# 内链打开外网方式
|
||||
router_path = menu.path
|
||||
if menu.parent_id != 0 and cls.is_inner_link(menu):
|
||||
router_path = cls.inner_link_replace_each(router_path)
|
||||
# 非外链并且是一级目录(类型为目录)
|
||||
if menu.parent_id == 0 and menu.menu_type == MenuConstant.TYPE_DIR and menu.is_frame == MenuConstant.NO_FRAME:
|
||||
router_path = f'/{menu.path}'
|
||||
# 非外链并且是一级目录(类型为菜单)
|
||||
elif cls.is_menu_frame(menu):
|
||||
router_path = '/'
|
||||
return router_path
|
||||
|
||||
@classmethod
|
||||
def get_component(cls, menu: MenuTreeModel) -> str:
|
||||
"""
|
||||
获取组件信息
|
||||
|
||||
:param menu: 菜单数对象
|
||||
:return: 组件信息
|
||||
"""
|
||||
component = MenuConstant.LAYOUT
|
||||
if menu.component and not cls.is_menu_frame(menu):
|
||||
component = menu.component
|
||||
elif (menu.component is None or menu.component == '') and menu.parent_id != 0 and cls.is_inner_link(menu):
|
||||
component = MenuConstant.INNER_LINK
|
||||
elif (menu.component is None or menu.component == '') and cls.is_parent_view(menu):
|
||||
component = MenuConstant.PARENT_VIEW
|
||||
return component
|
||||
|
||||
@classmethod
|
||||
def is_menu_frame(cls, menu: MenuTreeModel) -> bool:
|
||||
"""
|
||||
判断是否为菜单内部跳转
|
||||
|
||||
:param menu: 菜单数对象
|
||||
:return: 是否为菜单内部跳转
|
||||
"""
|
||||
return (
|
||||
menu.parent_id == 0 and menu.menu_type == MenuConstant.TYPE_MENU and menu.is_frame == MenuConstant.NO_FRAME
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def is_inner_link(cls, menu: MenuTreeModel) -> bool:
|
||||
"""
|
||||
判断是否为内链组件
|
||||
|
||||
:param menu: 菜单数对象
|
||||
:return: 是否为内链组件
|
||||
"""
|
||||
return menu.is_frame == MenuConstant.NO_FRAME and cls.is_http(menu.path)
|
||||
|
||||
@classmethod
|
||||
def is_parent_view(cls, menu: MenuTreeModel) -> bool:
|
||||
"""
|
||||
判断是否为parent_view组件
|
||||
|
||||
:param menu: 菜单数对象
|
||||
:return: 是否为parent_view组件
|
||||
"""
|
||||
return menu.parent_id != 0 and menu.menu_type == MenuConstant.TYPE_DIR
|
||||
|
||||
@classmethod
|
||||
def is_http(cls, link: str) -> bool:
|
||||
"""
|
||||
判断是否为http(s)://开头
|
||||
|
||||
:param link: 链接
|
||||
:return: 是否为http(s)://开头
|
||||
"""
|
||||
return link.startswith((CommonConstant.HTTP, CommonConstant.HTTPS))
|
||||
|
||||
@classmethod
|
||||
def inner_link_replace_each(cls, path: str) -> str:
|
||||
"""
|
||||
内链域名特殊字符替换
|
||||
|
||||
:param path: 内链域名
|
||||
:return: 替换后的内链域名
|
||||
"""
|
||||
old_values = [CommonConstant.HTTP, CommonConstant.HTTPS, CommonConstant.WWW, '.', ':']
|
||||
new_values = ['', '', '', '/', '/']
|
||||
for old, new in zip(old_values, new_values, strict=False):
|
||||
path = path.replace(old, new)
|
||||
return path
|
||||
@@ -0,0 +1,217 @@
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.constant import CommonConstant, MenuConstant
|
||||
from common.vo import CrudResponseModel
|
||||
from exceptions.exception import ServiceException, ServiceWarning
|
||||
from module_admin.dao.menu_dao import MenuDao
|
||||
from module_admin.dao.role_dao import RoleDao
|
||||
from module_admin.entity.do.menu_do import SysMenu
|
||||
from module_admin.entity.vo.menu_vo import DeleteMenuModel, MenuModel, MenuQueryModel, MenuTreeModel
|
||||
from module_admin.entity.vo.role_vo import RoleMenuQueryModel
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel
|
||||
from utils.common_util import CamelCaseUtil
|
||||
from utils.string_util import StringUtil
|
||||
|
||||
|
||||
class MenuService:
|
||||
"""
|
||||
菜单管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_menu_tree_services(
|
||||
cls, query_db: AsyncSession, current_user: CurrentUserModel | None = None
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
获取菜单树信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param current_user: 当前用户对象
|
||||
:return: 菜单树信息对象
|
||||
"""
|
||||
menu_list_result = await MenuDao.get_menu_list_for_tree(
|
||||
query_db, current_user.user.user_id, current_user.user.role
|
||||
)
|
||||
menu_tree_model_result = cls.list_to_tree(menu_list_result)
|
||||
menu_tree_result = [menu.model_dump(exclude_unset=True, by_alias=True) for menu in menu_tree_model_result]
|
||||
|
||||
return menu_tree_result
|
||||
|
||||
@classmethod
|
||||
async def get_role_menu_tree_services(
|
||||
cls, query_db: AsyncSession, role_id: int, current_user: CurrentUserModel | None = None
|
||||
) -> RoleMenuQueryModel:
|
||||
"""
|
||||
根据角色id获取菜单树信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param role_id: 角色id
|
||||
:param current_user: 当前用户对象
|
||||
:return: 当前角色id的菜单树信息对象
|
||||
"""
|
||||
menu_list_result = await MenuDao.get_menu_list_for_tree(
|
||||
query_db, current_user.user.user_id, current_user.user.role
|
||||
)
|
||||
menu_tree_result = cls.list_to_tree(menu_list_result)
|
||||
role = await RoleDao.get_role_detail_by_id(query_db, role_id)
|
||||
role_menu_list = await RoleDao.get_role_menu_dao(query_db, role)
|
||||
checked_keys = [row.menu_id for row in role_menu_list]
|
||||
result = RoleMenuQueryModel(menus=menu_tree_result, checkedKeys=checked_keys)
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def get_menu_list_services(
|
||||
cls, query_db: AsyncSession, page_object: MenuQueryModel, current_user: CurrentUserModel | None = None
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
获取菜单列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 分页查询参数对象
|
||||
:param current_user: 当前用户对象
|
||||
:return: 菜单列表信息对象
|
||||
"""
|
||||
menu_list_result = await MenuDao.get_menu_list(
|
||||
query_db, page_object, current_user.user.user_id, current_user.user.role
|
||||
)
|
||||
|
||||
return CamelCaseUtil.transform_result(menu_list_result)
|
||||
|
||||
@classmethod
|
||||
async def check_menu_name_unique_services(cls, query_db: AsyncSession, page_object: MenuModel) -> bool:
|
||||
"""
|
||||
校验菜单名称是否唯一service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 菜单对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
menu_id = -1 if page_object.menu_id is None else page_object.menu_id
|
||||
menu = await MenuDao.get_menu_detail_by_info(query_db, MenuModel(menuName=page_object.menu_name))
|
||||
if menu and menu.menu_id != menu_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def add_menu_services(cls, query_db: AsyncSession, page_object: MenuModel) -> CrudResponseModel:
|
||||
"""
|
||||
新增菜单信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增菜单对象
|
||||
:return: 新增菜单校验结果
|
||||
"""
|
||||
if not await cls.check_menu_name_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增菜单{page_object.menu_name}失败,菜单名称已存在')
|
||||
if page_object.is_frame == MenuConstant.YES_FRAME and not StringUtil.is_http(page_object.path):
|
||||
raise ServiceException(message=f'新增菜单{page_object.menu_name}失败,地址必须以http(s)://开头')
|
||||
try:
|
||||
await MenuDao.add_menu_dao(query_db, page_object)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='新增成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def edit_menu_services(cls, query_db: AsyncSession, page_object: MenuModel) -> CrudResponseModel:
|
||||
"""
|
||||
编辑菜单信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 编辑部门对象
|
||||
:return: 编辑菜单校验结果
|
||||
"""
|
||||
edit_menu = page_object.model_dump(exclude_unset=True)
|
||||
menu_info = await cls.menu_detail_services(query_db, page_object.menu_id)
|
||||
if menu_info.menu_id:
|
||||
if not await cls.check_menu_name_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改菜单{page_object.menu_name}失败,菜单名称已存在')
|
||||
if page_object.is_frame == MenuConstant.YES_FRAME and not StringUtil.is_http(page_object.path):
|
||||
raise ServiceException(message=f'修改菜单{page_object.menu_name}失败,地址必须以http(s)://开头')
|
||||
if page_object.menu_id == page_object.parent_id:
|
||||
raise ServiceException(message=f'修改菜单{page_object.menu_name}失败,上级菜单不能选择自己')
|
||||
try:
|
||||
await MenuDao.edit_menu_dao(query_db, edit_menu)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='更新成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='菜单不存在')
|
||||
|
||||
@classmethod
|
||||
async def delete_menu_services(cls, query_db: AsyncSession, page_object: DeleteMenuModel) -> CrudResponseModel:
|
||||
"""
|
||||
删除菜单信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除菜单对象
|
||||
:return: 删除菜单校验结果
|
||||
"""
|
||||
if page_object.menu_ids:
|
||||
menu_id_list = page_object.menu_ids.split(',')
|
||||
try:
|
||||
for menu_id in menu_id_list:
|
||||
if (await MenuDao.has_child_by_menu_id_dao(query_db, int(menu_id))) > 0:
|
||||
raise ServiceWarning(message='存在子菜单,不允许删除')
|
||||
if (await MenuDao.check_menu_exist_role_dao(query_db, int(menu_id))) > 0:
|
||||
raise ServiceWarning(message='菜单已分配,不允许删除')
|
||||
await MenuDao.delete_menu_dao(query_db, MenuModel(menuId=menu_id))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='传入菜单id为空')
|
||||
|
||||
@classmethod
|
||||
async def menu_detail_services(cls, query_db: AsyncSession, menu_id: int) -> MenuModel:
|
||||
"""
|
||||
获取菜单详细信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param menu_id: 菜单id
|
||||
:return: 菜单id对应的信息
|
||||
"""
|
||||
menu = await MenuDao.get_menu_detail_by_id(query_db, menu_id=menu_id)
|
||||
result = MenuModel(**CamelCaseUtil.transform_result(menu)) if menu else MenuModel()
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def list_to_tree(cls, permission_list: Sequence[SysMenu]) -> list[MenuTreeModel]:
|
||||
"""
|
||||
工具方法:根据菜单列表信息生成树形嵌套数据
|
||||
|
||||
:param permission_list: 菜单列表信息
|
||||
:return: 菜单树形嵌套数据
|
||||
"""
|
||||
_permission_list = [
|
||||
MenuTreeModel(id=item.menu_id, label=item.menu_name, parentId=item.parent_id) for item in permission_list
|
||||
]
|
||||
# 转成id为key的字典
|
||||
mapping: dict[int, MenuTreeModel] = dict(zip([i.id for i in _permission_list], _permission_list, strict=False))
|
||||
|
||||
# 树容器
|
||||
container: list[MenuTreeModel] = []
|
||||
|
||||
for d in _permission_list:
|
||||
# 如果找不到父级项,则是根节点
|
||||
parent = mapping.get(d.parent_id)
|
||||
if parent is None:
|
||||
container.append(d)
|
||||
else:
|
||||
children: list[MenuTreeModel] = parent.children
|
||||
if not children:
|
||||
children = []
|
||||
children.append(d)
|
||||
parent.children = children
|
||||
|
||||
return container
|
||||
@@ -0,0 +1,126 @@
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.constant import CommonConstant
|
||||
from common.vo import CrudResponseModel, PageModel
|
||||
from exceptions.exception import ServiceException
|
||||
from module_admin.dao.notice_dao import NoticeDao
|
||||
from module_admin.entity.vo.notice_vo import DeleteNoticeModel, NoticeModel, NoticePageQueryModel
|
||||
from utils.common_util import CamelCaseUtil
|
||||
|
||||
|
||||
class NoticeService:
|
||||
"""
|
||||
通知公告管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_notice_list_services(
|
||||
cls, query_db: AsyncSession, query_object: NoticePageQueryModel, is_page: bool = True
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
获取通知公告列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 通知公告列表信息对象
|
||||
"""
|
||||
notice_list_result = await NoticeDao.get_notice_list(query_db, query_object, is_page)
|
||||
|
||||
return notice_list_result
|
||||
|
||||
@classmethod
|
||||
async def check_notice_unique_services(cls, query_db: AsyncSession, page_object: NoticeModel) -> bool:
|
||||
"""
|
||||
校验通知公告是否存在service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 通知公告对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
notice_id = -1 if page_object.notice_id is None else page_object.notice_id
|
||||
notice = await NoticeDao.get_notice_detail_by_info(query_db, page_object)
|
||||
if notice and notice.notice_id != notice_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def add_notice_services(cls, query_db: AsyncSession, page_object: NoticeModel) -> CrudResponseModel:
|
||||
"""
|
||||
新增通知公告信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增通知公告对象
|
||||
:return: 新增通知公告校验结果
|
||||
"""
|
||||
if not await cls.check_notice_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增通知公告{page_object.notice_title}失败,通知公告已存在')
|
||||
try:
|
||||
await NoticeDao.add_notice_dao(query_db, page_object)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='新增成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def edit_notice_services(cls, query_db: AsyncSession, page_object: NoticeModel) -> CrudResponseModel:
|
||||
"""
|
||||
编辑通知公告信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 编辑通知公告对象
|
||||
:return: 编辑通知公告校验结果
|
||||
"""
|
||||
edit_notice = page_object.model_dump(exclude_unset=True)
|
||||
notice_info = await cls.notice_detail_services(query_db, page_object.notice_id)
|
||||
if notice_info.notice_id:
|
||||
if not await cls.check_notice_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改通知公告{page_object.notice_title}失败,通知公告已存在')
|
||||
try:
|
||||
await NoticeDao.edit_notice_dao(query_db, edit_notice)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='更新成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='通知公告不存在')
|
||||
|
||||
@classmethod
|
||||
async def delete_notice_services(cls, query_db: AsyncSession, page_object: DeleteNoticeModel) -> CrudResponseModel:
|
||||
"""
|
||||
删除通知公告信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除通知公告对象
|
||||
:return: 删除通知公告校验结果
|
||||
"""
|
||||
if page_object.notice_ids:
|
||||
notice_id_list = page_object.notice_ids.split(',')
|
||||
try:
|
||||
for notice_id in notice_id_list:
|
||||
await NoticeDao.delete_notice_dao(query_db, NoticeModel(noticeId=notice_id))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='传入通知公告id为空')
|
||||
|
||||
@classmethod
|
||||
async def notice_detail_services(cls, query_db: AsyncSession, notice_id: int) -> NoticeModel:
|
||||
"""
|
||||
获取通知公告详细信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param notice_id: 通知公告id
|
||||
:return: 通知公告id对应的信息
|
||||
"""
|
||||
notice = await NoticeDao.get_notice_detail_by_id(query_db, notice_id=notice_id)
|
||||
result = NoticeModel(**CamelCaseUtil.transform_result(notice)) if notice else NoticeModel()
|
||||
|
||||
return result
|
||||
@@ -0,0 +1,78 @@
|
||||
from typing import Any
|
||||
|
||||
import jwt
|
||||
from fastapi import Request
|
||||
|
||||
from common.enums import RedisInitKeyConfig
|
||||
from common.vo import CrudResponseModel
|
||||
from config.env import AppConfig, JwtConfig
|
||||
from exceptions.exception import ServiceException
|
||||
from module_admin.entity.vo.online_vo import DeleteOnlineModel, OnlineQueryModel
|
||||
from utils.common_util import CamelCaseUtil
|
||||
|
||||
|
||||
class OnlineService:
|
||||
"""
|
||||
在线用户管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_online_list_services(cls, request: Request, query_object: OnlineQueryModel) -> list[dict[str, Any]]:
|
||||
"""
|
||||
获取在线用户表信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_object: 查询参数对象
|
||||
:return: 在线用户列表信息
|
||||
"""
|
||||
access_token_keys = await request.app.state.redis.keys(f'{RedisInitKeyConfig.ACCESS_TOKEN.key}*')
|
||||
if not access_token_keys:
|
||||
access_token_keys = []
|
||||
access_token_values_list = [await request.app.state.redis.get(key) for key in access_token_keys]
|
||||
online_info_list = []
|
||||
for item in access_token_values_list:
|
||||
payload = jwt.decode(item, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm])
|
||||
online_dict = {
|
||||
'token_id': payload.get('session_id') if AppConfig.app_same_time_login else payload.get('user_id'),
|
||||
'user_name': payload.get('user_name'),
|
||||
'dept_name': payload.get('dept_name'),
|
||||
'ipaddr': payload.get('login_info').get('ipaddr'),
|
||||
'login_location': payload.get('login_info').get('loginLocation'),
|
||||
'browser': payload.get('login_info').get('browser'),
|
||||
'os': payload.get('login_info').get('os'),
|
||||
'login_time': payload.get('login_info').get('loginTime'),
|
||||
}
|
||||
if query_object.user_name and not query_object.ipaddr:
|
||||
if query_object.user_name == payload.get('user_name'):
|
||||
online_info_list = [online_dict]
|
||||
break
|
||||
elif not query_object.user_name and query_object.ipaddr:
|
||||
if query_object.ipaddr == payload.get('login_info').get('ipaddr'):
|
||||
online_info_list = [online_dict]
|
||||
break
|
||||
elif query_object.user_name and query_object.ipaddr:
|
||||
if query_object.user_name == payload.get('user_name') and query_object.ipaddr == payload.get(
|
||||
'login_info'
|
||||
).get('ipaddr'):
|
||||
online_info_list = [online_dict]
|
||||
break
|
||||
else:
|
||||
online_info_list.append(online_dict)
|
||||
|
||||
return CamelCaseUtil.transform_result(online_info_list)
|
||||
|
||||
@classmethod
|
||||
async def delete_online_services(cls, request: Request, page_object: DeleteOnlineModel) -> CrudResponseModel:
|
||||
"""
|
||||
强退在线用户信息service
|
||||
|
||||
:param request: Request对象
|
||||
:param page_object: 强退在线用户对象
|
||||
:return: 强退在线用户校验结果
|
||||
"""
|
||||
if page_object.token_ids:
|
||||
token_id_list = page_object.token_ids.split(',')
|
||||
for token_id in token_id_list:
|
||||
await request.app.state.redis.delete(f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{token_id}')
|
||||
return CrudResponseModel(is_success=True, message='强退成功')
|
||||
raise ServiceException(message='传入session_id为空')
|
||||
@@ -0,0 +1,180 @@
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.constant import CommonConstant
|
||||
from common.vo import CrudResponseModel, PageModel
|
||||
from exceptions.exception import ServiceException
|
||||
from module_admin.dao.post_dao import PostDao
|
||||
from module_admin.entity.vo.post_vo import DeletePostModel, PostModel, PostPageQueryModel
|
||||
from utils.common_util import CamelCaseUtil
|
||||
from utils.excel_util import ExcelUtil
|
||||
|
||||
|
||||
class PostService:
|
||||
"""
|
||||
岗位管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_post_list_services(
|
||||
cls, query_db: AsyncSession, query_object: PostPageQueryModel, is_page: bool = False
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
获取岗位列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 岗位列表信息对象
|
||||
"""
|
||||
post_list_result = await PostDao.get_post_list(query_db, query_object, is_page)
|
||||
|
||||
return post_list_result
|
||||
|
||||
@classmethod
|
||||
async def check_post_name_unique_services(cls, query_db: AsyncSession, page_object: PostModel) -> bool:
|
||||
"""
|
||||
检查岗位名称是否唯一service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 岗位对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
post_id = -1 if page_object.post_id is None else page_object.post_id
|
||||
post = await PostDao.get_post_detail_by_info(query_db, PostModel(postName=page_object.post_name))
|
||||
if post and post.post_id != post_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def check_post_code_unique_services(cls, query_db: AsyncSession, page_object: PostModel) -> bool:
|
||||
"""
|
||||
检查岗位编码是否唯一service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 岗位对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
post_id = -1 if page_object.post_id is None else page_object.post_id
|
||||
post = await PostDao.get_post_detail_by_info(query_db, PostModel(postCode=page_object.post_code))
|
||||
if post and post.post_id != post_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def add_post_services(cls, query_db: AsyncSession, page_object: PostModel) -> CrudResponseModel:
|
||||
"""
|
||||
新增岗位信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增岗位对象
|
||||
:return: 新增岗位校验结果
|
||||
"""
|
||||
if not await cls.check_post_name_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增岗位{page_object.post_name}失败,岗位名称已存在')
|
||||
if not await cls.check_post_code_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增岗位{page_object.post_name}失败,岗位编码已存在')
|
||||
try:
|
||||
await PostDao.add_post_dao(query_db, page_object)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='新增成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def edit_post_services(cls, query_db: AsyncSession, page_object: PostModel) -> CrudResponseModel:
|
||||
"""
|
||||
编辑岗位信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 编辑岗位对象
|
||||
:return: 编辑岗位校验结果
|
||||
"""
|
||||
edit_post = page_object.model_dump(exclude_unset=True)
|
||||
post_info = await cls.post_detail_services(query_db, page_object.post_id)
|
||||
if post_info.post_id:
|
||||
if not await cls.check_post_name_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改岗位{page_object.post_name}失败,岗位名称已存在')
|
||||
if not await cls.check_post_code_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改岗位{page_object.post_name}失败,岗位编码已存在')
|
||||
try:
|
||||
await PostDao.edit_post_dao(query_db, edit_post)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='更新成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='岗位不存在')
|
||||
|
||||
@classmethod
|
||||
async def delete_post_services(cls, query_db: AsyncSession, page_object: DeletePostModel) -> CrudResponseModel:
|
||||
"""
|
||||
删除岗位信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除岗位对象
|
||||
:return: 删除岗位校验结果
|
||||
"""
|
||||
if page_object.post_ids:
|
||||
post_id_list = page_object.post_ids.split(',')
|
||||
try:
|
||||
for post_id in post_id_list:
|
||||
post = await cls.post_detail_services(query_db, int(post_id))
|
||||
if (await PostDao.count_user_post_dao(query_db, int(post_id))) > 0:
|
||||
raise ServiceException(message=f'{post.post_name}已分配,不能删除')
|
||||
await PostDao.delete_post_dao(query_db, PostModel(postId=post_id))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='传入岗位id为空')
|
||||
|
||||
@classmethod
|
||||
async def post_detail_services(cls, query_db: AsyncSession, post_id: int) -> PostModel:
|
||||
"""
|
||||
获取岗位详细信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param post_id: 岗位id
|
||||
:return: 岗位id对应的信息
|
||||
"""
|
||||
post = await PostDao.get_post_detail_by_id(query_db, post_id=post_id)
|
||||
result = PostModel(**CamelCaseUtil.transform_result(post)) if post else PostModel()
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
async def export_post_list_services(post_list: list) -> bytes:
|
||||
"""
|
||||
导出岗位信息service
|
||||
|
||||
:param post_list: 岗位信息列表
|
||||
:return: 岗位信息对应excel的二进制数据
|
||||
"""
|
||||
# 创建一个映射字典,将英文键映射到中文键
|
||||
mapping_dict = {
|
||||
'postId': '岗位编号',
|
||||
'postCode': '岗位编码',
|
||||
'postName': '岗位名称',
|
||||
'postSort': '显示顺序',
|
||||
'status': '状态',
|
||||
'createBy': '创建者',
|
||||
'createTime': '创建时间',
|
||||
'updateBy': '更新者',
|
||||
'updateTime': '更新时间',
|
||||
'remark': '备注',
|
||||
}
|
||||
|
||||
for item in post_list:
|
||||
if item.get('status') == '0':
|
||||
item['status'] = '正常'
|
||||
else:
|
||||
item['status'] = '停用'
|
||||
binary_data = ExcelUtil.export_list2excel(post_list, mapping_dict)
|
||||
|
||||
return binary_data
|
||||
@@ -0,0 +1,368 @@
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import ColumnElement
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.constant import CommonConstant
|
||||
from common.vo import CrudResponseModel, PageModel
|
||||
from exceptions.exception import ServiceException
|
||||
from module_admin.dao.role_dao import RoleDao
|
||||
from module_admin.dao.user_dao import UserDao
|
||||
from module_admin.entity.vo.role_vo import (
|
||||
AddRoleModel,
|
||||
DeleteRoleModel,
|
||||
RoleDeptModel,
|
||||
RoleDeptQueryModel,
|
||||
RoleMenuModel,
|
||||
RoleModel,
|
||||
RolePageQueryModel,
|
||||
)
|
||||
from module_admin.entity.vo.user_vo import UserInfoModel, UserRolePageQueryModel
|
||||
from utils.common_util import CamelCaseUtil
|
||||
from utils.excel_util import ExcelUtil
|
||||
|
||||
|
||||
class RoleService:
|
||||
"""
|
||||
角色管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_role_select_option_services(cls, query_db: AsyncSession) -> list[dict[str, Any]]:
|
||||
"""
|
||||
获取角色列表不分页信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:return: 角色列表不分页信息对象
|
||||
"""
|
||||
role_list_result = await RoleDao.get_role_select_option_dao(query_db)
|
||||
|
||||
return CamelCaseUtil.transform_result(role_list_result)
|
||||
|
||||
@classmethod
|
||||
async def get_role_dept_tree_services(cls, query_db: AsyncSession, role_id: int) -> RoleDeptQueryModel:
|
||||
"""
|
||||
根据角色id获取部门树信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param role_id: 角色id
|
||||
:return: 当前角色id的部门树信息对象
|
||||
"""
|
||||
role = await cls.role_detail_services(query_db, role_id)
|
||||
role_dept_list = await RoleDao.get_role_dept_dao(query_db, role)
|
||||
checked_keys = [row.dept_id for row in role_dept_list]
|
||||
result = RoleDeptQueryModel(checkedKeys=checked_keys)
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def get_role_list_services(
|
||||
cls,
|
||||
query_db: AsyncSession,
|
||||
query_object: RolePageQueryModel,
|
||||
data_scope_sql: ColumnElement,
|
||||
is_page: bool = False,
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
"""
|
||||
获取角色列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:param is_page: 是否开启分页
|
||||
:return: 角色列表信息对象
|
||||
"""
|
||||
role_list_result = await RoleDao.get_role_list(query_db, query_object, data_scope_sql, is_page)
|
||||
|
||||
return role_list_result
|
||||
|
||||
@classmethod
|
||||
async def check_role_allowed_services(cls, check_role: RoleModel) -> CrudResponseModel:
|
||||
"""
|
||||
校验角色是否允许操作service
|
||||
|
||||
:param check_role: 角色信息
|
||||
:return: 校验结果
|
||||
"""
|
||||
if check_role.admin:
|
||||
raise ServiceException(message='不允许操作超级管理员角色')
|
||||
return CrudResponseModel(is_success=True, message='校验通过')
|
||||
|
||||
@classmethod
|
||||
async def check_role_data_scope_services(
|
||||
cls, query_db: AsyncSession, role_ids: str, data_scope_sql: ColumnElement
|
||||
) -> None:
|
||||
"""
|
||||
校验角色是否有数据权限service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param role_ids: 角色id
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:return: 校验结果
|
||||
"""
|
||||
role_id_list = role_ids.split(',') if role_ids else []
|
||||
if role_id_list:
|
||||
for role_id in role_id_list:
|
||||
roles = await RoleDao.get_role_list(
|
||||
query_db, RolePageQueryModel(roleId=int(role_id)), data_scope_sql, is_page=False
|
||||
)
|
||||
if roles:
|
||||
continue
|
||||
raise ServiceException(message='没有权限访问角色数据')
|
||||
|
||||
@classmethod
|
||||
async def check_role_name_unique_services(cls, query_db: AsyncSession, page_object: RoleModel) -> bool:
|
||||
"""
|
||||
校验角色名称是否唯一service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 角色对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
role_id = -1 if page_object.role_id is None else page_object.role_id
|
||||
role = await RoleDao.get_role_by_info(query_db, RoleModel(roleName=page_object.role_name))
|
||||
if role and role.role_id != role_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def check_role_key_unique_services(cls, query_db: AsyncSession, page_object: RoleModel) -> bool:
|
||||
"""
|
||||
校验角色权限字符是否唯一service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 角色对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
role_id = -1 if page_object.role_id is None else page_object.role_id
|
||||
role = await RoleDao.get_role_by_info(query_db, RoleModel(roleKey=page_object.role_key))
|
||||
if role and role.role_id != role_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def add_role_services(cls, query_db: AsyncSession, page_object: AddRoleModel) -> CrudResponseModel:
|
||||
"""
|
||||
新增角色信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增角色对象
|
||||
:return: 新增角色校验结果
|
||||
"""
|
||||
add_role = RoleModel(**page_object.model_dump(by_alias=True))
|
||||
if not await cls.check_role_name_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增角色{page_object.role_name}失败,角色名称已存在')
|
||||
if not await cls.check_role_key_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增角色{page_object.role_name}失败,角色权限已存在')
|
||||
try:
|
||||
add_result = await RoleDao.add_role_dao(query_db, add_role)
|
||||
role_id = add_result.role_id
|
||||
if page_object.menu_ids:
|
||||
for menu in page_object.menu_ids:
|
||||
await RoleDao.add_role_menu_dao(query_db, RoleMenuModel(roleId=role_id, menuId=menu))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='新增成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def edit_role_services(cls, query_db: AsyncSession, page_object: AddRoleModel) -> CrudResponseModel:
|
||||
"""
|
||||
编辑角色信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 编辑角色对象
|
||||
:return: 编辑角色校验结果
|
||||
"""
|
||||
edit_role = page_object.model_dump(exclude_unset=True, exclude={'admin'})
|
||||
if page_object.type != 'status':
|
||||
del edit_role['menu_ids']
|
||||
if page_object.type == 'status':
|
||||
del edit_role['type']
|
||||
role_info = await cls.role_detail_services(query_db, edit_role.get('role_id'))
|
||||
if role_info:
|
||||
if page_object.type != 'status':
|
||||
if not await cls.check_role_name_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改角色{page_object.role_name}失败,角色名称已存在')
|
||||
if not await cls.check_role_key_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改角色{page_object.role_name}失败,角色权限已存在')
|
||||
try:
|
||||
await RoleDao.edit_role_dao(query_db, edit_role)
|
||||
if page_object.type != 'status':
|
||||
await RoleDao.delete_role_menu_dao(query_db, RoleMenuModel(roleId=page_object.role_id))
|
||||
if page_object.menu_ids:
|
||||
for menu in page_object.menu_ids:
|
||||
await RoleDao.add_role_menu_dao(
|
||||
query_db, RoleMenuModel(roleId=page_object.role_id, menuId=menu)
|
||||
)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='更新成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='角色不存在')
|
||||
|
||||
@classmethod
|
||||
async def role_datascope_services(cls, query_db: AsyncSession, page_object: AddRoleModel) -> CrudResponseModel:
|
||||
"""
|
||||
分配角色数据权限service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 角色数据权限对象
|
||||
:return: 分配角色数据权限结果
|
||||
"""
|
||||
edit_role = page_object.model_dump(exclude_unset=True, exclude={'admin', 'dept_ids'})
|
||||
role_info = await cls.role_detail_services(query_db, page_object.role_id)
|
||||
if role_info.role_id:
|
||||
try:
|
||||
await RoleDao.edit_role_dao(query_db, edit_role)
|
||||
await RoleDao.delete_role_dept_dao(query_db, RoleDeptModel(roleId=page_object.role_id))
|
||||
if page_object.dept_ids and page_object.data_scope == '2':
|
||||
for dept in page_object.dept_ids:
|
||||
await RoleDao.add_role_dept_dao(
|
||||
query_db, RoleDeptModel(roleId=page_object.role_id, deptId=dept)
|
||||
)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='分配成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='角色不存在')
|
||||
|
||||
@classmethod
|
||||
async def delete_role_services(cls, query_db: AsyncSession, page_object: DeleteRoleModel) -> CrudResponseModel:
|
||||
"""
|
||||
删除角色信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除角色对象
|
||||
:return: 删除角色校验结果
|
||||
"""
|
||||
if page_object.role_ids:
|
||||
role_id_list = page_object.role_ids.split(',')
|
||||
try:
|
||||
for role_id in role_id_list:
|
||||
role = await cls.role_detail_services(query_db, int(role_id))
|
||||
if (await RoleDao.count_user_role_dao(query_db, int(role_id))) > 0:
|
||||
raise ServiceException(message=f'角色{role.role_name}已分配,不能删除')
|
||||
role_id_dict = {
|
||||
'roleId': role_id,
|
||||
'updateBy': page_object.update_by,
|
||||
'updateTime': page_object.update_time,
|
||||
}
|
||||
await RoleDao.delete_role_menu_dao(query_db, RoleMenuModel(**role_id_dict))
|
||||
await RoleDao.delete_role_dept_dao(query_db, RoleDeptModel(**role_id_dict))
|
||||
await RoleDao.delete_role_dao(query_db, RoleModel(**role_id_dict))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='传入角色id为空')
|
||||
|
||||
@classmethod
|
||||
async def role_detail_services(cls, query_db: AsyncSession, role_id: int) -> RoleModel:
|
||||
"""
|
||||
获取角色详细信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param role_id: 角色id
|
||||
:return: 角色id对应的信息
|
||||
"""
|
||||
role = await RoleDao.get_role_detail_by_id(query_db, role_id=role_id)
|
||||
result = RoleModel(**CamelCaseUtil.transform_result(role)) if role else RoleModel()
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
async def export_role_list_services(role_list: list) -> bytes:
|
||||
"""
|
||||
导出角色列表信息service
|
||||
|
||||
:param role_list: 角色信息列表
|
||||
:return: 角色列表信息对象
|
||||
"""
|
||||
# 创建一个映射字典,将英文键映射到中文键
|
||||
mapping_dict = {
|
||||
'roleId': '角色编号',
|
||||
'roleName': '角色名称',
|
||||
'roleKey': '权限字符',
|
||||
'roleSort': '显示顺序',
|
||||
'status': '状态',
|
||||
'createBy': '创建者',
|
||||
'createTime': '创建时间',
|
||||
'updateBy': '更新者',
|
||||
'updateTime': '更新时间',
|
||||
'remark': '备注',
|
||||
}
|
||||
|
||||
for item in role_list:
|
||||
if item.get('status') == '0':
|
||||
item['status'] = '正常'
|
||||
else:
|
||||
item['status'] = '停用'
|
||||
binary_data = ExcelUtil.export_list2excel(role_list, mapping_dict)
|
||||
|
||||
return binary_data
|
||||
|
||||
@classmethod
|
||||
async def get_role_user_allocated_list_services(
|
||||
cls,
|
||||
query_db: AsyncSession,
|
||||
page_object: UserRolePageQueryModel,
|
||||
data_scope_sql: ColumnElement,
|
||||
is_page: bool = False,
|
||||
) -> PageModel:
|
||||
"""
|
||||
根据角色id获取已分配用户列表
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 用户关联角色对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:param is_page: 是否开启分页
|
||||
:return: 已分配用户列表
|
||||
"""
|
||||
query_user_list = await UserDao.get_user_role_allocated_list_by_role_id(
|
||||
query_db, page_object, data_scope_sql, is_page
|
||||
)
|
||||
allocated_list = PageModel[UserInfoModel](
|
||||
**{
|
||||
**query_user_list.model_dump(by_alias=True),
|
||||
'rows': [UserInfoModel(**row) for row in query_user_list.rows],
|
||||
}
|
||||
)
|
||||
|
||||
return allocated_list
|
||||
|
||||
@classmethod
|
||||
async def get_role_user_unallocated_list_services(
|
||||
cls,
|
||||
query_db: AsyncSession,
|
||||
page_object: UserRolePageQueryModel,
|
||||
data_scope_sql: ColumnElement,
|
||||
is_page: bool = False,
|
||||
) -> PageModel:
|
||||
"""
|
||||
根据角色id获取未分配用户列表
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 用户关联角色对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:param is_page: 是否开启分页
|
||||
:return: 未分配用户列表
|
||||
"""
|
||||
query_user_list = await UserDao.get_user_role_unallocated_list_by_role_id(
|
||||
query_db, page_object, data_scope_sql, is_page
|
||||
)
|
||||
unallocated_list = PageModel[UserInfoModel](
|
||||
**{
|
||||
**query_user_list.model_dump(by_alias=True),
|
||||
'rows': [UserInfoModel(**row) for row in query_user_list.rows],
|
||||
}
|
||||
)
|
||||
|
||||
return unallocated_list
|
||||
@@ -0,0 +1,103 @@
|
||||
import os
|
||||
import platform
|
||||
import socket
|
||||
import time
|
||||
|
||||
import anyio
|
||||
import psutil
|
||||
|
||||
from module_admin.entity.vo.server_vo import CpuInfo, MemoryInfo, PyInfo, ServerMonitorModel, SysFiles, SysInfo
|
||||
from utils.common_util import bytes2human
|
||||
|
||||
|
||||
class ServerService:
|
||||
"""
|
||||
服务监控模块服务层
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
async def get_server_monitor_info() -> ServerMonitorModel:
|
||||
# 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 = str(await anyio.Path.cwd())
|
||||
sys = SysInfo(
|
||||
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 = PyInfo(
|
||||
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:
|
||||
try:
|
||||
o = psutil.disk_usage(i.device)
|
||||
disk_data = SysFiles(
|
||||
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)
|
||||
except Exception: # noqa: PERF203
|
||||
# 忽略所有异常,跳过有问题的磁盘
|
||||
continue
|
||||
|
||||
result = ServerMonitorModel(cpu=cpu, mem=mem, sys=sys, py=py, sysFiles=sys_files)
|
||||
|
||||
return result
|
||||
@@ -0,0 +1,44 @@
|
||||
from fastapi import Request
|
||||
|
||||
from module_admin.entity.vo.transport_crypto_vo import (
|
||||
TransportCryptoFrontendConfigModel,
|
||||
TransportCryptoMonitorModel,
|
||||
TransportCryptoPublicKeyModel,
|
||||
)
|
||||
from utils.transport_crypto_util import TransportCryptoMonitorUtil, TransportCryptoUtil
|
||||
|
||||
|
||||
class TransportCryptoService:
|
||||
"""
|
||||
传输加密模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_transport_frontend_config_services(cls) -> TransportCryptoFrontendConfigModel:
|
||||
"""
|
||||
获取前端传输加密运行配置service
|
||||
|
||||
:return: 前端传输加密运行配置
|
||||
"""
|
||||
return TransportCryptoFrontendConfigModel.model_validate(TransportCryptoUtil.build_frontend_config_payload())
|
||||
|
||||
@classmethod
|
||||
async def get_transport_public_key_services(cls) -> TransportCryptoPublicKeyModel:
|
||||
"""
|
||||
获取传输加密公钥service
|
||||
|
||||
:return: 传输加密公钥信息
|
||||
"""
|
||||
return TransportCryptoPublicKeyModel.model_validate(TransportCryptoUtil.build_public_key_payload())
|
||||
|
||||
@classmethod
|
||||
async def get_transport_crypto_monitor_info_services(cls, request: Request) -> TransportCryptoMonitorModel:
|
||||
"""
|
||||
获取传输加密监控信息service
|
||||
|
||||
:param request: Request对象
|
||||
:return: 传输加密监控信息
|
||||
"""
|
||||
transport_crypto_monitor_info = await TransportCryptoMonitorUtil.get_snapshot(request.app)
|
||||
|
||||
return TransportCryptoMonitorModel.model_validate(transport_crypto_monitor_info)
|
||||
@@ -0,0 +1,679 @@
|
||||
import io
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
import pandas as pd
|
||||
from fastapi import Request, UploadFile
|
||||
from sqlalchemy import ColumnElement
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.constant import CommonConstant
|
||||
from common.vo import CrudResponseModel, PageModel
|
||||
from exceptions.exception import ServiceException
|
||||
from module_admin.dao.user_dao import UserDao
|
||||
from module_admin.entity.do.user_do import SysUserRole
|
||||
from module_admin.entity.vo.post_vo import PostPageQueryModel
|
||||
from module_admin.entity.vo.user_vo import (
|
||||
AddUserModel,
|
||||
CrudUserRoleModel,
|
||||
CurrentUserModel,
|
||||
DeleteUserModel,
|
||||
EditUserModel,
|
||||
ResetUserModel,
|
||||
SelectedRoleModel,
|
||||
UserDetailModel,
|
||||
UserInfoModel,
|
||||
UserModel,
|
||||
UserPageQueryModel,
|
||||
UserPostModel,
|
||||
UserProfileModel,
|
||||
UserRoleModel,
|
||||
UserRoleQueryModel,
|
||||
UserRoleResponseModel,
|
||||
UserRowModel,
|
||||
)
|
||||
from module_admin.service.config_service import ConfigService
|
||||
from module_admin.service.dept_service import DeptService
|
||||
from module_admin.service.post_service import PostService
|
||||
from module_admin.service.role_service import RoleService
|
||||
from utils.common_util import CamelCaseUtil
|
||||
from utils.excel_util import ExcelUtil
|
||||
from utils.pwd_util import PwdUtil
|
||||
|
||||
|
||||
class UserService:
|
||||
"""
|
||||
用户管理模块服务层
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_user_list_services(
|
||||
cls,
|
||||
query_db: AsyncSession,
|
||||
query_object: UserPageQueryModel,
|
||||
data_scope_sql: ColumnElement,
|
||||
is_page: bool = False,
|
||||
) -> PageModel[UserRowModel] | list[dict[str, Any]]:
|
||||
"""
|
||||
获取用户列表信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:param is_page: 是否开启分页
|
||||
:return: 用户列表信息对象
|
||||
"""
|
||||
query_result = await UserDao.get_user_list(query_db, query_object, data_scope_sql, is_page)
|
||||
if is_page:
|
||||
user_list_result = PageModel[UserRowModel](
|
||||
**{
|
||||
**query_result.model_dump(by_alias=True),
|
||||
'rows': [{**row[0], 'dept': row[1]} for row in query_result.rows],
|
||||
}
|
||||
)
|
||||
else:
|
||||
user_list_result = []
|
||||
if query_result:
|
||||
user_list_result = [{**row[0], 'dept': row[1]} for row in query_result]
|
||||
|
||||
return user_list_result
|
||||
|
||||
@classmethod
|
||||
async def check_user_allowed_services(cls, check_user: UserModel) -> CrudResponseModel:
|
||||
"""
|
||||
校验用户是否允许操作service
|
||||
|
||||
:param check_user: 用户信息
|
||||
:return: 校验结果
|
||||
"""
|
||||
if check_user.admin:
|
||||
raise ServiceException(message='不允许操作超级管理员用户')
|
||||
return CrudResponseModel(is_success=True, message='校验通过')
|
||||
|
||||
@classmethod
|
||||
async def check_user_data_scope_services(
|
||||
cls, query_db: AsyncSession, user_id: int, data_scope_sql: ColumnElement
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
校验用户数据权限service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param user_id: 用户id
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:return: 校验结果
|
||||
"""
|
||||
users = await UserDao.get_user_list(query_db, UserPageQueryModel(userId=user_id), data_scope_sql, is_page=False)
|
||||
if users:
|
||||
return CrudResponseModel(is_success=True, message='校验通过')
|
||||
raise ServiceException(message='没有权限访问用户数据')
|
||||
|
||||
@classmethod
|
||||
async def check_user_name_unique_services(cls, query_db: AsyncSession, page_object: UserModel) -> bool:
|
||||
"""
|
||||
校验用户名是否唯一service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 用户对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
user_id = -1 if page_object.user_id is None else page_object.user_id
|
||||
user = await UserDao.get_user_by_info(query_db, UserModel(userName=page_object.user_name))
|
||||
if user and user.user_id != user_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def check_phonenumber_unique_services(cls, query_db: AsyncSession, page_object: UserModel) -> bool:
|
||||
"""
|
||||
校验用户手机号是否唯一service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 用户对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
user_id = -1 if page_object.user_id is None else page_object.user_id
|
||||
user = await UserDao.get_user_by_info(query_db, UserModel(phonenumber=page_object.phonenumber))
|
||||
if user and user.user_id != user_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def check_email_unique_services(cls, query_db: AsyncSession, page_object: UserModel) -> bool:
|
||||
"""
|
||||
校验用户邮箱是否唯一service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 用户对象
|
||||
:return: 校验结果
|
||||
"""
|
||||
user_id = -1 if page_object.user_id is None else page_object.user_id
|
||||
user = await UserDao.get_user_by_info(query_db, UserModel(email=page_object.email))
|
||||
if user and user.user_id != user_id:
|
||||
return CommonConstant.NOT_UNIQUE
|
||||
return CommonConstant.UNIQUE
|
||||
|
||||
@classmethod
|
||||
async def add_user_services(cls, query_db: AsyncSession, page_object: AddUserModel) -> CrudResponseModel:
|
||||
"""
|
||||
新增用户信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增用户对象
|
||||
:return: 新增用户校验结果
|
||||
"""
|
||||
add_user = UserModel(**page_object.model_dump(by_alias=True))
|
||||
if not await cls.check_user_name_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增用户{page_object.user_name}失败,登录账号已存在')
|
||||
if page_object.phonenumber and not await cls.check_phonenumber_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增用户{page_object.user_name}失败,手机号码已存在')
|
||||
if page_object.email and not await cls.check_email_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'新增用户{page_object.user_name}失败,邮箱账号已存在')
|
||||
try:
|
||||
add_result = await UserDao.add_user_dao(query_db, add_user)
|
||||
user_id = add_result.user_id
|
||||
if page_object.role_ids:
|
||||
for role in page_object.role_ids:
|
||||
await UserDao.add_user_role_dao(query_db, UserRoleModel(userId=user_id, roleId=role))
|
||||
if page_object.post_ids:
|
||||
for post in page_object.post_ids:
|
||||
await UserDao.add_user_post_dao(query_db, UserPostModel(userId=user_id, postId=post))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='新增成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
def _deal_edit_user(cls, page_object: EditUserModel, edit_user: dict[str, Any]) -> None:
|
||||
"""
|
||||
处理编辑用户字典
|
||||
|
||||
:param page_object: 编辑用户对象
|
||||
:param edit_user: 编辑用户字典
|
||||
:return: None
|
||||
"""
|
||||
if page_object.type not in ['status', 'avatar', 'pwd']:
|
||||
del edit_user['role_ids']
|
||||
del edit_user['post_ids']
|
||||
del edit_user['role']
|
||||
else:
|
||||
del edit_user['type']
|
||||
|
||||
@classmethod
|
||||
async def edit_user_services(cls, query_db: AsyncSession, page_object: EditUserModel) -> CrudResponseModel:
|
||||
"""
|
||||
编辑用户信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 编辑用户对象
|
||||
:return: 编辑用户校验结果
|
||||
"""
|
||||
edit_user = page_object.model_dump(exclude_unset=True, exclude={'admin'})
|
||||
cls._deal_edit_user(page_object, edit_user)
|
||||
user_info = await cls.user_detail_services(query_db, edit_user.get('user_id'))
|
||||
if user_info.data and user_info.data.user_id:
|
||||
if page_object.type not in ['status', 'avatar', 'pwd']:
|
||||
if not await cls.check_user_name_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改用户{page_object.user_name}失败,登录账号已存在')
|
||||
if page_object.phonenumber and not await cls.check_phonenumber_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改用户{page_object.user_name}失败,手机号码已存在')
|
||||
if page_object.email and not await cls.check_email_unique_services(query_db, page_object):
|
||||
raise ServiceException(message=f'修改用户{page_object.user_name}失败,邮箱账号已存在')
|
||||
try:
|
||||
await UserDao.edit_user_dao(query_db, edit_user)
|
||||
if page_object.type not in {'status', 'avatar', 'pwd'}:
|
||||
await UserDao.delete_user_role_dao(query_db, UserRoleModel(userId=page_object.user_id))
|
||||
await UserDao.delete_user_post_dao(query_db, UserPostModel(userId=page_object.user_id))
|
||||
if page_object.role_ids:
|
||||
for role in page_object.role_ids:
|
||||
await UserDao.add_user_role_dao(
|
||||
query_db, UserRoleModel(userId=page_object.user_id, roleId=role)
|
||||
)
|
||||
if page_object.post_ids:
|
||||
for post in page_object.post_ids:
|
||||
await UserDao.add_user_post_dao(
|
||||
query_db, UserPostModel(userId=page_object.user_id, postId=post)
|
||||
)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='更新成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='用户不存在')
|
||||
|
||||
@classmethod
|
||||
async def delete_user_services(cls, query_db: AsyncSession, page_object: DeleteUserModel) -> CrudResponseModel:
|
||||
"""
|
||||
删除用户信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除用户对象
|
||||
:return: 删除用户校验结果
|
||||
"""
|
||||
if page_object.user_ids:
|
||||
user_id_list = page_object.user_ids.split(',')
|
||||
try:
|
||||
for user_id in user_id_list:
|
||||
user_id_dict = {
|
||||
'userId': user_id,
|
||||
'updateBy': page_object.update_by,
|
||||
'updateTime': page_object.update_time,
|
||||
}
|
||||
await UserDao.delete_user_role_dao(query_db, UserRoleModel(**user_id_dict))
|
||||
await UserDao.delete_user_post_dao(query_db, UserPostModel(**user_id_dict))
|
||||
await UserDao.delete_user_dao(query_db, UserModel(**user_id_dict))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='传入用户id为空')
|
||||
|
||||
@classmethod
|
||||
async def user_detail_services(cls, query_db: AsyncSession, user_id: int | str) -> UserDetailModel:
|
||||
"""
|
||||
获取用户详细信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param user_id: 用户id
|
||||
:return: 用户id对应的信息
|
||||
"""
|
||||
posts = await PostService.get_post_list_services(query_db, PostPageQueryModel(), is_page=False)
|
||||
roles = await RoleService.get_role_select_option_services(query_db)
|
||||
if user_id != '':
|
||||
query_user = await UserDao.get_user_detail_by_id(query_db, user_id=user_id)
|
||||
post_ids = ','.join([str(row.post_id) for row in query_user.get('user_post_info')])
|
||||
post_ids_list = [row.post_id for row in query_user.get('user_post_info')]
|
||||
role_ids = ','.join([str(row.role_id) for row in query_user.get('user_role_info')])
|
||||
role_ids_list = [row.role_id for row in query_user.get('user_role_info')]
|
||||
|
||||
return UserDetailModel(
|
||||
data=UserInfoModel(
|
||||
**CamelCaseUtil.transform_result(query_user.get('user_basic_info')),
|
||||
postIds=post_ids,
|
||||
roleIds=role_ids,
|
||||
dept=CamelCaseUtil.transform_result(query_user.get('user_dept_info')),
|
||||
role=CamelCaseUtil.transform_result(query_user.get('user_role_info')),
|
||||
),
|
||||
postIds=post_ids_list,
|
||||
posts=posts,
|
||||
roleIds=role_ids_list,
|
||||
roles=roles,
|
||||
)
|
||||
|
||||
return UserDetailModel(posts=posts, roles=roles)
|
||||
|
||||
@classmethod
|
||||
async def user_profile_services(cls, query_db: AsyncSession, user_id: int) -> UserProfileModel:
|
||||
"""
|
||||
获取用户个人详细信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param user_id: 用户id
|
||||
:return: 用户id对应的信息
|
||||
"""
|
||||
query_user = await UserDao.get_user_detail_by_id(query_db, user_id=user_id)
|
||||
post_ids = ','.join([str(row.post_id) for row in query_user.get('user_post_info')])
|
||||
post_group = ','.join([row.post_name for row in query_user.get('user_post_info')])
|
||||
role_ids = ','.join([str(row.role_id) for row in query_user.get('user_role_info')])
|
||||
role_group = ','.join([row.role_name for row in query_user.get('user_role_info')])
|
||||
|
||||
return UserProfileModel(
|
||||
data=UserInfoModel(
|
||||
**CamelCaseUtil.transform_result(query_user.get('user_basic_info')),
|
||||
postIds=post_ids,
|
||||
roleIds=role_ids,
|
||||
dept=CamelCaseUtil.transform_result(query_user.get('user_dept_info')),
|
||||
role=CamelCaseUtil.transform_result(query_user.get('user_role_info')),
|
||||
),
|
||||
postGroup=post_group,
|
||||
roleGroup=role_group,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def reset_user_services(cls, query_db: AsyncSession, page_object: ResetUserModel) -> CrudResponseModel:
|
||||
"""
|
||||
重置用户密码service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 重置用户对象
|
||||
:return: 重置用户校验结果
|
||||
"""
|
||||
reset_user = page_object.model_dump(exclude_unset=True, exclude={'admin'})
|
||||
if page_object.old_password:
|
||||
user = (await UserDao.get_user_detail_by_id(query_db, user_id=page_object.user_id)).get('user_basic_info')
|
||||
if not PwdUtil.verify_password(page_object.old_password, user.password):
|
||||
raise ServiceException(message='修改密码失败,旧密码错误')
|
||||
if PwdUtil.verify_password(page_object.password, user.password):
|
||||
raise ServiceException(message='新密码不能与旧密码相同')
|
||||
del reset_user['old_password']
|
||||
if page_object.sms_code and page_object.session_id:
|
||||
del reset_user['sms_code']
|
||||
del reset_user['session_id']
|
||||
try:
|
||||
reset_user['password'] = PwdUtil.get_password_hash(page_object.password)
|
||||
await UserDao.edit_user_dao(query_db, reset_user)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='重置成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
def _set_row_sex_value(cls, row: pd.Series) -> None:
|
||||
"""
|
||||
设置行性别值
|
||||
|
||||
:param row: 行数据
|
||||
:return: None
|
||||
"""
|
||||
if row['sex'] == '男':
|
||||
row['sex'] = '0'
|
||||
if row['sex'] == '女':
|
||||
row['sex'] = '1'
|
||||
if row['sex'] == '未知':
|
||||
row['sex'] = '2'
|
||||
|
||||
@classmethod
|
||||
def _set_row_status_value(cls, row: pd.Series) -> None:
|
||||
"""
|
||||
设置行状态值
|
||||
|
||||
:param row: 行数据
|
||||
:return: None
|
||||
"""
|
||||
if row['status'] == '正常':
|
||||
row['status'] = '0'
|
||||
if row['status'] == '停用':
|
||||
row['status'] = '1'
|
||||
|
||||
@classmethod
|
||||
async def batch_import_user_services(
|
||||
cls,
|
||||
request: Request,
|
||||
query_db: AsyncSession,
|
||||
file: UploadFile,
|
||||
update_support: bool,
|
||||
current_user: CurrentUserModel,
|
||||
user_data_scope_sql: ColumnElement,
|
||||
dept_data_scope_sql: ColumnElement,
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
批量导入用户service
|
||||
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param file: 用户导入文件对象
|
||||
:param update_support: 用户存在时是否更新
|
||||
:param current_user: 当前用户对象
|
||||
:param user_data_scope_sql: 用户数据权限sql
|
||||
:param dept_data_scope_sql: 部门数据权限sql
|
||||
:return: 批量导入用户结果
|
||||
"""
|
||||
header_dict = {
|
||||
'部门编号': 'dept_id',
|
||||
'登录名称': 'user_name',
|
||||
'用户名称': 'nick_name',
|
||||
'用户邮箱': 'email',
|
||||
'手机号码': 'phonenumber',
|
||||
'用户性别': 'sex',
|
||||
'帐号状态': 'status',
|
||||
}
|
||||
contents = await file.read()
|
||||
df = pd.read_excel(io.BytesIO(contents))
|
||||
await file.close()
|
||||
df.rename(columns=header_dict, inplace=True)
|
||||
add_error_result = []
|
||||
count = 0
|
||||
try:
|
||||
for _index, row in df.iterrows():
|
||||
count = count + 1
|
||||
cls._set_row_sex_value(row)
|
||||
cls._set_row_status_value(row)
|
||||
add_user = UserModel(
|
||||
deptId=row['dept_id'],
|
||||
userName=row['user_name'],
|
||||
password=PwdUtil.get_password_hash(
|
||||
await ConfigService.query_config_list_from_cache_services(
|
||||
request.app.state.redis, 'sys.user.initPassword'
|
||||
)
|
||||
),
|
||||
nickName=row['nick_name'],
|
||||
email=row['email'],
|
||||
phonenumber=str(row['phonenumber']),
|
||||
sex=row['sex'],
|
||||
status=row['status'],
|
||||
createBy=current_user.user.user_name,
|
||||
createTime=datetime.now(),
|
||||
updateBy=current_user.user.user_name,
|
||||
updateTime=datetime.now(),
|
||||
)
|
||||
user_info = await UserDao.get_user_by_info(query_db, UserModel(userName=row['user_name']))
|
||||
if user_info:
|
||||
if update_support:
|
||||
edit_user_model = UserModel(
|
||||
userId=user_info.user_id,
|
||||
deptId=row['dept_id'],
|
||||
userName=row['user_name'],
|
||||
nickName=row['nick_name'],
|
||||
email=row['email'],
|
||||
phonenumber=str(row['phonenumber']),
|
||||
sex=row['sex'],
|
||||
status=row['status'],
|
||||
updateBy=current_user.user.user_name,
|
||||
updateTime=datetime.now(),
|
||||
)
|
||||
edit_user_model.validate_fields()
|
||||
await cls.check_user_allowed_services(edit_user_model)
|
||||
if not current_user.user.admin:
|
||||
await cls.check_user_data_scope_services(
|
||||
query_db, edit_user_model.user_id, user_data_scope_sql
|
||||
)
|
||||
await DeptService.check_dept_data_scope_services(
|
||||
query_db, edit_user_model.dept_id, dept_data_scope_sql
|
||||
)
|
||||
edit_user = edit_user_model.model_dump(exclude_unset=True)
|
||||
await UserDao.edit_user_dao(query_db, edit_user)
|
||||
else:
|
||||
add_error_result.append(f'{count}.用户账号{row["user_name"]}已存在')
|
||||
else:
|
||||
add_user.validate_fields()
|
||||
if not current_user.user.admin:
|
||||
await DeptService.check_dept_data_scope_services(
|
||||
query_db, add_user.dept_id, dept_data_scope_sql
|
||||
)
|
||||
await UserDao.add_user_dao(query_db, add_user)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='\n'.join(add_error_result))
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
|
||||
@staticmethod
|
||||
async def get_user_import_template_services() -> bytes:
|
||||
"""
|
||||
获取用户导入模板service
|
||||
|
||||
:return: 用户导入模板excel的二进制数据
|
||||
"""
|
||||
header_list = ['部门编号', '登录名称', '用户名称', '用户邮箱', '手机号码', '用户性别', '帐号状态']
|
||||
selector_header_list = ['用户性别', '帐号状态']
|
||||
option_list = [{'用户性别': ['男', '女', '未知']}, {'帐号状态': ['正常', '停用']}]
|
||||
binary_data = ExcelUtil.get_excel_template(
|
||||
header_list=header_list, selector_header_list=selector_header_list, option_list=option_list
|
||||
)
|
||||
|
||||
return binary_data
|
||||
|
||||
@staticmethod
|
||||
async def export_user_list_services(user_list: list) -> bytes:
|
||||
"""
|
||||
导出用户信息service
|
||||
|
||||
:param user_list: 用户信息列表
|
||||
:return: 用户信息对应excel的二进制数据
|
||||
"""
|
||||
# 创建一个映射字典,将英文键映射到中文键
|
||||
mapping_dict = {
|
||||
'userId': '用户编号',
|
||||
'userName': '用户名称',
|
||||
'nickName': '用户昵称',
|
||||
'deptName': '部门',
|
||||
'email': '邮箱地址',
|
||||
'phonenumber': '手机号码',
|
||||
'sex': '性别',
|
||||
'status': '状态',
|
||||
'createBy': '创建者',
|
||||
'createTime': '创建时间',
|
||||
'updateBy': '更新者',
|
||||
'updateTime': '更新时间',
|
||||
'remark': '备注',
|
||||
}
|
||||
|
||||
for item in user_list:
|
||||
item['deptName'] = item.get('dept').get('deptName')
|
||||
if item.get('status') == '0':
|
||||
item['status'] = '正常'
|
||||
else:
|
||||
item['status'] = '停用'
|
||||
if item.get('sex') == '0':
|
||||
item['sex'] = '男'
|
||||
elif item.get('sex') == '1':
|
||||
item['sex'] = '女'
|
||||
else:
|
||||
item['sex'] = '未知'
|
||||
binary_data = ExcelUtil.export_list2excel(user_list, mapping_dict)
|
||||
|
||||
return binary_data
|
||||
|
||||
@classmethod
|
||||
async def get_user_role_allocated_list_services(
|
||||
cls, query_db: AsyncSession, page_object: UserRoleQueryModel
|
||||
) -> UserRoleResponseModel:
|
||||
"""
|
||||
根据用户id获取已分配角色列表
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 用户关联角色对象
|
||||
:return: 已分配角色列表
|
||||
"""
|
||||
query_user = await UserDao.get_user_detail_by_id(query_db, page_object.user_id)
|
||||
post_ids = ','.join([str(row.post_id) for row in query_user.get('user_post_info')])
|
||||
role_ids = ','.join([str(row.role_id) for row in query_user.get('user_role_info')])
|
||||
user = UserInfoModel(
|
||||
**CamelCaseUtil.transform_result(query_user.get('user_basic_info')),
|
||||
postIds=post_ids,
|
||||
roleIds=role_ids,
|
||||
dept=CamelCaseUtil.transform_result(query_user.get('user_dept_info')),
|
||||
role=CamelCaseUtil.transform_result(query_user.get('user_role_info')),
|
||||
)
|
||||
query_role_list = [
|
||||
SelectedRoleModel(**row) for row in await RoleService.get_role_select_option_services(query_db)
|
||||
]
|
||||
for model_a in query_role_list:
|
||||
for model_b in user.role:
|
||||
if model_a.role_id == model_b.role_id:
|
||||
model_a.flag = True
|
||||
result = UserRoleResponseModel(roles=query_role_list, user=user)
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def add_user_role_services(cls, query_db: AsyncSession, page_object: CrudUserRoleModel) -> CrudResponseModel:
|
||||
"""
|
||||
新增用户关联角色信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 新增用户关联角色对象
|
||||
:return: 新增用户关联角色校验结果
|
||||
"""
|
||||
if page_object.user_id and page_object.role_ids:
|
||||
role_id_list = page_object.role_ids.split(',')
|
||||
try:
|
||||
await UserDao.delete_user_role_by_user_and_role_dao(query_db, UserRoleModel(userId=page_object.user_id))
|
||||
for role_id in role_id_list:
|
||||
await UserDao.add_user_role_dao(query_db, UserRoleModel(userId=page_object.user_id, roleId=role_id))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='分配成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
elif page_object.user_id and not page_object.role_ids:
|
||||
try:
|
||||
await UserDao.delete_user_role_by_user_and_role_dao(query_db, UserRoleModel(userId=page_object.user_id))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='分配成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
elif page_object.user_ids and page_object.role_id:
|
||||
user_id_list = page_object.user_ids.split(',')
|
||||
try:
|
||||
for user_id in user_id_list:
|
||||
user_role = await cls.detail_user_role_services(
|
||||
query_db, UserRoleModel(userId=user_id, roleId=page_object.role_id)
|
||||
)
|
||||
if user_role:
|
||||
continue
|
||||
await UserDao.add_user_role_dao(query_db, UserRoleModel(userId=user_id, roleId=page_object.role_id))
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='新增成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='不满足新增条件')
|
||||
|
||||
@classmethod
|
||||
async def delete_user_role_services(
|
||||
cls, query_db: AsyncSession, page_object: CrudUserRoleModel
|
||||
) -> CrudResponseModel:
|
||||
"""
|
||||
删除用户关联角色信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 删除用户关联角色对象
|
||||
:return: 删除用户关联角色校验结果
|
||||
"""
|
||||
if (page_object.user_id and page_object.role_id) or (page_object.user_ids and page_object.role_id):
|
||||
if page_object.user_id and page_object.role_id:
|
||||
try:
|
||||
await UserDao.delete_user_role_by_user_and_role_dao(
|
||||
query_db, UserRoleModel(userId=page_object.user_id, roleId=page_object.role_id)
|
||||
)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
elif page_object.user_ids and page_object.role_id:
|
||||
user_id_list = page_object.user_ids.split(',')
|
||||
try:
|
||||
for user_id in user_id_list:
|
||||
await UserDao.delete_user_role_by_user_and_role_dao(
|
||||
query_db, UserRoleModel(userId=user_id, roleId=page_object.role_id)
|
||||
)
|
||||
await query_db.commit()
|
||||
return CrudResponseModel(is_success=True, message='删除成功')
|
||||
except Exception as e:
|
||||
await query_db.rollback()
|
||||
raise e
|
||||
else:
|
||||
raise ServiceException(message='不满足删除条件')
|
||||
else:
|
||||
raise ServiceException(message='传入用户角色关联信息为空')
|
||||
|
||||
@classmethod
|
||||
async def detail_user_role_services(cls, query_db: AsyncSession, page_object: UserRoleModel) -> SysUserRole | None:
|
||||
"""
|
||||
获取用户关联角色详细信息service
|
||||
|
||||
:param query_db: orm对象
|
||||
:param page_object: 用户关联角色对象
|
||||
:return: 用户关联角色详细信息
|
||||
"""
|
||||
user_role = await UserDao.get_user_role_detail(query_db, page_object)
|
||||
|
||||
return user_role
|
||||
Reference in New Issue
Block a user