diff --git a/ruoyi-fastapi-backend/app.py b/ruoyi-fastapi-backend/app.py new file mode 100644 index 0000000..364d6b8 --- /dev/null +++ b/ruoyi-fastapi-backend/app.py @@ -0,0 +1,112 @@ +from fastapi import FastAPI, Request +import uvicorn +from fastapi.exceptions import HTTPException +from fastapi.middleware.cors import CORSMiddleware +from contextlib import asynccontextmanager +from module_admin.controller.login_controller import loginController +from module_admin.controller.captcha_controller import captchaController +from module_admin.controller.user_controller import userController +from module_admin.controller.menu_controller import menuController +from module_admin.controller.dept_controller import deptController +from module_admin.controller.role_controller import roleController +from module_admin.controller.post_controler import postController +from module_admin.controller.dict_controller import dictController +from module_admin.controller.config_controller import configController +from module_admin.controller.notice_controller import noticeController +from module_admin.controller.log_controller import logController +from module_admin.controller.online_controller import onlineController +from module_admin.controller.job_controller import jobController +from module_admin.controller.server_controller import serverController +from module_admin.controller.cache_controller import cacheController +from module_admin.controller.common_controller import commonController +from config.get_redis import RedisUtil +from config.get_db import init_create_table +from config.get_scheduler import SchedulerUtil +from utils.response_util import * +from utils.log_util import logger +from utils.common_util import worship + + +@asynccontextmanager +async def lifespan(app: FastAPI): + logger.info("RuoYi-FastAPI开始启动") + worship() + await init_create_table() + app.state.redis = await RedisUtil.create_redis_pool() + await RedisUtil.init_sys_dict(app.state.redis) + await RedisUtil.init_sys_config(app.state.redis) + await SchedulerUtil.init_system_scheduler() + logger.info("RuoYi-FastAPI启动成功") + yield + await RedisUtil.close_redis_pool(app) + await SchedulerUtil.close_system_scheduler() + + +app = FastAPI( + title='RuoYi-FastAPI', + description='RuoYi-FastAPI接口文档', + version='1.0.0', + lifespan=lifespan, + root_path='/dev-api' +) + +# 前端页面url +origins = [ + "http://localhost:81", + "http://127.0.0.1:81", +] + +# 后台api允许跨域 +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +# 自定义token检验异常 +@app.exception_handler(AuthException) +async def auth_exception_handler(request: Request, exc: AuthException): + return ResponseUtil.unauthorized(data=exc.data, msg=exc.message) + + +# 自定义权限检验异常 +@app.exception_handler(PermissionException) +async def permission_exception_handler(request: Request, exc: PermissionException): + return ResponseUtil.forbidden(data=exc.data, msg=exc.message) + + +@app.exception_handler(HTTPException) +async def http_exception_handler(request: Request, exc: HTTPException): + return JSONResponse( + content=jsonable_encoder({"message": exc.detail, "code": exc.status_code}), + status_code=exc.status_code + ) + + +controller_list = [ + {'router': loginController, 'tags': ['登录模块']}, + {'router': captchaController, 'tags': ['验证码模块']}, + {'router': userController, 'tags': ['系统管理-用户管理']}, + {'router': roleController, 'tags': ['系统管理-角色管理']}, + {'router': menuController, 'tags': ['系统管理-菜单管理']}, + {'router': deptController, 'tags': ['系统管理-部门管理']}, + {'router': postController, 'tags': ['系统管理-岗位管理']}, + {'router': dictController, 'tags': ['系统管理-字典管理']}, + {'router': configController, 'tags': ['系统管理-参数管理']}, + {'router': noticeController, 'tags': ['系统管理-通知公告管理']}, + {'router': logController, 'tags': ['系统管理-日志管理']}, + {'router': onlineController, 'tags': ['系统监控-在线用户']}, + {'router': jobController, 'tags': ['系统监控-定时任务']}, + {'router': serverController, 'tags': ['系统监控-菜单管理']}, + {'router': cacheController, 'tags': ['系统监控-缓存监控']}, + {'router': commonController, 'tags': ['通用模块']} +] + +for controller in controller_list: + app.include_router(router=controller.get('router'), tags=controller.get('tags')) + +if __name__ == '__main__': + uvicorn.run(app='app:app', host="0.0.0.0", port=9099, reload=True) diff --git a/ruoyi-fastapi-backend/assets/font/Arial.ttf b/ruoyi-fastapi-backend/assets/font/Arial.ttf new file mode 100644 index 0000000..9512aea Binary files /dev/null and b/ruoyi-fastapi-backend/assets/font/Arial.ttf differ diff --git a/ruoyi-fastapi-backend/caches/profile/avatar/blob_20240119210836.jpeg b/ruoyi-fastapi-backend/caches/profile/avatar/blob_20240119210836.jpeg new file mode 100644 index 0000000..98516d2 Binary files /dev/null and b/ruoyi-fastapi-backend/caches/profile/avatar/blob_20240119210836.jpeg differ diff --git a/ruoyi-fastapi-backend/config/database.py b/ruoyi-fastapi-backend/config/database.py new file mode 100644 index 0000000..a9acc4c --- /dev/null +++ b/ruoyi-fastapi-backend/config/database.py @@ -0,0 +1,14 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from urllib.parse import quote_plus +from config.env import DataBaseConfig + +SQLALCHEMY_DATABASE_URL = f"mysql+pymysql://{DataBaseConfig.USERNAME}:{quote_plus(DataBaseConfig.PASSWORD)}@" \ + f"{DataBaseConfig.HOST}:{DataBaseConfig.PORT}/{DataBaseConfig.DB}" + +engine = create_engine( + SQLALCHEMY_DATABASE_URL, echo=True +) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +Base = declarative_base() diff --git a/ruoyi-fastapi-backend/config/env.py b/ruoyi-fastapi-backend/config/env.py new file mode 100644 index 0000000..3f104db --- /dev/null +++ b/ruoyi-fastapi-backend/config/env.py @@ -0,0 +1,54 @@ +import os + + +class JwtConfig: + """ + Jwt配置 + """ + SECRET_KEY = "b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55" + ALGORITHM = "HS256" + ACCESS_TOKEN_EXPIRE_MINUTES = 1440 + REDIS_TOKEN_EXPIRE_MINUTES = 30 + + +class DataBaseConfig: + """ + 数据库配置 + """ + HOST = "127.0.0.1" + PORT = 3306 + USERNAME = 'root' + PASSWORD = 'mysqlroot' + DB = 'ruoyi-fastapi' + + +class RedisConfig: + """ + Redis配置 + """ + HOST = "127.0.0.1" + PORT = 6379 + USERNAME = '' + PASSWORD = '' + DB = 2 + + +class CachePathConfig: + """ + 缓存目录配置 + """ + PATH = os.path.join(os.path.abspath(os.getcwd()), 'caches') + PATHSTR = 'caches' + + +class RedisInitKeyConfig: + """ + 系统内置Redis键名 + """ + ACCESS_TOKEN = {'key': 'access_token', 'remark': '登录令牌信息'} + SYS_DICT = {'key': 'sys_dict', 'remark': '数据字典'} + SYS_CONFIG = {'key': 'sys_config', 'remark': '配置信息'} + CAPTCHA_CODES = {'key': 'captcha_codes', 'remark': '图片验证码'} + ACCOUNT_LOCK = {'key': 'account_lock', 'remark': '用户锁定'} + PASSWORD_ERROR_COUNT = {'key': 'password_error_count', 'remark': '密码错误次数'} + SMS_CODE = {'key': 'sms_code', 'remark': '短信验证码'} diff --git a/ruoyi-fastapi-backend/config/get_db.py b/ruoyi-fastapi-backend/config/get_db.py new file mode 100644 index 0000000..6d3a9cf --- /dev/null +++ b/ruoyi-fastapi-backend/config/get_db.py @@ -0,0 +1,27 @@ +from config.database import * +from utils.log_util import logger + + +def get_db_pro(): + """ + 每一个请求处理完毕后会关闭当前连接,不同的请求使用不同的连接 + :return: + """ + current_db = SessionLocal() + try: + yield current_db + finally: + current_db.close() + + +async def init_create_table(): + """ + 应用启动时初始化数据库连接 + :return: + """ + logger.info("初始化数据库连接...") + Base.metadata.create_all(bind=engine) + logger.info("数据库连接成功") + + +get_db = get_db_pro diff --git a/ruoyi-fastapi-backend/config/get_redis.py b/ruoyi-fastapi-backend/config/get_redis.py new file mode 100644 index 0000000..c7bc8ec --- /dev/null +++ b/ruoyi-fastapi-backend/config/get_redis.py @@ -0,0 +1,65 @@ +import aioredis +from module_admin.service.dict_service import DictDataService +from module_admin.service.config_service import ConfigService +from config.env import RedisConfig +from config.database import SessionLocal +from utils.log_util import logger + + +class RedisUtil: + """ + Redis相关方法 + """ + + @classmethod + async def create_redis_pool(cls) -> aioredis.Redis: + """ + 应用启动时初始化redis连接 + :return: Redis连接对象 + """ + logger.info("开始连接redis...") + redis = await aioredis.from_url( + url=f"redis://{RedisConfig.HOST}", + port=RedisConfig.PORT, + username=RedisConfig.USERNAME, + password=RedisConfig.PASSWORD, + db=RedisConfig.DB, + encoding="utf-8", + decode_responses=True + ) + logger.info("redis连接成功") + return redis + + @classmethod + async def close_redis_pool(cls, app): + """ + 应用关闭时关闭redis连接 + :param app: fastapi对象 + :return: + """ + await app.state.redis.close() + logger.info("关闭redis连接成功") + + @classmethod + async def init_sys_dict(cls, redis): + """ + 应用启动时缓存字典表 + :param redis: redis对象 + :return: + """ + session = SessionLocal() + await DictDataService.init_cache_sys_dict_services(session, redis) + + session.close() + + @classmethod + async def init_sys_config(cls, redis): + """ + 应用启动时缓存参数配置表 + :param redis: redis对象 + :return: + """ + session = SessionLocal() + await ConfigService.init_cache_sys_config_services(session, redis) + + session.close() diff --git a/ruoyi-fastapi-backend/config/get_scheduler.py b/ruoyi-fastapi-backend/config/get_scheduler.py new file mode 100644 index 0000000..3910ebe --- /dev/null +++ b/ruoyi-fastapi-backend/config/get_scheduler.py @@ -0,0 +1,232 @@ +from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore +from apscheduler.jobstores.memory import MemoryJobStore +from apscheduler.jobstores.redis import RedisJobStore +from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor +from apscheduler.triggers.cron import CronTrigger +from apscheduler.events import EVENT_ALL +import json +from datetime import datetime, timedelta +from config.database import engine, SQLALCHEMY_DATABASE_URL, SessionLocal +from config.env import RedisConfig +from module_admin.service.job_log_service import JobLogService, JobLogModel +from module_admin.dao.job_dao import Session, JobDao +from utils.log_util import logger +import module_task + + +# 重写Cron定时 +class MyCronTrigger(CronTrigger): + @classmethod + def from_crontab(cls, expr, timezone=None): + values = expr.split() + if len(values) != 6 and len(values) != 7: + raise ValueError('Wrong number of fields; got {}, expected 6 or 7'.format(len(values))) + + second = values[0] + minute = values[1] + hour = values[2] + if '?' in values[3]: + day = None + elif 'L' in values[5]: + day = f"last {values[5].replace('L', '')}" + elif 'W' in values[3]: + day = cls.__find_recent_workday(int(values[3].split('W')[0])) + else: + day = values[3].replace('L', 'last') + month = values[4] + if '?' in values[5] or 'L' in values[5]: + week = None + elif '#' in values[5]: + week = int(values[5].split('#')[1]) + else: + week = values[5] + if '#' in values[5]: + day_of_week = int(values[5].split('#')[0]) - 1 + else: + day_of_week = None + year = values[6] if len(values) == 7 else None + return cls(second=second, minute=minute, hour=hour, day=day, month=month, week=week, + day_of_week=day_of_week, year=year, timezone=timezone) + + @classmethod + def __find_recent_workday(cls, day): + now = datetime.now() + date = datetime(now.year, now.month, day) + if date.weekday() < 5: + return date.day + else: + diff = 1 + while True: + previous_day = date - timedelta(days=diff) + if previous_day.weekday() < 5: + return previous_day.day + else: + diff += 1 + + +job_stores = { + 'default': MemoryJobStore(), + 'sqlalchemy': SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URL, engine=engine), + 'redis': RedisJobStore( + **dict( + host=RedisConfig.HOST, + port=RedisConfig.PORT, + username=RedisConfig.USERNAME, + password=RedisConfig.PASSWORD, + db=RedisConfig.DB + ) + ) +} +executors = { + 'default': ThreadPoolExecutor(20), + 'processpool': ProcessPoolExecutor(5) +} +job_defaults = { + 'coalesce': False, + 'max_instance': 1 +} +scheduler = BackgroundScheduler() +scheduler.configure(jobstores=job_stores, executors=executors, job_defaults=job_defaults) + + +class SchedulerUtil: + """ + 定时任务相关方法 + """ + + @classmethod + async def init_system_scheduler(cls, query_db: Session = SessionLocal()): + """ + 应用启动时初始化定时任务 + :return: + """ + logger.info("开始启动定时任务...") + scheduler.start() + job_list = JobDao.get_job_list_for_scheduler(query_db) + for item in job_list: + query_job = cls.get_scheduler_job(job_id=str(item.job_id)) + if query_job: + cls.remove_scheduler_job(job_id=str(item.job_id)) + cls.add_scheduler_job(item) + query_db.close() + scheduler.add_listener(cls.scheduler_event_listener, EVENT_ALL) + logger.info("系统初始定时任务加载成功") + + @classmethod + async def close_system_scheduler(cls): + """ + 应用关闭时关闭定时任务 + :return: + """ + scheduler.shutdown() + logger.info("关闭定时任务成功") + + @classmethod + def get_scheduler_job(cls, job_id): + """ + 根据任务id获取任务对象 + :param job_id: 任务id + :return: 任务对象 + """ + query_job = scheduler.get_job(job_id=str(job_id)) + + return query_job + + @classmethod + def add_scheduler_job(cls, job_info): + """ + 根据输入的任务对象信息添加任务 + :param job_info: 任务对象信息 + :return: + """ + scheduler.add_job( + func=eval(job_info.invoke_target), + trigger=MyCronTrigger.from_crontab(job_info.cron_expression), + args=job_info.job_args.split(',') if job_info.job_args else None, + kwargs=json.loads(job_info.job_kwargs) if job_info.job_kwargs else None, + id=str(job_info.job_id), + name=job_info.job_name, + misfire_grace_time=1000000000000 if job_info.misfire_policy == '3' else None, + coalesce=True if job_info.misfire_policy == '2' else False, + max_instances=3 if job_info.concurrent == '0' else 1, + jobstore=job_info.job_group, + executor=job_info.job_executor + ) + + @classmethod + def execute_scheduler_job_once(cls, job_info): + """ + 根据输入的任务对象执行一次任务 + :param job_info: 任务对象信息 + :return: + """ + scheduler.add_job( + func=eval(job_info.invoke_target), + trigger='date', + run_date=datetime.now() + timedelta(seconds=1), + args=job_info.job_args.split(',') if job_info.job_args else None, + kwargs=json.loads(job_info.job_kwargs) if job_info.job_kwargs else None, + id=str(job_info.job_id), + name=job_info.job_name, + misfire_grace_time=1000000000000 if job_info.misfire_policy == '3' else None, + coalesce=True if job_info.misfire_policy == '2' else False, + max_instances=3 if job_info.concurrent == '0' else 1, + jobstore=job_info.job_group, + executor=job_info.job_executor + ) + + @classmethod + def remove_scheduler_job(cls, job_id): + """ + 根据任务id移除任务 + :param job_id: 任务id + :return: + """ + scheduler.remove_job(job_id=str(job_id)) + + @classmethod + def scheduler_event_listener(cls, event): + # 获取事件类型和任务ID + event_type = event.__class__.__name__ + # 获取任务执行异常信息 + status = '0' + exception_info = '' + if event_type == 'JobExecutionEvent' and event.exception: + exception_info = str(event.exception) + status = '1' + job_id = event.job_id + query_job = cls.get_scheduler_job(job_id=job_id) + if query_job: + query_job_info = query_job.__getstate__() + # 获取任务名称 + job_name = query_job_info.get('name') + # 获取任务组名 + job_group = query_job._jobstore_alias + # 获取任务执行器 + job_executor = query_job_info.get('executor') + # 获取调用目标字符串 + invoke_target = query_job_info.get('func') + # 获取调用函数位置参数 + job_args = ','.join(query_job_info.get('args')) + # 获取调用函数关键字参数 + job_kwargs = json.dumps(query_job_info.get('kwargs')) + # 获取任务触发器 + job_trigger = str(query_job_info.get('trigger')) + # 构造日志消息 + job_message = f"事件类型: {event_type}, 任务ID: {job_id}, 任务名称: {job_name}, 执行于{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" + job_log = JobLogModel( + jobName=job_name, + jobGroup=job_group, + jobExecutor=job_executor, + invokeTarget=invoke_target, + jobArgs=job_args, + jobKwargs=job_kwargs, + jobTrigger=job_trigger, + jobMessage=job_message, + status=status, + exceptionInfo=exception_info + ) + session = SessionLocal() + JobLogService.add_job_log_services(session, job_log) + session.close() diff --git a/ruoyi-fastapi-backend/module_admin/annotation/log_annotation.py b/ruoyi-fastapi-backend/module_admin/annotation/log_annotation.py new file mode 100644 index 0000000..13131f8 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/annotation/log_annotation.py @@ -0,0 +1,172 @@ +from functools import wraps +from fastapi import Request +from fastapi.responses import JSONResponse, ORJSONResponse, UJSONResponse +import inspect +import os +import json +import time +from datetime import datetime +import requests +from user_agents import parse +from typing import Optional +from module_admin.service.login_service import LoginService +from module_admin.service.log_service import OperationLogService, LoginLogService +from module_admin.entity.vo.log_vo import OperLogModel, LogininforModel + + +def log_decorator(title: str, business_type: int, log_type: Optional[str] = 'operation'): + """ + 日志装饰器 + :param log_type: 日志类型(login表示登录日志,为空表示为操作日志) + :param title: 当前日志装饰器装饰的模块标题 + :param business_type: 业务类型(0其它 1新增 2修改 3删除 4授权 5导出 6导入 7强退 8生成代码 9清空数据) + :return: + """ + def decorator(func): + @wraps(func) + async def wrapper(*args, **kwargs): + start_time = time.time() + # 获取被装饰函数的文件路径 + file_path = inspect.getfile(func) + # 获取项目根路径 + project_root = os.getcwd() + # 处理文件路径,去除项目根路径部分 + relative_path = os.path.relpath(file_path, start=project_root)[0:-2].replace('\\', '.') + # 获取当前被装饰函数所在路径 + func_path = f'{relative_path}{func.__name__}()' + # 获取上下文信息 + request: Request = kwargs.get('request') + token = request.headers.get('Authorization') + query_db = kwargs.get('query_db') + request_method = request.method + operator_type = 0 + user_agent = request.headers.get('User-Agent') + if "Windows" in user_agent or "Macintosh" in user_agent or "Linux" in user_agent: + operator_type = 1 + if "Mobile" in user_agent or "Android" in user_agent or "iPhone" in user_agent: + operator_type = 2 + # 获取请求的url + oper_url = request.url.path + # 获取请求的ip及ip归属区域 + oper_ip = request.headers.get("X-Forwarded-For") + oper_location = '内网IP' + try: + if oper_ip != '127.0.0.1' and oper_ip != 'localhost': + ip_result = requests.get(f'https://qifu-api.baidubce.com/ip/geo/v1/district?ip={oper_ip}') + if ip_result.status_code == 200: + prov = ip_result.json().get('data').get('prov') + city = ip_result.json().get('data').get('city') + if prov or city: + oper_location = f'{prov}-{city}' + else: + oper_location = '未知' + else: + oper_location = '未知' + except Exception as e: + oper_location = '未知' + print(e) + finally: + # 根据不同的请求类型使用不同的方法获取请求参数 + content_type = request.headers.get("Content-Type") + if content_type and ("multipart/form-data" in content_type or 'application/x-www-form-urlencoded' in content_type): + payload = await request.form() + oper_param = "\n".join([f"{key}: {value}" for key, value in payload.items()]) + else: + payload = await request.body() + # 通过 request.path_params 直接访问路径参数 + path_params = request.path_params + oper_param = {} + if payload: + oper_param.update(json.loads(str(payload, 'utf-8'))) + if path_params: + oper_param.update(path_params) + oper_param = json.dumps(oper_param, ensure_ascii=False) + # 日志表请求参数字段长度最大为2000,因此在此处判断长度 + if len(oper_param) > 2000: + oper_param = '请求参数过长' + + # 获取操作时间 + oper_time = datetime.now() + # 此处在登录之前向原始函数传递一些登录信息,用于监测在线用户的相关信息 + login_log = {} + if log_type == 'login': + user_agent_info = parse(user_agent) + browser = f'{user_agent_info.browser.family} {user_agent_info.browser.version[0]}' + system_os = f'{user_agent_info.os.family} {user_agent_info.os.version[0]}' + login_log = dict( + ipaddr=oper_ip, + loginLocation=oper_location, + browser=browser, + os=system_os, + loginTime=oper_time.strftime('%Y-%m-%d %H:%M:%S') + ) + kwargs['form_data'].login_info = login_log + # 调用原始函数 + result = await func(*args, **kwargs) + # 获取请求耗时 + cost_time = float(time.time() - start_time) * 100 + # 判断请求是否来自api文档 + 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 isinstance(result, JSONResponse) or isinstance(result, ORJSONResponse) or isinstance(result, UJSONResponse): + result_dict = json.loads(str(result.body, 'utf-8')) + else: + if request_from_swagger or request_from_redoc: + result_dict = {} + else: + if result.status_code == 200: + result_dict = {'code': result.status_code, 'message': '获取成功'} + else: + result_dict = {'code': result.status_code, 'message': '获取失败'} + json_result = json.dumps(result_dict, ensure_ascii=False) + # 根据响应结果获取响应状态及异常信息 + status = 1 + error_msg = '' + if result_dict.get('code') == 200: + status = 0 + else: + error_msg = result_dict.get('msg') + # 根据日志类型向对应的日志表插入数据 + if log_type == 'login': + # 登录请求来自于api文档时不记录登录日志,其余情况则记录 + if request_from_swagger or request_from_redoc: + pass + else: + user = kwargs.get('form_data') + user_name = user.username + login_log['loginTime'] = oper_time + login_log['userName'] = user_name + login_log['status'] = str(status) + login_log['msg'] = result_dict.get('msg') + + LoginLogService.add_login_log_services(query_db, LogininforModel(**login_log)) + else: + current_user = await LoginService.get_current_user(request, token, query_db) + oper_name = current_user.user.user_name + dept_name = current_user.user.dept.dept_name if current_user.user.dept else None + operation_log = OperLogModel( + title=title, + businessType=business_type, + method=func_path, + requestMethod=request_method, + operatorType=operator_type, + operName=oper_name, + deptName=dept_name, + operUrl=oper_url, + operIp=oper_ip, + operLocation=oper_location, + operParam=oper_param, + jsonResult=json_result, + status=status, + errorMsg=error_msg, + operTime=oper_time, + costTime=int(cost_time) + ) + OperationLogService.add_operation_log_services(query_db, operation_log) + + return result + + return wrapper + + return decorator diff --git a/ruoyi-fastapi-backend/module_admin/annotation/pydantic_annotation.py b/ruoyi-fastapi-backend/module_admin/annotation/pydantic_annotation.py new file mode 100644 index 0000000..3069015 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/annotation/pydantic_annotation.py @@ -0,0 +1,82 @@ +import inspect +from typing import Type + +from fastapi import Query, Form +from pydantic import BaseModel +from pydantic.fields import FieldInfo + + +def as_query(cls: Type[BaseModel]): + """ + pydantic模型查询参数装饰器,将pydantic模型用于接收查询参数 + """ + new_parameters = [] + + for field_name, model_field in cls.model_fields.items(): + model_field: FieldInfo # type: ignore + + if not model_field.is_required(): + new_parameters.append( + inspect.Parameter( + model_field.alias, + inspect.Parameter.POSITIONAL_ONLY, + default=Query(model_field.default), + annotation=model_field.annotation + ) + ) + else: + new_parameters.append( + inspect.Parameter( + model_field.alias, + inspect.Parameter.POSITIONAL_ONLY, + default=Query(...), + annotation=model_field.annotation + ) + ) + + async def as_query_func(**data): + return cls(**data) + + sig = inspect.signature(as_query_func) + sig = sig.replace(parameters=new_parameters) + as_query_func.__signature__ = sig # type: ignore + setattr(cls, 'as_query', as_query_func) + return cls + + +def as_form(cls: Type[BaseModel]): + """ + pydantic模型表单参数装饰器,将pydantic模型用于接收表单参数 + """ + new_parameters = [] + + for field_name, model_field in cls.model_fields.items(): + model_field: FieldInfo # type: ignore + + if not model_field.is_required(): + new_parameters.append( + inspect.Parameter( + model_field.alias, + inspect.Parameter.POSITIONAL_ONLY, + default=Form(model_field.default), + annotation=model_field.annotation + ) + ) + else: + new_parameters.append( + inspect.Parameter( + model_field.alias, + inspect.Parameter.POSITIONAL_ONLY, + default=Form(...), + annotation=model_field.annotation + ) + ) + + async def as_form_func(**data): + return cls(**data) + + sig = inspect.signature(as_form_func) + sig = sig.replace(parameters=new_parameters) + as_form_func.__signature__ = sig # type: ignore + setattr(cls, 'as_form', as_form_func) + return cls diff --git a/ruoyi-fastapi-backend/module_admin/aspect/data_scope.py b/ruoyi-fastapi-backend/module_admin/aspect/data_scope.py new file mode 100644 index 0000000..3e03918 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/aspect/data_scope.py @@ -0,0 +1,37 @@ +from fastapi import Depends +from module_admin.entity.vo.user_vo import CurrentUserModel +from module_admin.service.login_service import LoginService +from typing import Optional + + +class GetDataScope: + """ + 获取当前用户数据权限对应的查询sql语句 + """ + def __init__(self, query_alias: Optional[str] = '', db_alias: Optional[str] = 'db', user_alias: Optional[str] = 'user_id', dept_alias: Optional[str] = 'dept_id'): + self.query_alias = query_alias + self.db_alias = db_alias + self.user_alias = user_alias + self.dept_alias = dept_alias + + def __call__(self, current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + user_id = current_user.user.user_id + dept_id = current_user.user.dept_id + role_datascope_list = [dict(role_id=item.role_id, data_scope=int(item.data_scope)) for item in current_user.user.role] + max_data_scope_dict = min(role_datascope_list, key=lambda x: x['data_scope']) + max_role_id = max_data_scope_dict['role_id'] + max_data_scope = max_data_scope_dict['data_scope'] + if self.query_alias == '' or max_data_scope == 1 or user_id == 1: + param_sql = '1 == 1' + elif max_data_scope == 2: + param_sql = f"{self.query_alias}.{self.dept_alias}.in_({self.db_alias}.query(SysRoleDept.dept_id).filter(SysRoleDept.role_id == {max_role_id})) if hasattr({self.query_alias}, '{self.dept_alias}') else 1 == 1" + elif max_data_scope == 3: + param_sql = f"{self.query_alias}.{self.dept_alias} == {dept_id} if hasattr({self.query_alias}, '{self.dept_alias}') else 1 == 1" + elif max_data_scope == 4: + param_sql = f"{self.query_alias}.{self.dept_alias}.in_({self.db_alias}.query(SysDept.dept_id).filter(or_(SysDept.dept_id == {dept_id}, func.find_in_set({dept_id}, SysDept.ancestors)))) if hasattr({self.query_alias}, '{self.dept_alias}') else 1 == 1" + elif max_data_scope == 5: + param_sql = f"{self.query_alias}.{self.user_alias} == {user_id} if hasattr({self.query_alias}, '{self.user_alias}') else 1 == 1" + else: + param_sql = '1 == 0' + + return param_sql diff --git a/ruoyi-fastapi-backend/module_admin/aspect/interface_auth.py b/ruoyi-fastapi-backend/module_admin/aspect/interface_auth.py new file mode 100644 index 0000000..876670a --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/aspect/interface_auth.py @@ -0,0 +1,19 @@ +from fastapi import Depends +from module_admin.entity.vo.user_vo import CurrentUserModel +from module_admin.service.login_service import LoginService +from utils.response_util import PermissionException + + +class CheckUserInterfaceAuth: + """ + 校验当前用户是否具有相应的接口权限 + """ + def __init__(self, perm_str: str = 'common'): + self.perm_str = perm_str + + def __call__(self, current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + user_auth_list = current_user.permissions + user_auth_list.append('common') + if '*:*:*' in user_auth_list or self.perm_str in user_auth_list: + return True + raise PermissionException(data="", message="该用户无此接口权限") diff --git a/ruoyi-fastapi-backend/module_admin/controller/cache_controller.py b/ruoyi-fastapi-backend/module_admin/controller/cache_controller.py new file mode 100644 index 0000000..7b7da4b --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/cache_controller.py @@ -0,0 +1,94 @@ +from fastapi import APIRouter +from fastapi import Depends +from module_admin.service.login_service import LoginService +from module_admin.service.cache_service import * +from utils.response_util import * +from utils.log_util import * +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth + + +cacheController = APIRouter(prefix='/monitor/cache', dependencies=[Depends(LoginService.get_current_user)]) + + +@cacheController.get("", response_model=CacheMonitorModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))]) +async def get_monitor_cache_info(request: Request): + try: + # 获取全量数据 + cache_info_query_result = await CacheService.get_cache_monitor_statistical_info_services(request) + logger.info('获取成功') + return ResponseUtil.success(data=cache_info_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@cacheController.get("/getNames", response_model=List[CacheInfoModel], dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))]) +async def get_monitor_cache_name(request: Request): + try: + # 获取全量数据 + cache_name_list_result = CacheService.get_cache_monitor_cache_name_services() + logger.info('获取成功') + return ResponseUtil.success(data=cache_name_list_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@cacheController.get("/getKeys/{cache_name}", response_model=List[str], dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))]) +async def get_monitor_cache_key(request: Request, cache_name: str): + try: + # 获取全量数据 + 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) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@cacheController.get("/getValue/{cache_name}/{cache_key}", response_model=CacheInfoModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))]) +async def get_monitor_cache_value(request: Request, cache_name: str, cache_key: str): + try: + # 获取全量数据 + 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) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@cacheController.delete("/clearCacheName/{cache_name}", dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))]) +async def clear_monitor_cache_name(request: Request, cache_name: str): + try: + clear_cache_name_result = await CacheService.clear_cache_monitor_cache_name_services(request, cache_name) + if clear_cache_name_result.is_success: + logger.info(clear_cache_name_result.message) + return ResponseUtil.success(msg=clear_cache_name_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@cacheController.delete("/clearCacheKey/{cache_key}", dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))]) +async def clear_monitor_cache_key(request: Request, cache_key: str): + try: + clear_cache_key_result = await CacheService.clear_cache_monitor_cache_key_services(request, cache_key) + if clear_cache_key_result.is_success: + logger.info(clear_cache_key_result.message) + return ResponseUtil.success(msg=clear_cache_key_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@cacheController.delete("/clearCacheAll", dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))]) +async def clear_monitor_cache_all(request: Request): + try: + clear_cache_all_result = await CacheService.clear_cache_monitor_all_services(request) + if clear_cache_all_result.is_success: + logger.info(clear_cache_all_result.message) + return ResponseUtil.success(msg=clear_cache_all_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/captcha_controller.py b/ruoyi-fastapi-backend/module_admin/controller/captcha_controller.py new file mode 100644 index 0000000..8028ece --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/captcha_controller.py @@ -0,0 +1,29 @@ +import uuid +from fastapi import APIRouter, Request +from config.env import RedisInitKeyConfig +from module_admin.service.captcha_service import * +from module_admin.entity.vo.login_vo import CaptchaCode +from utils.response_util import * +from utils.log_util import * +from datetime import timedelta + + +captchaController = APIRouter() + + +@captchaController.get("/captchaImage") +async def get_captcha_image(request: Request): + try: + captcha_enabled = True if await request.app.state.redis.get(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:sys.account.captchaEnabled") == 'true' else False + session_id = str(uuid.uuid4()) + captcha_result = CaptchaService.create_captcha_image_service() + image = captcha_result[0] + computed_result = captcha_result[1] + await request.app.state.redis.set(f"{RedisInitKeyConfig.CAPTCHA_CODES.get('key')}:{session_id}", computed_result, ex=timedelta(minutes=2)) + logger.info(f'编号为{session_id}的会话获取图片验证码成功') + return ResponseUtil.success( + model_content=CaptchaCode(captchaEnabled=captcha_enabled, img=image, uuid=session_id) + ) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/common_controller.py b/ruoyi-fastapi-backend/module_admin/controller/common_controller.py new file mode 100644 index 0000000..73aa890 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/common_controller.py @@ -0,0 +1,87 @@ +from fastapi import APIRouter, Request +from fastapi import Depends, File, Form, Query +from sqlalchemy.orm import Session +from config.env import CachePathConfig +from config.get_db import get_db +from module_admin.service.login_service import LoginService +from module_admin.service.common_service import * +from module_admin.service.config_service import ConfigService +from utils.response_util import * +from utils.log_util import * +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth +from typing import Optional + + +commonController = APIRouter(prefix='/common') + + +@commonController.post("/upload", dependencies=[Depends(LoginService.get_current_user), Depends(CheckUserInterfaceAuth('common'))]) +async def common_upload(request: Request, taskPath: str = Form(), uploadId: str = Form(), file: UploadFile = File(...)): + try: + try: + os.makedirs(os.path.join(CachePathConfig.PATH, taskPath, uploadId)) + except FileExistsError: + pass + CommonService.upload_service(CachePathConfig.PATH, taskPath, uploadId, file) + logger.info('上传成功') + return response_200(data={'filename': file.filename, 'path': f'/common/{CachePathConfig.PATHSTR}?taskPath={taskPath}&taskId={uploadId}&filename={file.filename}'}, message="上传成功") + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + +@commonController.post("/uploadForEditor", dependencies=[Depends(LoginService.get_current_user), Depends(CheckUserInterfaceAuth('common'))]) +async def editor_upload(request: Request, baseUrl: str = Form(), uploadId: str = Form(), taskPath: str = Form(), file: UploadFile = File(...)): + try: + try: + os.makedirs(os.path.join(CachePathConfig.PATH, taskPath, uploadId)) + except FileExistsError: + pass + CommonService.upload_service(CachePathConfig.PATH, taskPath, uploadId, file) + logger.info('上传成功') + return JSONResponse( + status_code=status.HTTP_200_OK, + content=jsonable_encoder( + { + 'errno': 0, + 'data': { + 'url': f'{baseUrl}/common/{CachePathConfig.PATHSTR}?taskPath={taskPath}&taskId={uploadId}&filename={file.filename}' + }, + } + ) + ) + except Exception as e: + logger.exception(e) + return JSONResponse( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + content=jsonable_encoder( + { + 'errno': 1, + 'message': str(e), + } + ) + ) + + +@commonController.get(f"/{CachePathConfig.PATHSTR}") +async def common_download(request: Request, task_path: str = Query(alias='taskPath'), task_id: str = Query(alias='taskId'), filename: str = Query()): + try: + def generate_file(): + with open(os.path.join(CachePathConfig.PATH, task_path, task_id, filename), 'rb') as response_file: + yield from response_file + return streaming_response_200(data=generate_file()) + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + +@commonController.get("/config/query/{config_key}") +async def query_system_config(request: Request, config_key: str): + try: + # 获取全量数据 + config_query_result = await ConfigService.query_config_list_from_cache_services(request.app.state.redis, config_key) + logger.info('获取成功') + return response_200(data=config_query_result, message="获取成功") + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/config_controller.py b/ruoyi-fastapi-backend/module_admin/controller/config_controller.py new file mode 100644 index 0000000..65f1b6c --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/config_controller.py @@ -0,0 +1,136 @@ +from fastapi import APIRouter +from fastapi import Depends +from config.get_db import get_db +from module_admin.service.login_service import LoginService, CurrentUserModel +from module_admin.service.config_service import * +from utils.response_util import * +from utils.log_util import * +from utils.page_util import * +from utils.common_util import bytes2file_response +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth +from module_admin.annotation.log_annotation import log_decorator + + +configController = APIRouter(prefix='/system/config', dependencies=[Depends(LoginService.get_current_user)]) + + +@configController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:config:list'))]) +async def get_system_config_list(request: Request, config_page_query: ConfigPageQueryModel = Depends(ConfigPageQueryModel.as_query), query_db: Session = Depends(get_db)): + try: + config_query = ConfigQueryModel(**config_page_query.model_dump(by_alias=True)) + # 获取全量数据 + config_query_result = ConfigService.get_config_list_services(query_db, config_query) + # 分页操作 + config_page_query_result = get_page_obj(config_query_result, config_page_query.page_num, config_page_query.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=config_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@configController.post("", dependencies=[Depends(CheckUserInterfaceAuth('system:config:add'))]) +@log_decorator(title='参数管理', business_type=1) +async def add_system_config(request: Request, add_config: ConfigModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + add_config.create_by = current_user.user.user_name + add_config.update_by = current_user.user.user_name + add_config_result = await ConfigService.add_config_services(request, query_db, add_config) + if add_config_result.is_success: + logger.info(add_config_result.message) + return ResponseUtil.success(msg=add_config_result.message) + else: + logger.warning(add_config_result.message) + return ResponseUtil.failure(msg=add_config_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@configController.put("", dependencies=[Depends(CheckUserInterfaceAuth('system:config:edit'))]) +@log_decorator(title='参数管理', business_type=2) +async def edit_system_config(request: Request, edit_config: ConfigModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + 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) + if edit_config_result.is_success: + logger.info(edit_config_result.message) + return ResponseUtil.success(msg=edit_config_result.message) + else: + logger.warning(edit_config_result.message) + return ResponseUtil.failure(msg=edit_config_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@configController.delete("/refreshCache", dependencies=[Depends(CheckUserInterfaceAuth('system:config:edit'))]) +@log_decorator(title='参数管理', business_type=2) +async def refresh_system_config(request: Request, query_db: Session = Depends(get_db)): + try: + refresh_config_result = await ConfigService.refresh_sys_config_services(request, query_db) + if refresh_config_result.is_success: + logger.info(refresh_config_result.message) + return ResponseUtil.success(msg=refresh_config_result.message) + else: + logger.warning(refresh_config_result.message) + return ResponseUtil.failure(msg=refresh_config_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@configController.delete("/{config_ids}", dependencies=[Depends(CheckUserInterfaceAuth('system:config:remove'))]) +@log_decorator(title='参数管理', business_type=3) +async def delete_system_config(request: Request, config_ids: str, query_db: Session = Depends(get_db)): + try: + delete_config = DeleteConfigModel(configIds=config_ids) + delete_config_result = await ConfigService.delete_config_services(request, query_db, delete_config) + if delete_config_result.is_success: + logger.info(delete_config_result.message) + return ResponseUtil.success(msg=delete_config_result.message) + else: + logger.warning(delete_config_result.message) + return ResponseUtil.failure(msg=delete_config_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@configController.get("/{config_id}", response_model=ConfigModel, dependencies=[Depends(CheckUserInterfaceAuth('system:config:query'))]) +async def query_detail_system_config(request: Request, config_id: int, query_db: Session = Depends(get_db)): + try: + config_detail_result = ConfigService.config_detail_services(query_db, config_id) + logger.info(f'获取config_id为{config_id}的信息成功') + return ResponseUtil.success(data=config_detail_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@configController.get("/configKey/{config_key}") +async def query_system_config(request: Request, config_key: str): + try: + # 获取全量数据 + 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) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@configController.post("/export", dependencies=[Depends(CheckUserInterfaceAuth('system:config:export'))]) +@log_decorator(title='参数管理', business_type=5) +async def export_system_config_list(request: Request, config_page_query: ConfigPageQueryModel = Depends(ConfigPageQueryModel.as_form), query_db: Session = Depends(get_db)): + try: + config_query = ConfigQueryModel(**config_page_query.model_dump(by_alias=True)) + # 获取全量数据 + config_query_result = ConfigService.get_config_list_services(query_db, config_query) + config_export_result = ConfigService.export_config_list_services(config_query_result) + logger.info('导出成功') + return ResponseUtil.streaming(data=bytes2file_response(config_export_result)) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/dept_controller.py b/ruoyi-fastapi-backend/module_admin/controller/dept_controller.py new file mode 100644 index 0000000..a39b7ad --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/dept_controller.py @@ -0,0 +1,102 @@ +from fastapi import APIRouter, Request +from fastapi import Depends +from config.get_db import get_db +from module_admin.service.login_service import LoginService, CurrentUserModel +from module_admin.service.dept_service import * +from utils.response_util import * +from utils.log_util import * +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth +from module_admin.aspect.data_scope import GetDataScope +from module_admin.annotation.log_annotation import log_decorator + + +deptController = APIRouter(prefix='/system/dept', dependencies=[Depends(LoginService.get_current_user)]) + + +@deptController.get("/list/exclude/{dept_id}", response_model=List[DeptModel], dependencies=[Depends(CheckUserInterfaceAuth('common'))]) +async def get_system_dept_tree_for_edit_option(request: Request, dept_id: int, query_db: Session = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysDept'))): + try: + dept_query = DeptModel(deptId=dept_id) + dept_query_result = DeptService.get_dept_for_edit_option_services(query_db, dept_query, data_scope_sql) + logger.info('获取成功') + return ResponseUtil.success(data=dept_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@deptController.get("/list", response_model=List[DeptModel], dependencies=[Depends(CheckUserInterfaceAuth('system:dept:list'))]) +async def get_system_dept_list(request: Request, dept_query: DeptQueryModel = Depends(DeptQueryModel.as_query), query_db: Session = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysDept'))): + try: + dept_query_result = DeptService.get_dept_list_services(query_db, dept_query, data_scope_sql) + logger.info('获取成功') + return ResponseUtil.success(data=dept_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@deptController.post("", dependencies=[Depends(CheckUserInterfaceAuth('system:dept:add'))]) +@log_decorator(title='部门管理', business_type=1) +async def add_system_dept(request: Request, add_dept: DeptModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + add_dept.create_by = current_user.user.user_name + add_dept.update_by = current_user.user.user_name + add_dept_result = DeptService.add_dept_services(query_db, add_dept) + if add_dept_result.is_success: + logger.info(add_dept_result.message) + return ResponseUtil.success(data=add_dept_result) + else: + logger.warning(add_dept_result.message) + return ResponseUtil.failure(msg=add_dept_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@deptController.put("", dependencies=[Depends(CheckUserInterfaceAuth('system:dept:edit'))]) +@log_decorator(title='部门管理', business_type=2) +async def edit_system_dept(request: Request, edit_dept: DeptModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + edit_dept.update_by = current_user.user.user_name + edit_dept.update_time = datetime.now() + edit_dept_result = DeptService.edit_dept_services(query_db, edit_dept) + if edit_dept_result.is_success: + logger.info(edit_dept_result.message) + return ResponseUtil.success(msg=edit_dept_result.message) + else: + logger.warning(edit_dept_result.message) + return ResponseUtil.failure(msg=edit_dept_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@deptController.delete("/{dept_ids}", dependencies=[Depends(CheckUserInterfaceAuth('system:dept:remove'))]) +@log_decorator(title='部门管理', business_type=3) +async def delete_system_dept(request: Request, dept_ids: str, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + delete_dept = DeleteDeptModel(deptIds=dept_ids) + delete_dept.update_by = current_user.user.user_name + delete_dept.update_time = datetime.now() + delete_dept_result = DeptService.delete_dept_services(query_db, delete_dept) + if delete_dept_result.is_success: + logger.info(delete_dept_result.message) + return ResponseUtil.success(msg=delete_dept_result.message) + else: + logger.warning(delete_dept_result.message) + return ResponseUtil.failure(msg=delete_dept_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@deptController.get("/{dept_id}", response_model=DeptModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dept:query'))]) +async def query_detail_system_dept(request: Request, dept_id: int, query_db: Session = Depends(get_db)): + try: + detail_dept_result = DeptService.dept_detail_services(query_db, dept_id) + logger.info(f'获取dept_id为{dept_id}的信息成功') + return ResponseUtil.success(data=detail_dept_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/dict_controller.py b/ruoyi-fastapi-backend/module_admin/controller/dict_controller.py new file mode 100644 index 0000000..d69a839 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/dict_controller.py @@ -0,0 +1,241 @@ +from fastapi import APIRouter +from fastapi import Depends +from config.get_db import get_db +from module_admin.service.login_service import LoginService, CurrentUserModel +from module_admin.service.dict_service import * +from utils.response_util import * +from utils.log_util import * +from utils.page_util import * +from utils.common_util import bytes2file_response +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth +from module_admin.annotation.log_annotation import log_decorator + + +dictController = APIRouter(prefix='/system/dict', dependencies=[Depends(LoginService.get_current_user)]) + + +@dictController.get("/type/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))]) +async def get_system_dict_type_list(request: Request, dict_type_page_query: DictTypePageQueryModel = Depends(DictTypePageQueryModel.as_query), query_db: Session = Depends(get_db)): + try: + dict_type_query = DictTypeQueryModel(**dict_type_page_query.model_dump(by_alias=True)) + # 获取全量数据 + dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_query) + # 分页操作 + dict_type_page_query_result = get_page_obj(dict_type_query_result, dict_type_page_query.page_num, dict_type_page_query.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=dict_type_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.post("/type", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:add'))]) +@log_decorator(title='字典管理', business_type=1) +async def add_system_dict_type(request: Request, add_dict_type: DictTypeModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + add_dict_type.create_by = current_user.user.user_name + add_dict_type.update_by = current_user.user.user_name + add_dict_type_result = await DictTypeService.add_dict_type_services(request, query_db, add_dict_type) + if add_dict_type_result.is_success: + logger.info(add_dict_type_result.message) + return ResponseUtil.success(msg=add_dict_type_result.message) + else: + logger.warning(add_dict_type_result.message) + return ResponseUtil.failure(msg=add_dict_type_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.put("/type", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:edit'))]) +@log_decorator(title='字典管理', business_type=2) +async def edit_system_dict_type(request: Request, edit_dict_type: DictTypeModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + 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) + if edit_dict_type_result.is_success: + logger.info(edit_dict_type_result.message) + return ResponseUtil.success(msg=edit_dict_type_result.message) + else: + logger.warning(edit_dict_type_result.message) + return ResponseUtil.failure(msg=edit_dict_type_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.delete("/type/refreshCache", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:edit'))]) +@log_decorator(title='字典管理', business_type=2) +async def refresh_system_dict(request: Request, query_db: Session = Depends(get_db)): + try: + refresh_dict_result = await DictTypeService.refresh_sys_dict_services(request, query_db) + if refresh_dict_result.is_success: + logger.info(refresh_dict_result.message) + return ResponseUtil.success(msg=refresh_dict_result.message) + else: + logger.warning(refresh_dict_result.message) + return ResponseUtil.failure(msg=refresh_dict_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.delete("/type/{dict_ids}", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:remove'))]) +@log_decorator(title='字典管理', business_type=3) +async def delete_system_dict_type(request: Request, dict_ids: str, query_db: Session = Depends(get_db)): + try: + delete_dict_type = DeleteDictTypeModel(dictIds=dict_ids) + delete_dict_type_result = await DictTypeService.delete_dict_type_services(request, query_db, delete_dict_type) + if delete_dict_type_result.is_success: + logger.info(delete_dict_type_result.message) + return ResponseUtil.success(msg=delete_dict_type_result.message) + else: + logger.warning(delete_dict_type_result.message) + return ResponseUtil.failure(msg=delete_dict_type_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.get("/type/optionselect", response_model=List[DictTypeModel], dependencies=[Depends(CheckUserInterfaceAuth('system:dict:query'))]) +async def query_system_dict_type_options(request: Request, query_db: Session = Depends(get_db)): + try: + dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, DictTypeQueryModel(**dict())) + logger.info(f'获取成功') + return ResponseUtil.success(data=dict_type_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.get("/type/{dict_id}", response_model=DictTypeModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:query'))]) +async def query_detail_system_dict_type(request: Request, dict_id: int, query_db: Session = Depends(get_db)): + try: + dict_type_detail_result = DictTypeService.dict_type_detail_services(query_db, dict_id) + logger.info(f'获取dict_id为{dict_id}的信息成功') + return ResponseUtil.success(data=dict_type_detail_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.post("/type/export", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:export'))]) +@log_decorator(title='字典管理', business_type=5) +async def export_system_dict_type_list(request: Request, dict_type_page_query: DictTypePageQueryModel = Depends(DictTypePageQueryModel.as_form), query_db: Session = Depends(get_db)): + try: + dict_type_query = DictTypeQueryModel(**dict_type_page_query.model_dump(by_alias=True)) + # 获取全量数据 + dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_query) + dict_type_export_result = DictTypeService.export_dict_type_list_services(dict_type_query_result) + logger.info('导出成功') + return ResponseUtil.streaming(data=bytes2file_response(dict_type_export_result)) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.get("/data/type/{dict_type}", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))]) +async def query_system_dict_type_data(request: Request, dict_type: str, query_db: Session = Depends(get_db)): + try: + # 获取全量数据 + 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) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.get("/data/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))]) +async def get_system_dict_data_list(request: Request, dict_data_page_query: DictDataPageQueryModel = Depends(DictDataPageQueryModel.as_query), query_db: Session = Depends(get_db)): + try: + dict_data_query = DictDataModel(**dict_data_page_query.model_dump(by_alias=True)) + # 获取全量数据 + dict_data_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_query) + # 分页操作 + dict_data_page_query_result = get_page_obj(dict_data_query_result, dict_data_page_query.page_num, dict_data_page_query.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=dict_data_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.post("/data", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:add'))]) +@log_decorator(title='字典管理', business_type=1) +async def add_system_dict_data(request: Request, add_dict_data: DictDataModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + add_dict_data.create_by = current_user.user.user_name + add_dict_data.update_by = current_user.user.user_name + add_dict_data_result = await DictDataService.add_dict_data_services(request, query_db, add_dict_data) + if add_dict_data_result.is_success: + logger.info(add_dict_data_result.message) + return ResponseUtil.success(msg=add_dict_data_result.message) + else: + logger.warning(add_dict_data_result.message) + return ResponseUtil.failure(msg=add_dict_data_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.put("/data", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:edit'))]) +@log_decorator(title='字典管理', business_type=2) +async def edit_system_dict_data(request: Request, edit_dict_data: DictDataModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + 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) + if edit_dict_data_result.is_success: + logger.info(edit_dict_data_result.message) + return ResponseUtil.success(msg=edit_dict_data_result.message) + else: + logger.warning(edit_dict_data_result.message) + return ResponseUtil.failure(msg=edit_dict_data_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.delete("/data/{dict_codes}", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:remove'))]) +@log_decorator(title='字典管理', business_type=3) +async def delete_system_dict_data(request: Request, dict_codes: str, query_db: Session = Depends(get_db)): + try: + delete_dict_data = DeleteDictDataModel(dictCodes=dict_codes) + delete_dict_data_result = await DictDataService.delete_dict_data_services(request, query_db, delete_dict_data) + if delete_dict_data_result.is_success: + logger.info(delete_dict_data_result.message) + return ResponseUtil.success(msg=delete_dict_data_result.message) + else: + logger.warning(delete_dict_data_result.message) + return ResponseUtil.failure(msg=delete_dict_data_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.get("/data/{dict_code}", response_model=DictDataModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:query'))]) +async def query_detail_system_dict_data(request: Request, dict_code: int, query_db: Session = Depends(get_db)): + try: + detail_dict_data_result = DictDataService.dict_data_detail_services(query_db, dict_code) + logger.info(f'获取dict_code为{dict_code}的信息成功') + return ResponseUtil.success(data=detail_dict_data_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@dictController.post("/data/export", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:export'))]) +@log_decorator(title='字典管理', business_type=5) +async def export_system_dict_data_list(request: Request, dict_data_page_query: DictDataPageQueryModel = Depends(DictDataPageQueryModel.as_form), query_db: Session = Depends(get_db)): + try: + dict_data_query = DictDataModel(**dict_data_page_query.model_dump(by_alias=True)) + # 获取全量数据 + dict_data_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_query) + dict_data_export_result = DictDataService.export_dict_data_list_services(dict_data_query_result) + logger.info('导出成功') + return ResponseUtil.streaming(data=bytes2file_response(dict_data_export_result)) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/job_controller.py b/ruoyi-fastapi-backend/module_admin/controller/job_controller.py new file mode 100644 index 0000000..dce5501 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/job_controller.py @@ -0,0 +1,207 @@ +from fastapi import APIRouter +from fastapi import Depends +from config.get_db import get_db +from module_admin.service.login_service import LoginService, CurrentUserModel +from module_admin.service.job_service import * +from module_admin.service.job_log_service import * +from utils.response_util import * +from utils.log_util import * +from utils.page_util import * +from utils.common_util import bytes2file_response +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth +from module_admin.annotation.log_annotation import log_decorator + + +jobController = APIRouter(prefix='/monitor', dependencies=[Depends(LoginService.get_current_user)]) + + +@jobController.get("/job/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:list'))]) +async def get_system_job_list(request: Request, job_page_query: JobPageQueryModel = Depends(JobPageQueryModel.as_query), query_db: Session = Depends(get_db)): + try: + job_query = JobModel(**job_page_query.model_dump(by_alias=True)) + # 获取全量数据 + job_query_result = JobService.get_job_list_services(query_db, job_query) + # 分页操作 + notice_page_query_result = get_page_obj(job_query_result, job_page_query.page_num, job_page_query.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=notice_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@jobController.post("/job", dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:add'))]) +@log_decorator(title='定时任务管理', business_type=1) +async def add_system_job(request: Request, add_job: JobModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + add_job.create_by = current_user.user.user_name + add_job.update_by = current_user.user.user_name + add_job_result = JobService.add_job_services(query_db, add_job) + if add_job_result.is_success: + logger.info(add_job_result.message) + return ResponseUtil.success(msg=add_job_result.message) + else: + logger.warning(add_job_result.message) + return ResponseUtil.failure(msg=add_job_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@jobController.put("/job", dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:edit'))]) +@log_decorator(title='定时任务管理', business_type=2) +async def edit_system_job(request: Request, edit_job: EditJobModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + edit_job.update_by = current_user.user.user_name + edit_job.update_time = datetime.now() + edit_job_result = JobService.edit_job_services(query_db, edit_job) + if edit_job_result.is_success: + logger.info(edit_job_result.message) + return ResponseUtil.success(msg=edit_job_result.message) + else: + logger.warning(edit_job_result.message) + return ResponseUtil.failure(msg=edit_job_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@jobController.put("/job/changeStatus", dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:edit'))]) +@log_decorator(title='定时任务管理', business_type=2) +async def edit_system_job(request: Request, edit_job: EditJobModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + edit_job.update_by = current_user.user.user_name + edit_job.update_time = datetime.now() + edit_job.type = 'status' + edit_job_result = JobService.edit_job_services(query_db, edit_job) + if edit_job_result.is_success: + logger.info(edit_job_result.message) + return ResponseUtil.success(msg=edit_job_result.message) + else: + logger.warning(edit_job_result.message) + return ResponseUtil.failure(msg=edit_job_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@jobController.put("/job/run", dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:changeStatus'))]) +@log_decorator(title='定时任务管理', business_type=2) +async def execute_system_job(request: Request, execute_job: JobModel, query_db: Session = Depends(get_db)): + try: + execute_job_result = JobService.execute_job_once_services(query_db, execute_job) + if execute_job_result.is_success: + logger.info(execute_job_result.message) + return ResponseUtil.success(msg=execute_job_result.message) + else: + logger.warning(execute_job_result.message) + return ResponseUtil.failure(msg=execute_job_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@jobController.delete("/job/{job_ids}", dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:remove'))]) +@log_decorator(title='定时任务管理', business_type=3) +async def delete_system_job(request: Request, job_ids: str, query_db: Session = Depends(get_db)): + try: + delete_job = DeleteJobModel(jobIds=job_ids) + delete_job_result = JobService.delete_job_services(query_db, delete_job) + if delete_job_result.is_success: + logger.info(delete_job_result.message) + return ResponseUtil.success(msg=delete_job_result.message) + else: + logger.warning(delete_job_result.message) + return ResponseUtil.failure(msg=delete_job_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@jobController.get("/job/{job_id}", response_model=JobModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:query'))]) +async def query_detail_system_job(request: Request, job_id: int, query_db: Session = Depends(get_db)): + try: + job_detail_result = JobService.job_detail_services(query_db, job_id) + logger.info(f'获取job_id为{job_id}的信息成功') + return ResponseUtil.success(data=job_detail_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@jobController.post("/job/export", dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:export'))]) +@log_decorator(title='定时任务管理', business_type=5) +async def export_system_job_list(request: Request, job_page_query: JobPageQueryModel = Depends(JobPageQueryModel.as_form), query_db: Session = Depends(get_db)): + try: + job_query = JobModel(**job_page_query.model_dump(by_alias=True)) + # 获取全量数据 + job_query_result = JobService.get_job_list_services(query_db, job_query) + job_export_result = await JobService.export_job_list_services(request, job_query_result) + logger.info('导出成功') + return ResponseUtil.streaming(data=bytes2file_response(job_export_result)) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@jobController.get("/jobLog/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:list'))]) +async def get_system_job_log_list(request: Request, job_log_page_query: JobLogPageQueryModel = Depends(JobLogPageQueryModel.as_query), query_db: Session = Depends(get_db)): + try: + job_log_query = JobLogQueryModel(**job_log_page_query.model_dump(by_alias=True)) + # 获取全量数据 + job_log_query_result = JobLogService.get_job_log_list_services(query_db, job_log_query) + # 分页操作 + notice_page_query_result = get_page_obj(job_log_query_result, job_log_page_query.page_num, job_log_page_query.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=notice_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@jobController.delete("/jobLog/{job_log_ids}", dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:remove'))]) +@log_decorator(title='定时任务日志管理', business_type=3) +async def delete_system_job_log(request: Request, job_log_ids: str, query_db: Session = Depends(get_db)): + try: + delete_job_log = DeleteJobLogModel(jobLogIds=job_log_ids) + delete_job_log_result = JobLogService.delete_job_log_services(query_db, delete_job_log) + if delete_job_log_result.is_success: + logger.info(delete_job_log_result.message) + return ResponseUtil.success(msg=delete_job_log_result.message) + else: + logger.warning(delete_job_log_result.message) + return ResponseUtil.failure(msg=delete_job_log_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@jobController.post("/jobLog/clean", dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:remove'))]) +@log_decorator(title='定时任务日志管理', business_type=9) +async def clear_system_job_log(request: Request, query_db: Session = Depends(get_db)): + try: + clear_job_log_result = JobLogService.clear_job_log_services(query_db) + if clear_job_log_result.is_success: + logger.info(clear_job_log_result.message) + return ResponseUtil.success(msg=clear_job_log_result.message) + else: + logger.warning(clear_job_log_result.message) + return ResponseUtil.failure(msg=clear_job_log_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@jobController.post("/jobLog/export", dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:export'))]) +@log_decorator(title='定时任务日志管理', business_type=5) +async def export_system_job_log_list(request: Request, job_log_page_query: JobLogPageQueryModel = Depends(JobLogPageQueryModel.as_form), query_db: Session = Depends(get_db)): + try: + job_log_query = JobLogQueryModel(**job_log_page_query.model_dump(by_alias=True)) + # 获取全量数据 + job_log_query_result = JobLogService.get_job_log_list_services(query_db, job_log_query) + job_log_export_result = JobLogService.export_job_log_list_services(query_db, job_log_query_result) + logger.info('导出成功') + return ResponseUtil.streaming(data=bytes2file_response(job_log_export_result)) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/log_controller.py b/ruoyi-fastapi-backend/module_admin/controller/log_controller.py new file mode 100644 index 0000000..bf00fd8 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/log_controller.py @@ -0,0 +1,157 @@ +from fastapi import APIRouter +from fastapi import Depends +from config.get_db import get_db +from module_admin.service.login_service import LoginService +from module_admin.service.log_service import * +from utils.response_util import * +from utils.log_util import * +from utils.page_util import * +from utils.common_util import bytes2file_response +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth +from module_admin.annotation.log_annotation import log_decorator + + +logController = APIRouter(prefix='/monitor', dependencies=[Depends(LoginService.get_current_user)]) + + +@logController.get("/operlog/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:list'))]) +async def get_system_operation_log_list(request: Request, operation_log_page_query: OperLogPageQueryModel = Depends(OperLogPageQueryModel.as_query), query_db: Session = Depends(get_db)): + try: + operation_log_query = OperLogQueryModel(**operation_log_page_query.model_dump(by_alias=True)) + # 获取全量数据 + operation_log_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_query) + # 分页操作 + operation_log_page_query_result = get_page_obj(operation_log_query_result, operation_log_page_query.page_num, operation_log_page_query.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=operation_log_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@logController.delete("/operlog/clean", dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:remove'))]) +@log_decorator(title='操作日志管理', business_type=9) +async def clear_system_operation_log(request: Request, query_db: Session = Depends(get_db)): + try: + clear_operation_log_result = OperationLogService.clear_operation_log_services(query_db) + if clear_operation_log_result.is_success: + logger.info(clear_operation_log_result.message) + return ResponseUtil.success(msg=clear_operation_log_result.message) + else: + logger.warning(clear_operation_log_result.message) + return ResponseUtil.failure(msg=clear_operation_log_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@logController.delete("/operlog/{oper_ids}", dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:remove'))]) +@log_decorator(title='操作日志管理', business_type=3) +async def delete_system_operation_log(request: Request, oper_ids: str, query_db: Session = Depends(get_db)): + try: + delete_operation_log = DeleteOperLogModel(operIds=oper_ids) + delete_operation_log_result = OperationLogService.delete_operation_log_services(query_db, delete_operation_log) + if delete_operation_log_result.is_success: + logger.info(delete_operation_log_result.message) + return ResponseUtil.success(msg=delete_operation_log_result.message) + else: + logger.warning(delete_operation_log_result.message) + return ResponseUtil.failure(msg=delete_operation_log_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@logController.post("/operlog/export", dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:export'))]) +@log_decorator(title='操作日志管理', business_type=5) +async def export_system_operation_log_list(request: Request, operation_log_page_query: OperLogPageQueryModel = Depends(OperLogPageQueryModel.as_form), query_db: Session = Depends(get_db)): + try: + operation_log_query = OperLogQueryModel(**operation_log_page_query.model_dump(by_alias=True)) + # 获取全量数据 + operation_log_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_query) + 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)) + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + +@logController.get("/logininfor/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:list'))]) +async def get_system_login_log_list(request: Request, login_log_page_query: LoginLogPageQueryModel = Depends(LoginLogPageQueryModel.as_query), query_db: Session = Depends(get_db)): + try: + login_log_query = LoginLogQueryModel(**login_log_page_query.model_dump(by_alias=True)) + # 获取全量数据 + login_log_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_query) + # 分页操作 + login_log_page_query_result = get_page_obj(login_log_query_result, login_log_page_query.page_num, login_log_page_query.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=login_log_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@logController.delete("/logininfor/clean", dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:remove'))]) +@log_decorator(title='登录日志管理', business_type=9) +async def clear_system_login_log(request: Request, query_db: Session = Depends(get_db)): + try: + clear_login_log_result = LoginLogService.clear_login_log_services(query_db) + if clear_login_log_result.is_success: + logger.info(clear_login_log_result.message) + return ResponseUtil.success(msg=clear_login_log_result.message) + else: + logger.warning(clear_login_log_result.message) + return ResponseUtil.failure(msg=clear_login_log_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@logController.delete("/logininfor/{info_ids}", dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:remove'))]) +@log_decorator(title='登录日志管理', business_type=3) +async def delete_system_login_log(request: Request, info_ids: str, query_db: Session = Depends(get_db)): + try: + delete_login_log = DeleteLoginLogModel(infoIds=info_ids) + delete_login_log_result = LoginLogService.delete_login_log_services(query_db, delete_login_log) + if delete_login_log_result.is_success: + logger.info(delete_login_log_result.message) + return ResponseUtil.success(msg=delete_login_log_result.message) + else: + logger.warning(delete_login_log_result.message) + return ResponseUtil.failure(msg=delete_login_log_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@logController.get("/logininfor/unlock/{user_name}", dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:unlock'))]) +@log_decorator(title='登录日志管理', business_type=0) +async def clear_system_login_log(request: Request, user_name: str, query_db: Session = Depends(get_db)): + try: + unlock_user = UnlockUser(userName=user_name) + unlock_user_result = await LoginLogService.unlock_user_services(request, unlock_user) + if unlock_user_result.is_success: + logger.info(unlock_user_result.message) + return ResponseUtil.success(msg=unlock_user_result.message) + else: + logger.warning(unlock_user_result.message) + return ResponseUtil.failure(msg=unlock_user_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@logController.post("/logininfor/export", dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:export'))]) +@log_decorator(title='登录日志管理', business_type=5) +async def export_system_login_log_list(request: Request, login_log_page_query: LoginLogPageQueryModel = Depends(LoginLogPageQueryModel.as_form), query_db: Session = Depends(get_db)): + try: + login_log_query = LoginLogQueryModel(**login_log_page_query.model_dump(by_alias=True)) + # 获取全量数据 + login_log_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_query) + login_log_export_result = LoginLogService.export_login_log_list_services(login_log_query_result) + logger.info('导出成功') + return ResponseUtil.streaming(data=bytes2file_response(login_log_export_result)) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/login_controller.py b/ruoyi-fastapi-backend/module_admin/controller/login_controller.py new file mode 100644 index 0000000..4d52d15 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/login_controller.py @@ -0,0 +1,125 @@ +from fastapi import APIRouter +from module_admin.service.login_service import * +from module_admin.entity.vo.login_vo import * +from module_admin.dao.login_dao import * +from config.env import JwtConfig, RedisInitKeyConfig +from utils.response_util import * +from utils.log_util import * +from module_admin.annotation.log_annotation import log_decorator +from datetime import timedelta + + +loginController = APIRouter() + + +@loginController.post("/login", response_model=Token) +@log_decorator(title='用户登录', business_type=0, log_type='login') +async def login(request: Request, form_data: CustomOAuth2PasswordRequestForm = Depends(), query_db: Session = Depends(get_db)): + captcha_enabled = True if await request.app.state.redis.get(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:sys.account.captchaEnabled") == 'true' else False + 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 + ) + try: + result = await LoginService.authenticate_user(request, query_db, user) + except LoginException as e: + return ResponseUtil.failure(msg=e.message) + try: + access_token_expires = timedelta(minutes=JwtConfig.ACCESS_TOKEN_EXPIRE_MINUTES) + session_id = str(uuid.uuid4()) + access_token = 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 + ) + await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}", access_token, + ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES)) + # 此方法可实现同一账号同一时间只能登录一次 + # await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{result[0].user_id}", access_token, + # ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES)) + 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} + ) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@loginController.get("/getInfo", response_model=CurrentUserModel) +async def get_login_user_info(request: Request, current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + logger.info('获取成功') + return ResponseUtil.success(model_content=current_user) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@loginController.get("/getRouters") +async def get_login_user_routers(request: Request, current_user: CurrentUserModel = Depends(LoginService.get_current_user), query_db: Session = Depends(get_db)): + try: + logger.info('获取成功') + user_routers = await LoginService.get_current_user_routers(current_user.user.user_id, query_db) + return ResponseUtil.success(data=user_routers) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@loginController.post("/getSmsCode", response_model=SmsCode) +async def get_sms_code(request: Request, user: ResetUserModel, query_db: Session = Depends(get_db)): + try: + sms_result = await get_sms_code_services(request, query_db, user) + if sms_result.is_success: + logger.info('获取成功') + return response_200(data=sms_result, message='获取成功') + else: + logger.warning(sms_result.message) + return response_400(data='', message=sms_result.message) + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + +@loginController.post("/forgetPwd", response_model=CrudResponseModel) +async def forget_user_pwd(request: Request, forget_user: ResetUserModel, query_db: Session = Depends(get_db)): + try: + forget_user_result = await forget_user_services(request, query_db, forget_user) + if forget_user_result.is_success: + logger.info(forget_user_result.message) + return response_200(data=forget_user_result, message=forget_user_result.message) + else: + logger.warning(forget_user_result.message) + return response_400(data="", message=forget_user_result.message) + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + +@loginController.post("/logout") +async def logout(request: Request, token: Optional[str] = Depends(oauth2_scheme)): + try: + payload = jwt.decode(token, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM]) + session_id: str = payload.get("session_id") + await logout_services(request, session_id) + logger.info('退出成功') + return ResponseUtil.success(msg="退出成功") + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/menu_controller.py b/ruoyi-fastapi-backend/module_admin/controller/menu_controller.py new file mode 100644 index 0000000..79663fa --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/menu_controller.py @@ -0,0 +1,109 @@ +from fastapi import APIRouter, Request +from fastapi import Depends +from config.get_db import get_db +from module_admin.service.login_service import LoginService +from module_admin.service.menu_service import * +from utils.response_util import * +from utils.log_util import * +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth +from module_admin.annotation.log_annotation import log_decorator + + +menuController = APIRouter(prefix='/system/menu', dependencies=[Depends(LoginService.get_current_user)]) + + +@menuController.get("/treeselect", dependencies=[Depends(CheckUserInterfaceAuth('common'))]) +async def get_system_menu_tree(request: Request, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + menu_query_result = MenuService.get_menu_tree_services(query_db, current_user) + logger.info('获取成功') + return ResponseUtil.success(data=menu_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@menuController.get("/roleMenuTreeselect/{role_id}", dependencies=[Depends(CheckUserInterfaceAuth('common'))]) +async def get_system_role_menu_tree(request: Request, role_id: int, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + role_menu_query_result = MenuService.get_role_menu_tree_services(query_db, role_id, current_user) + logger.info('获取成功') + return ResponseUtil.success(model_content=role_menu_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@menuController.get("/list", response_model=List[MenuModel], dependencies=[Depends(CheckUserInterfaceAuth('system:menu:list'))]) +async def get_system_menu_list(request: Request, menu_query: MenuQueryModel = Depends(MenuQueryModel.as_query), query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + menu_query_result = MenuService.get_menu_list_services(query_db, menu_query, current_user) + logger.info('获取成功') + return ResponseUtil.success(data=menu_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@menuController.post("", dependencies=[Depends(CheckUserInterfaceAuth('system:menu:add'))]) +@log_decorator(title='菜单管理', business_type=1) +async def add_system_menu(request: Request, add_menu: MenuModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + add_menu.create_by = current_user.user.user_name + add_menu.update_by = current_user.user.user_name + add_menu_result = MenuService.add_menu_services(query_db, add_menu) + if add_menu_result.is_success: + logger.info(add_menu_result.message) + return ResponseUtil.success(msg=add_menu_result.message) + else: + logger.warning(add_menu_result.message) + return ResponseUtil.failure(msg=add_menu_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@menuController.put("", dependencies=[Depends(CheckUserInterfaceAuth('system:menu:edit'))]) +@log_decorator(title='菜单管理', business_type=2) +async def edit_system_menu(request: Request, edit_menu: MenuModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + edit_menu.update_by = current_user.user.user_name + edit_menu.update_time = datetime.now() + edit_menu_result = MenuService.edit_menu_services(query_db, edit_menu) + if edit_menu_result.is_success: + logger.info(edit_menu_result.message) + return ResponseUtil.success(msg=edit_menu_result.message) + else: + logger.warning(edit_menu_result.message) + return ResponseUtil.failure(msg=edit_menu_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@menuController.delete("/{menu_ids}", dependencies=[Depends(CheckUserInterfaceAuth('system:menu:remove'))]) +@log_decorator(title='菜单管理', business_type=3) +async def delete_system_menu(request: Request, menu_ids: str, query_db: Session = Depends(get_db)): + try: + delete_menu = DeleteMenuModel(menuIds=menu_ids) + delete_menu_result = MenuService.delete_menu_services(query_db, delete_menu) + if delete_menu_result.is_success: + logger.info(delete_menu_result.message) + return ResponseUtil.success(msg=delete_menu_result.message) + else: + logger.warning(delete_menu_result.message) + return ResponseUtil.failure(msg=delete_menu_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@menuController.get("/{menu_id}", response_model=MenuModel, dependencies=[Depends(CheckUserInterfaceAuth('system:menu:query'))]) +async def query_detail_system_menu(request: Request, menu_id: int, query_db: Session = Depends(get_db)): + try: + menu_detail_result = MenuService.menu_detail_services(query_db, menu_id) + logger.info(f'获取menu_id为{menu_id}的信息成功') + return ResponseUtil.success(data=menu_detail_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/notice_controller.py b/ruoyi-fastapi-backend/module_admin/controller/notice_controller.py new file mode 100644 index 0000000..6c98361 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/notice_controller.py @@ -0,0 +1,92 @@ +from fastapi import APIRouter, Request +from fastapi import Depends +from config.get_db import get_db +from module_admin.service.login_service import LoginService, CurrentUserModel +from module_admin.service.notice_service import * +from utils.response_util import * +from utils.log_util import * +from utils.page_util import * +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth +from module_admin.annotation.log_annotation import log_decorator + + +noticeController = APIRouter(prefix='/system/notice', dependencies=[Depends(LoginService.get_current_user)]) + + +@noticeController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:notice:list'))]) +async def get_system_notice_list(request: Request, notice_page_query: NoticePageQueryModel = Depends(NoticePageQueryModel.as_query), query_db: Session = Depends(get_db)): + try: + notice_query = NoticeQueryModel(**notice_page_query.model_dump(by_alias=True)) + # 获取全量数据 + notice_query_result = NoticeService.get_notice_list_services(query_db, notice_query) + # 分页操作 + notice_page_query_result = get_page_obj(notice_query_result, notice_page_query.page_num, notice_page_query.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=notice_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@noticeController.post("", dependencies=[Depends(CheckUserInterfaceAuth('system:notice:add'))]) +@log_decorator(title='通知公告管理', business_type=1) +async def add_system_notice(request: Request, add_notice: NoticeModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + add_notice.create_by = current_user.user.user_name + add_notice.update_by = current_user.user.user_name + add_notice_result = NoticeService.add_notice_services(query_db, add_notice) + if add_notice_result.is_success: + logger.info(add_notice_result.message) + return ResponseUtil.success(msg=add_notice_result.message) + else: + logger.warning(add_notice_result.message) + return ResponseUtil.failure(msg=add_notice_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@noticeController.put("", dependencies=[Depends(CheckUserInterfaceAuth('system:notice:edit'))]) +@log_decorator(title='通知公告管理', business_type=2) +async def edit_system_notice(request: Request, edit_notice: NoticeModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + edit_notice.update_by = current_user.user.user_name + edit_notice.update_time = datetime.now() + edit_notice_result = NoticeService.edit_notice_services(query_db, edit_notice) + if edit_notice_result.is_success: + logger.info(edit_notice_result.message) + return ResponseUtil.success(msg=edit_notice_result.message) + else: + logger.warning(edit_notice_result.message) + return ResponseUtil.failure(msg=edit_notice_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@noticeController.delete("/{notice_ids}", dependencies=[Depends(CheckUserInterfaceAuth('system:notice:remove'))]) +@log_decorator(title='通知公告管理', business_type=3) +async def delete_system_notice(request: Request, notice_ids: str, query_db: Session = Depends(get_db)): + try: + delete_notice = DeleteNoticeModel(noticeIds=notice_ids) + delete_notice_result = NoticeService.delete_notice_services(query_db, delete_notice) + if delete_notice_result.is_success: + logger.info(delete_notice_result.message) + return ResponseUtil.success(msg=delete_notice_result.message) + else: + logger.warning(delete_notice_result.message) + return ResponseUtil.failure(msg=delete_notice_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@noticeController.get("/{notice_id}", response_model=NoticeModel, dependencies=[Depends(CheckUserInterfaceAuth('system:notice:query'))]) +async def query_detail_system_post(request: Request, notice_id: int, query_db: Session = Depends(get_db)): + try: + notice_detail_result = NoticeService.notice_detail_services(query_db, notice_id) + logger.info(f'获取notice_id为{notice_id}的信息成功') + return ResponseUtil.success(data=notice_detail_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/online_controller.py b/ruoyi-fastapi-backend/module_admin/controller/online_controller.py new file mode 100644 index 0000000..cecee5f --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/online_controller.py @@ -0,0 +1,42 @@ +from fastapi import APIRouter +from fastapi import Depends +from config.get_db import get_db +from module_admin.service.login_service import LoginService, Session +from module_admin.service.online_service import * +from utils.response_util import * +from utils.log_util import * +from utils.page_util import * +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth +from module_admin.annotation.log_annotation import log_decorator + + +onlineController = APIRouter(prefix='/monitor/online', dependencies=[Depends(LoginService.get_current_user)]) + + +@onlineController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:online:list'))]) +async def get_monitor_online_list(request: Request, online_page_query: OnlineQueryModel = Depends(OnlineQueryModel.as_query)): + try: + # 获取全量数据 + online_query_result = await OnlineService.get_online_list_services(request, online_page_query) + logger.info('获取成功') + return ResponseUtil.success(model_content=PageResponseModel(rows=online_query_result, total=len(online_query_result))) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@onlineController.delete("/{token_ids}", dependencies=[Depends(CheckUserInterfaceAuth('monitor:online:forceLogout'))]) +@log_decorator(title='在线用户', business_type=7) +async def delete_monitor_online(request: Request, token_ids: str, query_db: Session = Depends(get_db)): + try: + delete_online = DeleteOnlineModel(tokenIds=token_ids) + delete_online_result = await OnlineService.delete_online_services(request, delete_online) + if delete_online_result.is_success: + logger.info(delete_online_result.message) + return ResponseUtil.success(msg=delete_online_result.message) + else: + logger.warning(delete_online_result.message) + return ResponseUtil.failure(msg=delete_online_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/post_controler.py b/ruoyi-fastapi-backend/module_admin/controller/post_controler.py new file mode 100644 index 0000000..d07d2b1 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/post_controler.py @@ -0,0 +1,109 @@ +from fastapi import APIRouter, Request +from fastapi import Depends +from config.get_db import get_db +from module_admin.service.login_service import LoginService, CurrentUserModel +from module_admin.service.post_service import * +from module_admin.entity.vo.post_vo import * +from utils.response_util import * +from utils.log_util import * +from utils.page_util import * +from utils.common_util import bytes2file_response +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth +from module_admin.annotation.log_annotation import log_decorator + + +postController = APIRouter(prefix='/system/post', dependencies=[Depends(LoginService.get_current_user)]) + + +@postController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:post:list'))]) +async def get_system_post_list(request: Request, post_page_query: PostPageQueryModel = Depends(PostPageQueryModel.as_query), query_db: Session = Depends(get_db)): + try: + post_query = PostModel(**post_page_query.model_dump(by_alias=True)) + # 获取全量数据 + post_query_result = PostService.get_post_list_services(query_db, post_query) + # 分页操作 + post_page_query_result = get_page_obj(post_query_result, post_page_query.page_num, post_page_query.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=post_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@postController.post("", dependencies=[Depends(CheckUserInterfaceAuth('system:post:add'))]) +@log_decorator(title='岗位管理', business_type=1) +async def add_system_post(request: Request, add_post: PostModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + add_post.create_by = current_user.user.user_name + add_post.update_by = current_user.user.user_name + add_post_result = PostService.add_post_services(query_db, add_post) + if add_post_result.is_success: + logger.info(add_post_result.message) + return ResponseUtil.success(msg=add_post_result.message) + else: + logger.warning(add_post_result.message) + return ResponseUtil.failure(msg=add_post_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@postController.put("", dependencies=[Depends(CheckUserInterfaceAuth('system:post:edit'))]) +@log_decorator(title='岗位管理', business_type=2) +async def edit_system_post(request: Request, edit_post: PostModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + edit_post.update_by = current_user.user.user_name + edit_post.update_time = datetime.now() + edit_post_result = PostService.edit_post_services(query_db, edit_post) + if edit_post_result.is_success: + logger.info(edit_post_result.message) + return ResponseUtil.success(msg=edit_post_result.message) + else: + logger.warning(edit_post_result.message) + return ResponseUtil.failure(msg=edit_post_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@postController.delete("/{post_ids}", dependencies=[Depends(CheckUserInterfaceAuth('system:post:remove'))]) +@log_decorator(title='岗位管理', business_type=3) +async def delete_system_post(request: Request, post_ids: str, query_db: Session = Depends(get_db)): + try: + delete_post = DeletePostModel(postIds=post_ids) + delete_post_result = PostService.delete_post_services(query_db, delete_post) + if delete_post_result.is_success: + logger.info(delete_post_result.message) + return ResponseUtil.success(msg=delete_post_result.message) + else: + logger.warning(delete_post_result.message) + return ResponseUtil.failure(msg=delete_post_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@postController.get("/{post_id}", response_model=PostModel, dependencies=[Depends(CheckUserInterfaceAuth('system:post:query'))]) +async def query_detail_system_post(request: Request, post_id: int, query_db: Session = Depends(get_db)): + try: + post_detail_result = PostService.post_detail_services(query_db, post_id) + logger.info(f'获取post_id为{post_id}的信息成功') + return ResponseUtil.success(data=post_detail_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@postController.post("/export", dependencies=[Depends(CheckUserInterfaceAuth('system:post:export'))]) +@log_decorator(title='岗位管理', business_type=5) +async def export_system_post_list(request: Request, post_page_query: PostPageQueryModel = Depends(PostPageQueryModel.as_form), query_db: Session = Depends(get_db)): + try: + post_query = PostModel(**post_page_query.model_dump(by_alias=True)) + # 获取全量数据 + post_query_result = PostService.get_post_list_services(query_db, post_query) + post_export_result = PostService.export_post_list_services(post_query_result) + logger.info('导出成功') + return ResponseUtil.streaming(data=bytes2file_response(post_export_result)) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/role_controller.py b/ruoyi-fastapi-backend/module_admin/controller/role_controller.py new file mode 100644 index 0000000..4007ec5 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/role_controller.py @@ -0,0 +1,240 @@ +from fastapi import APIRouter, Request +from fastapi import Depends +from config.get_db import get_db +from module_admin.service.login_service import LoginService, CurrentUserModel +from module_admin.service.role_service import * +from module_admin.service.dept_service import DeptService, DeptModel +from module_admin.service.user_service import UserService, UserRoleQueryModel, UserRolePageQueryModel, CrudUserRoleModel +from utils.response_util import * +from utils.log_util import * +from utils.page_util import * +from utils.common_util import bytes2file_response +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth +from module_admin.aspect.data_scope import GetDataScope +from module_admin.annotation.log_annotation import log_decorator + + +roleController = APIRouter(prefix='/system/role', dependencies=[Depends(LoginService.get_current_user)]) + + +@roleController.get("/deptTree/{role_id}", dependencies=[Depends(CheckUserInterfaceAuth('common'))]) +async def get_system_role_dept_tree(request: Request, role_id: int, query_db: Session = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysDept'))): + try: + dept_query_result = DeptService.get_dept_tree_services(query_db, DeptModel(**{}), data_scope_sql) + role_dept_query_result = 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) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:role:list'))]) +async def get_system_role_list(request: Request, role_page_query: RolePageQueryModel = Depends(RolePageQueryModel.as_query), query_db: Session = Depends(get_db)): + try: + role_query = RoleQueryModel(**role_page_query.model_dump(by_alias=True)) + role_query_result = RoleService.get_role_list_services(query_db, role_query) + # 分页操作 + role_page_query_result = get_page_obj(role_query_result, role_page_query.page_num, role_page_query.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=role_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.post("", dependencies=[Depends(CheckUserInterfaceAuth('system:role:add'))]) +@log_decorator(title='角色管理', business_type=1) +async def add_system_role(request: Request, add_role: AddRoleModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + add_role.create_by = current_user.user.user_name + add_role.update_by = current_user.user.user_name + add_role_result = RoleService.add_role_services(query_db, add_role) + if add_role_result.is_success: + logger.info(add_role_result.message) + return ResponseUtil.success(msg=add_role_result.message) + else: + logger.warning(add_role_result.message) + return ResponseUtil.failure(msg=add_role_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.put("", dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) +@log_decorator(title='角色管理', business_type=2) +async def edit_system_role(request: Request, edit_role: AddRoleModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + edit_role.update_by = current_user.user.user_name + edit_role.update_time = datetime.now() + edit_role_result = RoleService.edit_role_services(query_db, edit_role) + if edit_role_result.is_success: + logger.info(edit_role_result.message) + return ResponseUtil.success(msg=edit_role_result.message) + else: + logger.warning(edit_role_result.message) + return ResponseUtil.failure(msg=edit_role_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.put("/dataScope", dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) +@log_decorator(title='角色管理', business_type=4) +async def edit_system_role_datascope(request: Request, role_data_scope: AddRoleModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + role_data_scope.update_by = current_user.user.user_name + role_data_scope.update_time = datetime.now() + role_data_scope_result = RoleService.role_datascope_services(query_db, role_data_scope) + if role_data_scope_result.is_success: + logger.info(role_data_scope_result.message) + return ResponseUtil.success(msg=role_data_scope_result.message) + else: + logger.warning(role_data_scope_result.message) + return ResponseUtil.failure(msg=role_data_scope_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.delete("/{role_ids}", dependencies=[Depends(CheckUserInterfaceAuth('system:role:remove'))]) +@log_decorator(title='角色管理', business_type=3) +async def delete_system_role(request: Request, role_ids: str, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + delete_role = DeleteRoleModel( + roleIds=role_ids, + updateBy=current_user.user.user_name, + updateTime=datetime.now() + ) + delete_role_result = RoleService.delete_role_services(query_db, delete_role) + if delete_role_result.is_success: + logger.info(delete_role_result.message) + return ResponseUtil.success(msg=delete_role_result.message) + else: + logger.warning(delete_role_result.message) + return ResponseUtil.failure(msg=delete_role_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.get("/{role_id}", response_model=RoleModel, dependencies=[Depends(CheckUserInterfaceAuth('system:role:query'))]) +async def query_detail_system_role(request: Request, role_id: int, query_db: Session = Depends(get_db)): + try: + role_detail_result = 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)) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.post("/export", dependencies=[Depends(CheckUserInterfaceAuth('system:role:export'))]) +@log_decorator(title='角色管理', business_type=5) +async def export_system_role_list(request: Request, role_page_query: RolePageQueryModel = Depends(RolePageQueryModel.as_form), query_db: Session = Depends(get_db)): + try: + role_query = RoleQueryModel(**role_page_query.model_dump(by_alias=True)) + # 获取全量数据 + role_query_result = RoleService.get_role_list_services(query_db, role_query) + role_export_result = RoleService.export_role_list_services(role_query_result) + logger.info('导出成功') + return ResponseUtil.streaming(data=bytes2file_response(role_export_result)) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.put("/changeStatus", dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) +@log_decorator(title='角色管理', business_type=2) +async def reset_system_role_status(request: Request, edit_role: AddRoleModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + edit_role.update_by = current_user.user.user_name + edit_role.update_time = datetime.now() + edit_role.type = 'status' + edit_role_result = RoleService.edit_role_services(query_db, edit_role) + if edit_role_result.is_success: + logger.info(edit_role_result.message) + return ResponseUtil.success(msg=edit_role_result.message) + else: + logger.warning(edit_role_result.message) + return ResponseUtil.failure(msg=edit_role_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.get("/authUser/allocatedList", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('common'))]) +async def get_system_allocated_user_list(request: Request, user_role: UserRolePageQueryModel = Depends(UserRolePageQueryModel.as_query), query_db: Session = Depends(get_db)): + try: + role_user_query = UserRoleQueryModel(**user_role.model_dump(by_alias=True)) + role_user_allocated_query_result = RoleService.get_role_user_allocated_list_services(query_db, role_user_query) + # 分页操作 + role_user_allocated_page_query_result = get_page_obj(role_user_allocated_query_result, user_role.page_num, user_role.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=role_user_allocated_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.get("/authUser/unallocatedList", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('common'))]) +async def get_system_unallocated_user_list(request: Request, user_role: UserRolePageQueryModel = Depends(UserRolePageQueryModel.as_query), query_db: Session = Depends(get_db)): + try: + role_user_query = UserRoleQueryModel(**user_role.model_dump(by_alias=True)) + role_user_unallocated_query_result = RoleService.get_role_user_unallocated_list_services(query_db, role_user_query) + # 分页操作 + role_user_unallocated_page_query_result = get_page_obj(role_user_unallocated_query_result, user_role.page_num, user_role.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=role_user_unallocated_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.put("/authUser/selectAll", dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) +@log_decorator(title='角色管理', business_type=4) +async def add_system_role_user(request: Request, add_role_user: CrudUserRoleModel = Depends(CrudUserRoleModel.as_query), query_db: Session = Depends(get_db)): + try: + add_role_user_result = UserService.add_user_role_services(query_db, add_role_user) + if add_role_user_result.is_success: + logger.info(add_role_user_result.message) + return ResponseUtil.success(msg=add_role_user_result.message) + else: + logger.warning(add_role_user_result.message) + return ResponseUtil.failure(msg=add_role_user_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.put("/authUser/cancel", dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) +@log_decorator(title='角色管理', business_type=4) +async def cancel_system_role_user(request: Request, cancel_user_role: CrudUserRoleModel, query_db: Session = Depends(get_db)): + try: + cancel_user_role_result = UserService.delete_user_role_services(query_db, cancel_user_role) + if cancel_user_role_result.is_success: + logger.info(cancel_user_role_result.message) + return ResponseUtil.success(msg=cancel_user_role_result.message) + else: + logger.warning(cancel_user_role_result.message) + return ResponseUtil.failure(msg=cancel_user_role_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@roleController.put("/authUser/cancelAll", dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) +@log_decorator(title='角色管理', business_type=4) +async def batch_cancel_system_role_user(request: Request, batch_cancel_user_role: CrudUserRoleModel = Depends(CrudUserRoleModel.as_query), query_db: Session = Depends(get_db)): + try: + batch_cancel_user_role_result = UserService.delete_user_role_services(query_db, batch_cancel_user_role) + if batch_cancel_user_role_result.is_success: + logger.info(batch_cancel_user_role_result.message) + return ResponseUtil.success(msg=batch_cancel_user_role_result.message) + else: + logger.warning(batch_cancel_user_role_result.message) + return ResponseUtil.failure(msg=batch_cancel_user_role_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/server_controller.py b/ruoyi-fastapi-backend/module_admin/controller/server_controller.py new file mode 100644 index 0000000..b089912 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/server_controller.py @@ -0,0 +1,22 @@ +from fastapi import APIRouter, Request +from fastapi import Depends +from module_admin.service.login_service import LoginService +from module_admin.service.server_service import * +from utils.response_util import * +from utils.log_util import * +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth + + +serverController = APIRouter(prefix='/monitor/server', dependencies=[Depends(LoginService.get_current_user)]) + + +@serverController.get("", response_model=ServerMonitorModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:server:list'))]) +async def get_monitor_server_info(request: Request): + try: + # 获取全量数据 + server_info_query_result = ServerService.get_server_monitor_info() + logger.info('获取成功') + return ResponseUtil.success(data=server_info_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/controller/user_controller.py b/ruoyi-fastapi-backend/module_admin/controller/user_controller.py new file mode 100644 index 0000000..c7760d6 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/controller/user_controller.py @@ -0,0 +1,307 @@ +from fastapi import APIRouter, Request +from fastapi import Depends, File, Query +from config.get_db import get_db +from module_admin.service.login_service import LoginService +from module_admin.service.user_service import * +from module_admin.service.dept_service import DeptService +from utils.page_util import * +from utils.response_util import * +from utils.log_util import * +from utils.common_util import bytes2file_response +from module_admin.aspect.interface_auth import CheckUserInterfaceAuth +from module_admin.aspect.data_scope import GetDataScope +from module_admin.annotation.log_annotation import log_decorator + + +userController = APIRouter(prefix='/system/user', dependencies=[Depends(LoginService.get_current_user)]) + + +@userController.get("/deptTree", dependencies=[Depends(CheckUserInterfaceAuth('system:user:list'))]) +async def get_system_dept_tree(request: Request, query_db: Session = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysDept'))): + try: + dept_query_result = DeptService.get_dept_tree_services(query_db, DeptModel(**{}), data_scope_sql) + logger.info('获取成功') + return ResponseUtil.success(data=dept_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:user:list'))]) +async def get_system_user_list(request: Request, user_page_query: UserPageQueryModel = Depends(UserPageQueryModel.as_query), query_db: Session = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysUser'))): + try: + user_query = UserQueryModel(**user_page_query.model_dump(by_alias=True)) + # 获取全量数据 + user_query_result = UserService.get_user_list_services(query_db, user_query, data_scope_sql) + # 分页操作 + user_page_query_result = get_page_obj(user_query_result, user_page_query.page_num, user_page_query.page_size) + logger.info('获取成功') + return ResponseUtil.success(model_content=user_page_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.post("", dependencies=[Depends(CheckUserInterfaceAuth('system:user:add'))]) +@log_decorator(title='用户管理', business_type=1) +async def add_system_user(request: Request, add_user: AddUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + add_user.password = PwdUtil.get_password_hash(add_user.password) + add_user.create_by = current_user.user.user_name + add_user.update_by = current_user.user.user_name + add_user_result = UserService.add_user_services(query_db, add_user) + if add_user_result.is_success: + logger.info(add_user_result.message) + return ResponseUtil.success(msg=add_user_result.message) + else: + logger.warning(add_user_result.message) + return ResponseUtil.failure(msg=add_user_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.put("", dependencies=[Depends(CheckUserInterfaceAuth('system:user:edit'))]) +@log_decorator(title='用户管理', business_type=2) +async def edit_system_user(request: Request, edit_user: EditUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + edit_user.update_by = current_user.user.user_name + edit_user.update_time = datetime.now() + edit_user_result = UserService.edit_user_services(query_db, edit_user) + if edit_user_result.is_success: + logger.info(edit_user_result.message) + return ResponseUtil.success(msg=edit_user_result.message) + else: + logger.warning(edit_user_result.message) + return ResponseUtil.failure(msg=edit_user_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.delete("/{user_ids}", dependencies=[Depends(CheckUserInterfaceAuth('system:user:remove'))]) +@log_decorator(title='用户管理', business_type=3) +async def delete_system_user(request: Request, user_ids: str, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + delete_user = DeleteUserModel( + userIds=user_ids, + updateBy=current_user.user.user_name, + updateTime=datetime.now() + ) + delete_user_result = UserService.delete_user_services(query_db, delete_user) + if delete_user_result.is_success: + logger.info(delete_user_result.message) + return ResponseUtil.success(msg=delete_user_result.message) + else: + logger.warning(delete_user_result.message) + return ResponseUtil.failure(msg=delete_user_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.put("/resetPwd", dependencies=[Depends(CheckUserInterfaceAuth('system:user:resetPwd'))]) +@log_decorator(title='用户管理', business_type=2) +async def reset_system_user_pwd(request: Request, edit_user: EditUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + edit_user.password = PwdUtil.get_password_hash(edit_user.password) + edit_user.update_by = current_user.user.user_name + edit_user.update_time = datetime.now() + edit_user.type = 'pwd' + edit_user_result = UserService.edit_user_services(query_db, edit_user) + if edit_user_result.is_success: + logger.info(edit_user_result.message) + return ResponseUtil.success(msg=edit_user_result.message) + else: + logger.warning(edit_user_result.message) + return ResponseUtil.failure(msg=edit_user_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.put("/changeStatus", dependencies=[Depends(CheckUserInterfaceAuth('system:user:edit'))]) +@log_decorator(title='用户管理', business_type=2) +async def change_system_user_status(request: Request, edit_user: EditUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + edit_user.update_by = current_user.user.user_name + edit_user.update_time = datetime.now() + edit_user.type = 'status' + edit_user_result = UserService.edit_user_services(query_db, edit_user) + if edit_user_result.is_success: + logger.info(edit_user_result.message) + return ResponseUtil.success(msg=edit_user_result.message) + else: + logger.warning(edit_user_result.message) + return ResponseUtil.failure(msg=edit_user_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.get("/profile", response_model=UserProfileModel) +async def query_detail_system_user(request: Request, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + profile_user_result = 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) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.get("/{user_id}", response_model=UserDetailModel, dependencies=[Depends(CheckUserInterfaceAuth('system:user:query'))]) +@userController.get("/", response_model=UserDetailModel, dependencies=[Depends(CheckUserInterfaceAuth('system:user:query'))]) +async def query_detail_system_user(request: Request, user_id: Optional[Union[int, str]] = '', query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + detail_user_result = UserService.user_detail_services(query_db, user_id) + logger.info(f'获取user_id为{user_id}的信息成功') + return ResponseUtil.success(model_content=detail_user_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.post("/profile/avatar") +@log_decorator(title='个人信息', business_type=2) +async def change_system_user_profile_avatar(request: Request, avatarfile: bytes = File(), query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + dir_path = os.path.join(CachePathConfig.PATH, 'profile', 'avatar') + try: + os.makedirs(dir_path) + except FileExistsError: + pass + avatar_name = f'blob_{datetime.now().strftime("%Y%m%d%H%M%S")}.jpeg' + avatar_path = os.path.join(dir_path, avatar_name) + with open(avatar_path, 'wb') as f: + f.write(avatarfile) + edit_user = EditUserModel( + userId=current_user.user.user_id, + avatar=f'/common/{CachePathConfig.PATHSTR}?taskPath=profile&taskId=avatar&filename={avatar_name}', + updateBy=current_user.user.user_name, + updateTime=datetime.now(), + type='avatar' + ) + edit_user_result = UserService.edit_user_services(query_db, edit_user) + if edit_user_result.is_success: + logger.info(edit_user_result.message) + return ResponseUtil.success(dict_content={'imgUrl': edit_user.avatar}, msg=edit_user_result.message) + else: + logger.warning(edit_user_result.message) + return ResponseUtil.failure(msg=edit_user_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.put("/profile") +@log_decorator(title='个人信息', business_type=2) +async def change_system_user_profile_info(request: Request, user_info: UserInfoModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + edit_user = EditUserModel(**user_info.model_dump(by_alias=True, exclude={'role_ids', 'post_ids'}), roleIds=user_info.role_ids.split(','), postIds=user_info.post_ids.split(',')) + edit_user.user_id = current_user.user.user_id + edit_user.update_by = current_user.user.user_name + edit_user.update_time = datetime.now() + print(edit_user.model_dump()) + edit_user_result = UserService.edit_user_services(query_db, edit_user) + if edit_user_result.is_success: + logger.info(edit_user_result.message) + return ResponseUtil.success(msg=edit_user_result.message) + else: + logger.warning(edit_user_result.message) + return ResponseUtil.failure(msg=edit_user_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.put("/profile/updatePwd") +@log_decorator(title='个人信息', business_type=2) +async def reset_system_user_password(request: Request, old_password: str = Query(alias='oldPassword'), new_password: str = Query(alias='newPassword'), query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + reset_user = ResetUserModel( + userId=current_user.user.user_id, + oldPassword=old_password, + password=PwdUtil.get_password_hash(new_password), + updateBy=current_user.user.user_name, + updateTime=datetime.now() + ) + reset_user_result = UserService.reset_user_services(query_db, reset_user) + if reset_user_result.is_success: + logger.info(reset_user_result.message) + return ResponseUtil.success(msg=reset_user_result.message) + else: + logger.warning(reset_user_result.message) + return ResponseUtil.failure(msg=reset_user_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.post("/importData", dependencies=[Depends(CheckUserInterfaceAuth('system:user:import'))]) +@log_decorator(title='用户管理', business_type=6) +async def batch_import_system_user(request: Request, file: UploadFile = File(...), update_support: bool = Query(alias='updateSupport'), query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)): + try: + batch_import_result = await UserService.batch_import_user_services(query_db, file, update_support, current_user) + if batch_import_result.is_success: + logger.info(batch_import_result.message) + return ResponseUtil.success(msg=batch_import_result.message) + else: + logger.warning(batch_import_result.message) + return ResponseUtil.failure(msg=batch_import_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.post("/importTemplate", dependencies=[Depends(CheckUserInterfaceAuth('system:user:import'))]) +async def export_system_user_template(request: Request, query_db: Session = Depends(get_db)): + try: + user_import_template_result = UserService.get_user_import_template_services() + logger.info('获取成功') + return ResponseUtil.streaming(data=bytes2file_response(user_import_template_result)) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.post("/export", dependencies=[Depends(CheckUserInterfaceAuth('system:user:export'))]) +@log_decorator(title='用户管理', business_type=5) +async def export_system_user_list(request: Request, user_page_query: UserPageQueryModel = Depends(UserPageQueryModel.as_form), query_db: Session = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysUser'))): + try: + user_query = UserQueryModel(**user_page_query.model_dump(by_alias=True)) + # 获取全量数据 + user_query_result = UserService.get_user_list_services(query_db, user_query, data_scope_sql) + user_export_result = UserService.export_user_list_services(user_query_result) + logger.info('导出成功') + return ResponseUtil.streaming(data=bytes2file_response(user_export_result)) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.get("/authRole/{user_id}", response_model=UserRoleResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:user:query'))]) +async def get_system_allocated_role_list(request: Request, user_id: int, query_db: Session = Depends(get_db)): + try: + user_role_query = UserRoleQueryModel(userId=user_id) + user_role_allocated_query_result = UserService.get_user_role_allocated_list_services(query_db, user_role_query) + logger.info('获取成功') + return ResponseUtil.success(model_content=user_role_allocated_query_result) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) + + +@userController.put("/authRole", response_model=UserRoleResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:user:edit'))]) +async def update_system_role_user(request: Request, user_id: int = Query(alias='userId'), role_ids: str = Query(alias='roleIds'), query_db: Session = Depends(get_db)): + try: + add_user_role_result = UserService.add_user_role_services(query_db, CrudUserRoleModel(userId=user_id, roleIds=role_ids)) + if add_user_role_result.is_success: + logger.info(add_user_role_result.message) + return ResponseUtil.success(msg=add_user_role_result.message) + else: + logger.warning(add_user_role_result.message) + return ResponseUtil.failure(msg=add_user_role_result.message) + except Exception as e: + logger.exception(e) + return ResponseUtil.error(msg=str(e)) diff --git a/ruoyi-fastapi-backend/module_admin/dao/config_dao.py b/ruoyi-fastapi-backend/module_admin/dao/config_dao.py new file mode 100644 index 0000000..3efea39 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/dao/config_dao.py @@ -0,0 +1,98 @@ +from sqlalchemy.orm import Session +from module_admin.entity.do.config_do import SysConfig +from module_admin.entity.vo.config_vo import * +from datetime import datetime, time + + +class ConfigDao: + """ + 参数配置管理模块数据库操作层 + """ + + @classmethod + def get_config_detail_by_id(cls, db: Session, config_id: int): + """ + 根据参数配置id获取参数配置详细信息 + :param db: orm对象 + :param config_id: 参数配置id + :return: 参数配置信息对象 + """ + config_info = db.query(SysConfig) \ + .filter(SysConfig.config_id == config_id) \ + .first() + + return config_info + + @classmethod + def get_config_detail_by_info(cls, db: Session, config: ConfigModel): + """ + 根据参数配置参数获取参数配置信息 + :param db: orm对象 + :param config: 参数配置参数对象 + :return: 参数配置信息对象 + """ + config_info = db.query(SysConfig) \ + .filter(SysConfig.config_key == config.config_key if config.config_key else True, + SysConfig.config_value == config.config_value if config.config_value else True) \ + .first() + + return config_info + + @classmethod + def get_config_list(cls, db: Session, query_object: ConfigQueryModel): + """ + 根据查询参数获取参数配置列表信息 + :param db: orm对象 + :param query_object: 查询参数对象 + :return: 参数配置列表信息对象 + """ + config_list = db.query(SysConfig) \ + .filter(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 + ) \ + .distinct().all() + + return config_list + + @classmethod + def add_config_dao(cls, db: Session, config: ConfigModel): + """ + 新增参数配置数据库操作 + :param db: orm对象 + :param config: 参数配置对象 + :return: + """ + db_config = SysConfig(**config.model_dump()) + db.add(db_config) + db.flush() + + return db_config + + @classmethod + def edit_config_dao(cls, db: Session, config: dict): + """ + 编辑参数配置数据库操作 + :param db: orm对象 + :param config: 需要更新的参数配置字典 + :return: + """ + db.query(SysConfig) \ + .filter(SysConfig.config_id == config.get('config_id')) \ + .update(config) + + @classmethod + def delete_config_dao(cls, db: Session, config: ConfigModel): + """ + 删除参数配置数据库操作 + :param db: orm对象 + :param config: 参数配置对象 + :return: + """ + db.query(SysConfig) \ + .filter(SysConfig.config_id == config.config_id) \ + .delete() diff --git a/ruoyi-fastapi-backend/module_admin/dao/dept_dao.py b/ruoyi-fastapi-backend/module_admin/dao/dept_dao.py new file mode 100644 index 0000000..ffe3b67 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/dao/dept_dao.py @@ -0,0 +1,194 @@ +from sqlalchemy.orm import Session +from module_admin.entity.do.dept_do import SysDept +from module_admin.entity.vo.dept_vo import * +from utils.time_format_util import list_format_datetime + + +class DeptDao: + """ + 部门管理模块数据库操作层 + """ + + @classmethod + def get_dept_by_id(cls, db: Session, dept_id: int): + """ + 根据部门id获取在用部门信息 + :param db: orm对象 + :param dept_id: 部门id + :return: 在用部门信息对象 + """ + dept_info = db.query(SysDept) \ + .filter(SysDept.dept_id == dept_id, + SysDept.status == 0, + SysDept.del_flag == 0) \ + .first() + + return dept_info + + @classmethod + def get_dept_by_id_for_list(cls, db: Session, dept_id: int): + """ + 用于获取部门列表的工具方法 + :param db: orm对象 + :param dept_id: 部门id + :return: 部门id对应的信息对象 + """ + dept_info = db.query(SysDept) \ + .filter(SysDept.dept_id == dept_id, + SysDept.del_flag == 0) \ + .first() + + return dept_info + + @classmethod + def get_dept_detail_by_id(cls, db: Session, dept_id: int): + """ + 根据部门id获取部门详细信息 + :param db: orm对象 + :param dept_id: 部门id + :return: 部门信息对象 + """ + dept_info = db.query(SysDept) \ + .filter(SysDept.dept_id == dept_id, + SysDept.del_flag == 0) \ + .first() + + return dept_info + + @classmethod + def get_dept_detail_by_info(cls, db: Session, dept: DeptModel): + """ + 根据部门参数获取部门信息 + :param db: orm对象 + :param dept: 部门参数对象 + :return: 部门信息对象 + """ + dept_info = db.query(SysDept) \ + .filter(SysDept.parent_id == dept.parent_id if dept.parent_id else True, + SysDept.dept_name == dept.dept_name if dept.dept_name else True) \ + .first() + + return dept_info + + @classmethod + def get_dept_info_for_edit_option(cls, db: Session, dept_info: DeptModel, data_scope_sql: str): + """ + 获取部门编辑对应的在用部门列表信息 + :param db: orm对象 + :param dept_info: 部门对象 + :param data_scope_sql: 数据权限对应的查询sql语句 + :return: 部门列表信息 + """ + dept_result = db.query(SysDept) \ + .filter(SysDept.dept_id != dept_info.dept_id, + SysDept.parent_id != dept_info.dept_id, + SysDept.del_flag == 0, SysDept.status == 0, + eval(data_scope_sql)) \ + .order_by(SysDept.order_num) \ + .distinct().all() + + return list_format_datetime(dept_result) + + @classmethod + def get_children_dept(cls, db: Session, dept_id: int): + """ + 根据部门id查询当前部门的子部门列表信息 + :param db: orm对象 + :param dept_id: 部门id + :return: 子部门信息列表 + """ + dept_result = db.query(SysDept) \ + .filter(SysDept.parent_id == dept_id, + SysDept.del_flag == 0) \ + .all() + + return list_format_datetime(dept_result) + + @classmethod + def get_dept_all_ancestors(cls, db: Session): + """ + 获取所有部门的ancestors信息 + :param db: orm对象 + :return: ancestors信息列表 + """ + ancestors = db.query(SysDept.ancestors)\ + .filter(SysDept.del_flag == 0)\ + .all() + + return ancestors + + @classmethod + def get_dept_list_for_tree(cls, db: Session, dept_info: DeptModel, data_scope_sql: str): + """ + 获取所有在用部门列表信息 + :param db: orm对象 + :param dept_info: 部门对象 + :param data_scope_sql: 数据权限对应的查询sql语句 + :return: 在用部门列表信息 + """ + dept_result = db.query(SysDept) \ + .filter(SysDept.status == 0, + SysDept.del_flag == 0, + SysDept.dept_name.like(f'%{dept_info.dept_name}%') if dept_info.dept_name else True, + eval(data_scope_sql)) \ + .order_by(SysDept.order_num) \ + .distinct().all() + + return dept_result + + @classmethod + def get_dept_list(cls, db: Session, page_object: DeptModel, data_scope_sql: str): + """ + 根据查询参数获取部门列表信息 + :param db: orm对象 + :param page_object: 不分页查询参数对象 + :param data_scope_sql: 数据权限对应的查询sql语句 + :return: 部门列表信息对象 + """ + dept_result = db.query(SysDept) \ + .filter(SysDept.del_flag == 0, + 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, + eval(data_scope_sql)) \ + .order_by(SysDept.order_num) \ + .distinct().all() + + return dept_result + + @classmethod + def add_dept_dao(cls, db: Session, dept: DeptModel): + """ + 新增部门数据库操作 + :param db: orm对象 + :param dept: 部门对象 + :return: 新增校验结果 + """ + db_dept = SysDept(**dept.model_dump()) + db.add(db_dept) + db.flush() + + return db_dept + + @classmethod + def edit_dept_dao(cls, db: Session, dept: dict): + """ + 编辑部门数据库操作 + :param db: orm对象 + :param dept: 需要更新的部门字典 + :return: 编辑校验结果 + """ + db.query(SysDept) \ + .filter(SysDept.dept_id == dept.get('dept_id')) \ + .update(dept) + + @classmethod + def delete_dept_dao(cls, db: Session, dept: DeptModel): + """ + 删除部门数据库操作 + :param db: orm对象 + :param dept: 部门对象 + :return: + """ + db.query(SysDept) \ + .filter(SysDept.dept_id == dept.dept_id) \ + .update({SysDept.del_flag: '2', SysDept.update_by: dept.update_by, SysDept.update_time: dept.update_time}) diff --git a/ruoyi-fastapi-backend/module_admin/dao/dict_dao.py b/ruoyi-fastapi-backend/module_admin/dao/dict_dao.py new file mode 100644 index 0000000..d938c14 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/dao/dict_dao.py @@ -0,0 +1,219 @@ +from sqlalchemy import and_ +from sqlalchemy.orm import Session +from module_admin.entity.do.dict_do import SysDictType, SysDictData +from module_admin.entity.vo.dict_vo import * +from utils.time_format_util import list_format_datetime +from datetime import datetime, time + + +class DictTypeDao: + """ + 字典类型管理模块数据库操作层 + """ + + @classmethod + def get_dict_type_detail_by_id(cls, db: Session, dict_id: int): + """ + 根据字典类型id获取字典类型详细信息 + :param db: orm对象 + :param dict_id: 字典类型id + :return: 字典类型信息对象 + """ + dict_type_info = db.query(SysDictType) \ + .filter(SysDictType.dict_id == dict_id) \ + .first() + + return dict_type_info + + @classmethod + def get_dict_type_detail_by_info(cls, db: Session, dict_type: DictTypeModel): + """ + 根据字典类型参数获取字典类型信息 + :param db: orm对象 + :param dict_type: 字典类型参数对象 + :return: 字典类型信息对象 + """ + dict_type_info = db.query(SysDictType) \ + .filter(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) \ + .first() + + return dict_type_info + + @classmethod + def get_all_dict_type(cls, db: Session): + """ + 获取所有的字典类型信息 + :param db: orm对象 + :return: 字典类型信息列表对象 + """ + dict_type_info = db.query(SysDictType).all() + + return list_format_datetime(dict_type_info) + + @classmethod + def get_dict_type_list(cls, db: Session, query_object: DictTypeQueryModel): + """ + 根据查询参数获取字典类型列表信息 + :param db: orm对象 + :param query_object: 查询参数对象 + :return: 字典类型列表信息对象 + """ + dict_type_list = db.query(SysDictType) \ + .filter(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 + ) \ + .distinct().all() + + return dict_type_list + + @classmethod + def add_dict_type_dao(cls, db: Session, dict_type: DictTypeModel): + """ + 新增字典类型数据库操作 + :param db: orm对象 + :param dict_type: 字典类型对象 + :return: + """ + db_dict_type = SysDictType(**dict_type.dict()) + db.add(db_dict_type) + db.flush() + + return db_dict_type + + @classmethod + def edit_dict_type_dao(cls, db: Session, dict_type: dict): + """ + 编辑字典类型数据库操作 + :param db: orm对象 + :param dict_type: 需要更新的字典类型字典 + :return: + """ + db.query(SysDictType) \ + .filter(SysDictType.dict_id == dict_type.get('dict_id')) \ + .update(dict_type) + + @classmethod + def delete_dict_type_dao(cls, db: Session, dict_type: DictTypeModel): + """ + 删除字典类型数据库操作 + :param db: orm对象 + :param dict_type: 字典类型对象 + :return: + """ + db.query(SysDictType) \ + .filter(SysDictType.dict_id == dict_type.dict_id) \ + .delete() + + +class DictDataDao: + """ + 字典数据管理模块数据库操作层 + """ + + @classmethod + def get_dict_data_detail_by_id(cls, db: Session, dict_code: int): + """ + 根据字典数据id获取字典数据详细信息 + :param db: orm对象 + :param dict_code: 字典数据id + :return: 字典数据信息对象 + """ + dict_data_info = db.query(SysDictData) \ + .filter(SysDictData.dict_code == dict_code) \ + .first() + + return dict_data_info + + @classmethod + def get_dict_data_detail_by_info(cls, db: Session, dict_data: DictDataModel): + """ + 根据字典数据参数获取字典数据信息 + :param db: orm对象 + :param dict_data: 字典数据参数对象 + :return: 字典数据信息对象 + """ + dict_data_info = db.query(SysDictData) \ + .filter(SysDictData.dict_type == dict_data.dict_type if dict_data.dict_type else True, + SysDictData.dict_label == dict_data.dict_label if dict_data.dict_label else True, + SysDictData.dict_value == dict_data.dict_value if dict_data.dict_value else True) \ + .first() + + return dict_data_info + + @classmethod + def get_dict_data_list(cls, db: Session, query_object: DictDataModel): + """ + 根据查询参数获取字典数据列表信息 + :param db: orm对象 + :param query_object: 查询参数对象 + :return: 字典数据列表信息对象 + """ + dict_data_list = db.query(SysDictData) \ + .filter(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().all() + + return dict_data_list + + @classmethod + def query_dict_data_list(cls, db: Session, dict_type: str): + """ + 根据查询参数获取字典数据列表信息 + :param db: orm对象 + :param dict_type: 字典类型 + :return: 字典数据列表信息对象 + """ + dict_data_list = db.query(SysDictData).select_from(SysDictType) \ + .filter(SysDictType.dict_type == dict_type if dict_type else True, SysDictType.status == 0) \ + .outerjoin(SysDictData, and_(SysDictType.dict_type == SysDictData.dict_type, SysDictData.status == 0)) \ + .order_by(SysDictData.dict_sort) \ + .distinct().all() + + return dict_data_list + + @classmethod + def add_dict_data_dao(cls, db: Session, dict_data: DictDataModel): + """ + 新增字典数据数据库操作 + :param db: orm对象 + :param dict_data: 字典数据对象 + :return: + """ + db_data_type = SysDictData(**dict_data.dict()) + db.add(db_data_type) + db.flush() + + return db_data_type + + @classmethod + def edit_dict_data_dao(cls, db: Session, dict_data: dict): + """ + 编辑字典数据数据库操作 + :param db: orm对象 + :param dict_data: 需要更新的字典数据字典 + :return: + """ + db.query(SysDictData) \ + .filter(SysDictData.dict_code == dict_data.get('dict_code')) \ + .update(dict_data) + + @classmethod + def delete_dict_data_dao(cls, db: Session, dict_data: DictDataModel): + """ + 删除字典数据数据库操作 + :param db: orm对象 + :param dict_data: 字典数据对象 + :return: + """ + db.query(SysDictData) \ + .filter(SysDictData.dict_code == dict_data.dict_code) \ + .delete() diff --git a/ruoyi-fastapi-backend/module_admin/dao/job_dao.py b/ruoyi-fastapi-backend/module_admin/dao/job_dao.py new file mode 100644 index 0000000..2456270 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/dao/job_dao.py @@ -0,0 +1,108 @@ +from sqlalchemy.orm import Session +from module_admin.entity.do.job_do import SysJob +from module_admin.entity.vo.job_vo import * + + +class JobDao: + """ + 定时任务管理模块数据库操作层 + """ + + @classmethod + def get_job_detail_by_id(cls, db: Session, job_id: int): + """ + 根据定时任务id获取定时任务详细信息 + :param db: orm对象 + :param job_id: 定时任务id + :return: 定时任务信息对象 + """ + job_info = db.query(SysJob) \ + .filter(SysJob.job_id == job_id) \ + .first() + + return job_info + + @classmethod + def get_job_detail_by_info(cls, db: Session, job: JobModel): + """ + 根据定时任务参数获取定时任务信息 + :param db: orm对象 + :param job: 定时任务参数对象 + :return: 定时任务信息对象 + """ + job_info = db.query(SysJob) \ + .filter(SysJob.job_name == job.job_name if job.job_name else True, + SysJob.job_group == job.job_group if job.job_group else True, + SysJob.invoke_target == job.invoke_target if job.invoke_target else True, + SysJob.cron_expression == job.cron_expression if job.cron_expression else True) \ + .first() + + return job_info + + @classmethod + def get_job_list(cls, db: Session, query_object: JobModel): + """ + 根据查询参数获取定时任务列表信息 + :param db: orm对象 + :param query_object: 查询参数对象 + :return: 定时任务列表信息对象 + """ + job_list = db.query(SysJob) \ + .filter(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 + ) \ + .distinct().all() + + return job_list + + @classmethod + def get_job_list_for_scheduler(cls, db: Session): + """ + 获取定时任务列表信息 + :param db: orm对象 + :return: 定时任务列表信息对象 + """ + job_list = db.query(SysJob) \ + .filter(SysJob.status == 0) \ + .distinct().all() + + return job_list + + @classmethod + def add_job_dao(cls, db: Session, job: JobModel): + """ + 新增定时任务数据库操作 + :param db: orm对象 + :param job: 定时任务对象 + :return: + """ + db_job = SysJob(**job.model_dump()) + db.add(db_job) + db.flush() + + return db_job + + @classmethod + def edit_job_dao(cls, db: Session, job: dict): + """ + 编辑定时任务数据库操作 + :param db: orm对象 + :param job: 需要更新的定时任务字典 + :return: + """ + db.query(SysJob) \ + .filter(SysJob.job_id == job.get('job_id')) \ + .update(job) + + @classmethod + def delete_job_dao(cls, db: Session, job: JobModel): + """ + 删除定时任务数据库操作 + :param db: orm对象 + :param job: 定时任务对象 + :return: + """ + db.query(SysJob) \ + .filter(SysJob.job_id == job.job_id) \ + .delete() diff --git a/ruoyi-fastapi-backend/module_admin/dao/job_log_dao.py b/ruoyi-fastapi-backend/module_admin/dao/job_log_dao.py new file mode 100644 index 0000000..6c090c7 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/dao/job_log_dao.py @@ -0,0 +1,67 @@ +from sqlalchemy.orm import Session +from module_admin.entity.do.job_do import SysJobLog +from module_admin.entity.vo.job_vo import * +from datetime import datetime, time + + +class JobLogDao: + """ + 定时任务日志管理模块数据库操作层 + """ + + @classmethod + def get_job_log_list(cls, db: Session, query_object: JobLogQueryModel): + """ + 根据查询参数获取定时任务日志列表信息 + :param db: orm对象 + :param query_object: 查询参数对象 + :return: 定时任务日志列表信息对象 + """ + job_log_list = db.query(SysJobLog) \ + .filter(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 + ) \ + .distinct().all() + + return job_log_list + + @classmethod + def add_job_log_dao(cls, db: Session, job_log: JobLogModel): + """ + 新增定时任务日志数据库操作 + :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 + def delete_job_log_dao(cls, db: Session, job_log: JobLogModel): + """ + 删除定时任务日志数据库操作 + :param db: orm对象 + :param job_log: 定时任务日志对象 + :return: + """ + db.query(SysJobLog) \ + .filter(SysJobLog.job_log_id == job_log.job_log_id) \ + .delete() + + @classmethod + def clear_job_log_dao(cls, db: Session): + """ + 清除定时任务日志数据库操作 + :param db: orm对象 + :return: + """ + db.query(SysJobLog) \ + .delete() diff --git a/ruoyi-fastapi-backend/module_admin/dao/log_dao.py b/ruoyi-fastapi-backend/module_admin/dao/log_dao.py new file mode 100644 index 0000000..b6fdaac --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/dao/log_dao.py @@ -0,0 +1,131 @@ +from sqlalchemy.orm import Session +from module_admin.entity.do.log_do import SysOperLog, SysLogininfor +from module_admin.entity.vo.log_vo import * +from utils.time_format_util import object_format_datetime, list_format_datetime +from datetime import datetime, time + + +class OperationLogDao: + """ + 操作日志管理模块数据库操作层 + """ + @classmethod + def get_operation_log_list(cls, db: Session, query_object: OperLogQueryModel): + """ + 根据查询参数获取操作日志列表信息 + :param db: orm对象 + :param query_object: 查询参数对象 + :return: 操作日志列表信息对象 + """ + operation_log_list = db.query(SysOperLog) \ + .filter(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(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 + )\ + .distinct().all() + + return operation_log_list + + @classmethod + def add_operation_log_dao(cls, db: Session, operation_log: OperLogModel): + """ + 新增操作日志数据库操作 + :param db: orm对象 + :param operation_log: 操作日志对象 + :return: 新增校验结果 + """ + db_operation_log = SysOperLog(**operation_log.model_dump()) + db.add(db_operation_log) + db.flush() + + return db_operation_log + + @classmethod + def delete_operation_log_dao(cls, db: Session, operation_log: OperLogModel): + """ + 删除操作日志数据库操作 + :param db: orm对象 + :param operation_log: 操作日志对象 + :return: + """ + db.query(SysOperLog) \ + .filter(SysOperLog.oper_id == operation_log.oper_id) \ + .delete() + + @classmethod + def clear_operation_log_dao(cls, db: Session): + """ + 清除操作日志数据库操作 + :param db: orm对象 + :return: + """ + db.query(SysOperLog) \ + .delete() + + +class LoginLogDao: + """ + 登录日志管理模块数据库操作层 + """ + + @classmethod + def get_login_log_list(cls, db: Session, query_object: LoginLogQueryModel): + """ + 根据查询参数获取登录日志列表信息 + :param db: orm对象 + :param query_object: 查询参数对象 + :return: 登录日志列表信息对象 + """ + login_log_list = db.query(SysLogininfor) \ + .filter(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(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 + )\ + .distinct().all() + + return login_log_list + + @classmethod + def add_login_log_dao(cls, db: Session, login_log: LogininforModel): + """ + 新增登录日志数据库操作 + :param db: orm对象 + :param login_log: 登录日志对象 + :return: 新增校验结果 + """ + db_login_log = SysLogininfor(**login_log.model_dump()) + db.add(db_login_log) + db.flush() + + return db_login_log + + @classmethod + def delete_login_log_dao(cls, db: Session, login_log: LogininforModel): + """ + 删除登录日志数据库操作 + :param db: orm对象 + :param login_log: 登录日志对象 + :return: + """ + db.query(SysLogininfor) \ + .filter(SysLogininfor.info_id == login_log.info_id) \ + .delete() + + @classmethod + def clear_login_log_dao(cls, db: Session): + """ + 清除登录日志数据库操作 + :param db: orm对象 + :return: + """ + db.query(SysLogininfor) \ + .delete() diff --git a/ruoyi-fastapi-backend/module_admin/dao/login_dao.py b/ruoyi-fastapi-backend/module_admin/dao/login_dao.py new file mode 100644 index 0000000..b932bc1 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/dao/login_dao.py @@ -0,0 +1,20 @@ +from sqlalchemy.orm import Session +from sqlalchemy import and_ +from module_admin.entity.do.user_do import SysUser +from module_admin.entity.do.dept_do import SysDept + + +def login_by_account(db: Session, user_name: str): + """ + 根据用户名查询用户信息 + :param db: orm对象 + :param user_name: 用户名 + :return: 用户对象 + """ + user = db.query(SysUser, SysDept)\ + .filter(SysUser.user_name == user_name, SysUser.del_flag == '0') \ + .outerjoin(SysDept, and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == 0, SysDept.del_flag == 0)) \ + .distinct() \ + .first() + + return user diff --git a/ruoyi-fastapi-backend/module_admin/dao/menu_dao.py b/ruoyi-fastapi-backend/module_admin/dao/menu_dao.py new file mode 100644 index 0000000..272529b --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/dao/menu_dao.py @@ -0,0 +1,142 @@ +from sqlalchemy import and_ +from sqlalchemy.orm import Session +from module_admin.entity.do.menu_do import SysMenu +from module_admin.entity.do.user_do import SysUser, SysUserRole +from module_admin.entity.do.role_do import SysRole, SysRoleMenu +from module_admin.entity.vo.menu_vo import * + + +class MenuDao: + """ + 菜单管理模块数据库操作层 + """ + + @classmethod + def get_menu_detail_by_id(cls, db: Session, menu_id: int): + """ + 根据菜单id获取菜单详细信息 + :param db: orm对象 + :param menu_id: 菜单id + :return: 菜单信息对象 + """ + menu_info = db.query(SysMenu) \ + .filter(SysMenu.menu_id == menu_id) \ + .first() + + return menu_info + + @classmethod + def get_menu_detail_by_info(cls, db: Session, menu: MenuModel): + """ + 根据菜单参数获取菜单信息 + :param db: orm对象 + :param menu: 菜单参数对象 + :return: 菜单信息对象 + """ + menu_info = db.query(SysMenu) \ + .filter(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) \ + .first() + + return menu_info + + @classmethod + def get_menu_list_for_tree(cls, db: Session, user_id: int, role: list): + """ + 根据角色信息获取所有在用菜单列表信息 + :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 = db.query(SysMenu) \ + .filter(SysMenu.status == 0) \ + .order_by(SysMenu.order_num) \ + .distinct().all() + else: + menu_query_all = db.query(SysMenu).select_from(SysUser) \ + .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \ + .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \ + .outerjoin(SysRole, + and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \ + .outerjoin(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id) \ + .join(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == 0,)) \ + .order_by(SysMenu.order_num) \ + .distinct().all() + + return menu_query_all + + @classmethod + def get_menu_list(cls, db: Session, page_object: MenuQueryModel, user_id: int, role: list): + """ + 根据查询参数获取菜单列表信息 + :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 = db.query(SysMenu) \ + .filter(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().all() + else: + menu_query_all = db.query(SysMenu).select_from(SysUser) \ + .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \ + .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \ + .outerjoin(SysRole, + and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \ + .outerjoin(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id) \ + .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().all() + + return menu_query_all + + @classmethod + def add_menu_dao(cls, db: Session, menu: MenuModel): + """ + 新增菜单数据库操作 + :param db: orm对象 + :param menu: 菜单对象 + :return: + """ + db_menu = SysMenu(**menu.model_dump()) + db.add(db_menu) + db.flush() + + return db_menu + + @classmethod + def edit_menu_dao(cls, db: Session, menu: dict): + """ + 编辑菜单数据库操作 + :param db: orm对象 + :param menu: 需要更新的菜单字典 + :return: + """ + db.query(SysMenu) \ + .filter(SysMenu.menu_id == menu.get('menu_id')) \ + .update(menu) + + @classmethod + def delete_menu_dao(cls, db: Session, menu: MenuModel): + """ + 删除菜单数据库操作 + :param db: orm对象 + :param menu: 菜单对象 + :return: + """ + db.query(SysMenu) \ + .filter(SysMenu.menu_id == menu.menu_id) \ + .delete() diff --git a/ruoyi-fastapi-backend/module_admin/dao/notice_dao.py b/ruoyi-fastapi-backend/module_admin/dao/notice_dao.py new file mode 100644 index 0000000..ff4b347 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/dao/notice_dao.py @@ -0,0 +1,99 @@ +from sqlalchemy.orm import Session +from module_admin.entity.do.notice_do import SysNotice +from module_admin.entity.vo.notice_vo import * +from datetime import datetime, time + + +class NoticeDao: + """ + 通知公告管理模块数据库操作层 + """ + + @classmethod + def get_notice_detail_by_id(cls, db: Session, notice_id: int): + """ + 根据通知公告id获取通知公告详细信息 + :param db: orm对象 + :param notice_id: 通知公告id + :return: 通知公告信息对象 + """ + notice_info = db.query(SysNotice) \ + .filter(SysNotice.notice_id == notice_id) \ + .first() + + return notice_info + + @classmethod + def get_notice_detail_by_info(cls, db: Session, notice: NoticeModel): + """ + 根据通知公告参数获取通知公告信息 + :param db: orm对象 + :param notice: 通知公告参数对象 + :return: 通知公告信息对象 + """ + notice_info = db.query(SysNotice) \ + .filter(SysNotice.notice_title == notice.notice_title if notice.notice_title else True, + SysNotice.notice_type == notice.notice_type if notice.notice_type else True, + SysNotice.notice_content == notice.notice_content if notice.notice_content else True) \ + .first() + + return notice_info + + @classmethod + def get_notice_list(cls, db: Session, query_object: NoticeQueryModel): + """ + 根据查询参数获取通知公告列表信息 + :param db: orm对象 + :param query_object: 查询参数对象 + :return: 通知公告列表信息对象 + """ + notice_list = db.query(SysNotice) \ + .filter(SysNotice.notice_title.like(f'%{query_object.notice_title}%') if query_object.notice_title else True, + SysNotice.update_by.like(f'%{query_object.update_by}%') if query_object.update_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 + ) \ + .distinct().all() + + return notice_list + + @classmethod + def add_notice_dao(cls, db: Session, notice: NoticeModel): + """ + 新增通知公告数据库操作 + :param db: orm对象 + :param notice: 通知公告对象 + :return: + """ + db_notice = SysNotice(**notice.model_dump()) + db.add(db_notice) + db.flush() + + return db_notice + + @classmethod + def edit_notice_dao(cls, db: Session, notice: dict): + """ + 编辑通知公告数据库操作 + :param db: orm对象 + :param notice: 需要更新的通知公告字典 + :return: + """ + db.query(SysNotice) \ + .filter(SysNotice.notice_id == notice.get('notice_id')) \ + .update(notice) + + @classmethod + def delete_notice_dao(cls, db: Session, notice: NoticeModel): + """ + 删除通知公告数据库操作 + :param db: orm对象 + :param notice: 通知公告对象 + :return: + """ + db.query(SysNotice) \ + .filter(SysNotice.notice_id == notice.notice_id) \ + .delete() diff --git a/ruoyi-fastapi-backend/module_admin/dao/post_dao.py b/ruoyi-fastapi-backend/module_admin/dao/post_dao.py new file mode 100644 index 0000000..1c32dc3 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/dao/post_dao.py @@ -0,0 +1,110 @@ +from sqlalchemy.orm import Session +from module_admin.entity.do.post_do import SysPost +from module_admin.entity.vo.post_vo import * + + +class PostDao: + """ + 岗位管理模块数据库操作层 + """ + + @classmethod + def get_post_by_id(cls, db: Session, post_id: int): + """ + 根据岗位id获取在用岗位详细信息 + :param db: orm对象 + :param post_id: 岗位id + :return: 在用岗位信息对象 + """ + post_info = db.query(SysPost) \ + .filter(SysPost.post_id == post_id, + SysPost.status == 0) \ + .first() + + return post_info + + @classmethod + def get_post_detail_by_id(cls, db: Session, post_id: int): + """ + 根据岗位id获取岗位详细信息 + :param db: orm对象 + :param post_id: 岗位id + :return: 岗位信息对象 + """ + post_info = db.query(SysPost) \ + .filter(SysPost.post_id == post_id) \ + .first() + + return post_info + + @classmethod + def get_post_detail_by_info(cls, db: Session, post: PostModel): + """ + 根据岗位参数获取岗位信息 + :param db: orm对象 + :param post: 岗位参数对象 + :return: 岗位信息对象 + """ + post_info = db.query(SysPost) \ + .filter(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) \ + .first() + + return post_info + + @classmethod + def get_post_list(cls, db: Session, query_object: PostModel): + """ + 根据查询参数获取岗位列表信息 + :param db: orm对象 + :param query_object: 查询参数对象 + :return: 岗位列表信息对象 + """ + post_list = db.query(SysPost) \ + .filter(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().all() + + return post_list + + @classmethod + def add_post_dao(cls, db: Session, post: PostModel): + """ + 新增岗位数据库操作 + :param db: orm对象 + :param post: 岗位对象 + :return: + """ + db_post = SysPost(**post.model_dump()) + db.add(db_post) + db.flush() + + return db_post + + @classmethod + def edit_post_dao(cls, db: Session, post: dict): + """ + 编辑岗位数据库操作 + :param db: orm对象 + :param post: 需要更新的岗位字典 + :return: + """ + db.query(SysPost) \ + .filter(SysPost.post_id == post.get('post_id')) \ + .update(post) + + @classmethod + def delete_post_dao(cls, db: Session, post: PostModel): + """ + 删除岗位数据库操作 + :param db: orm对象 + :param post: 岗位对象 + :return: + """ + db.query(SysPost) \ + .filter(SysPost.post_id == post.post_id) \ + .delete() diff --git a/ruoyi-fastapi-backend/module_admin/dao/role_dao.py b/ruoyi-fastapi-backend/module_admin/dao/role_dao.py new file mode 100644 index 0000000..88bdac7 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/dao/role_dao.py @@ -0,0 +1,222 @@ +from sqlalchemy import desc, func +from sqlalchemy.orm import Session +from module_admin.entity.do.role_do import SysRole, SysRoleMenu, SysRoleDept +from module_admin.entity.do.dept_do import SysDept +from module_admin.entity.vo.role_vo import * +from datetime import datetime, time + + +class RoleDao: + """ + 角色管理模块数据库操作层 + """ + + @classmethod + def get_role_by_name(cls, db: Session, role_name: str): + """ + 根据角色名获取在用角色信息 + :param db: orm对象 + :param role_name: 角色名 + :return: 当前角色名的角色信息对象 + """ + query_role_info = db.query(SysRole) \ + .filter(SysRole.status == 0, SysRole.del_flag == 0, SysRole.role_name == role_name) \ + .order_by(desc(SysRole.create_time)).distinct().first() + + return query_role_info + + @classmethod + def get_role_by_info(cls, db: Session, role: RoleModel): + """ + 根据角色参数获取角色信息 + :param db: orm对象 + :param role: 角色参数 + :return: 当前角色参数的角色信息对象 + """ + query_role_info = db.query(SysRole) \ + .filter(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().first() + + return query_role_info + + @classmethod + def get_role_by_id(cls, db: Session, role_id: int): + """ + 根据角色id获取在用角色信息 + :param db: orm对象 + :param role_id: 角色id + :return: 当前角色id的角色信息对象 + """ + role_info = db.query(SysRole) \ + .filter(SysRole.role_id == role_id, + SysRole.status == 0, + SysRole.del_flag == 0) \ + .first() + + return role_info + + @classmethod + def get_role_detail_by_id(cls, db: Session, role_id: int): + """ + 根据role_id获取角色详细信息 + :param db: orm对象 + :param role_id: 角色id + :return: 当前role_id的角色信息对象 + """ + query_role_info = db.query(SysRole) \ + .filter(SysRole.del_flag == 0, SysRole.role_id == role_id) \ + .distinct().first() + + return query_role_info + + @classmethod + def get_role_select_option_dao(cls, db: Session): + """ + 获取编辑页面对应的在用角色列表信息 + :param db: orm对象 + :return: 角色列表信息 + """ + role_info = db.query(SysRole) \ + .filter(SysRole.role_id != 1, SysRole.status == 0, SysRole.del_flag == 0) \ + .all() + + return role_info + + @classmethod + def get_role_list(cls, db: Session, query_object: RoleQueryModel): + """ + 根据查询参数获取角色列表信息 + :param db: orm对象 + :param query_object: 查询参数对象 + :return: 角色列表信息对象 + """ + role_list = db.query(SysRole) \ + .filter(SysRole.del_flag == 0, + 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 + ) \ + .order_by(SysRole.role_sort) \ + .distinct().all() + + return role_list + + @classmethod + def add_role_dao(cls, db: Session, role: RoleModel): + """ + 新增角色数据库操作 + :param db: orm对象 + :param role: 角色对象 + :return: + """ + db_role = SysRole(**role.model_dump()) + db.add(db_role) + db.flush() + + return db_role + + @classmethod + def edit_role_dao(cls, db: Session, role: dict): + """ + 编辑角色数据库操作 + :param db: orm对象 + :param role: 需要更新的角色字典 + :return: + """ + db.query(SysRole) \ + .filter(SysRole.role_id == role.get('role_id')) \ + .update(role) + + @classmethod + def delete_role_dao(cls, db: Session, role: RoleModel): + """ + 删除角色数据库操作 + :param db: orm对象 + :param role: 角色对象 + :return: + """ + db.query(SysRole) \ + .filter(SysRole.role_id == role.role_id) \ + .update({SysRole.del_flag: '2', SysRole.update_by: role.update_by, SysRole.update_time: role.update_time}) + + @classmethod + def get_role_menu_dao(cls, db: Session, role_id: int): + """ + 根据角色id获取角色菜单关联列表信息 + :param db: orm对象 + :param role_id: 角色id + :return: 角色菜单关联列表信息 + """ + role_menu_query_all = db.query(SysRoleMenu) \ + .filter(SysRoleMenu.role_id == role_id) \ + .distinct().all() + + return role_menu_query_all + + @classmethod + def add_role_menu_dao(cls, db: Session, role_menu: RoleMenuModel): + """ + 新增角色菜单关联信息数据库操作 + :param db: orm对象 + :param role_menu: 用户角色菜单关联对象 + :return: + """ + db_role_menu = SysRoleMenu(**role_menu.model_dump()) + db.add(db_role_menu) + + @classmethod + def delete_role_menu_dao(cls, db: Session, role_menu: RoleMenuModel): + """ + 删除角色菜单关联信息数据库操作 + :param db: orm对象 + :param role_menu: 角色菜单关联对象 + :return: + """ + db.query(SysRoleMenu) \ + .filter(SysRoleMenu.role_id == role_menu.role_id) \ + .delete() + + @classmethod + def get_role_dept_dao(cls, db: Session, role_id: int): + """ + 根据角色id获取角色部门关联列表信息 + :param db: orm对象 + :param role_id: 角色id + :return: 角色部门关联列表信息 + """ + role_dept_query_all = db.query(SysRoleDept) \ + .filter(SysRoleDept.role_id == role_id, + ~db.query(SysDept).filter(func.find_in_set(SysRoleDept.dept_id, SysDept.ancestors)).exists() + ) \ + .distinct().all() + + return role_dept_query_all + + @classmethod + def add_role_dept_dao(cls, db: Session, role_dept: RoleDeptModel): + """ + 新增角色部门关联信息数据库操作 + :param db: orm对象 + :param role_dept: 用户角色部门关联对象 + :return: + """ + db_role_dept = SysRoleDept(**role_dept.dict()) + db.add(db_role_dept) + + @classmethod + def delete_role_dept_dao(cls, db: Session, role_dept: RoleDeptModel): + """ + 删除角色部门关联信息数据库操作 + :param db: orm对象 + :param role_dept: 角色部门关联对象 + :return: + """ + db.query(SysRoleDept) \ + .filter(SysRoleDept.role_id == role_dept.role_id) \ + .delete() diff --git a/ruoyi-fastapi-backend/module_admin/dao/user_dao.py b/ruoyi-fastapi-backend/module_admin/dao/user_dao.py new file mode 100644 index 0000000..982ca81 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/dao/user_dao.py @@ -0,0 +1,372 @@ +from sqlalchemy import and_, or_, desc, func +from sqlalchemy.orm import Session +from module_admin.entity.do.user_do import SysUser, SysUserRole, SysUserPost +from module_admin.entity.do.role_do import SysRole, SysRoleMenu +from module_admin.entity.do.dept_do import SysDept +from module_admin.entity.do.post_do import SysPost +from module_admin.entity.do.menu_do import SysMenu +from module_admin.entity.vo.user_vo import * +from utils.time_format_util import list_format_datetime +from datetime import datetime, time + + +class UserDao: + """ + 用户管理模块数据库操作层 + """ + + @classmethod + def get_user_by_name(cls, db: Session, user_name: str): + """ + 根据用户名获取用户信息 + :param db: orm对象 + :param user_name: 用户名 + :return: 当前用户名的用户信息对象 + """ + query_user_info = db.query(SysUser) \ + .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_name == user_name) \ + .order_by(desc(SysUser.create_time)).distinct().first() + + return query_user_info + + @classmethod + def get_user_by_info(cls, db: Session, user: UserModel): + """ + 根据用户参数获取用户信息 + :param db: orm对象 + :param user: 用户参数 + :return: 当前用户参数的用户信息对象 + """ + query_user_info = db.query(SysUser) \ + .filter(SysUser.del_flag == 0, + SysUser.user_name == user.user_name) \ + .order_by(desc(SysUser.create_time)).distinct().first() + + return query_user_info + + @classmethod + def get_user_by_id(cls, db: Session, user_id: int): + """ + 根据user_id获取用户信息 + :param db: orm对象 + :param user_id: 用户id + :return: 当前user_id的用户信息对象 + """ + query_user_basic_info = db.query(SysUser) \ + .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \ + .distinct().first() + query_user_dept_info = db.query(SysDept).select_from(SysUser) \ + .filter(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().first() + query_user_role_info = db.query(SysRole).select_from(SysUser) \ + .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \ + .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \ + .join(SysRole, and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \ + .distinct().all() + query_user_post_info = db.query(SysPost).select_from(SysUser) \ + .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \ + .outerjoin(SysUserPost, SysUser.user_id == SysUserPost.user_id) \ + .join(SysPost, and_(SysUserPost.post_id == SysPost.post_id, SysPost.status == 0)) \ + .distinct().all() + role_id_list = [item.role_id for item in query_user_role_info] + if 1 in role_id_list: + query_user_menu_info = db.query(SysMenu) \ + .filter(SysMenu.status == 0) \ + .distinct().all() + else: + query_user_menu_info = db.query(SysMenu).select_from(SysUser) \ + .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \ + .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \ + .outerjoin(SysRole, and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \ + .outerjoin(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id) \ + .join(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == 0)) \ + .order_by(SysMenu.order_num) \ + .distinct().all() + + results = dict( + 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 + def get_user_detail_by_id(cls, db: Session, user_id: int): + """ + 根据user_id获取用户详细信息 + :param db: orm对象 + :param user_id: 用户id + :return: 当前user_id的用户信息对象 + """ + query_user_basic_info = db.query(SysUser) \ + .filter(SysUser.del_flag == 0, SysUser.user_id == user_id) \ + .distinct().first() + query_user_dept_info = db.query(SysDept).select_from(SysUser) \ + .filter(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().first() + query_user_role_info = db.query(SysRole).select_from(SysUser) \ + .filter(SysUser.del_flag == 0, SysUser.user_id == user_id) \ + .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \ + .join(SysRole, and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \ + .distinct().all() + query_user_post_info = db.query(SysPost).select_from(SysUser) \ + .filter(SysUser.del_flag == 0, SysUser.user_id == user_id) \ + .outerjoin(SysUserPost, SysUser.user_id == SysUserPost.user_id) \ + .join(SysPost, and_(SysUserPost.post_id == SysPost.post_id, SysPost.status == 0)) \ + .distinct().all() + query_user_menu_info = db.query(SysMenu).select_from(SysUser) \ + .filter(SysUser.del_flag == 0, SysUser.user_id == user_id) \ + .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \ + .outerjoin(SysRole, and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \ + .outerjoin(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id) \ + .join(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == 0)) \ + .distinct().all() + results = dict( + 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 + def get_user_list(cls, db: Session, query_object: UserQueryModel, data_scope_sql: str): + """ + 根据查询参数获取用户列表信息 + :param db: orm对象 + :param query_object: 查询参数对象 + :param data_scope_sql: 数据权限对应的查询sql语句 + :return: 用户列表信息对象 + """ + user_list = db.query(SysUser, SysDept) \ + .filter(SysUser.del_flag == 0, + or_(SysUser.dept_id == query_object.dept_id, SysUser.dept_id.in_( + db.query(SysDept.dept_id).filter(func.find_in_set(query_object.dept_id, SysDept.ancestors)) + )) if query_object.dept_id 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, + eval(data_scope_sql) + ) \ + .outerjoin(SysDept, and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == 0, SysDept.del_flag == 0)) \ + .distinct().all() + + return user_list + + @classmethod + def add_user_dao(cls, db: Session, user: UserModel): + """ + 新增用户数据库操作 + :param db: orm对象 + :param user: 用户对象 + :return: 新增校验结果 + """ + db_user = SysUser(**user.model_dump()) + db.add(db_user) + db.flush() + + return db_user + + @classmethod + def edit_user_dao(cls, db: Session, user: dict): + """ + 编辑用户数据库操作 + :param db: orm对象 + :param user: 需要更新的用户字典 + :return: 编辑校验结果 + """ + db.query(SysUser) \ + .filter(SysUser.user_id == user.get('user_id')) \ + .update(user) + + @classmethod + def delete_user_dao(cls, db: Session, user: UserModel): + """ + 删除用户数据库操作 + :param db: orm对象 + :param user: 用户对象 + :return: + """ + db.query(SysUser) \ + .filter(SysUser.user_id == user.user_id) \ + .update({SysUser.del_flag: '2', SysUser.update_by: user.update_by, SysUser.update_time: user.update_time}) + + @classmethod + def get_user_role_allocated_list_by_user_id(cls, db: Session, query_object: UserRoleQueryModel): + """ + 根据用户id获取用户已分配的角色列表信息数据库操作 + :param db: orm对象 + :param query_object: 用户角色查询对象 + :return: 用户已分配的角色列表信息 + """ + allocated_role_list = db.query(SysRole) \ + .filter( + 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_( + db.query(SysUserRole.role_id).filter(SysUserRole.user_id == query_object.user_id) + ) + ).distinct().all() + + return allocated_role_list + + @classmethod + def get_user_role_unallocated_list_by_user_id(cls, db: Session, query_object: UserRoleQueryModel): + """ + 根据用户id获取用户未分配的角色列表信息数据库操作 + :param db: orm对象 + :param query_object: 用户角色查询对象 + :return: 用户未分配的角色列表信息 + """ + unallocated_role_list = db.query(SysRole) \ + .filter( + 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_( + db.query(SysUserRole.role_id).filter(SysUserRole.user_id == query_object.user_id) + ) + ).distinct().all() + + return list_format_datetime(unallocated_role_list) + + @classmethod + def get_user_role_allocated_list_by_role_id(cls, db: Session, query_object: UserRoleQueryModel): + """ + 根据角色id获取已分配的用户列表信息 + :param db: orm对象 + :param query_object: 用户角色查询对象 + :return: 角色已分配的用户列表信息 + """ + allocated_user_list = db.query(SysUser) \ + .filter( + SysUser.del_flag == 0, + SysUser.user_id != 1, + 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, + SysUser.user_id.in_( + db.query(SysUserRole.user_id).filter(SysUserRole.role_id == query_object.role_id) + ) + ).distinct().all() + + return allocated_user_list + + @classmethod + def get_user_role_unallocated_list_by_role_id(cls, db: Session, query_object: UserRoleQueryModel): + """ + 根据角色id获取未分配的用户列表信息 + :param db: orm对象 + :param query_object: 用户角色查询对象 + :return: 角色未分配的用户列表信息 + """ + unallocated_user_list = db.query(SysUser) \ + .filter( + SysUser.del_flag == 0, + SysUser.user_id != 1, + 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, + ~SysUser.user_id.in_( + db.query(SysUserRole.user_id).filter(SysUserRole.role_id == query_object.role_id) + ) + ).distinct().all() + + return unallocated_user_list + + @classmethod + def add_user_role_dao(cls, db: Session, user_role: UserRoleModel): + """ + 新增用户角色关联信息数据库操作 + :param db: orm对象 + :param user_role: 用户角色关联对象 + :return: + """ + db_user_role = SysUserRole(**user_role.model_dump()) + db.add(db_user_role) + + @classmethod + def delete_user_role_dao(cls, db: Session, user_role: UserRoleModel): + """ + 删除用户角色关联信息数据库操作 + :param db: orm对象 + :param user_role: 用户角色关联对象 + :return: + """ + db.query(SysUserRole) \ + .filter(SysUserRole.user_id == user_role.user_id) \ + .delete() + + @classmethod + def delete_user_role_by_user_and_role_dao(cls, db: Session, user_role: UserRoleModel): + """ + 根据用户id及角色id删除用户角色关联信息数据库操作 + :param db: orm对象 + :param user_role: 用户角色关联对象 + :return: + """ + db.query(SysUserRole) \ + .filter(SysUserRole.user_id == user_role.user_id, + SysUserRole.role_id == user_role.role_id if user_role.role_id else True) \ + .delete() + + @classmethod + def get_user_role_detail(cls, db: Session, user_role: UserRoleModel): + """ + 根据用户角色关联获取用户角色关联详细信息 + :param db: orm对象 + :param user_role: 用户角色关联对象 + :return: 用户角色关联信息 + """ + user_role_info = db.query(SysUserRole) \ + .filter(SysUserRole.user_id == user_role.user_id, SysUserRole.role_id == user_role.role_id) \ + .distinct().first() + + return user_role_info + + @classmethod + def add_user_post_dao(cls, db: Session, user_post: UserPostModel): + """ + 新增用户岗位关联信息数据库操作 + :param db: orm对象 + :param user_post: 用户岗位关联对象 + :return: + """ + db_user_post = SysUserPost(**user_post.model_dump()) + db.add(db_user_post) + + @classmethod + def delete_user_post_dao(cls, db: Session, user_post: UserPostModel): + """ + 删除用户岗位关联信息数据库操作 + :param db: orm对象 + :param user_post: 用户岗位关联对象 + :return: + """ + db.query(SysUserPost) \ + .filter(SysUserPost.user_id == user_post.user_id) \ + .delete() + + @classmethod + def get_user_dept_info(cls, db: Session, dept_id: int): + dept_basic_info = db.query(SysDept) \ + .filter(SysDept.dept_id == dept_id, + SysDept.status == 0, + SysDept.del_flag == 0) \ + .first() + return dept_basic_info diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/config_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/config_do.py new file mode 100644 index 0000000..5fb6476 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/do/config_do.py @@ -0,0 +1,21 @@ +from sqlalchemy import Column, Integer, String, DateTime +from config.database import Base +from datetime import datetime + + +class SysConfig(Base): + """ + 参数配置表 + """ + __tablename__ = 'sys_config' + + config_id = Column(Integer, primary_key=True, autoincrement=True, comment='参数主键') + config_name = Column(String(100), nullable=True, default='', comment='参数名称') + config_key = Column(String(100), nullable=True, default='', comment='参数键名') + config_value = Column(String(500), nullable=True, default='', comment='参数键值') + config_type = Column(String(1), nullable=True, default='N', comment='系统内置(Y是 N否)') + create_by = Column(String(64), nullable=True, default='', comment='创建者') + create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') + update_by = Column(String(64), nullable=True, default='', comment='更新者') + update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') + remark = Column(String(500), nullable=True, default='', comment='备注') \ No newline at end of file diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/dept_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/dept_do.py new file mode 100644 index 0000000..0572927 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/do/dept_do.py @@ -0,0 +1,25 @@ +from sqlalchemy import Column, Integer, String, DateTime +from config.database import Base +from datetime import datetime + + +class SysDept(Base): + """ + 部门表 + """ + __tablename__ = 'sys_dept' + + dept_id = Column(Integer, primary_key=True, autoincrement=True, comment='部门id') + parent_id = Column(Integer, default=0, comment='父部门id') + ancestors = Column(String(50), nullable=True, default='', comment='祖级列表') + dept_name = Column(String(30), nullable=True, default='', comment='部门名称') + order_num = Column(Integer, default=0, comment='显示顺序') + leader = Column(String(20), nullable=True, default=None, comment='负责人') + phone = Column(String(11), nullable=True, default=None, comment='联系电话') + email = Column(String(50), nullable=True, default=None, comment='邮箱') + status = Column(String(1), nullable=True, default=0, comment='部门状态(0正常 1停用)') + del_flag = Column(String(1), nullable=True, default=0, comment='删除标志(0代表存在 2代表删除)') + create_by = Column(String(64), nullable=True, default='', comment='创建者') + create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') + update_by = Column(String(64), nullable=True, default='', comment='更新者') + update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/dict_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/dict_do.py new file mode 100644 index 0000000..a7d630e --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/do/dict_do.py @@ -0,0 +1,46 @@ +from sqlalchemy import Column, Integer, String, DateTime, UniqueConstraint +from config.database import Base +from datetime import datetime + + +class SysDictType(Base): + """ + 字典类型表 + """ + __tablename__ = 'sys_dict_type' + + dict_id = Column(Integer, primary_key=True, autoincrement=True, comment='字典主键') + dict_name = Column(String(100), nullable=True, default='', comment='字典名称') + dict_type = Column(String(100), nullable=True, default='', comment='字典类型') + status = Column(String(1), nullable=True, default='0', comment='状态(0正常 1停用)') + create_by = Column(String(64), nullable=True, default='', comment='创建者') + create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') + update_by = Column(String(64), nullable=True, default='', comment='更新者') + update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') + remark = Column(String(500), nullable=True, default='', comment='备注') + + __table_args__ = ( + UniqueConstraint('dict_type', name='uq_sys_dict_type_dict_type'), + ) + + +class SysDictData(Base): + """ + 字典数据表 + """ + __tablename__ = 'sys_dict_data' + + dict_code = Column(Integer, primary_key=True, autoincrement=True, comment='字典编码') + dict_sort = Column(Integer, nullable=True, default=0, comment='字典排序') + dict_label = Column(String(100), nullable=True, default='', comment='字典标签') + dict_value = Column(String(100), nullable=True, default='', comment='字典键值') + dict_type = Column(String(100), nullable=True, default='', comment='字典类型') + css_class = Column(String(100), nullable=True, default='', comment='样式属性(其他样式扩展)') + list_class = Column(String(100), nullable=True, default='', comment='表格回显样式') + is_default = Column(String(1), nullable=True, default='N', comment='是否默认(Y是 N否)') + status = Column(String(1), nullable=True, default='0', comment='状态(0正常 1停用)') + create_by = Column(String(64), nullable=True, default='', comment='创建者') + create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') + update_by = Column(String(64), nullable=True, default='', comment='更新者') + update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') + remark = Column(String(500), nullable=True, default='', comment='备注') diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/job_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/job_do.py new file mode 100644 index 0000000..aef168f --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/do/job_do.py @@ -0,0 +1,47 @@ +from sqlalchemy import Column, Integer, String, DateTime +from config.database import Base +from datetime import datetime + + +class SysJob(Base): + """ + 定时任务调度表 + """ + __tablename__ = 'sys_job' + + job_id = Column(Integer, primary_key=True, autoincrement=True, comment='任务ID') + job_name = Column(String(64, collation='utf8_general_ci'), nullable=False, comment='任务名称') + job_group = Column(String(64, collation='utf8_general_ci'), nullable=False, default='default', comment='任务组名') + job_executor = Column(String(64, collation='utf8_general_ci'), nullable=False, default='default', comment='任务执行器') + invoke_target = Column(String(500, collation='utf8_general_ci'), nullable=False, comment='调用目标字符串') + job_args = Column(String(255, collation='utf8_general_ci'), nullable=True, comment='位置参数') + job_kwargs = Column(String(255, collation='utf8_general_ci'), nullable=True, comment='关键字参数') + cron_expression = Column(String(255, collation='utf8_general_ci'), nullable=True, default='', comment='cron执行表达式') + misfire_policy = Column(String(20, collation='utf8_general_ci'), nullable=True, default='3', comment='计划执行错误策略(1立即执行 2执行一次 3放弃执行)') + concurrent = Column(String(1, collation='utf8_general_ci'), nullable=True, default='1', comment='是否并发执行(0允许 1禁止)') + status = Column(String(1, collation='utf8_general_ci'), nullable=True, default='0', comment='状态(0正常 1暂停)') + create_by = Column(String(64, collation='utf8_general_ci'), nullable=True, default='', comment='创建者') + create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') + update_by = Column(String(64, collation='utf8_general_ci'), nullable=True, default='', comment='更新者') + update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') + remark = Column(String(500, collation='utf8_general_ci'), nullable=True, default='', comment='备注信息') + + +class SysJobLog(Base): + """ + 定时任务调度日志表 + """ + __tablename__ = 'sys_job_log' + + job_log_id = Column(Integer, primary_key=True, autoincrement=True, comment='任务日志ID') + job_name = Column(String(64, collation='utf8_general_ci'), nullable=False, comment='任务名称') + job_group = Column(String(64, collation='utf8_general_ci'), nullable=False, comment='任务组名') + job_executor = Column(String(64, collation='utf8_general_ci'), nullable=False, default='default', comment='任务执行器') + invoke_target = Column(String(500, collation='utf8_general_ci'), nullable=False, comment='调用目标字符串') + job_args = Column(String(255, collation='utf8_general_ci'), nullable=True, comment='位置参数') + job_kwargs = Column(String(255, collation='utf8_general_ci'), nullable=True, comment='关键字参数') + job_trigger = Column(String(255, collation='utf8_general_ci'), nullable=True, comment='任务触发器') + job_message = Column(String(500, collation='utf8_general_ci'), nullable=True, default='', comment='日志信息') + status = Column(String(1, collation='utf8_general_ci'), nullable=True, default='0', comment='执行状态(0正常 1失败)') + exception_info = Column(String(2000, collation='utf8_general_ci'), nullable=True, default='', comment='异常信息') + create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/log_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/log_do.py new file mode 100644 index 0000000..19d2b18 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/do/log_do.py @@ -0,0 +1,52 @@ +from sqlalchemy import Column, Integer, String, DateTime, Text, BigInteger, Index +from config.database import Base +from datetime import datetime + + +class SysLogininfor(Base): + """ + 系统访问记录 + """ + __tablename__ = 'sys_logininfor' + + info_id = Column(Integer, primary_key=True, autoincrement=True, comment='访问ID') + user_name = Column(String(50, collation='utf8_general_ci'), nullable=True, default='', comment='用户账号') + ipaddr = Column(String(128, collation='utf8_general_ci'), nullable=True, default='', comment='登录IP地址') + login_location = Column(String(255, collation='utf8_general_ci'), nullable=True, default='', comment='登录地点') + browser = Column(String(50, collation='utf8_general_ci'), nullable=True, default='', comment='浏览器类型') + os = Column(String(50, collation='utf8_general_ci'), nullable=True, default='', comment='操作系统') + status = Column(String(1, collation='utf8_general_ci'), nullable=True, default='0', comment='登录状态(0成功 1失败)') + msg = Column(String(255, collation='utf8_general_ci'), nullable=True, 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' + + oper_id = Column(BigInteger, primary_key=True, autoincrement=True, comment='日志主键') + title = Column(String(50, collation='utf8_general_ci'), nullable=True, default='', comment='模块标题') + business_type = Column(Integer, default=0, comment='业务类型(0其它 1新增 2修改 3删除)') + method = Column(String(100, collation='utf8_general_ci'), nullable=True, default='', comment='方法名称') + request_method = Column(String(10, collation='utf8_general_ci'), nullable=True, default='', comment='请求方式') + operator_type = Column(Integer, default=0, comment='操作类别(0其它 1后台用户 2手机端用户)') + oper_name = Column(String(50, collation='utf8_general_ci'), nullable=True, default='', comment='操作人员') + dept_name = Column(String(50, collation='utf8_general_ci'), nullable=True, default='', comment='部门名称') + oper_url = Column(String(255, collation='utf8_general_ci'), nullable=True, default='', comment='请求URL') + oper_ip = Column(String(128, collation='utf8_general_ci'), nullable=True, default='', comment='主机地址') + oper_location = Column(String(255, collation='utf8_general_ci'), nullable=True, default='', comment='操作地点') + oper_param = Column(String(2000, collation='utf8_general_ci'), nullable=True, default='', comment='请求参数') + json_result = Column(String(2000, collation='utf8_general_ci'), nullable=True, default='', comment='返回参数') + status = Column(Integer, default=0, comment='操作状态(0正常 1异常)') + error_msg = Column(String(2000, collation='utf8_general_ci'), nullable=True, default='', comment='错误消息') + oper_time = Column(DateTime, nullable=True, default=datetime.now(), comment='操作时间') + cost_time = Column(BigInteger, 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) diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/menu_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/menu_do.py new file mode 100644 index 0000000..8f98be4 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/do/menu_do.py @@ -0,0 +1,30 @@ +from sqlalchemy import Column, Integer, String, DateTime +from config.database import Base +from datetime import datetime + + +class SysMenu(Base): + """ + 菜单权限表 + """ + __tablename__ = 'sys_menu' + + menu_id = Column(Integer, primary_key=True, autoincrement=True, comment='菜单ID') + menu_name = Column(String(50), nullable=False, default='', comment='菜单名称') + parent_id = Column(Integer, default=0, comment='父菜单ID') + order_num = Column(Integer, default=0, comment='显示顺序') + path = Column(String(200), nullable=True, default='', comment='路由地址') + component = Column(String(255), nullable=True, default=None, comment='组件路径') + query = Column(String(255), nullable=True, default=None, comment='路由参数') + is_frame = Column(Integer, default=1, comment='是否为外链(0是 1否)') + is_cache = Column(Integer, default=0, comment='是否缓存(0缓存 1不缓存)') + menu_type = Column(String(1), nullable=True, default='', comment='菜单类型(M目录 C菜单 F按钮)') + visible = Column(String(1), nullable=True, default='0', comment='菜单状态(0显示 1隐藏)') + status = Column(String(1), nullable=True, default='0', comment='菜单状态(0正常 1停用)') + perms = Column(String(100), nullable=True, default=None, comment='权限标识') + icon = Column(String(100), nullable=True, default='#', comment='菜单图标') + create_by = Column(String(64), nullable=True, default='', comment='创建者') + create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') + update_by = Column(String(64), nullable=True, default='', comment='更新者') + update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') + remark = Column(String(500), nullable=True, default='', comment='备注') diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/notice_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/notice_do.py new file mode 100644 index 0000000..3856658 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/do/notice_do.py @@ -0,0 +1,21 @@ +from sqlalchemy import Column, Integer, String, DateTime, LargeBinary +from config.database import Base +from datetime import datetime + + +class SysNotice(Base): + """ + 通知公告表 + """ + __tablename__ = 'sys_notice' + + notice_id = Column(Integer, primary_key=True, autoincrement=True, comment='公告ID') + notice_title = Column(String(50, collation='utf8_general_ci'), nullable=False, comment='公告标题') + notice_type = Column(String(1, collation='utf8_general_ci'), nullable=False, comment='公告类型(1通知 2公告)') + notice_content = Column(LargeBinary, comment='公告内容') + status = Column(String(1, collation='utf8_general_ci'), default='0', comment='公告状态(0正常 1关闭)') + create_by = Column(String(64, collation='utf8_general_ci'), default='', comment='创建者') + create_time = Column(DateTime, comment='创建时间', default=datetime.now()) + update_by = Column(String(64, collation='utf8_general_ci'), default='', comment='更新者') + update_time = Column(DateTime, comment='更新时间', default=datetime.now()) + remark = Column(String(255, collation='utf8_general_ci'), comment='备注') diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/post_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/post_do.py new file mode 100644 index 0000000..c6b189b --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/do/post_do.py @@ -0,0 +1,21 @@ +from sqlalchemy import Column, Integer, String, DateTime +from config.database import Base +from datetime import datetime + + +class SysPost(Base): + """ + 岗位信息表 + """ + __tablename__ = 'sys_post' + + post_id = Column(Integer, primary_key=True, 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(String(1), nullable=False, default='0', comment='状态(0正常 1停用)') + create_by = Column(String(64), default='', comment='创建者') + create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间') + update_by = Column(String(64), default='', comment='更新者') + update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间') + remark = Column(String(500), nullable=True, default='', comment='备注') diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/role_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/role_do.py new file mode 100644 index 0000000..db29244 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/do/role_do.py @@ -0,0 +1,45 @@ +from sqlalchemy import Column, Integer, String, DateTime +from config.database import Base +from datetime import datetime + + +class SysRole(Base): + """ + 角色信息表 + """ + __tablename__ = 'sys_role' + + role_id = Column(Integer, primary_key=True, autoincrement=True, comment='角色ID') + role_name = Column(String(30, collation='utf8_general_ci'), nullable=False, comment='角色名称') + role_key = Column(String(100, collation='utf8_general_ci'), nullable=False, comment='角色权限字符串') + role_sort = Column(Integer, nullable=False, comment='显示顺序') + data_scope = Column(String(1, collation='utf8_general_ci'), default='1', comment='数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)') + menu_check_strictly = Column(Integer, default=1, comment='菜单树选择项是否关联显示') + dept_check_strictly = Column(Integer, default=1, comment='部门树选择项是否关联显示') + status = Column(String(1, collation='utf8_general_ci'), nullable=False, comment='角色状态(0正常 1停用)') + del_flag = Column(String(1, collation='utf8_general_ci'), default='0', comment='删除标志(0代表存在 2代表删除)') + create_by = Column(String(64, collation='utf8_general_ci'), default='', comment='创建者') + create_time = Column(DateTime, default=datetime.now(), comment='创建时间') + update_by = Column(String(64, collation='utf8_general_ci'), default='', comment='更新者') + update_time = Column(DateTime, default=datetime.now(), comment='更新时间') + remark = Column(String(500, collation='utf8_general_ci'), comment='备注') + + +class SysRoleDept(Base): + """ + 角色和部门关联表 + """ + __tablename__ = 'sys_role_dept' + + role_id = Column(Integer, primary_key=True, nullable=False, comment='角色ID') + dept_id = Column(Integer, primary_key=True, nullable=False, comment='部门ID') + + +class SysRoleMenu(Base): + """ + 角色和菜单关联表 + """ + __tablename__ = 'sys_role_menu' + + role_id = Column(Integer, primary_key=True, nullable=False, comment='角色ID') + menu_id = Column(Integer, primary_key=True, nullable=False, comment='菜单ID') diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/user_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/user_do.py new file mode 100644 index 0000000..9351547 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/do/user_do.py @@ -0,0 +1,50 @@ +from sqlalchemy import Column, Integer, String, DateTime +from config.database import Base +from datetime import datetime + + +class SysUser(Base): + """ + 用户信息表 + """ + __tablename__ = 'sys_user' + + user_id = Column(Integer, primary_key=True, autoincrement=True, comment='用户ID') + dept_id = Column(Integer, comment='部门ID') + user_name = Column(String(30, collation='utf8_general_ci'), nullable=False, comment='用户账号') + nick_name = Column(String(30, collation='utf8_general_ci'), nullable=False, comment='用户昵称') + user_type = Column(String(2, collation='utf8_general_ci'), default='00', comment='用户类型(00系统用户)') + email = Column(String(50, collation='utf8_general_ci'), default='', comment='用户邮箱') + phonenumber = Column(String(11, collation='utf8_general_ci'), default='', comment='手机号码') + sex = Column(String(1, collation='utf8_general_ci'), default='0', comment='用户性别(0男 1女 2未知)') + avatar = Column(String(100, collation='utf8_general_ci'), default='', comment='头像地址') + password = Column(String(100, collation='utf8_general_ci'), default='', comment='密码') + status = Column(String(1, collation='utf8_general_ci'), default='0', comment='帐号状态(0正常 1停用)') + del_flag = Column(String(1, collation='utf8_general_ci'), default='0', comment='删除标志(0代表存在 2代表删除)') + login_ip = Column(String(128, collation='utf8_general_ci'), default='', comment='最后登录IP') + login_date = Column(DateTime, comment='最后登录时间') + create_by = Column(String(64, collation='utf8_general_ci'), default='', comment='创建者') + create_time = Column(DateTime, comment='创建时间', default=datetime.now()) + update_by = Column(String(64, collation='utf8_general_ci'), default='', comment='更新者') + update_time = Column(DateTime, comment='更新时间', default=datetime.now()) + remark = Column(String(500, collation='utf8_general_ci'), comment='备注') + + +class SysUserRole(Base): + """ + 用户和角色关联表 + """ + __tablename__ = 'sys_user_role' + + user_id = Column(Integer, primary_key=True, nullable=False, comment='用户ID') + role_id = Column(Integer, primary_key=True, nullable=False, comment='角色ID') + + +class SysUserPost(Base): + """ + 用户与岗位关联表 + """ + __tablename__ = 'sys_user_post' + + user_id = Column(Integer, primary_key=True, nullable=False, comment='用户ID') + post_id = Column(Integer, primary_key=True, nullable=False, comment='岗位ID') diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/cache_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/cache_vo.py new file mode 100644 index 0000000..f2fc9ff --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/cache_vo.py @@ -0,0 +1,26 @@ +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel +from typing import Optional, List, Any + + +class CacheMonitorModel(BaseModel): + """ + 缓存监控信息对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + command_stats: Optional[List] = [] + db_size: Optional[int] = None + info: Optional[dict] = {} + + +class CacheInfoModel(BaseModel): + """ + 缓存监控对象对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + cache_key: Optional[str] = None + cache_name: Optional[str] = None + cache_value: Optional[Any] = None + remark: Optional[str] = None diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/common_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/common_vo.py new file mode 100644 index 0000000..072269e --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/common_vo.py @@ -0,0 +1,9 @@ +from pydantic import BaseModel + + +class CrudResponseModel(BaseModel): + """ + 操作响应模型 + """ + is_success: bool + message: str diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/config_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/config_vo.py new file mode 100644 index 0000000..3d3cc39 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/config_vo.py @@ -0,0 +1,50 @@ +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel +from typing import Union, Optional, List +from datetime import datetime +from module_admin.annotation.pydantic_annotation import as_query, as_form + + +class ConfigModel(BaseModel): + """ + 参数配置表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + config_id: Optional[int] = None + config_name: Optional[str] = None + config_key: Optional[str] = None + config_value: Optional[str] = None + config_type: Optional[str] = None + create_by: Optional[str] = None + create_time: Optional[datetime] = None + update_by: Optional[str] = None + update_time: Optional[datetime] = None + remark: Optional[str] = None + + +class ConfigQueryModel(ConfigModel): + """ + 参数配置管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +@as_query +@as_form +class ConfigPageQueryModel(ConfigQueryModel): + """ + 参数配置管理分页查询模型 + """ + page_num: int + page_size: int + + +class DeleteConfigModel(BaseModel): + """ + 删除参数配置模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + config_ids: str diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/dept_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/dept_vo.py new file mode 100644 index 0000000..9a8eb3e --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/dept_vo.py @@ -0,0 +1,47 @@ +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel +from typing import Union, Optional, List +from datetime import datetime +from module_admin.annotation.pydantic_annotation import as_query + + +class DeptModel(BaseModel): + """ + 部门表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + dept_id: Optional[int] = None + parent_id: Optional[int] = None + ancestors: Optional[str] = None + dept_name: Optional[str] = None + order_num: Optional[int] = None + leader: Optional[str] = None + phone: Optional[str] = None + email: Optional[str] = None + status: Optional[str] = None + del_flag: Optional[str] = None + create_by: Optional[str] = None + create_time: Optional[datetime] = None + update_by: Optional[str] = None + update_time: Optional[datetime] = None + + +@as_query +class DeptQueryModel(DeptModel): + """ + 部门管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +class DeleteDeptModel(BaseModel): + """ + 删除部门模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + dept_ids: str + update_by: Optional[str] = None + update_time: Optional[str] = None diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/dict_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/dict_vo.py new file mode 100644 index 0000000..f380802 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/dict_vo.py @@ -0,0 +1,98 @@ +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel +from typing import Union, Optional, List +from datetime import datetime +from module_admin.annotation.pydantic_annotation import as_query, as_form + + +class DictTypeModel(BaseModel): + """ + 字典类型表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + dict_id: Optional[int] = None + dict_name: Optional[str] = None + dict_type: Optional[str] = None + status: Optional[str] = None + create_by: Optional[str] = None + create_time: Optional[datetime] = None + update_by: Optional[str] = None + update_time: Optional[datetime] = None + remark: Optional[str] = None + + +class DictDataModel(BaseModel): + """ + 字典数据表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + dict_code: Optional[int] = None + dict_sort: Optional[int] = None + dict_label: Optional[str] = None + dict_value: Optional[str] = None + dict_type: Optional[str] = None + css_class: Optional[str] = None + list_class: Optional[str] = None + is_default: Optional[str] = None + status: Optional[str] = None + create_by: Optional[str] = None + create_time: Optional[datetime] = None + update_by: Optional[str] = None + update_time: Optional[datetime] = None + remark: Optional[str] = None + + +class DictTypeQueryModel(DictTypeModel): + """ + 字典类型管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +@as_query +@as_form +class DictTypePageQueryModel(DictTypeQueryModel): + """ + 字典类型管理分页查询模型 + """ + page_num: int + page_size: int + + +class DeleteDictTypeModel(BaseModel): + """ + 删除字典类型模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + dict_ids: str + + +class DictDataQueryModel(DictTypeModel): + """ + 字典数据管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +@as_query +@as_form +class DictDataPageQueryModel(DictDataQueryModel): + """ + 字典数据管理分页查询模型 + """ + page_num: int + page_size: int + + +class DeleteDictDataModel(BaseModel): + """ + 删除字典数据模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + dict_codes: str diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/job_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/job_vo.py new file mode 100644 index 0000000..fadcbea --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/job_vo.py @@ -0,0 +1,110 @@ +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel +from typing import Union, Optional, List +from datetime import datetime +from module_admin.annotation.pydantic_annotation import as_query, as_form + + +class JobModel(BaseModel): + """ + 定时任务调度表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + job_id: Optional[int] = None + job_name: Optional[str] = None + job_group: Optional[str] = None + job_executor: Optional[str] = None + invoke_target: Optional[str] = None + job_args: Optional[str] = None + job_kwargs: Optional[str] = None + cron_expression: Optional[str] = None + misfire_policy: Optional[str] = None + concurrent: Optional[str] = None + status: Optional[str] = None + create_by: Optional[str] = None + create_time: Optional[datetime] = None + update_by: Optional[str] = None + update_time: Optional[datetime] = None + remark: Optional[str] = None + + +class JobLogModel(BaseModel): + """ + 定时任务调度日志表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + job_log_id: Optional[int] = None + job_name: Optional[str] = None + job_group: Optional[str] = None + job_executor: Optional[str] = None + invoke_target: Optional[str] = None + job_args: Optional[str] = None + job_kwargs: Optional[str] = None + job_trigger: Optional[str] = None + job_message: Optional[str] = None + status: Optional[str] = None + exception_info: Optional[str] = None + create_time: Optional[datetime] = None + + +class JobQueryModel(JobModel): + """ + 定时任务管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +@as_query +@as_form +class JobPageQueryModel(JobQueryModel): + """ + 定时任务管理分页查询模型 + """ + page_num: int + page_size: int + + +class EditJobModel(JobModel): + """ + 编辑定时任务模型 + """ + type: Optional[str] = None + + +class DeleteJobModel(BaseModel): + """ + 删除定时任务模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + job_ids: str + + +class JobLogQueryModel(JobLogModel): + """ + 定时任务日志不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +@as_query +@as_form +class JobLogPageQueryModel(JobLogQueryModel): + """ + 定时任务日志管理分页查询模型 + """ + page_num: int + page_size: int + + +class DeleteJobLogModel(BaseModel): + """ + 删除定时任务日志模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + job_log_ids: str diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/log_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/log_vo.py new file mode 100644 index 0000000..e7a53bf --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/log_vo.py @@ -0,0 +1,111 @@ +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel +from typing import Union, Optional, List +from datetime import datetime +from module_admin.annotation.pydantic_annotation import as_query, as_form + + +class OperLogModel(BaseModel): + """ + 操作日志表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + oper_id: Optional[int] = None + title: Optional[str] = None + business_type: Optional[int] = None + method: Optional[str] = None + request_method: Optional[str] = None + operator_type: Optional[int] = None + oper_name: Optional[str] = None + dept_name: Optional[str] = None + oper_url: Optional[str] = None + oper_ip: Optional[str] = None + oper_location: Optional[str] = None + oper_param: Optional[str] = None + json_result: Optional[str] = None + status: Optional[int] = None + error_msg: Optional[str] = None + oper_time: Optional[datetime] = None + cost_time: Optional[int] = None + + +class LogininforModel(BaseModel): + """ + 登录日志表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + info_id: Optional[int] = None + user_name: Optional[str] = None + ipaddr: Optional[str] = None + login_location: Optional[str] = None + browser: Optional[str] = None + os: Optional[str] = None + status: Optional[str] = None + msg: Optional[str] = None + login_time: Optional[datetime] = None + + +class OperLogQueryModel(OperLogModel): + """ + 操作日志管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +@as_query +@as_form +class OperLogPageQueryModel(OperLogQueryModel): + """ + 操作日志管理分页查询模型 + """ + page_num: int + page_size: int + + +class DeleteOperLogModel(BaseModel): + """ + 删除操作日志模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + oper_ids: str + + +class LoginLogQueryModel(LogininforModel): + """ + 登录日志管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + + +@as_query +@as_form +class LoginLogPageQueryModel(LoginLogQueryModel): + """ + 登录日志管理分页查询模型 + """ + page_num: int + page_size: int + + +class DeleteLoginLogModel(BaseModel): + """ + 删除登录日志模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + info_ids: str + + +class UnlockUser(BaseModel): + """ + 解锁用户模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + user_name: str diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/login_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/login_vo.py new file mode 100644 index 0000000..6c1ba12 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/login_vo.py @@ -0,0 +1,34 @@ +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel +from typing import Optional + + +class UserLogin(BaseModel): + model_config = ConfigDict(alias_generator=to_camel) + + user_name: str + password: str + code: Optional[str] = None + uuid: Optional[str] = None + login_info: Optional[dict] = None + captcha_enabled: Optional[bool] = None + + +class Token(BaseModel): + access_token: str + token_type: str + + +class CaptchaCode(BaseModel): + model_config = ConfigDict(alias_generator=to_camel) + + captcha_enabled: bool + img: str + uuid: str + + +class SmsCode(BaseModel): + is_success: Optional[bool] = None + sms_code: str + session_id: str + message: Optional[str] = None \ No newline at end of file diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/menu_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/menu_vo.py new file mode 100644 index 0000000..08f22e1 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/menu_vo.py @@ -0,0 +1,50 @@ +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel +from datetime import datetime +from typing import Union, Optional, List +from module_admin.annotation.pydantic_annotation import as_query + + +class MenuModel(BaseModel): + """ + 菜单表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + menu_id: Optional[int] = None + menu_name: Optional[str] = None + parent_id: Optional[int] = None + order_num: Optional[int] = None + path: Optional[str] = None + component: Optional[str] = None + query: Optional[str] = None + is_frame: Optional[int] = None + is_cache: Optional[int] = None + menu_type: Optional[str] = None + visible: Optional[str] = None + status: Optional[str] = None + perms: Optional[str] = None + icon: Optional[str] = None + create_by: Optional[str] = None + create_time: Optional[datetime] = None + update_by: Optional[str] = None + update_time: Optional[datetime] = None + remark: Optional[str] = None + + +@as_query +class MenuQueryModel(MenuModel): + """ + 菜单管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +class DeleteMenuModel(BaseModel): + """ + 删除菜单模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + menu_ids: str diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/notice_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/notice_vo.py new file mode 100644 index 0000000..cf2eab0 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/notice_vo.py @@ -0,0 +1,50 @@ +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel +from typing import Union, Optional, List +from datetime import datetime +from module_admin.annotation.pydantic_annotation import as_query, as_form + + +class NoticeModel(BaseModel): + """ + 通知公告表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + notice_id: Optional[int] = None + notice_title: Optional[str] = None + notice_type: Optional[str] = None + notice_content: Optional[bytes] = None + status: Optional[str] = None + create_by: Optional[str] = None + create_time: Optional[datetime] = None + update_by: Optional[str] = None + update_time: Optional[datetime] = None + remark: Optional[str] = None + + +class NoticeQueryModel(NoticeModel): + """ + 通知公告管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +@as_query +@as_form +class NoticePageQueryModel(NoticeQueryModel): + """ + 通知公告管理分页查询模型 + """ + page_num: int + page_size: int + + +class DeleteNoticeModel(BaseModel): + """ + 删除通知公告模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + notice_ids: str diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/online_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/online_vo.py new file mode 100644 index 0000000..616fd2a --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/online_vo.py @@ -0,0 +1,39 @@ +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel +from typing import Union, Optional, List +from datetime import datetime +from module_admin.annotation.pydantic_annotation import as_query + + +class OnlineModel(BaseModel): + """ + 在线用户对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + token_id: Optional[str] = None + user_name: Optional[str] = None + dept_name: Optional[str] = None + ipaddr: Optional[str] = None + login_location: Optional[str] = None + browser: Optional[str] = None + os: Optional[str] = None + login_time: Optional[datetime] = None + + +@as_query +class OnlineQueryModel(OnlineModel): + """ + 岗位管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +class DeleteOnlineModel(BaseModel): + """ + 强退在线用户模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + token_ids: str diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/post_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/post_vo.py new file mode 100644 index 0000000..cd2136a --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/post_vo.py @@ -0,0 +1,50 @@ +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel +from typing import Union, Optional, List +from datetime import datetime +from module_admin.annotation.pydantic_annotation import as_query, as_form + + +class PostModel(BaseModel): + """ + 岗位信息表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + post_id: Optional[int] = None + post_code: Optional[str] = None + post_name: Optional[str] = None + post_sort: Optional[int] = None + status: Optional[str] = None + create_by: Optional[str] = None + create_time: Optional[datetime] = None + update_by: Optional[str] = None + update_time: Optional[datetime] = None + remark: Optional[str] = None + + +class PostQueryModel(PostModel): + """ + 岗位管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +@as_query +@as_form +class PostPageQueryModel(PostQueryModel): + """ + 岗位管理分页查询模型 + """ + page_num: int + page_size: int + + +class DeletePostModel(BaseModel): + """ + 删除岗位模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + post_ids: str diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/role_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/role_vo.py new file mode 100644 index 0000000..3dfe305 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/role_vo.py @@ -0,0 +1,127 @@ +from pydantic import BaseModel, ConfigDict, field_validator, model_validator +from pydantic.alias_generators import to_camel +from typing import Union, Optional, List +from datetime import datetime +from module_admin.annotation.pydantic_annotation import as_query, as_form + + +class RoleModel(BaseModel): + """ + 角色表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + role_id: Optional[int] = None + role_name: Optional[str] = None + role_key: Optional[str] = None + role_sort: Optional[int] = None + data_scope: Optional[str] = None + menu_check_strictly: Optional[Union[int, bool]] = None + dept_check_strictly: Optional[Union[int, bool]] = None + status: Optional[str] = None + del_flag: Optional[str] = None + create_by: Optional[str] = None + create_time: Optional[datetime] = None + update_by: Optional[str] = None + update_time: Optional[datetime] = None + remark: Optional[str] = None + admin: Optional[bool] = False + + @field_validator('menu_check_strictly', 'dept_check_strictly') + @classmethod + def check_filed_mapping(cls, v: Union[int, bool]) -> Union[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 + + +class RoleMenuModel(BaseModel): + """ + 角色和菜单关联表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + role_id: Optional[int] = None + menu_id: Optional[int] = None + + +class RoleDeptModel(BaseModel): + """ + 角色和部门关联表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + role_id: Optional[int] = None + dept_id: Optional[int] = None + + +class RoleQueryModel(RoleModel): + """ + 角色管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +@as_query +@as_form +class RolePageQueryModel(RoleQueryModel): + """ + 角色管理分页查询模型 + """ + page_num: int + page_size: int + + +class RoleMenuQueryModel(BaseModel): + """ + 角色菜单查询模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + menus: List = [] + checked_keys: List[int] = [] + + +class RoleDeptQueryModel(BaseModel): + """ + 角色部门查询模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + depts: List = [] + checked_keys: List[int] = [] + + +class AddRoleModel(RoleModel): + """ + 新增角色模型 + """ + dept_ids: List = [] + menu_ids: List = [] + type: Optional[str] = None + + +class DeleteRoleModel(BaseModel): + """ + 删除角色模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + role_ids: str + update_by: Optional[str] = None + update_time: Optional[datetime] = None diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/server_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/server_vo.py new file mode 100644 index 0000000..4323ddf --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/server_vo.py @@ -0,0 +1,66 @@ +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel +from typing import Optional, List + + +class CpuInfo(BaseModel): + model_config = ConfigDict(alias_generator=to_camel) + + cpu_num: Optional[int] = None + used: Optional[float] = None + sys: Optional[float] = None + free: Optional[float] = None + + +class MemoryInfo(BaseModel): + model_config = ConfigDict(alias_generator=to_camel) + + total: Optional[str] = None + used: Optional[str] = None + free: Optional[str] = None + usage: Optional[float] = None + + +class SysInfo(BaseModel): + model_config = ConfigDict(alias_generator=to_camel) + + computer_ip: Optional[str] = None + computer_name: Optional[str] = None + os_arch: Optional[str] = None + os_name: Optional[str] = None + user_dir: Optional[str] = None + + +class PyInfo(MemoryInfo): + model_config = ConfigDict(alias_generator=to_camel) + + name: Optional[str] = None + version: Optional[str] = None + start_time: Optional[str] = None + run_time: Optional[str] = None + home: Optional[str] = None + + +class SysFiles(BaseModel): + model_config = ConfigDict(alias_generator=to_camel) + + dir_name: Optional[str] = None + sys_type_name: Optional[str] = None + type_name: Optional[str] = None + total: Optional[str] = None + used: Optional[str] = None + free: Optional[str] = None + usage: Optional[str] = None + + +class ServerMonitorModel(BaseModel): + """ + 服务监控对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + cpu: Optional[CpuInfo] + py: Optional[PyInfo] + mem: Optional[MemoryInfo] + sys: Optional[SysInfo] + sys_files: Optional[List[SysFiles]] diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py new file mode 100644 index 0000000..9f7cc1b --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py @@ -0,0 +1,210 @@ +from pydantic import BaseModel, ConfigDict, model_validator +from pydantic.alias_generators import to_camel +from typing import Union, Optional, List +from datetime import datetime +from module_admin.entity.vo.role_vo import RoleModel +from module_admin.entity.vo.dept_vo import DeptModel +from module_admin.entity.vo.post_vo import PostModel +from module_admin.annotation.pydantic_annotation import as_query, as_form + + +class TokenData(BaseModel): + """ + token解析结果 + """ + user_id: Union[int, None] = None + + +class UserModel(BaseModel): + """ + 用户表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + user_id: Optional[int] = None + dept_id: Optional[int] = None + user_name: Optional[str] = None + nick_name: Optional[str] = None + user_type: Optional[str] = None + email: Optional[str] = None + phonenumber: Optional[str] = None + sex: Optional[str] = None + avatar: Optional[str] = None + password: Optional[str] = None + status: Optional[str] = None + del_flag: Optional[str] = None + login_ip: Optional[str] = None + login_date: Optional[datetime] = None + create_by: Optional[str] = None + create_time: Optional[datetime] = None + update_by: Optional[str] = None + update_time: Optional[datetime] = None + remark: Optional[str] = None + admin: Optional[bool] = False + + @model_validator(mode='after') + def check_admin(self) -> 'UserModel': + if self.user_id == 1: + self.admin = True + else: + self.admin = False + return self + + +class UserRoleModel(BaseModel): + """ + 用户和角色关联表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + user_id: Optional[int] = None + role_id: Optional[int] = None + + +class UserPostModel(BaseModel): + """ + 用户与岗位关联表对应pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + user_id: Optional[int] = None + post_id: Optional[int] = None + + +class UserInfoModel(UserModel): + post_ids: Optional[Union[str, None]] = None + role_ids: Optional[Union[str, None]] = None + dept: Optional[Union[DeptModel, None]] = None + role: Optional[List[Union[RoleModel, None]]] = [] + + +class CurrentUserModel(BaseModel): + model_config = ConfigDict(alias_generator=to_camel) + + permissions: List + roles: List + user: Union[UserInfoModel, None] + + +class UserDetailModel(BaseModel): + """ + 获取用户详情信息响应模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + data: Optional[Union[UserInfoModel, None]] = None + post_ids: Optional[List] = None + posts: List[Union[PostModel, None]] + role_ids: Optional[List] = None + roles: List[Union[RoleModel, None]] + + +class UserProfileModel(BaseModel): + """ + 获取个人信息响应模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + data: Union[UserInfoModel, None] + post_group: Union[str, None] + role_group: Union[str, None] + + +class UserQueryModel(UserModel): + """ + 用户管理不分页查询模型 + """ + begin_time: Optional[str] = None + end_time: Optional[str] = None + + +@as_query +@as_form +class UserPageQueryModel(UserQueryModel): + """ + 用户管理分页查询模型 + """ + page_num: int + page_size: int + + +class AddUserModel(UserModel): + """ + 新增用户模型 + """ + role_ids: Optional[List] = [] + post_ids: Optional[List] = [] + type: Optional[str] = None + + +class EditUserModel(AddUserModel): + """ + 编辑用户模型 + """ + role: Optional[List] = [] + + +class ResetUserModel(UserModel): + """ + 重置用户密码模型 + """ + old_password: Optional[str] = None + sms_code: Optional[str] = None + session_id: Optional[str] = None + + +class DeleteUserModel(BaseModel): + """ + 删除用户模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + user_ids: str + update_by: Optional[str] = None + update_time: Optional[datetime] = None + + +class UserRoleQueryModel(UserModel): + """ + 用户角色关联管理不分页查询模型 + """ + role_id: Optional[int] = None + + +@as_query +class UserRolePageQueryModel(UserRoleQueryModel): + """ + 用户角色关联管理分页查询模型 + """ + page_num: int + page_size: int + + +class SelectedRoleModel(RoleModel): + """ + 是否选择角色模型 + """ + flag: Optional[bool] = False + + +class UserRoleResponseModel(BaseModel): + """ + 用户角色关联管理列表返回模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + roles: List[Union[SelectedRoleModel, None]] = [] + user: UserInfoModel + + +@as_query +class CrudUserRoleModel(BaseModel): + """ + 新增、删除用户关联角色及角色关联用户模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + user_id: Optional[int] = None + user_ids: Optional[str] = None + role_id: Optional[int] = None + role_ids: Optional[str] = None diff --git a/ruoyi-fastapi-backend/module_admin/service/cache_service.py b/ruoyi-fastapi-backend/module_admin/service/cache_service.py new file mode 100644 index 0000000..d021ef4 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/cache_service.py @@ -0,0 +1,123 @@ +from fastapi import Request +from module_admin.entity.vo.cache_vo import * +from config.env import RedisInitKeyConfig +from config.get_redis import RedisUtil +from module_admin.entity.vo.common_vo import CrudResponseModel + +class CacheService: + """ + 缓存监控模块服务层 + """ + + @classmethod + async def get_cache_monitor_statistical_info_services(cls, request: Request): + """ + 获取缓存监控信息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 = [dict(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 + def get_cache_monitor_cache_name_services(cls): + """ + 获取缓存名称列表信息service + :return: 缓存名称列表信息 + """ + name_list = [] + for attr_name in dir(RedisInitKeyConfig): + if not attr_name.startswith('__') and isinstance(getattr(RedisInitKeyConfig, attr_name), dict): + name_list.append( + CacheInfoModel( + cacheKey="", + cacheName=getattr(RedisInitKeyConfig, attr_name).get('key'), + cacheValue="", + remark=getattr(RedisInitKeyConfig, attr_name).get('remark') + ) + ) + + return name_list + + @classmethod + async def get_cache_monitor_cache_key_services(cls, request: Request, cache_name: str): + """ + 获取缓存键名列表信息service + :param request: Request对象 + :param cache_name: 缓存名称 + :return: 缓存键名列表信息 + """ + cache_keys = 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): + """ + 获取缓存内容信息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): + """ + 清除缓存名称对应所有键值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) + result = dict(is_success=True, message=f"{cache_name}对应键值清除成功") + + return CrudResponseModel(**result) + + @classmethod + async def clear_cache_monitor_cache_key_services(cls, request: Request, cache_key: str): + """ + 清除缓存名称对应所有键值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) + result = dict(is_success=True, message=f"{cache_key}清除成功") + + return CrudResponseModel(**result) + + @classmethod + async def clear_cache_monitor_all_services(cls, request: Request): + """ + 清除所有缓存service + :param request: Request对象 + :return: 操作缓存响应信息 + """ + cache_keys = await request.app.state.redis.keys() + if cache_keys: + await request.app.state.redis.delete(*cache_keys) + + result = dict(is_success=True, message="所有缓存清除成功") + await RedisUtil.init_sys_dict(request.app.state.redis) + await RedisUtil.init_sys_config(request.app.state.redis) + + return CrudResponseModel(**result) diff --git a/ruoyi-fastapi-backend/module_admin/service/captcha_service.py b/ruoyi-fastapi-backend/module_admin/service/captcha_service.py new file mode 100644 index 0000000..b685f06 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/captcha_service.py @@ -0,0 +1,48 @@ +from PIL import Image, ImageDraw, ImageFont +import io +import os +import random +import base64 + + +class CaptchaService: + """ + 验证码模块服务层 + """ + + @classmethod + def create_captcha_image_service(cls): + # 创建空白图像 + image = Image.new('RGB', (160, 60), color='#EAEAEA') + + # 创建绘图对象 + draw = ImageDraw.Draw(image) + + # 设置字体 + font = ImageFont.truetype(os.path.join(os.path.abspath(os.getcwd()), 'assets', 'font', 'Arial.ttf'), 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 + 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] diff --git a/ruoyi-fastapi-backend/module_admin/service/common_service.py b/ruoyi-fastapi-backend/module_admin/service/common_service.py new file mode 100644 index 0000000..447f28a --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/common_service.py @@ -0,0 +1,18 @@ +import os +from fastapi import UploadFile + + +class CommonService: + """ + 通用模块服务层 + """ + + @classmethod + def upload_service(cls, path: str, task_path: str, upload_id: str, file: UploadFile): + + filepath = os.path.join(path, task_path, upload_id, f'{file.filename}') + with open(filepath, 'wb') as f: + # 流式写出大型文件,这里的10代表10MB + for chunk in iter(lambda: file.file.read(1024 * 1024 * 10), b''): + f.write(chunk) + diff --git a/ruoyi-fastapi-backend/module_admin/service/config_service.py b/ruoyi-fastapi-backend/module_admin/service/config_service.py new file mode 100644 index 0000000..50e3d41 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/config_service.py @@ -0,0 +1,190 @@ +from fastapi import Request +from config.env import RedisInitKeyConfig +from module_admin.dao.config_dao import * +from module_admin.entity.vo.common_vo import CrudResponseModel +from utils.common_util import export_list2excel, CamelCaseUtil + + +class ConfigService: + """ + 参数配置管理模块服务层 + """ + + @classmethod + def get_config_list_services(cls, query_db: Session, query_object: ConfigQueryModel): + """ + 获取参数配置列表信息service + :param query_db: orm对象 + :param query_object: 查询参数对象 + :return: 参数配置列表信息对象 + """ + config_list_result = ConfigDao.get_config_list(query_db, query_object) + + return CamelCaseUtil.transform_result(config_list_result) + + @classmethod + async def init_cache_sys_config_services(cls, query_db: Session, redis): + """ + 应用初始化:获取所有参数配置对应的键值对信息并缓存service + :param query_db: orm对象 + :param redis: redis对象 + :return: + """ + # 获取以sys_config:开头的键列表 + keys = await redis.keys(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:*") + # 删除匹配的键 + if keys: + await redis.delete(*keys) + config_all = ConfigDao.get_config_list(query_db, ConfigQueryModel(**dict())) + for config_obj in config_all: + if config_obj.config_type == 'Y': + await redis.set(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:{config_obj.config_key}", config_obj.config_value) + + @classmethod + async def query_config_list_from_cache_services(cls, redis, config_key: str): + """ + 从缓存获取参数键名对应值service + :param redis: redis对象 + :param config_key: 参数键名 + :return: 参数键名对应值 + """ + result = await redis.get(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:{config_key}") + + return result + + @classmethod + async def add_config_services(cls, request: Request, query_db: Session, page_object: ConfigModel): + """ + 新增参数配置信息service + :param request: Request对象 + :param query_db: orm对象 + :param page_object: 新增参数配置对象 + :return: 新增参数配置校验结果 + """ + config = ConfigDao.get_config_detail_by_info(query_db, ConfigModel(configKey=page_object.config_key)) + if config: + result = dict(is_success=False, message='参数键名已存在') + else: + try: + ConfigDao.add_config_dao(query_db, page_object) + query_db.commit() + await cls.init_cache_sys_config_services(query_db, request.app.state.redis) + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + async def edit_config_services(cls, request: Request, query_db: Session, page_object: ConfigModel): + """ + 编辑参数配置信息service + :param request: Request对象 + :param query_db: orm对象 + :param page_object: 编辑参数配置对象 + :return: 编辑参数配置校验结果 + """ + edit_config = page_object.model_dump(exclude_unset=True) + config_info = cls.config_detail_services(query_db, edit_config.get('config_id')) + if config_info: + if config_info.config_key != page_object.config_key or config_info.config_value != page_object.config_value: + config = ConfigDao.get_config_detail_by_info(query_db, page_object) + if config: + result = dict(is_success=False, message='参数配置已存在') + return CrudResponseModel(**result) + try: + ConfigDao.edit_config_dao(query_db, edit_config) + query_db.commit() + await cls.init_cache_sys_config_services(query_db, request.app.state.redis) + result = dict(is_success=True, message='更新成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='参数配置不存在') + + return CrudResponseModel(**result) + + @classmethod + async def delete_config_services(cls, request: Request, query_db: Session, page_object: DeleteConfigModel): + """ + 删除参数配置信息service + :param request: Request对象 + :param query_db: orm对象 + :param page_object: 删除参数配置对象 + :return: 删除参数配置校验结果 + """ + if page_object.config_ids.split(','): + config_id_list = page_object.config_ids.split(',') + try: + for config_id in config_id_list: + ConfigDao.delete_config_dao(query_db, ConfigModel(configId=config_id)) + query_db.commit() + await cls.init_cache_sys_config_services(query_db, request.app.state.redis) + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入字典数据id为空') + return CrudResponseModel(**result) + + @classmethod + def config_detail_services(cls, query_db: Session, config_id: int): + """ + 获取参数配置详细信息service + :param query_db: orm对象 + :param config_id: 参数配置id + :return: 参数配置id对应的信息 + """ + config = ConfigDao.get_config_detail_by_id(query_db, config_id=config_id) + result = ConfigModel(**CamelCaseUtil.transform_result(config)) + + return result + + @staticmethod + def export_config_list_services(config_list: List): + """ + 导出参数配置信息service + :param config_list: 参数配置信息列表 + :return: 参数配置信息对应excel的二进制数据 + """ + # 创建一个映射字典,将英文键映射到中文键 + mapping_dict = { + "configId": "参数主键", + "configName": "参数名称", + "configKey": "参数键名", + "configValue": "参数键值", + "configType": "系统内置", + "createBy": "创建者", + "createTime": "创建时间", + "updateBy": "更新者", + "updateTime": "更新时间", + "remark": "备注", + } + + data = config_list + + for item in data: + if item.get('configType') == 'Y': + item['configType'] = '是' + else: + item['configType'] = '否' + new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data] + binary_data = export_list2excel(new_data) + + return binary_data + + @classmethod + async def refresh_sys_config_services(cls, request: Request, query_db: Session): + """ + 刷新字典缓存信息service + :param request: Request对象 + :param query_db: orm对象 + :return: 刷新字典缓存校验结果 + """ + await cls.init_cache_sys_config_services(query_db, request.app.state.redis) + result = dict(is_success=True, message='刷新成功') + + return CrudResponseModel(**result) diff --git a/ruoyi-fastapi-backend/module_admin/service/dept_service.py b/ruoyi-fastapi-backend/module_admin/service/dept_service.py new file mode 100644 index 0000000..2c9db67 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/dept_service.py @@ -0,0 +1,213 @@ +from module_admin.dao.dept_dao import * +from module_admin.entity.vo.common_vo import CrudResponseModel +from utils.common_util import CamelCaseUtil + + +class DeptService: + """ + 部门管理模块服务层 + """ + + @classmethod + def get_dept_tree_services(cls, query_db: Session, page_object: DeptModel, data_scope_sql: str): + """ + 获取部门树信息service + :param query_db: orm对象 + :param page_object: 查询参数对象 + :param data_scope_sql: 数据权限对应的查询sql语句 + :return: 部门树信息对象 + """ + dept_list_result = DeptDao.get_dept_list_for_tree(query_db, page_object, data_scope_sql) + dept_tree_result = cls.list_to_tree(dept_list_result) + + return dept_tree_result + + @classmethod + def get_dept_for_edit_option_services(cls, query_db: Session, page_object: DeptModel, data_scope_sql: str): + """ + 获取部门编辑部门树信息service + :param query_db: orm对象 + :param page_object: 查询参数对象 + :param data_scope_sql: 数据权限对应的查询sql语句 + :return: 部门树信息对象 + """ + dept_list_result = DeptDao.get_dept_info_for_edit_option(query_db, page_object, data_scope_sql) + + return CamelCaseUtil.transform_result(dept_list_result) + + @classmethod + def get_dept_list_services(cls, query_db: Session, page_object: DeptModel, data_scope_sql: str): + """ + 获取部门列表信息service + :param query_db: orm对象 + :param page_object: 分页查询参数对象 + :param data_scope_sql: 数据权限对应的查询sql语句 + :return: 部门列表信息对象 + """ + dept_list_result = DeptDao.get_dept_list(query_db, page_object, data_scope_sql) + + return CamelCaseUtil.transform_result(dept_list_result) + + @classmethod + def add_dept_services(cls, query_db: Session, page_object: DeptModel): + """ + 新增部门信息service + :param query_db: orm对象 + :param page_object: 新增部门对象 + :return: 新增部门校验结果 + """ + parent_info = DeptDao.get_dept_by_id(query_db, page_object.parent_id) + if parent_info: + page_object.ancestors = f'{parent_info.ancestors},{page_object.parent_id}' + else: + page_object.ancestors = '0' + dept = DeptDao.get_dept_detail_by_info(query_db, DeptModel(parentId=page_object.parent_id, + deptName=page_object.dept_name)) + if dept: + result = dict(is_success=False, message='同一部门下不允许存在同名的部门') + else: + try: + DeptDao.add_dept_dao(query_db, page_object) + query_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + def edit_dept_services(cls, query_db: Session, page_object: DeptModel): + """ + 编辑部门信息service + :param query_db: orm对象 + :param page_object: 编辑部门对象 + :return: 编辑部门校验结果 + """ + parent_info = DeptDao.get_dept_by_id(query_db, page_object.parent_id) + if parent_info: + page_object.ancestors = f'{parent_info.ancestors},{page_object.parent_id}' + else: + page_object.ancestors = '0' + edit_dept = page_object.model_dump(exclude_unset=True) + dept_info = cls.dept_detail_services(query_db, edit_dept.get('dept_id')) + if dept_info: + if dept_info.parent_id != page_object.parent_id or dept_info.dept_name != page_object.dept_name: + dept = DeptDao.get_dept_detail_by_info(query_db, DeptModel(parentId=page_object.parent_id, + deptName=page_object.dept_name)) + if dept: + result = dict(is_success=False, message='同一部门下不允许存在同名的部门') + return CrudResponseModel(**result) + try: + DeptDao.edit_dept_dao(query_db, edit_dept) + cls.update_children_info(query_db, DeptModel(deptId=page_object.dept_id, + ancestors=page_object.ancestors, + updateBy=page_object.update_by, + updateTime=page_object.update_time + ) + ) + query_db.commit() + result = dict(is_success=True, message='更新成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='部门不存在') + + return CrudResponseModel(**result) + + @classmethod + def delete_dept_services(cls, query_db: Session, page_object: DeleteDeptModel): + """ + 删除部门信息service + :param query_db: orm对象 + :param page_object: 删除部门对象 + :return: 删除部门校验结果 + """ + if page_object.dept_ids.split(','): + dept_id_list = page_object.dept_ids.split(',') + ancestors = DeptDao.get_dept_all_ancestors(query_db) + try: + for dept_id in dept_id_list: + for ancestor in ancestors: + if dept_id in ancestor[0]: + result = dict(is_success=False, message='该部门下有子部门,不允许删除') + + return CrudResponseModel(**result) + + DeptDao.delete_dept_dao(query_db, DeptModel(deptId=dept_id)) + query_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入部门id为空') + return CrudResponseModel(**result) + + @classmethod + def dept_detail_services(cls, query_db: Session, dept_id: int): + """ + 获取部门详细信息service + :param query_db: orm对象 + :param dept_id: 部门id + :return: 部门id对应的信息 + """ + dept = DeptDao.get_dept_detail_by_id(query_db, dept_id=dept_id) + result = DeptModel(**CamelCaseUtil.transform_result(dept)) + + return result + + @classmethod + def list_to_tree(cls, permission_list: list) -> list: + """ + 工具方法:根据部门列表信息生成树形嵌套数据 + :param permission_list: 部门列表信息 + :return: 部门树形嵌套数据 + """ + permission_list = [dict(id=item.dept_id, label=item.dept_name, parentId=item.parent_id) for item in + permission_list] + # 转成id为key的字典 + mapping: dict = dict(zip([i['id'] for i in permission_list], permission_list)) + + # 树容器 + container: list = [] + + for d in permission_list: + # 如果找不到父级项,则是根节点 + parent: dict = mapping.get(d['parentId']) + if parent is None: + container.append(d) + else: + children: list = parent.get('children') + if not children: + children = [] + children.append(d) + parent.update({'children': children}) + + return container + + @classmethod + def update_children_info(cls, query_db, page_object): + """ + 工具方法:递归更新子部门信息 + :param query_db: orm对象 + :param page_object: 编辑部门对象 + :return: + """ + children_info = DeptDao.get_children_dept(query_db, page_object.dept_id) + if children_info: + for child in children_info: + child.ancestors = f'{page_object.ancestors},{page_object.dept_id}' + DeptDao.edit_dept_dao(query_db, + dict(dept_id=child.dept_id, + ancestors=child.ancestors, + update_by=page_object.update_by, + update_time=page_object.update_time + ) + ) + cls.update_children_info(query_db, DeptModel(dept_id=child.dept_id, + ancestors=child.ancestors, + update_by=page_object.update_by, + update_time=page_object.update_time + )) diff --git a/ruoyi-fastapi-backend/module_admin/service/dict_service.py b/ruoyi-fastapi-backend/module_admin/service/dict_service.py new file mode 100644 index 0000000..d4bfe00 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/dict_service.py @@ -0,0 +1,363 @@ +from fastapi import Request +import json +from config.env import RedisInitKeyConfig +from module_admin.dao.dict_dao import * +from module_admin.entity.vo.common_vo import CrudResponseModel +from utils.common_util import export_list2excel, CamelCaseUtil + + +class DictTypeService: + """ + 字典类型管理模块服务层 + """ + + @classmethod + def get_dict_type_list_services(cls, query_db: Session, query_object: DictTypeQueryModel): + """ + 获取字典类型列表信息service + :param query_db: orm对象 + :param query_object: 查询参数对象 + :return: 字典类型列表信息对象 + """ + dict_type_list_result = DictTypeDao.get_dict_type_list(query_db, query_object) + + return CamelCaseUtil.transform_result(dict_type_list_result) + + @classmethod + async def add_dict_type_services(cls, request: Request, query_db: Session, page_object: DictTypeModel): + """ + 新增字典类型信息service + :param request: Request对象 + :param query_db: orm对象 + :param page_object: 新增岗位对象 + :return: 新增字典类型校验结果 + """ + dict_type = DictTypeDao.get_dict_type_detail_by_info(query_db, DictTypeModel(dictType=page_object.dict_type)) + if dict_type: + result = dict(is_success=False, message='字典类型已存在') + else: + try: + DictTypeDao.add_dict_type_dao(query_db, page_object) + query_db.commit() + await DictDataService.init_cache_sys_dict_services(query_db, request.app.state.redis) + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + async def edit_dict_type_services(cls, request: Request, query_db: Session, page_object: DictTypeModel): + """ + 编辑字典类型信息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 = cls.dict_type_detail_services(query_db, edit_dict_type.get('dict_id')) + if dict_type_info: + if dict_type_info.dict_type != page_object.dict_type or dict_type_info.dict_name != page_object.dict_name: + dict_type = DictTypeDao.get_dict_type_detail_by_info(query_db, DictTypeModel(dictType=page_object.dict_type)) + if dict_type: + result = dict(is_success=False, message='字典类型已存在') + return CrudResponseModel(**result) + try: + if dict_type_info.dict_type != page_object.dict_type: + query_dict_data = DictDataModel(dictType=dict_type_info.dict_type) + dict_data_list = DictDataDao.get_dict_data_list(query_db, query_dict_data) + for dict_data in dict_data_list: + edit_dict_data = DictDataModel(dictCode=dict_data.dict_code, dictType=page_object.dict_type, updateBy=page_object.update_by).model_dump(exclude_unset=True) + DictDataDao.edit_dict_data_dao(query_db, edit_dict_data) + DictTypeDao.edit_dict_type_dao(query_db, edit_dict_type) + query_db.commit() + await DictDataService.init_cache_sys_dict_services(query_db, request.app.state.redis) + result = dict(is_success=True, message='更新成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='字典类型不存在') + + return CrudResponseModel(**result) + + @classmethod + async def delete_dict_type_services(cls, request: Request, query_db: Session, page_object: DeleteDictTypeModel): + """ + 删除字典类型信息service + :param request: Request对象 + :param query_db: orm对象 + :param page_object: 删除字典类型对象 + :return: 删除字典类型校验结果 + """ + if page_object.dict_ids.split(','): + dict_id_list = page_object.dict_ids.split(',') + try: + for dict_id in dict_id_list: + DictTypeDao.delete_dict_type_dao(query_db, DictTypeModel(dictId=dict_id)) + query_db.commit() + await DictDataService.init_cache_sys_dict_services(query_db, request.app.state.redis) + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入字典类型id为空') + return CrudResponseModel(**result) + + @classmethod + def dict_type_detail_services(cls, query_db: Session, dict_id: int): + """ + 获取字典类型详细信息service + :param query_db: orm对象 + :param dict_id: 字典类型id + :return: 字典类型id对应的信息 + """ + dict_type = DictTypeDao.get_dict_type_detail_by_id(query_db, dict_id=dict_id) + result = DictTypeModel(**CamelCaseUtil.transform_result(dict_type)) + + return result + + @staticmethod + def export_dict_type_list_services(dict_type_list: List): + """ + 导出字典类型信息service + :param dict_type_list: 字典信息列表 + :return: 字典信息对应excel的二进制数据 + """ + # 创建一个映射字典,将英文键映射到中文键 + mapping_dict = { + "dictId": "字典编号", + "dictName": "字典名称", + "dictType": "字典类型", + "status": "状态", + "createBy": "创建者", + "createTime": "创建时间", + "updateBy": "更新者", + "updateTime": "更新时间", + "remark": "备注", + } + + data = dict_type_list + + for item in data: + if item.get('status') == '0': + item['status'] = '正常' + else: + item['status'] = '停用' + new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data] + binary_data = export_list2excel(new_data) + + return binary_data + + @classmethod + async def refresh_sys_dict_services(cls, request: Request, query_db: Session): + """ + 刷新字典缓存信息service + :param request: Request对象 + :param query_db: orm对象 + :return: 刷新字典缓存校验结果 + """ + await DictDataService.init_cache_sys_dict_services(query_db, request.app.state.redis) + result = dict(is_success=True, message='刷新成功') + + return CrudResponseModel(**result) + + +class DictDataService: + """ + 字典数据管理模块服务层 + """ + + @classmethod + def get_dict_data_list_services(cls, query_db: Session, query_object: DictDataModel): + """ + 获取字典数据列表信息service + :param query_db: orm对象 + :param query_object: 查询参数对象 + :return: 字典数据列表信息对象 + """ + dict_data_list_result = DictDataDao.get_dict_data_list(query_db, query_object) + + return CamelCaseUtil.transform_result(dict_data_list_result) + + @classmethod + def query_dict_data_list_services(cls, query_db: Session, dict_type: str): + """ + 获取字典数据列表信息service + :param query_db: orm对象 + :param dict_type: 字典类型 + :return: 字典数据列表信息对象 + """ + dict_data_list_result = 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: Session, redis): + """ + 应用初始化:获取所有字典类型对应的字典数据信息并缓存service + :param query_db: orm对象 + :param redis: redis对象 + :return: + """ + # 获取以sys_dict:开头的键列表 + keys = await redis.keys(f"{RedisInitKeyConfig.SYS_DICT.get('key')}:*") + # 删除匹配的键 + if keys: + await redis.delete(*keys) + dict_type_all = 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 = 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.get('key')}:{dict_type}", json.dumps(dict_data, ensure_ascii=False, default=str)) + + @classmethod + async def query_dict_data_list_from_cache_services(cls, redis, dict_type: str): + """ + 从缓存获取字典数据列表信息service + :param redis: redis对象 + :param dict_type: 字典类型 + :return: 字典数据列表信息对象 + """ + result = [] + dict_data_list_result = await redis.get(f"{RedisInitKeyConfig.SYS_DICT.get('key')}:{dict_type}") + if dict_data_list_result: + result = json.loads(dict_data_list_result) + + return CamelCaseUtil.transform_result(result) + + @classmethod + async def add_dict_data_services(cls, request: Request, query_db: Session, page_object: DictDataModel): + """ + 新增字典数据信息service + :param request: Request对象 + :param query_db: orm对象 + :param page_object: 新增岗位对象 + :return: 新增字典数据校验结果 + """ + dict_data = DictDataDao.get_dict_data_detail_by_info(query_db, page_object) + if dict_data: + result = dict(is_success=False, message='字典数据已存在') + else: + try: + DictDataDao.add_dict_data_dao(query_db, page_object) + query_db.commit() + await cls.init_cache_sys_dict_services(query_db, request.app.state.redis) + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + async def edit_dict_data_services(cls, request: Request, query_db: Session, page_object: DictDataModel): + """ + 编辑字典数据信息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 = cls.dict_data_detail_services(query_db, edit_data_type.get('dict_code')) + if dict_data_info: + if dict_data_info.dict_type != page_object.dict_type or dict_data_info.dict_label != page_object.dict_label or dict_data_info.dict_value != page_object.dict_value: + dict_data = DictDataDao.get_dict_data_detail_by_info(query_db, page_object) + if dict_data: + result = dict(is_success=False, message='字典数据已存在') + return CrudResponseModel(**result) + try: + DictDataDao.edit_dict_data_dao(query_db, edit_data_type) + query_db.commit() + await cls.init_cache_sys_dict_services(query_db, request.app.state.redis) + result = dict(is_success=True, message='更新成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='字典数据不存在') + + return CrudResponseModel(**result) + + @classmethod + async def delete_dict_data_services(cls, request: Request, query_db: Session, page_object: DeleteDictDataModel): + """ + 删除字典数据信息service + :param request: Request对象 + :param query_db: orm对象 + :param page_object: 删除字典数据对象 + :return: 删除字典数据校验结果 + """ + if page_object.dict_codes.split(','): + dict_code_list = page_object.dict_codes.split(',') + try: + for dict_code in dict_code_list: + DictDataDao.delete_dict_data_dao(query_db, DictDataModel(dictCode=dict_code)) + query_db.commit() + await cls.init_cache_sys_dict_services(query_db, request.app.state.redis) + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入字典数据id为空') + return CrudResponseModel(**result) + + @classmethod + def dict_data_detail_services(cls, query_db: Session, dict_code: int): + """ + 获取字典数据详细信息service + :param query_db: orm对象 + :param dict_code: 字典数据id + :return: 字典数据id对应的信息 + """ + dict_data = DictDataDao.get_dict_data_detail_by_id(query_db, dict_code=dict_code) + result = DictDataModel(**CamelCaseUtil.transform_result(dict_data)) + + return result + + @staticmethod + def export_dict_data_list_services(dict_data_list: List): + """ + 导出字典数据信息service + :param dict_data_list: 字典数据信息列表 + :return: 字典数据信息对应excel的二进制数据 + """ + # 创建一个映射字典,将英文键映射到中文键 + mapping_dict = { + "dictCode": "字典编码", + "dictSort": "字典标签", + "dictLabel": "字典键值", + "dictValue": "字典排序", + "dictType": "字典类型", + "cssClass": "样式属性", + "listClass": "表格回显样式", + "isDefault": "是否默认", + "status": "状态", + "createBy": "创建者", + "createTime": "创建时间", + "updateBy": "更新者", + "updateTime": "更新时间", + "remark": "备注", + } + + data = dict_data_list + + for item in data: + if item.get('status') == '0': + item['status'] = '正常' + else: + item['status'] = '停用' + if item.get('isDefault') == 'Y': + item['isDefault'] = '是' + else: + item['isDefault'] = '否' + new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data] + binary_data = export_list2excel(new_data) + + return binary_data diff --git a/ruoyi-fastapi-backend/module_admin/service/job_log_service.py b/ruoyi-fastapi-backend/module_admin/service/job_log_service.py new file mode 100644 index 0000000..28bac95 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/job_log_service.py @@ -0,0 +1,121 @@ +from module_admin.dao.job_log_dao import * +from module_admin.dao.dict_dao import DictDataDao +from module_admin.entity.vo.common_vo import CrudResponseModel +from utils.common_util import export_list2excel, CamelCaseUtil + + +class JobLogService: + """ + 定时任务日志管理模块服务层 + """ + + @classmethod + def get_job_log_list_services(cls, query_db: Session, query_object: JobLogQueryModel): + """ + 获取定时任务日志列表信息service + :param query_db: orm对象 + :param query_object: 查询参数对象 + :return: 定时任务日志列表信息对象 + """ + job_log_list_result = JobLogDao.get_job_log_list(query_db, query_object) + + return CamelCaseUtil.transform_result(job_log_list_result) + + @classmethod + def add_job_log_services(cls, query_db: Session, page_object: JobLogModel): + """ + 新增定时任务日志信息service + :param query_db: orm对象 + :param page_object: 新增定时任务日志对象 + :return: 新增定时任务日志校验结果 + """ + try: + JobLogDao.add_job_log_dao(query_db, page_object) + query_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + result = dict(is_success=False, message=str(e)) + + return CrudResponseModel(**result) + + @classmethod + def delete_job_log_services(cls, query_db: Session, page_object: DeleteJobLogModel): + """ + 删除定时任务日志信息service + :param query_db: orm对象 + :param page_object: 删除定时任务日志对象 + :return: 删除定时任务日志校验结果 + """ + if page_object.job_log_ids.split(','): + job_log_id_list = page_object.job_log_ids.split(',') + try: + for job_log_id in job_log_id_list: + JobLogDao.delete_job_log_dao(query_db, JobLogModel(jobLogId=job_log_id)) + query_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入定时任务日志id为空') + return CrudResponseModel(**result) + + @classmethod + def clear_job_log_services(cls, query_db: Session): + """ + 清除定时任务日志信息service + :param query_db: orm对象 + :return: 清除定时任务日志校验结果 + """ + try: + JobLogDao.clear_job_log_dao(query_db) + query_db.commit() + result = dict(is_success=True, message='清除成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @staticmethod + def export_job_log_list_services(query_db, job_log_list: List): + """ + 导出定时任务日志信息service + :param query_db: orm对象 + :param job_log_list: 定时任务日志信息列表 + :return: 定时任务日志信息对应excel的二进制数据 + """ + # 创建一个映射字典,将英文键映射到中文键 + mapping_dict = { + "jobLogId": "任务日志编码", + "jobName": "任务名称", + "jobGroup": "任务组名", + "jobExecutor": "任务执行器", + "invokeTarget": "调用目标字符串", + "jobArgs": "位置参数", + "jobKwargs": "关键字参数", + "jobTrigger": "任务触发器", + "jobMessage": "日志信息", + "status": "执行状态", + "exceptionInfo": "异常信息", + "createTime": "创建时间", + } + + data = job_log_list + job_group_list = DictDataDao.query_dict_data_list(query_db, dict_type='sys_job_group') + job_group_option = [dict(label=item.dict_label, value=item.dict_value) for item in job_group_list] + job_group_option_dict = {item.get('value'): item for item in job_group_option} + + for item in data: + if item.get('status') == '0': + item['status'] = '正常' + else: + item['status'] = '暂停' + if str(item.get('job_group')) in job_group_option_dict.keys(): + item['job_group'] = job_group_option_dict.get(str(item.get('job_group'))).get('label') + new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in + data] + binary_data = export_list2excel(new_data) + + return binary_data diff --git a/ruoyi-fastapi-backend/module_admin/service/job_service.py b/ruoyi-fastapi-backend/module_admin/service/job_service.py new file mode 100644 index 0000000..727ccd1 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/job_service.py @@ -0,0 +1,198 @@ +from module_admin.dao.job_dao import * +from module_admin.service.dict_service import Request, DictDataService +from module_admin.entity.vo.common_vo import CrudResponseModel +from utils.common_util import export_list2excel, CamelCaseUtil +from config.get_scheduler import SchedulerUtil + + +class JobService: + """ + 定时任务管理模块服务层 + """ + + @classmethod + def get_job_list_services(cls, query_db: Session, query_object: JobModel): + """ + 获取定时任务列表信息service + :param query_db: orm对象 + :param query_object: 查询参数对象 + :return: 定时任务列表信息对象 + """ + job_list_result = JobDao.get_job_list(query_db, query_object) + + return CamelCaseUtil.transform_result(job_list_result) + + @classmethod + def add_job_services(cls, query_db: Session, page_object: JobModel): + """ + 新增定时任务信息service + :param query_db: orm对象 + :param page_object: 新增定时任务对象 + :return: 新增定时任务校验结果 + """ + job = JobDao.get_job_detail_by_info(query_db, page_object) + if job: + result = dict(is_success=False, message='定时任务已存在') + else: + try: + JobDao.add_job_dao(query_db, page_object) + job_info = JobDao.get_job_detail_by_info(query_db, page_object) + if job_info.status == '0': + SchedulerUtil.add_scheduler_job(job_info=job_info) + query_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + def edit_job_services(cls, query_db: Session, page_object: EditJobModel): + """ + 编辑定时任务信息service + :param query_db: orm对象 + :param page_object: 编辑定时任务对象 + :return: 编辑定时任务校验结果 + """ + edit_job = page_object.model_dump(exclude_unset=True) + if page_object.type == 'status': + del edit_job['type'] + job_info = cls.job_detail_services(query_db, edit_job.get('job_id')) + if job_info: + if page_object.type != 'status' and (job_info.job_name != page_object.job_name or job_info.job_group != page_object.job_group or job_info.invoke_target != page_object.invoke_target or job_info.cron_expression != page_object.cron_expression): + job = JobDao.get_job_detail_by_info(query_db, page_object) + if job: + result = dict(is_success=False, message='定时任务已存在') + return CrudResponseModel(**result) + try: + JobDao.edit_job_dao(query_db, edit_job) + query_job = SchedulerUtil.get_scheduler_job(job_id=edit_job.get('job_id')) + if query_job: + SchedulerUtil.remove_scheduler_job(job_id=edit_job.get('job_id')) + if edit_job.get('status') == '0': + SchedulerUtil.add_scheduler_job(job_info=job_info) + query_db.commit() + result = dict(is_success=True, message='更新成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='定时任务不存在') + + return CrudResponseModel(**result) + + @classmethod + def execute_job_once_services(cls, query_db: Session, page_object: JobModel): + """ + 执行一次定时任务service + :param query_db: orm对象 + :param page_object: 定时任务对象 + :return: 执行一次定时任务结果 + """ + query_job = SchedulerUtil.get_scheduler_job(job_id=page_object.job_id) + if query_job: + SchedulerUtil.remove_scheduler_job(job_id=page_object.job_id) + job_info = cls.job_detail_services(query_db, page_object.job_id) + if job_info: + SchedulerUtil.execute_scheduler_job_once(job_info=job_info) + result = dict(is_success=True, message='执行成功') + else: + result = dict(is_success=False, message='定时任务不存在') + + return CrudResponseModel(**result) + + @classmethod + def delete_job_services(cls, query_db: Session, page_object: DeleteJobModel): + """ + 删除定时任务信息service + :param query_db: orm对象 + :param page_object: 删除定时任务对象 + :return: 删除定时任务校验结果 + """ + if page_object.job_ids.split(','): + job_id_list = page_object.job_ids.split(',') + try: + for job_id in job_id_list: + JobDao.delete_job_dao(query_db, JobModel(jobId=job_id)) + query_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入定时任务id为空') + return CrudResponseModel(**result) + + @classmethod + def job_detail_services(cls, query_db: Session, job_id: int): + """ + 获取定时任务详细信息service + :param query_db: orm对象 + :param job_id: 定时任务id + :return: 定时任务id对应的信息 + """ + job = JobDao.get_job_detail_by_id(query_db, job_id=job_id) + result = JobModel(**CamelCaseUtil.transform_result(job)) + + return result + + @staticmethod + async def export_job_list_services(request: Request, job_list: List): + """ + 导出定时任务信息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": "备注", + } + + data = job_list + job_group_list = await DictDataService.query_dict_data_list_from_cache_services(request.app.state.redis, dict_type='sys_job_group') + job_group_option = [dict(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 = [dict(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 data: + if item.get('status') == '0': + item['status'] = '正常' + else: + item['status'] = '暂停' + if str(item.get('jobGroup')) in job_group_option_dict.keys(): + item['jobGroup'] = job_group_option_dict.get(str(item.get('jobGroup'))).get('label') + if str(item.get('jobExecutor')) in job_executor_option_dict.keys(): + 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'] = '禁止' + new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data] + binary_data = export_list2excel(new_data) + + return binary_data diff --git a/ruoyi-fastapi-backend/module_admin/service/log_service.py b/ruoyi-fastapi-backend/module_admin/service/log_service.py new file mode 100644 index 0000000..a4546e5 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/log_service.py @@ -0,0 +1,242 @@ +from module_admin.dao.log_dao import * +from module_admin.service.dict_service import Request, DictDataService +from module_admin.entity.vo.common_vo import CrudResponseModel +from utils.common_util import export_list2excel, CamelCaseUtil + + +class OperationLogService: + """ + 操作日志管理模块服务层 + """ + + @classmethod + def get_operation_log_list_services(cls, query_db: Session, query_object: OperLogQueryModel): + """ + 获取操作日志列表信息service + :param query_db: orm对象 + :param query_object: 查询参数对象 + :return: 操作日志列表信息对象 + """ + operation_log_list_result = OperationLogDao.get_operation_log_list(query_db, query_object) + + return CamelCaseUtil.transform_result(operation_log_list_result) + + @classmethod + def add_operation_log_services(cls, query_db: Session, page_object: OperLogModel): + """ + 新增操作日志service + :param query_db: orm对象 + :param page_object: 新增操作日志对象 + :return: 新增操作日志校验结果 + """ + try: + OperationLogDao.add_operation_log_dao(query_db, page_object) + query_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + result = dict(is_success=False, message=str(e)) + + return CrudResponseModel(**result) + + @classmethod + def delete_operation_log_services(cls, query_db: Session, page_object: DeleteOperLogModel): + """ + 删除操作日志信息service + :param query_db: orm对象 + :param page_object: 删除操作日志对象 + :return: 删除操作日志校验结果 + """ + if page_object.oper_ids.split(','): + oper_id_list = page_object.oper_ids.split(',') + try: + for oper_id in oper_id_list: + OperationLogDao.delete_operation_log_dao(query_db, OperLogModel(operId=oper_id)) + query_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入操作日志id为空') + return CrudResponseModel(**result) + + @classmethod + def clear_operation_log_services(cls, query_db: Session): + """ + 清除操作日志信息service + :param query_db: orm对象 + :return: 清除操作日志校验结果 + """ + try: + OperationLogDao.clear_operation_log_dao(query_db) + query_db.commit() + result = dict(is_success=True, message='清除成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + async def export_operation_log_list_services(cls, request: Request, operation_log_list: List): + """ + 导出操作日志信息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": "消耗时间(毫秒)" + } + + data = operation_log_list + operation_type_list = await DictDataService.query_dict_data_list_from_cache_services(request.app.state.redis, dict_type='sys_oper_type') + operation_type_option = [dict(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 data: + if item.get('status') == 0: + item['status'] = '成功' + else: + item['status'] = '失败' + if str(item.get('businessType')) in operation_type_option_dict.keys(): + item['businessType'] = operation_type_option_dict.get(str(item.get('businessType'))).get('label') + + new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data] + binary_data = export_list2excel(new_data) + + return binary_data + + +class LoginLogService: + """ + 登录日志管理模块服务层 + """ + + @classmethod + def get_login_log_list_services(cls, query_db: Session, query_object: LoginLogQueryModel): + """ + 获取登录日志列表信息service + :param query_db: orm对象 + :param query_object: 查询参数对象 + :return: 登录日志列表信息对象 + """ + operation_log_list_result = LoginLogDao.get_login_log_list(query_db, query_object) + + return CamelCaseUtil.transform_result(operation_log_list_result) + + @classmethod + def add_login_log_services(cls, query_db: Session, page_object: LogininforModel): + """ + 新增登录日志service + :param query_db: orm对象 + :param page_object: 新增登录日志对象 + :return: 新增登录日志校验结果 + """ + try: + LoginLogDao.add_login_log_dao(query_db, page_object) + query_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + result = dict(is_success=False, message=str(e)) + + return CrudResponseModel(**result) + + @classmethod + def delete_login_log_services(cls, query_db: Session, page_object: DeleteLoginLogModel): + """ + 删除操作日志信息service + :param query_db: orm对象 + :param page_object: 删除操作日志对象 + :return: 删除操作日志校验结果 + """ + if page_object.info_ids.split(','): + info_id_list = page_object.info_ids.split(',') + try: + for info_id in info_id_list: + LoginLogDao.delete_login_log_dao(query_db, LogininforModel(infoId=info_id)) + query_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入登录日志id为空') + return CrudResponseModel(**result) + + @classmethod + def clear_login_log_services(cls, query_db: Session): + """ + 清除操作日志信息service + :param query_db: orm对象 + :return: 清除操作日志校验结果 + """ + try: + LoginLogDao.clear_login_log_dao(query_db) + query_db.commit() + result = dict(is_success=True, message='清除成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + async def unlock_user_services(cls, request: Request, unlock_user: UnlockUser): + 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}") + result = dict(is_success=True, message='解锁成功') + else: + result = dict(is_success=False, message='该用户未锁定') + return CrudResponseModel(**result) + + @staticmethod + def export_login_log_list_services(login_log_list: List): + """ + 导出登录日志信息service + :param login_log_list: 登录日志信息列表 + :return: 登录日志信息对应excel的二进制数据 + """ + # 创建一个映射字典,将英文键映射到中文键 + mapping_dict = { + "infoId": "访问编号", + "userName": "用户名称", + "ipaddr": "登录地址", + "loginLocation": "登录地点", + "browser": "浏览器", + "os": "操作系统", + "status": "登录状态", + "msg": "操作信息", + "loginTime": "登录日期" + } + + data = login_log_list + + for item in data: + if item.get('status') == '0': + item['status'] = '成功' + else: + item['status'] = '失败' + new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data] + binary_data = export_list2excel(new_data) + + return binary_data diff --git a/ruoyi-fastapi-backend/module_admin/service/login_service.py b/ruoyi-fastapi-backend/module_admin/service/login_service.py new file mode 100644 index 0000000..ca1cee3 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/login_service.py @@ -0,0 +1,316 @@ +from fastapi import Request, Form +from fastapi import Depends +from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm +from jose import JWTError, jwt +import random +import uuid +from datetime import timedelta +from module_admin.service.user_service import * +from module_admin.entity.vo.login_vo import * +from module_admin.entity.vo.common_vo import CrudResponseModel +from module_admin.dao.login_dao import * +from config.env import JwtConfig, RedisInitKeyConfig +from utils.common_util import CamelCaseUtil +from utils.pwd_util import * +from utils.response_util import * +from utils.message_util import * +from config.get_db import get_db + +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login") + + +class CustomOAuth2PasswordRequestForm(OAuth2PasswordRequestForm): + """ + 自定义OAuth2PasswordRequestForm类,增加验证码及会话编号参数 + """ + def __init__( + self, + grant_type: str = Form(default=None, regex="password"), + username: str = Form(), + password: str = Form(), + scope: str = Form(default=""), + client_id: Optional[str] = Form(default=None), + client_secret: Optional[str] = Form(default=None), + code: Optional[str] = Form(default=""), + uuid: Optional[str] = Form(default=""), + login_info: Optional[Dict[str, str]] = Form(default=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: Session, login_user: UserLogin): + """ + 根据用户名密码校验用户登录 + :param request: Request对象 + :param query_db: orm对象 + :param login_user: 登录用户对象 + :return: 校验结果 + """ + account_lock = await request.app.state.redis.get( + f"{RedisInitKeyConfig.ACCOUNT_LOCK.get('key')}:{login_user.user_name}") + if login_user.user_name == account_lock: + logger.warning("账号已锁定,请稍后再试") + raise LoginException(data="", message="账号已锁定,请稍后再试") + # 判断是否开启验证码,开启则验证,否则不验证 + if login_user.captcha_enabled: + await cls.__check_login_captcha(request, login_user) + user = 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.get('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.get('key')}:{login_user.user_name}", password_error_count, + ex=timedelta(minutes=10)) + if password_error_count > 5: + await request.app.state.redis.delete( + f"{RedisInitKeyConfig.PASSWORD_ERROR_COUNT.get('key')}:{login_user.user_name}") + await request.app.state.redis.set( + f"{RedisInitKeyConfig.ACCOUNT_LOCK.get('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.get('key')}:{login_user.user_name}") + return user + + @classmethod + async def __check_login_captcha(cls, request: Request, login_user: UserLogin): + """ + 校验用户登录验证码 + :param request: Request对象 + :param login_user: 登录用户对象 + :return: 校验结果 + """ + captcha_value = await request.app.state.redis.get( + f"{RedisInitKeyConfig.CAPTCHA_CODES.get('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 + def create_access_token(cls, data: dict, expires_delta: Union[timedelta, None] = None): + """ + 根据登录信息创建当前用户token + :param data: 登录信息 + :param expires_delta: token有效期 + :return: token + """ + to_encode = data.copy() + if expires_delta: + expire = datetime.utcnow() + expires_delta + else: + expire = datetime.utcnow() + timedelta(minutes=15) + to_encode.update({"exp": expire}) + encoded_jwt = jwt.encode(to_encode, JwtConfig.SECRET_KEY, algorithm=JwtConfig.ALGORITHM) + return encoded_jwt + + @classmethod + async def get_current_user(cls, request: Request = Request, token: str = Depends(oauth2_scheme), + query_db: Session = Depends(get_db)): + """ + 根据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.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM]) + user_id: str = payload.get("user_id") + session_id: str = payload.get("session_id") + if user_id is None: + logger.warning("用户token不合法") + raise AuthException(data="", message="用户token不合法") + token_data = TokenData(user_id=int(user_id)) + except JWTError: + logger.warning("用户token已失效,请重新登录") + raise AuthException(data="", message="用户token已失效,请重新登录") + query_user = 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不合法") + redis_token = await request.app.state.redis.get(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}") + # 此方法可实现同一账号同一时间只能登录一次 + # redis_token = await request.app.state.redis.get(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{user.user_basic_info.user_id}") + if token == redis_token: + await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}", redis_token, + ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES)) + # await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{user.user_basic_info.user_id}", redis_token, + # ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES)) + + role_id_list = [item.role_id for item in query_user.get('user_role_info')] + if 1 in role_id_list: + 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')] + + 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')) + ) + ) + return current_user + else: + logger.warning("用户token已失效,请重新登录") + raise AuthException(data="", message="用户token已失效,请重新登录") + + @classmethod + async def get_current_user_routers(cls, user_id: int, query_db: Session): + """ + 根据用户id获取当前用户路由信息 + :param user_id: 用户id + :param query_db: orm对象 + :return: 当前用户路由信息对象 + """ + query_user = UserDao.get_user_by_id(query_db, user_id=user_id) + user_router_menu = [row for row in query_user.get('user_menu_info') if row.menu_type in ['M', 'C']] + user_router = cls.__generate_user_router_menu(0, user_router_menu) + return user_router + + @classmethod + def __generate_user_router_menu(cls, pid: int, permission_list): + """ + 工具方法:根据菜单信息生成路由信息树形嵌套数据 + :param pid: 菜单id + :param permission_list: 菜单列表信息 + :return: 路由信息树形嵌套数据 + """ + router_list = [] + for permission in permission_list: + if permission.parent_id == pid: + children = cls.__generate_user_router_menu(permission.menu_id, permission_list) + router_list_data = {} + if permission.menu_type == 'M': + router_list_data['name'] = permission.path.capitalize() + router_list_data['path'] = f'/{permission.path}' + router_list_data['hidden'] = False if permission.visible == '0' else True + if permission.is_frame == 1: + router_list_data['redirect'] = 'noRedirect' + if permission.parent_id == 0: + router_list_data['component'] = 'Layout' + else: + router_list_data['component'] = 'ParentView' + if children: + router_list_data['alwaysShow'] = True + router_list_data['children'] = children + router_list_data['meta'] = { + 'title': permission.menu_name, + 'icon': permission.icon, + 'noCache': False if permission.is_cache == '0' else True, + 'link': permission.path if permission.is_frame == 0 else None + } + elif permission.menu_type == 'C': + router_list_data['name'] = permission.path.capitalize() + router_list_data['path'] = permission.path + router_list_data['hidden'] = False if permission.visible == '0' else True + router_list_data['component'] = permission.component + router_list_data['meta'] = { + 'title': permission.menu_name, + 'icon': permission.icon, + 'noCache': False if permission.is_cache == '0' else True, + 'link': permission.path if permission.is_frame == 0 else None + } + router_list.append(router_list_data) + + return router_list + + +async def get_sms_code_services(request: Request, query_db: Session, user: ResetUserModel): + """ + 获取短信验证码service + :param request: Request对象 + :param query_db: orm对象 + :param user: 用户对象 + :return: 短信验证码对象 + """ + redis_sms_result = await request.app.state.redis.get(f"{RedisInitKeyConfig.SMS_CODE.get('key')}:{user.session_id}") + if redis_sms_result: + return SmsCode(**dict(is_success=False, sms_code='', session_id='', message='短信验证码仍在有效期内')) + is_user = 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.get('key')}:{session_id}", sms_code, ex=timedelta(minutes=2)) + # 此处模拟调用短信服务 + message_service(sms_code) + + return SmsCode(**dict(is_success=True, sms_code=sms_code, session_id=session_id, message='获取成功')) + + return SmsCode(**dict(is_success=False, sms_code='', session_id='', message='用户不存在')) + + +async def forget_user_services(request: Request, query_db: Session, forget_user: ResetUserModel): + """ + 用户忘记密码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.get('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 = UserDao.get_user_by_name(query_db, forget_user.user_name).user_id + edit_result = UserService.reset_user_services(query_db, forget_user) + result = edit_result.dict() + elif not redis_sms_result: + result = dict(is_success=False, message='短信验证码已过期') + else: + await request.app.state.redis.delete(f"{RedisInitKeyConfig.SMS_CODE.get('key')}:{forget_user.session_id}") + result = dict(is_success=False, message='短信验证码不正确') + + return CrudResponseModel(**result) + + +async def logout_services(request: Request, session_id: str): + """ + 退出登录services + :param request: Request对象 + :param session_id: 会话编号 + :return: 退出登录结果 + """ + await request.app.state.redis.delete(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_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 diff --git a/ruoyi-fastapi-backend/module_admin/service/menu_service.py b/ruoyi-fastapi-backend/module_admin/service/menu_service.py new file mode 100644 index 0000000..10a295d --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/menu_service.py @@ -0,0 +1,171 @@ +from module_admin.entity.vo.user_vo import CurrentUserModel +from module_admin.entity.vo.role_vo import RoleMenuQueryModel +from module_admin.entity.vo.common_vo import CrudResponseModel +from module_admin.dao.role_dao import RoleDao +from module_admin.dao.menu_dao import * +from utils.common_util import CamelCaseUtil + + +class MenuService: + """ + 菜单管理模块服务层 + """ + + @classmethod + def get_menu_tree_services(cls, query_db: Session, current_user: Optional[CurrentUserModel] = None): + """ + 获取菜单树信息service + :param query_db: orm对象 + :param current_user: 当前用户对象 + :return: 菜单树信息对象 + """ + menu_list_result = 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) + + return menu_tree_result + + @classmethod + def get_role_menu_tree_services(cls, query_db: Session, role_id: int, current_user: Optional[CurrentUserModel] = None): + """ + 根据角色id获取菜单树信息service + :param query_db: orm对象 + :param role_id: 角色id + :param current_user: 当前用户对象 + :return: 当前角色id的菜单树信息对象 + """ + menu_list_result = 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_menu_list = RoleDao.get_role_menu_dao(query_db, role_id) + checked_keys = [row.menu_id for row in role_menu_list] + result = RoleMenuQueryModel( + menus=menu_tree_result, + checkedKeys=checked_keys + ) + + return result + + @classmethod + def get_menu_list_services(cls, query_db: Session, page_object: MenuQueryModel, current_user: Optional[CurrentUserModel] = None): + """ + 获取菜单列表信息service + :param query_db: orm对象 + :param page_object: 分页查询参数对象 + :param current_user: 当前用户对象 + :return: 菜单列表信息对象 + """ + menu_list_result = 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 + def add_menu_services(cls, query_db: Session, page_object: MenuModel): + """ + 新增菜单信息service + :param query_db: orm对象 + :param page_object: 新增菜单对象 + :return: 新增菜单校验结果 + """ + menu = MenuDao.get_menu_detail_by_info(query_db, MenuModel(parentId=page_object.parent_id, menuName=page_object.menu_name, menuType=page_object.menu_type)) + if menu: + result = dict(is_success=False, message='同一目录下不允许存在同名同类型的菜单') + else: + try: + MenuDao.add_menu_dao(query_db, page_object) + query_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + def edit_menu_services(cls, query_db: Session, page_object: MenuModel): + """ + 编辑菜单信息service + :param query_db: orm对象 + :param page_object: 编辑部门对象 + :return: 编辑菜单校验结果 + """ + edit_menu = page_object.model_dump(exclude_unset=True) + menu_info = cls.menu_detail_services(query_db, edit_menu.get('menu_id')) + if menu_info: + if menu_info.parent_id != page_object.parent_id or menu_info.menu_name != page_object.menu_name or menu_info.menu_type != page_object.menu_type: + menu = MenuDao.get_menu_detail_by_info(query_db, MenuModel(parentId=page_object.parent_id, menuName=page_object.menu_name, menuType=page_object.menu_type)) + if menu: + result = dict(is_success=False, message='同一目录下不允许存在同名同类型的菜单') + return CrudResponseModel(**result) + try: + MenuDao.edit_menu_dao(query_db, edit_menu) + query_db.commit() + result = dict(is_success=True, message='更新成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='菜单不存在') + + return CrudResponseModel(**result) + + @classmethod + def delete_menu_services(cls, query_db: Session, page_object: DeleteMenuModel): + """ + 删除菜单信息service + :param query_db: orm对象 + :param page_object: 删除菜单对象 + :return: 删除菜单校验结果 + """ + if page_object.menu_ids.split(','): + menu_id_list = page_object.menu_ids.split(',') + try: + for menu_id in menu_id_list: + MenuDao.delete_menu_dao(query_db, MenuModel(menuId=menu_id)) + query_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入菜单id为空') + return CrudResponseModel(**result) + + @classmethod + def menu_detail_services(cls, query_db: Session, menu_id: int): + """ + 获取菜单详细信息service + :param query_db: orm对象 + :param menu_id: 菜单id + :return: 菜单id对应的信息 + """ + menu = MenuDao.get_menu_detail_by_id(query_db, menu_id=menu_id) + result = MenuModel(**CamelCaseUtil.transform_result(menu)) + + return result + + @classmethod + def list_to_tree(cls, permission_list: list) -> list: + """ + 工具方法:根据菜单列表信息生成树形嵌套数据 + :param permission_list: 菜单列表信息 + :return: 菜单树形嵌套数据 + """ + permission_list = [dict(id=item.menu_id, label=item.menu_name, parentId=item.parent_id) for item in permission_list] + # 转成id为key的字典 + mapping: dict = dict(zip([i['id'] for i in permission_list], permission_list)) + + # 树容器 + container: list = [] + + for d in permission_list: + # 如果找不到父级项,则是根节点 + parent: dict = mapping.get(d['parentId']) + if parent is None: + container.append(d) + else: + children: list = parent.get('children') + if not children: + children = [] + children.append(d) + parent.update({'children': children}) + + return container diff --git a/ruoyi-fastapi-backend/module_admin/service/notice_service.py b/ruoyi-fastapi-backend/module_admin/service/notice_service.py new file mode 100644 index 0000000..3b393be --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/notice_service.py @@ -0,0 +1,106 @@ +from module_admin.dao.notice_dao import * +from module_admin.entity.vo.common_vo import CrudResponseModel +from utils.common_util import export_list2excel, CamelCaseUtil + + +class NoticeService: + """ + 通知公告管理模块服务层 + """ + + @classmethod + def get_notice_list_services(cls, query_db: Session, query_object: NoticeQueryModel): + """ + 获取通知公告列表信息service + :param query_db: orm对象 + :param query_object: 查询参数对象 + :return: 通知公告列表信息对象 + """ + notice_list_result = NoticeDao.get_notice_list(query_db, query_object) + + return CamelCaseUtil.transform_result(notice_list_result) + + @classmethod + def add_notice_services(cls, query_db: Session, page_object: NoticeModel): + """ + 新增通知公告信息service + :param query_db: orm对象 + :param page_object: 新增通知公告对象 + :return: 新增通知公告校验结果 + """ + notice = NoticeDao.get_notice_detail_by_info(query_db, page_object) + if notice: + result = dict(is_success=False, message='通知公告已存在') + else: + try: + NoticeDao.add_notice_dao(query_db, page_object) + query_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + def edit_notice_services(cls, query_db: Session, page_object: NoticeModel): + """ + 编辑通知公告信息service + :param query_db: orm对象 + :param page_object: 编辑通知公告对象 + :return: 编辑通知公告校验结果 + """ + edit_notice = page_object.model_dump(exclude_unset=True) + notice_info = cls.notice_detail_services(query_db, edit_notice.get('notice_id')) + if notice_info: + if notice_info.notice_title != page_object.notice_title or notice_info.notice_type != page_object.notice_type or notice_info.notice_content != page_object.notice_content: + notice = NoticeDao.get_notice_detail_by_info(query_db, page_object) + if notice: + result = dict(is_success=False, message='通知公告已存在') + return CrudResponseModel(**result) + try: + NoticeDao.edit_notice_dao(query_db, edit_notice) + query_db.commit() + result = dict(is_success=True, message='更新成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='通知公告不存在') + + return CrudResponseModel(**result) + + @classmethod + def delete_notice_services(cls, query_db: Session, page_object: DeleteNoticeModel): + """ + 删除通知公告信息service + :param query_db: orm对象 + :param page_object: 删除通知公告对象 + :return: 删除通知公告校验结果 + """ + if page_object.notice_ids.split(','): + notice_id_list = page_object.notice_ids.split(',') + try: + for notice_id in notice_id_list: + NoticeDao.delete_notice_dao(query_db, NoticeModel(noticeId=notice_id)) + query_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入通知公告id为空') + return CrudResponseModel(**result) + + @classmethod + def notice_detail_services(cls, query_db: Session, notice_id: int): + """ + 获取通知公告详细信息service + :param query_db: orm对象 + :param notice_id: 通知公告id + :return: 通知公告id对应的信息 + """ + notice = NoticeDao.get_notice_detail_by_id(query_db, notice_id=notice_id) + result = NoticeModel(**CamelCaseUtil.transform_result(notice)) + + return result diff --git a/ruoyi-fastapi-backend/module_admin/service/online_service.py b/ruoyi-fastapi-backend/module_admin/service/online_service.py new file mode 100644 index 0000000..3839c58 --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/online_service.py @@ -0,0 +1,71 @@ +from fastapi import Request +from jose import jwt +from config.env import JwtConfig, RedisInitKeyConfig +from module_admin.entity.vo.online_vo import * +from module_admin.entity.vo.common_vo import CrudResponseModel +from utils.common_util import CamelCaseUtil + + +class OnlineService: + """ + 在线用户管理模块服务层 + """ + + @classmethod + async def get_online_list_services(cls, request: Request, query_object: OnlineQueryModel): + """ + 获取在线用户表信息service + :param request: Request对象 + :param query_object: 查询参数对象 + :return: 在线用户列表信息 + """ + access_token_keys = await request.app.state.redis.keys(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('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.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM]) + online_dict = dict( + token_id=payload.get('session_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('login_info').get('ipaddr'): + online_info_list = [online_dict] + break + elif not query_object.user_name and query_object.ipaddr: + if query_object.ipaddr == payload.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): + """ + 强退在线用户信息service + :param request: Request对象 + :param page_object: 强退在线用户对象 + :return: 强退在线用户校验结果 + """ + if page_object.token_ids.split(','): + 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.get('key')}:{token_id}") + result = dict(is_success=True, message='强退成功') + else: + result = dict(is_success=False, message='传入session_id为空') + return CrudResponseModel(**result) diff --git a/ruoyi-fastapi-backend/module_admin/service/post_service.py b/ruoyi-fastapi-backend/module_admin/service/post_service.py new file mode 100644 index 0000000..bc6f21f --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/post_service.py @@ -0,0 +1,138 @@ +from module_admin.dao.post_dao import * +from module_admin.entity.vo.common_vo import CrudResponseModel +from utils.common_util import export_list2excel, CamelCaseUtil + + +class PostService: + """ + 岗位管理模块服务层 + """ + @classmethod + def get_post_list_services(cls, query_db: Session, query_object: PostModel): + """ + 获取岗位列表信息service + :param query_db: orm对象 + :param query_object: 查询参数对象 + :return: 岗位列表信息对象 + """ + post_list_result = PostDao.get_post_list(query_db, query_object) + + return CamelCaseUtil.transform_result(post_list_result) + + @classmethod + def add_post_services(cls, query_db: Session, page_object: PostModel): + """ + 新增岗位信息service + :param query_db: orm对象 + :param page_object: 新增岗位对象 + :return: 新增岗位校验结果 + """ + post = PostDao.get_post_detail_by_info(query_db, PostModel(postName=page_object.post_name)) + if post: + result = dict(is_success=False, message='岗位名称已存在') + else: + try: + PostDao.add_post_dao(query_db, page_object) + query_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + def edit_post_services(cls, query_db: Session, page_object: PostModel): + """ + 编辑岗位信息service + :param query_db: orm对象 + :param page_object: 编辑岗位对象 + :return: 编辑岗位校验结果 + """ + edit_post = page_object.model_dump(exclude_unset=True) + post_info = cls.post_detail_services(query_db, edit_post.get('post_id')) + if post_info: + if post_info.post_name != page_object.post_name: + post = PostDao.get_post_detail_by_info(query_db, PostModel(postName=page_object.post_name)) + if post: + result = dict(is_success=False, message='岗位名称已存在') + return CrudResponseModel(**result) + try: + PostDao.edit_post_dao(query_db, edit_post) + query_db.commit() + result = dict(is_success=True, message='更新成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='岗位不存在') + + return CrudResponseModel(**result) + + @classmethod + def delete_post_services(cls, query_db: Session, page_object: DeletePostModel): + """ + 删除岗位信息service + :param query_db: orm对象 + :param page_object: 删除岗位对象 + :return: 删除岗位校验结果 + """ + if page_object.post_ids.split(','): + post_id_list = page_object.post_ids.split(',') + try: + for post_id in post_id_list: + PostDao.delete_post_dao(query_db, PostModel(postId=post_id)) + query_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入岗位id为空') + return CrudResponseModel(**result) + + @classmethod + def post_detail_services(cls, query_db: Session, post_id: int): + """ + 获取岗位详细信息service + :param query_db: orm对象 + :param post_id: 岗位id + :return: 岗位id对应的信息 + """ + post = PostDao.get_post_detail_by_id(query_db, post_id=post_id) + result = PostModel(**CamelCaseUtil.transform_result(post)) + + return result + + @staticmethod + def export_post_list_services(post_list: List): + """ + 导出岗位信息service + :param post_list: 岗位信息列表 + :return: 岗位信息对应excel的二进制数据 + """ + # 创建一个映射字典,将英文键映射到中文键 + mapping_dict = { + "postId": "岗位编号", + "postCode": "岗位编码", + "postName": "岗位名称", + "postSort": "显示顺序", + "status": "状态", + "createBy": "创建者", + "createTime": "创建时间", + "updateBy": "更新者", + "updateTime": "更新时间", + "remark": "备注", + } + + data = post_list + + for item in data: + if item.get('status') == '0': + item['status'] = '正常' + else: + item['status'] = '停用' + new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data] + binary_data = export_list2excel(new_data) + + return binary_data diff --git a/ruoyi-fastapi-backend/module_admin/service/role_service.py b/ruoyi-fastapi-backend/module_admin/service/role_service.py new file mode 100644 index 0000000..cf95b6d --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/role_service.py @@ -0,0 +1,256 @@ +from module_admin.entity.vo.user_vo import UserInfoModel, UserRoleQueryModel +from module_admin.entity.vo.common_vo import CrudResponseModel +from module_admin.dao.user_dao import UserDao +from module_admin.dao.role_dao import * +from utils.common_util import export_list2excel, CamelCaseUtil + + +class RoleService: + """ + 角色管理模块服务层 + """ + + @classmethod + def get_role_select_option_services(cls, query_db: Session): + """ + 获取角色列表不分页信息service + :param query_db: orm对象 + :return: 角色列表不分页信息对象 + """ + role_list_result = RoleDao.get_role_select_option_dao(query_db) + + return CamelCaseUtil.transform_result(role_list_result) + + @classmethod + def get_role_dept_tree_services(cls, query_db: Session, role_id: int): + """ + 根据角色id获取部门树信息service + :param query_db: orm对象 + :param role_id: 角色id + :return: 当前角色id的部门树信息对象 + """ + role_dept_list = RoleDao.get_role_dept_dao(query_db, role_id) + checked_keys = [row.dept_id for row in role_dept_list] + result = RoleDeptQueryModel( + checkedKeys=checked_keys + ) + + return result + + @classmethod + def get_role_list_services(cls, query_db: Session, query_object: RoleQueryModel): + """ + 获取角色列表信息service + :param query_db: orm对象 + :param query_object: 查询参数对象 + :return: 角色列表信息对象 + """ + role_list_result = RoleDao.get_role_list(query_db, query_object) + + return CamelCaseUtil.transform_result(role_list_result) + + @classmethod + def add_role_services(cls, query_db: Session, page_object: AddRoleModel): + """ + 新增角色信息service + :param query_db: orm对象 + :param page_object: 新增角色对象 + :return: 新增角色校验结果 + """ + add_role = RoleModel(**page_object.model_dump(by_alias=True)) + role_name = RoleDao.get_role_by_info(query_db, RoleModel(roleName=page_object.role_name)) + role_key = RoleDao.get_role_by_info(query_db, RoleModel(roleKey=page_object.role_key)) + if role_name: + result = dict(is_success=False, message='角色名称已存在') + elif role_key: + result = dict(is_success=False, message='权限字符已存在') + else: + try: + add_result = 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: + RoleDao.add_role_menu_dao(query_db, RoleMenuModel(roleId=role_id, menuId=menu)) + query_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + def edit_role_services(cls, query_db: Session, page_object: AddRoleModel): + """ + 编辑角色信息service + :param query_db: orm对象 + :param page_object: 编辑角色对象 + :return: 编辑角色校验结果 + """ + edit_role = page_object.model_dump(exclude_unset=True) + if page_object.type != 'status': + del edit_role['menu_ids'] + if page_object.type == 'status': + del edit_role['type'] + role_info = cls.role_detail_services(query_db, edit_role.get('role_id')) + if role_info: + if page_object.type != 'status' and role_info.role_name != page_object.role_name: + role_name = RoleDao.get_role_by_info(query_db, RoleModel(roleName=page_object.role_name)) + if role_name: + result = dict(is_success=False, message='角色名称已存在') + return CrudResponseModel(**result) + elif page_object.type != 'status' and role_info.role_key != page_object.role_key: + role_key = RoleDao.get_role_by_info(query_db, RoleModel(roleKey=page_object.role_key)) + if role_key: + result = dict(is_success=False, message='权限字符已存在') + return CrudResponseModel(**result) + try: + RoleDao.edit_role_dao(query_db, edit_role) + if page_object.type != 'status': + 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: + RoleDao.add_role_menu_dao(query_db, RoleMenuModel(roleId=page_object.role_id, menuId=menu)) + query_db.commit() + result = dict(is_success=True, message='更新成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='角色不存在') + + return CrudResponseModel(**result) + + @classmethod + def role_datascope_services(cls, query_db: Session, page_object: AddRoleModel): + """ + 分配角色数据权限service + :param query_db: orm对象 + :param page_object: 角色数据权限对象 + :return: 分配角色数据权限结果 + """ + edit_role = page_object.model_dump(exclude_unset=True) + del edit_role['dept_ids'] + role_info = cls.role_detail_services(query_db, edit_role.get('role_id')) + if role_info: + if role_info.role_name != page_object.role_name: + role_name = RoleDao.get_role_by_info(query_db, RoleModel(roleName=page_object.role_name)) + if role_name: + result = dict(is_success=False, message='角色名称已存在') + return CrudResponseModel(**result) + elif role_info.role_key != page_object.role_key: + role_key = RoleDao.get_role_by_info(query_db, RoleModel(roleKey=page_object.role_key)) + if role_key: + result = dict(is_success=False, message='权限字符已存在') + return CrudResponseModel(**result) + try: + RoleDao.edit_role_dao(query_db, edit_role) + 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: + RoleDao.add_role_dept_dao(query_db, RoleDeptModel(roleId=page_object.role_id, deptId=dept)) + query_db.commit() + result = dict(is_success=True, message='分配成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='角色不存在') + + return CrudResponseModel(**result) + + @classmethod + def delete_role_services(cls, query_db: Session, page_object: DeleteRoleModel): + """ + 删除角色信息service + :param query_db: orm对象 + :param page_object: 删除角色对象 + :return: 删除角色校验结果 + """ + if page_object.role_ids.split(','): + role_id_list = page_object.role_ids.split(',') + try: + for role_id in role_id_list: + role_id_dict = dict(roleId=role_id, updateBy=page_object.update_by, updateTime=page_object.update_time) + RoleDao.delete_role_menu_dao(query_db, RoleMenuModel(**role_id_dict)) + RoleDao.delete_role_dao(query_db, RoleModel(**role_id_dict)) + query_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入角色id为空') + return CrudResponseModel(**result) + + @classmethod + def role_detail_services(cls, query_db: Session, role_id: int): + """ + 获取角色详细信息service + :param query_db: orm对象 + :param role_id: 角色id + :return: 角色id对应的信息 + """ + role = RoleDao.get_role_detail_by_id(query_db, role_id=role_id) + result = RoleModel(**CamelCaseUtil.transform_result(role)) + + return result + + @staticmethod + def export_role_list_services(role_list: List): + """ + 导出角色列表信息service + :param role_list: 角色信息列表 + :return: 角色列表信息对象 + """ + # 创建一个映射字典,将英文键映射到中文键 + mapping_dict = { + "roleId": "角色编号", + "roleName": "角色名称", + "roleKey": "权限字符", + "roleSort": "显示顺序", + "status": "状态", + "createBy": "创建者", + "createTime": "创建时间", + "updateBy": "更新者", + "updateTime": "更新时间", + "remark": "备注", + } + + data = role_list + + for item in data: + if item.get('status') == '0': + item['status'] = '正常' + else: + item['status'] = '停用' + new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data] + binary_data = export_list2excel(new_data) + + return binary_data + + @classmethod + def get_role_user_allocated_list_services(cls, query_db: Session, page_object: UserRoleQueryModel): + """ + 根据角色id获取已分配用户列表 + :param query_db: orm对象 + :param page_object: 用户关联角色对象 + :return: 已分配用户列表 + """ + query_user_list = UserDao.get_user_role_allocated_list_by_role_id(query_db, page_object) + allocated_list = [UserInfoModel(**CamelCaseUtil.transform_result(row)) for row in query_user_list] + + return allocated_list + + @classmethod + def get_role_user_unallocated_list_services(cls, query_db: Session, page_object: UserRoleQueryModel): + """ + 根据角色id获取未分配用户列表 + :param query_db: orm对象 + :param page_object: 用户关联角色对象 + :return: 未分配用户列表 + """ + query_user_list = UserDao.get_user_role_unallocated_list_by_role_id(query_db, page_object) + unallocated_list = [UserInfoModel(**CamelCaseUtil.transform_result(row)) for row in query_user_list] + + return unallocated_list diff --git a/ruoyi-fastapi-backend/module_admin/service/server_service.py b/ruoyi-fastapi-backend/module_admin/service/server_service.py new file mode 100644 index 0000000..760823b --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/server_service.py @@ -0,0 +1,94 @@ +import psutil +from utils.common_util import bytes2human +import platform +import socket +import os +import time +from module_admin.entity.vo.server_vo import * + + +class ServerService: + """ + 服务监控模块服务层 + """ + + @staticmethod + def get_server_monitor_info(): + # CPU信息 + # 获取CPU总核心数 + cpu_num = psutil.cpu_count(logical=True) + cpu_usage_percent = psutil.cpu_times_percent() + cpu_used = cpu_usage_percent.user + cpu_sys = cpu_usage_percent.system + cpu_free = cpu_usage_percent.idle + cpu = CpuInfo(cpuNum=cpu_num, used=cpu_used, sys=cpu_sys, free=cpu_free) + + # 内存信息 + memory_info = psutil.virtual_memory() + memory_total = bytes2human(memory_info.total) + memory_used = bytes2human(memory_info.used) + memory_free = bytes2human(memory_info.free) + memory_usage = memory_info.percent + mem = MemoryInfo(total=memory_total, used=memory_used, free=memory_free, usage=memory_usage) + + # 主机信息 + # 获取主机名 + hostname = socket.gethostname() + # 获取IP + computer_ip = socket.gethostbyname(hostname) + os_name = platform.platform() + computer_name = platform.node() + os_arch = platform.machine() + user_dir = os.path.abspath(os.getcwd()) + sys = 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: + 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) + + result = ServerMonitorModel(cpu=cpu, mem=mem, sys=sys, py=py, sysFiles=sys_files) + + return result diff --git a/ruoyi-fastapi-backend/module_admin/service/user_service.py b/ruoyi-fastapi-backend/module_admin/service/user_service.py new file mode 100644 index 0000000..65c5c2b --- /dev/null +++ b/ruoyi-fastapi-backend/module_admin/service/user_service.py @@ -0,0 +1,475 @@ +from fastapi import UploadFile +from module_admin.service.role_service import RoleService +from module_admin.service.post_service import PostService +from module_admin.entity.vo.common_vo import CrudResponseModel +from module_admin.dao.user_dao import * +from utils.pwd_util import * +from utils.common_util import * + + +class UserService: + """ + 用户管理模块服务层 + """ + + @classmethod + def get_user_list_services(cls, query_db: Session, query_object: UserQueryModel, data_scope_sql: str): + """ + 获取用户列表信息service + :param query_db: orm对象 + :param query_object: 查询参数对象 + :param data_scope_sql: 数据权限对应的查询sql语句 + :return: 用户列表信息对象 + """ + query_result = UserDao.get_user_list(query_db, query_object, data_scope_sql) + user_list_result = [] + if query_result: + user_list_result = [{**CamelCaseUtil.transform_result(row[0]), 'dept': CamelCaseUtil.transform_result(row[1])} for row in query_result] + + return user_list_result + + @classmethod + def add_user_services(cls, query_db: Session, page_object: AddUserModel): + """ + 新增用户信息service + :param query_db: orm对象 + :param page_object: 新增用户对象 + :return: 新增用户校验结果 + """ + add_user = UserModel(**page_object.model_dump(by_alias=True)) + user = UserDao.get_user_by_info(query_db, UserModel(userName=page_object.user_name)) + if user: + result = dict(is_success=False, message='用户名已存在') + else: + try: + add_result = 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: + 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: + UserDao.add_user_post_dao(query_db, UserPostModel(userId=user_id, postId=post)) + query_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + def edit_user_services(cls, query_db: Session, page_object: EditUserModel): + """ + 编辑用户信息service + :param query_db: orm对象 + :param page_object: 编辑用户对象 + :return: 编辑用户校验结果 + """ + edit_user = page_object.model_dump(exclude_unset=True) + if page_object.type != 'status' and page_object.type != 'avatar' and page_object.type != 'pwd': + del edit_user['role_ids'] + del edit_user['post_ids'] + del edit_user['role'] + if page_object.type == 'status' or page_object.type == 'avatar' or page_object.type == 'pwd': + del edit_user['type'] + user_info = cls.user_detail_services(query_db, edit_user.get('user_id')) + if user_info: + if page_object.type != 'status' and page_object.type != 'avatar' and page_object.type == 'pwd' and user_info.data.user_name != page_object.user_name: + user = UserDao.get_user_by_info(query_db, UserModel(userName=page_object.user_name)) + if user: + result = dict(is_success=False, message='用户名已存在') + return CrudResponseModel(**result) + try: + UserDao.edit_user_dao(query_db, edit_user) + if page_object.type != 'status' and page_object.type != 'avatar': + UserDao.delete_user_role_dao(query_db, UserRoleModel(userId=page_object.user_id)) + 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: + 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: + UserDao.add_user_post_dao(query_db, UserPostModel(userId=page_object.user_id, postId=post)) + query_db.commit() + result = dict(is_success=True, message='更新成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='用户不存在') + + return CrudResponseModel(**result) + + @classmethod + def delete_user_services(cls, query_db: Session, page_object: DeleteUserModel): + """ + 删除用户信息service + :param query_db: orm对象 + :param page_object: 删除用户对象 + :return: 删除用户校验结果 + """ + if page_object.user_ids.split(','): + user_id_list = page_object.user_ids.split(',') + try: + for user_id in user_id_list: + user_id_dict = dict(userId=user_id, updateBy=page_object.update_by, updateTime=page_object.update_time) + UserDao.delete_user_role_dao(query_db, UserRoleModel(**user_id_dict)) + UserDao.delete_user_post_dao(query_db, UserPostModel(**user_id_dict)) + UserDao.delete_user_dao(query_db, UserModel(**user_id_dict)) + query_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='传入用户id为空') + return CrudResponseModel(**result) + + @classmethod + def user_detail_services(cls, query_db: Session, user_id: Union[int, str]): + """ + 获取用户详细信息service + :param query_db: orm对象 + :param user_id: 用户id + :return: 用户id对应的信息 + """ + posts = PostService.get_post_list_services(query_db, PostModel(**{})) + roles = RoleService.get_role_select_option_services(query_db) + if user_id != '': + query_user = 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 + def user_profile_services(cls, query_db: Session, user_id: int): + """ + 获取用户详细信息service + :param query_db: orm对象 + :param user_id: 用户id + :return: 用户id对应的信息 + """ + query_user = 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 + def reset_user_services(cls, query_db: Session, page_object: ResetUserModel): + """ + 重置用户密码service + :param query_db: orm对象 + :param page_object: 重置用户对象 + :return: 重置用户校验结果 + """ + reset_user = page_object.model_dump(exclude_unset=True) + if page_object.old_password: + user = 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): + result = dict(is_success=False, message='旧密码不正确') + return CrudResponseModel(**result) + else: + 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: + UserDao.edit_user_dao(query_db, reset_user) + query_db.commit() + result = dict(is_success=True, message='重置成功') + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @classmethod + async def batch_import_user_services(cls, query_db: Session, file: UploadFile, update_support: bool, current_user: CurrentUserModel): + """ + 批量导入用户service + :param query_db: orm对象 + :param file: 用户导入文件对象 + :param update_support: 用户存在时是否更新 + :param current_user: 当前用户对象 + :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 + if row['sex'] == '男': + row['sex'] = '0' + if row['sex'] == '女': + row['sex'] = '1' + if row['sex'] == '未知': + row['sex'] = '2' + if row['status'] == '正常': + row['status'] = '0' + if row['status'] == '停用': + row['status'] = '1' + add_user = UserModel( + deptId=row['dept_id'], + userName=row['user_name'], + password=PwdUtil.get_password_hash('123456'), + nickName=row['nick_name'], + email=row['email'], + phonenumber=str(row['phonenumber']), + sex=row['sex'], + status=row['status'], + createBy=current_user.user.user_name, + updateBy=current_user.user.user_name + ) + user_info = UserDao.get_user_by_info(query_db, UserModel(userName=row['user_name'])) + if user_info: + if update_support: + edit_user = 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 + ).model_dump(exclude_unset=True) + UserDao.edit_user_dao(query_db, edit_user) + else: + add_error_result.append(f"{count}.用户账号{row['user_name']}已存在") + else: + UserDao.add_user_dao(query_db, add_user) + query_db.commit() + result = dict(is_success=True, message='\n'.join(add_error_result)) + except Exception as e: + query_db.rollback() + raise e + + return CrudResponseModel(**result) + + @staticmethod + def get_user_import_template_services(): + """ + 获取用户导入模板service + :return: 用户导入模板excel的二进制数据 + """ + header_list = ["部门编号", "登录名称", "用户名称", "用户邮箱", "手机号码", "用户性别", "帐号状态"] + selector_header_list = ["用户性别", "帐号状态"] + option_list = [{"用户性别": ["男", "女", "未知"]}, {"帐号状态": ["正常", "停用"]}] + binary_data = get_excel_template(header_list=header_list, selector_header_list=selector_header_list, option_list=option_list) + + return binary_data + + @staticmethod + def export_user_list_services(user_list: List): + """ + 导出用户信息service + :param user_list: 用户信息列表 + :return: 用户信息对应excel的二进制数据 + """ + # 创建一个映射字典,将英文键映射到中文键 + mapping_dict = { + "userId": "用户编号", + "userName": "用户名称", + "nickName": "用户昵称", + "deptName": "部门", + "email": "邮箱地址", + "phonenumber": "手机号码", + "sex": "性别", + "status": "状态", + "createBy": "创建者", + "createTime": "创建时间", + "updateBy": "更新者", + "updateTime": "更新时间", + "remark": "备注", + } + + data = user_list + + for item in data: + 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'] = '未知' + new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data] + binary_data = export_list2excel(new_data) + + return binary_data + + @classmethod + def get_user_role_allocated_list_services(cls, query_db: Session, page_object: UserRoleQueryModel): + """ + 根据用户id获取已分配角色列表 + :param query_db: orm对象 + :param page_object: 用户关联角色对象 + :return: 已分配角色列表 + """ + query_user = 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 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 + def add_user_role_services(cls, query_db: Session, page_object: CrudUserRoleModel): + """ + 新增用户关联角色信息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: + for role_id in role_id_list: + user_role = cls.detail_user_role_services(query_db, UserRoleModel(userId=page_object.user_id, roleId=role_id)) + if user_role: + continue + else: + UserDao.add_user_role_dao(query_db, UserRoleModel(userId=page_object.user_id, roleId=role_id)) + query_db.commit() + result = dict(is_success=True, message='分配成功') + except Exception as e: + query_db.rollback() + raise e + elif page_object.user_id and not page_object.role_ids: + try: + UserDao.delete_user_role_by_user_and_role_dao(query_db, UserRoleModel(userId=page_object.user_id)) + query_db.commit() + result = dict(is_success=True, message='分配成功') + except Exception as e: + 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 = cls.detail_user_role_services(query_db, UserRoleModel(userId=user_id, roleId=page_object.role_id)) + if user_role: + continue + else: + UserDao.add_user_role_dao(query_db, UserRoleModel(userId=user_id, roleId=page_object.role_id)) + query_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='不满足新增条件') + + return CrudResponseModel(**result) + + @classmethod + def delete_user_role_services(cls, query_db: Session, page_object: CrudUserRoleModel): + """ + 删除用户关联角色信息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: + UserDao.delete_user_role_by_user_and_role_dao(query_db, UserRoleModel(userId=page_object.user_id, roleId=page_object.role_id)) + query_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + 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: + UserDao.delete_user_role_by_user_and_role_dao(query_db, UserRoleModel(userId=user_id, roleId=page_object.role_id)) + query_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + query_db.rollback() + raise e + else: + result = dict(is_success=False, message='不满足删除条件') + else: + result = dict(is_success=False, message='传入用户角色关联信息为空') + + return CrudResponseModel(**result) + + @classmethod + def detail_user_role_services(cls, query_db: Session, page_object: UserRoleModel): + """ + 获取用户关联角色详细信息service + :param query_db: orm对象 + :param page_object: 用户关联角色对象 + :return: 用户关联角色详细信息 + """ + user_role = UserDao.get_user_role_detail(query_db, page_object) + + return user_role diff --git a/ruoyi-fastapi-backend/module_task/__init__.py b/ruoyi-fastapi-backend/module_task/__init__.py new file mode 100644 index 0000000..36fefe9 --- /dev/null +++ b/ruoyi-fastapi-backend/module_task/__init__.py @@ -0,0 +1 @@ +from . import scheduler_test diff --git a/ruoyi-fastapi-backend/module_task/scheduler_test.py b/ruoyi-fastapi-backend/module_task/scheduler_test.py new file mode 100644 index 0000000..b22dc6f --- /dev/null +++ b/ruoyi-fastapi-backend/module_task/scheduler_test.py @@ -0,0 +1,7 @@ +from datetime import datetime + + +def job(*args, **kwargs): + print(args) + print(kwargs) + print(f"{datetime.now()}执行了") diff --git a/ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql b/ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql new file mode 100644 index 0000000..6380c98 --- /dev/null +++ b/ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql @@ -0,0 +1,711 @@ +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +drop table if exists sys_dept; +create table sys_dept ( + dept_id bigint(20) not null auto_increment comment '部门id', + parent_id bigint(20) default 0 comment '父部门id', + ancestors varchar(50) default '' comment '祖级列表', + dept_name varchar(30) default '' comment '部门名称', + order_num int(4) default 0 comment '显示顺序', + leader varchar(20) default null comment '负责人', + phone varchar(11) default null comment '联系电话', + email varchar(50) default null comment '邮箱', + status char(1) default '0' comment '部门状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + primary key (dept_id) +) engine=innodb auto_increment=200 comment = '部门表'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- +insert into sys_dept values(100, 0, '0', '集团总公司', 0, '年糕', '15888888888', 'niangao@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(101, 100, '0,100', '深圳分公司', 1, '年糕', '15888888888', 'niangao@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '年糕', '15888888888', 'niangao@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '年糕', '15888888888', 'niangao@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '年糕', '15888888888', 'niangao@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '年糕', '15888888888', 'niangao@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '年糕', '15888888888', 'niangao@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '年糕', '15888888888', 'niangao@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '年糕', '15888888888', 'niangao@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '年糕', '15888888888', 'niangao@qq.com', '0', '0', 'admin', sysdate(), '', null); + + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +drop table if exists sys_user; +create table sys_user ( + user_id bigint(20) not null auto_increment comment '用户ID', + dept_id bigint(20) default null comment '部门ID', + user_name varchar(30) not null comment '用户账号', + nick_name varchar(30) not null comment '用户昵称', + user_type varchar(2) default '00' comment '用户类型(00系统用户)', + email varchar(50) default '' comment '用户邮箱', + phonenumber varchar(11) default '' comment '手机号码', + sex char(1) default '0' comment '用户性别(0男 1女 2未知)', + avatar varchar(100) default '' comment '头像地址', + password varchar(100) default '' comment '密码', + status char(1) default '0' comment '帐号状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + login_ip varchar(128) default '' comment '最后登录IP', + login_date datetime comment '最后登录时间', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (user_id) +) engine=innodb auto_increment=100 comment = '用户信息表'; + +-- ---------------------------- +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, 103, 'admin', '超级管理员', '00', 'niangao@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '管理员'); +insert into sys_user values(2, 105, 'niangao', '年糕', '00', 'niangao@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '测试员'); + + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +drop table if exists sys_post; +create table sys_post +( + post_id bigint(20) not null auto_increment comment '岗位ID', + post_code varchar(64) not null comment '岗位编码', + post_name varchar(50) not null comment '岗位名称', + post_sort int(4) not null comment '显示顺序', + status char(1) not null comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (post_id) +) engine=innodb comment = '岗位信息表'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +drop table if exists sys_role; +create table sys_role ( + role_id bigint(20) not null auto_increment comment '角色ID', + role_name varchar(30) not null comment '角色名称', + role_key varchar(100) not null comment '角色权限字符串', + role_sort int(4) not null comment '显示顺序', + data_scope char(1) default '1' comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', + menu_check_strictly tinyint(1) default 1 comment '菜单树选择项是否关联显示', + dept_check_strictly tinyint(1) default 1 comment '部门树选择项是否关联显示', + status char(1) not null comment '角色状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (role_id) +) engine=innodb auto_increment=100 comment = '角色信息表'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values('1', '超级管理员', 'admin', 1, 1, 1, 1, '0', '0', 'admin', sysdate(), '', null, '超级管理员'); +insert into sys_role values('2', '普通角色', 'common', 2, 2, 1, 1, '0', '0', 'admin', sysdate(), '', null, '普通角色'); + + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +drop table if exists sys_menu; +create table sys_menu ( + menu_id bigint(20) not null auto_increment comment '菜单ID', + menu_name varchar(50) not null comment '菜单名称', + parent_id bigint(20) default 0 comment '父菜单ID', + order_num int(4) default 0 comment '显示顺序', + path varchar(200) default '' comment '路由地址', + component varchar(255) default null comment '组件路径', + query varchar(255) default null comment '路由参数', + is_frame int(1) default 1 comment '是否为外链(0是 1否)', + is_cache int(1) default 0 comment '是否缓存(0缓存 1不缓存)', + menu_type char(1) default '' comment '菜单类型(M目录 C菜单 F按钮)', + visible char(1) default 0 comment '菜单状态(0显示 1隐藏)', + status char(1) default 0 comment '菜单状态(0正常 1停用)', + perms varchar(100) default null comment '权限标识', + icon varchar(100) default '#' comment '菜单图标', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default '' comment '备注', + primary key (menu_id) +) engine=innodb auto_increment=2000 comment = '菜单权限表'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', 1, 0, 'M', '0', '0', '', 'system', 'admin', sysdate(), '', null, '系统管理目录'); +insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', sysdate(), '', null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', sysdate(), '', null, '系统工具目录'); +insert into sys_menu values('4', '若依官网', '0', '4', 'http://ruoyi.vip', null, '', 0, 0, 'M', '0', '0', '', 'guide', 'admin', sysdate(), '', null, '若依官网地址'); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', sysdate(), '', null, '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', sysdate(), '', null, '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 'admin', sysdate(), '', null, '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 'admin', sysdate(), '', null, '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 'admin', sysdate(), '', null, '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 'admin', sysdate(), '', null, '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 'admin', sysdate(), '', null, '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 'admin', sysdate(), '', null, '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 'admin', sysdate(), '', null, '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 'admin', sysdate(), '', null, '在线用户菜单'); +insert into sys_menu values('110', '定时任务', '2', '2', 'job', 'monitor/job/index', '', 1, 0, 'C', '0', '0', 'monitor:job:list', 'job', 'admin', sysdate(), '', null, '定时任务菜单'); +insert into sys_menu values('111', '数据监控', '2', '3', 'druid', 'monitor/druid/index', '', 1, 0, 'C', '0', '0', 'monitor:druid:list', 'druid', 'admin', sysdate(), '', null, '数据监控菜单'); +insert into sys_menu values('112', '服务监控', '2', '4', 'server', 'monitor/server/index', '', 1, 0, 'C', '0', '0', 'monitor:server:list', 'server', 'admin', sysdate(), '', null, '服务监控菜单'); +insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 'admin', sysdate(), '', null, '缓存监控菜单'); +insert into sys_menu values('114', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', sysdate(), '', null, '缓存列表菜单'); +insert into sys_menu values('115', '表单构建', '3', '1', 'build', 'tool/build/index', '', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 'admin', sysdate(), '', null, '表单构建菜单'); +insert into sys_menu values('116', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 'admin', sysdate(), '', null, '代码生成菜单'); +insert into sys_menu values('117', '系统接口', '3', '3', 'swagger', 'tool/swagger/index', '', 1, 0, 'C', '0', '0', 'tool:swagger:list', 'swagger', 'admin', sysdate(), '', null, '系统接口菜单'); +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 'admin', sysdate(), '', null, '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 'admin', sysdate(), '', null, '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1000', '用户查询', '100', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1001', '用户新增', '100', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1002', '用户修改', '100', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1003', '用户删除', '100', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1004', '用户导出', '100', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1005', '用户导入', '100', '6', '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1006', '重置密码', '100', '7', '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', sysdate(), '', null, ''); +-- 角色管理按钮 +insert into sys_menu values('1007', '角色查询', '101', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1008', '角色新增', '101', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1009', '角色修改', '101', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1010', '角色删除', '101', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1011', '角色导出', '101', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 'admin', sysdate(), '', null, ''); +-- 菜单管理按钮 +insert into sys_menu values('1012', '菜单查询', '102', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1013', '菜单新增', '102', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1014', '菜单修改', '102', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1015', '菜单删除', '102', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 'admin', sysdate(), '', null, ''); +-- 部门管理按钮 +insert into sys_menu values('1016', '部门查询', '103', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1017', '部门新增', '103', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1018', '部门修改', '103', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1019', '部门删除', '103', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 'admin', sysdate(), '', null, ''); +-- 岗位管理按钮 +insert into sys_menu values('1020', '岗位查询', '104', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1021', '岗位新增', '104', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1022', '岗位修改', '104', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1023', '岗位删除', '104', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1024', '岗位导出', '104', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 'admin', sysdate(), '', null, ''); +-- 字典管理按钮 +insert into sys_menu values('1025', '字典查询', '105', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1026', '字典新增', '105', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1027', '字典修改', '105', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1028', '字典删除', '105', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1029', '字典导出', '105', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 'admin', sysdate(), '', null, ''); +-- 参数设置按钮 +insert into sys_menu values('1030', '参数查询', '106', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1031', '参数新增', '106', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1032', '参数修改', '106', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1033', '参数删除', '106', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1034', '参数导出', '106', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 'admin', sysdate(), '', null, ''); +-- 通知公告按钮 +insert into sys_menu values('1035', '公告查询', '107', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1036', '公告新增', '107', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1037', '公告修改', '107', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1038', '公告删除', '107', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 'admin', sysdate(), '', null, ''); +-- 操作日志按钮 +insert into sys_menu values('1039', '操作查询', '500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1040', '操作删除', '500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1041', '日志导出', '500', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 'admin', sysdate(), '', null, ''); +-- 登录日志按钮 +insert into sys_menu values('1042', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1043', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1044', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1045', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', sysdate(), '', null, ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', sysdate(), '', null, ''); +-- 定时任务按钮 +insert into sys_menu values('1049', '任务查询', '110', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:job:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1050', '任务新增', '110', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:job:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1051', '任务修改', '110', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:job:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1052', '任务删除', '110', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:job:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1053', '状态修改', '110', '5', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:job:changeStatus', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1054', '任务导出', '110', '6', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:job:export', '#', 'admin', sysdate(), '', null, ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '116', '1', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1056', '生成修改', '116', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1057', '生成删除', '116', '3', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1058', '导入代码', '116', '4', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1059', '预览代码', '116', '5', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1060', '生成代码', '116', '6', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +drop table if exists sys_user_role; +create table sys_user_role ( + user_id bigint(20) not null comment '用户ID', + role_id bigint(20) not null comment '角色ID', + primary key(user_id, role_id) +) engine=innodb comment = '用户和角色关联表'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('2', '2'); + + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +drop table if exists sys_role_menu; +create table sys_role_menu ( + role_id bigint(20) not null comment '角色ID', + menu_id bigint(20) not null comment '菜单ID', + primary key(role_id, menu_id) +) engine=innodb comment = '角色和菜单关联表'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('2', '1'); +insert into sys_role_menu values ('2', '2'); +insert into sys_role_menu values ('2', '3'); +insert into sys_role_menu values ('2', '4'); +insert into sys_role_menu values ('2', '100'); +insert into sys_role_menu values ('2', '101'); +insert into sys_role_menu values ('2', '102'); +insert into sys_role_menu values ('2', '103'); +insert into sys_role_menu values ('2', '104'); +insert into sys_role_menu values ('2', '105'); +insert into sys_role_menu values ('2', '106'); +insert into sys_role_menu values ('2', '107'); +insert into sys_role_menu values ('2', '108'); +insert into sys_role_menu values ('2', '109'); +insert into sys_role_menu values ('2', '110'); +insert into sys_role_menu values ('2', '111'); +insert into sys_role_menu values ('2', '112'); +insert into sys_role_menu values ('2', '113'); +insert into sys_role_menu values ('2', '114'); +insert into sys_role_menu values ('2', '115'); +insert into sys_role_menu values ('2', '116'); +insert into sys_role_menu values ('2', '117'); +insert into sys_role_menu values ('2', '500'); +insert into sys_role_menu values ('2', '501'); +insert into sys_role_menu values ('2', '1000'); +insert into sys_role_menu values ('2', '1001'); +insert into sys_role_menu values ('2', '1002'); +insert into sys_role_menu values ('2', '1003'); +insert into sys_role_menu values ('2', '1004'); +insert into sys_role_menu values ('2', '1005'); +insert into sys_role_menu values ('2', '1006'); +insert into sys_role_menu values ('2', '1007'); +insert into sys_role_menu values ('2', '1008'); +insert into sys_role_menu values ('2', '1009'); +insert into sys_role_menu values ('2', '1010'); +insert into sys_role_menu values ('2', '1011'); +insert into sys_role_menu values ('2', '1012'); +insert into sys_role_menu values ('2', '1013'); +insert into sys_role_menu values ('2', '1014'); +insert into sys_role_menu values ('2', '1015'); +insert into sys_role_menu values ('2', '1016'); +insert into sys_role_menu values ('2', '1017'); +insert into sys_role_menu values ('2', '1018'); +insert into sys_role_menu values ('2', '1019'); +insert into sys_role_menu values ('2', '1020'); +insert into sys_role_menu values ('2', '1021'); +insert into sys_role_menu values ('2', '1022'); +insert into sys_role_menu values ('2', '1023'); +insert into sys_role_menu values ('2', '1024'); +insert into sys_role_menu values ('2', '1025'); +insert into sys_role_menu values ('2', '1026'); +insert into sys_role_menu values ('2', '1027'); +insert into sys_role_menu values ('2', '1028'); +insert into sys_role_menu values ('2', '1029'); +insert into sys_role_menu values ('2', '1030'); +insert into sys_role_menu values ('2', '1031'); +insert into sys_role_menu values ('2', '1032'); +insert into sys_role_menu values ('2', '1033'); +insert into sys_role_menu values ('2', '1034'); +insert into sys_role_menu values ('2', '1035'); +insert into sys_role_menu values ('2', '1036'); +insert into sys_role_menu values ('2', '1037'); +insert into sys_role_menu values ('2', '1038'); +insert into sys_role_menu values ('2', '1039'); +insert into sys_role_menu values ('2', '1040'); +insert into sys_role_menu values ('2', '1041'); +insert into sys_role_menu values ('2', '1042'); +insert into sys_role_menu values ('2', '1043'); +insert into sys_role_menu values ('2', '1044'); +insert into sys_role_menu values ('2', '1045'); +insert into sys_role_menu values ('2', '1046'); +insert into sys_role_menu values ('2', '1047'); +insert into sys_role_menu values ('2', '1048'); +insert into sys_role_menu values ('2', '1049'); +insert into sys_role_menu values ('2', '1050'); +insert into sys_role_menu values ('2', '1051'); +insert into sys_role_menu values ('2', '1052'); +insert into sys_role_menu values ('2', '1053'); +insert into sys_role_menu values ('2', '1054'); +insert into sys_role_menu values ('2', '1055'); +insert into sys_role_menu values ('2', '1056'); +insert into sys_role_menu values ('2', '1057'); +insert into sys_role_menu values ('2', '1058'); +insert into sys_role_menu values ('2', '1059'); +insert into sys_role_menu values ('2', '1060'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +drop table if exists sys_role_dept; +create table sys_role_dept ( + role_id bigint(20) not null comment '角色ID', + dept_id bigint(20) not null comment '部门ID', + primary key(role_id, dept_id) +) engine=innodb comment = '角色和部门关联表'; + +-- ---------------------------- +-- 初始化-角色和部门关联表数据 +-- ---------------------------- +insert into sys_role_dept values ('2', '100'); +insert into sys_role_dept values ('2', '101'); +insert into sys_role_dept values ('2', '105'); + + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +drop table if exists sys_user_post; +create table sys_user_post +( + user_id bigint(20) not null comment '用户ID', + post_id bigint(20) not null comment '岗位ID', + primary key (user_id, post_id) +) engine=innodb comment = '用户与岗位关联表'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); +insert into sys_user_post values ('2', '2'); + + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +drop table if exists sys_oper_log; +create table sys_oper_log ( + oper_id bigint(20) not null auto_increment comment '日志主键', + title varchar(50) default '' comment '模块标题', + business_type int(2) default 0 comment '业务类型(0其它 1新增 2修改 3删除)', + method varchar(100) default '' comment '方法名称', + request_method varchar(10) default '' comment '请求方式', + operator_type int(1) default 0 comment '操作类别(0其它 1后台用户 2手机端用户)', + oper_name varchar(50) default '' comment '操作人员', + dept_name varchar(50) default '' comment '部门名称', + oper_url varchar(255) default '' comment '请求URL', + oper_ip varchar(128) default '' comment '主机地址', + oper_location varchar(255) default '' comment '操作地点', + oper_param varchar(2000) default '' comment '请求参数', + json_result varchar(2000) default '' comment '返回参数', + status int(1) default 0 comment '操作状态(0正常 1异常)', + error_msg varchar(2000) default '' comment '错误消息', + oper_time datetime comment '操作时间', + cost_time bigint(20) default 0 comment '消耗时间', + primary key (oper_id), + key idx_sys_oper_log_bt (business_type), + key idx_sys_oper_log_s (status), + key idx_sys_oper_log_ot (oper_time) +) engine=innodb auto_increment=100 comment = '操作日志记录'; + + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +drop table if exists sys_dict_type; +create table sys_dict_type +( + dict_id bigint(20) not null auto_increment comment '字典主键', + dict_name varchar(100) default '' comment '字典名称', + dict_type varchar(100) default '' comment '字典类型', + status char(1) default '0' comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_id), + unique (dict_type) +) engine=innodb auto_increment=100 comment = '字典类型表'; + +insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', sysdate(), '', null, '用户性别列表'); +insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', sysdate(), '', null, '菜单状态列表'); +insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', sysdate(), '', null, '系统开关列表'); +insert into sys_dict_type values(4, '任务状态', 'sys_job_status', '0', 'admin', sysdate(), '', null, '任务状态列表'); +insert into sys_dict_type values(5, '任务分组', 'sys_job_group', '0', 'admin', sysdate(), '', null, '任务分组列表'); +insert into sys_dict_type values(6, '任务执行器', 'sys_job_executor', '0', 'admin', sysdate(), '', null, '任务执行器列表'); +insert into sys_dict_type values(7, '系统是否', 'sys_yes_no', '0', 'admin', sysdate(), '', null, '系统是否列表'); +insert into sys_dict_type values(8, '通知类型', 'sys_notice_type', '0', 'admin', sysdate(), '', null, '通知类型列表'); +insert into sys_dict_type values(9, '通知状态', 'sys_notice_status', '0', 'admin', sysdate(), '', null, '通知状态列表'); +insert into sys_dict_type values(10, '操作类型', 'sys_oper_type', '0', 'admin', sysdate(), '', null, '操作类型列表'); +insert into sys_dict_type values(11, '系统状态', 'sys_common_status', '0', 'admin', sysdate(), '', null, '登录状态列表'); + + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +drop table if exists sys_dict_data; +create table sys_dict_data +( + dict_code bigint(20) not null auto_increment comment '字典编码', + dict_sort int(4) default 0 comment '字典排序', + dict_label varchar(100) default '' comment '字典标签', + dict_value varchar(100) default '' comment '字典键值', + dict_type varchar(100) default '' comment '字典类型', + css_class varchar(100) default null comment '样式属性(其他样式扩展)', + list_class varchar(100) default null comment '表格回显样式', + is_default char(1) default 'N' comment '是否默认(Y是 N否)', + status char(1) default '0' comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_code) +) engine=innodb auto_increment=100 comment = '字典数据表'; + +insert into sys_dict_data values(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', sysdate(), '', null, '性别男'); +insert into sys_dict_data values(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别女'); +insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别未知'); +insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '显示菜单'); +insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '隐藏菜单'); +insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); +insert into sys_dict_data values(8, 1, '正常', '0', 'sys_job_status', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(9, 2, '暂停', '1', 'sys_job_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); +insert into sys_dict_data values(10, 1, '默认', 'default', 'sys_job_group', '', '', 'Y', '0', 'admin', sysdate(), '', null, '默认分组'); +insert into sys_dict_data values(11, 2, '数据库', 'sqlalchemy', 'sys_job_group', '', '', 'N', '0', 'admin', sysdate(), '', null, '数据库分组'); +insert into sys_dict_data values(12, 3, 'redis', 'redis', 'sys_job_group', '', '', 'N', '0', 'admin', sysdate(), '', null, 'reids分组'); +insert into sys_dict_data values(13, 1, '默认', 'default', 'sys_job_executor', '', '', 'N', '0', 'admin', sysdate(), '', null, '线程池'); +insert into sys_dict_data values(14, 2, '进程池', 'processpool', 'sys_job_executor', '', '', 'N', '0', 'admin', sysdate(), '', null, '进程池'); +insert into sys_dict_data values(15, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '系统默认是'); +insert into sys_dict_data values(16, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '系统默认否'); +insert into sys_dict_data values(17, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', sysdate(), '', null, '通知'); +insert into sys_dict_data values(18, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', sysdate(), '', null, '公告'); +insert into sys_dict_data values(19, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(20, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '关闭状态'); +insert into sys_dict_data values(21, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '其他操作'); +insert into sys_dict_data values(22, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '新增操作'); +insert into sys_dict_data values(23, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '修改操作'); +insert into sys_dict_data values(24, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '删除操作'); +insert into sys_dict_data values(25, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '授权操作'); +insert into sys_dict_data values(26, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导出操作'); +insert into sys_dict_data values(27, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导入操作'); +insert into sys_dict_data values(28, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '强退操作'); +insert into sys_dict_data values(29, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '生成操作'); +insert into sys_dict_data values(30, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '清空操作'); +insert into sys_dict_data values(31, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(32, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +drop table if exists sys_config; +create table sys_config ( + config_id int(5) not null auto_increment comment '参数主键', + config_name varchar(100) default '' comment '参数名称', + config_key varchar(100) default '' comment '参数键名', + config_value varchar(500) default '' comment '参数键值', + config_type char(1) default 'N' comment '系统内置(Y是 N否)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (config_id) +) engine=innodb auto_increment=100 comment = '参数配置表'; + +insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', sysdate(), '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); +insert into sys_config values(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', sysdate(), '', null, '初始化密码 123456' ); +insert into sys_config values(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', sysdate(), '', null, '深色主题theme-dark,浅色主题theme-light' ); +insert into sys_config values(4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', sysdate(), '', null, '是否开启验证码功能(true开启,false关闭)'); +insert into sys_config values(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', sysdate(), '', null, '是否开启注册用户功能(true开启,false关闭)'); +insert into sys_config values(6, '用户登录-黑名单列表', 'sys.login.blackIPList', '', 'Y', 'admin', sysdate(), '', null, '设置登录IP黑名单限制,多个匹配项以;分隔,支持匹配(*通配、网段)'); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +drop table if exists sys_logininfor; +create table sys_logininfor ( + info_id bigint(20) not null auto_increment comment '访问ID', + user_name varchar(50) default '' comment '用户账号', + ipaddr varchar(128) default '' comment '登录IP地址', + login_location varchar(255) default '' comment '登录地点', + browser varchar(50) default '' comment '浏览器类型', + os varchar(50) default '' comment '操作系统', + status char(1) default '0' comment '登录状态(0成功 1失败)', + msg varchar(255) default '' comment '提示消息', + login_time datetime comment '访问时间', + primary key (info_id), + key idx_sys_logininfor_s (status), + key idx_sys_logininfor_lt (login_time) +) engine=innodb auto_increment=100 comment = '系统访问记录'; + + +-- ---------------------------- +-- 15、定时任务调度表 +-- ---------------------------- +drop table if exists sys_job; +create table sys_job ( + job_id bigint(20) not null auto_increment comment '任务ID', + job_name varchar(64) default '' comment '任务名称', + job_group varchar(64) default 'default' comment '任务组名', + job_executor varchar(64) default 'default' comment '任务执行器', + invoke_target varchar(500) not null comment '调用目标字符串', + job_args varchar(255) default '' comment '位置参数', + job_kwargs varchar(255) default '' comment '关键字参数', + cron_expression varchar(255) default '' comment 'cron执行表达式', + misfire_policy varchar(20) default '3' comment '计划执行错误策略(1立即执行 2执行一次 3放弃执行)', + concurrent char(1) default '1' comment '是否并发执行(0允许 1禁止)', + status char(1) default '0' comment '状态(0正常 1暂停)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default '' comment '备注信息', + primary key (job_id, job_name, job_group) +) engine=innodb auto_increment=100 comment = '定时任务调度表'; + +insert into sys_job values(1, '系统默认(无参)', 'default', 'default', 'module_task.scheduler_test.job', NULL, NULL, '0/10 * * * * * *', '3', '1', '1', 'admin', sysdate(), '', null, ''); +insert into sys_job values(2, '系统默认(有参)', 'default', 'default', 'module_task.scheduler_test.job', 'test', NULL, '0/15 * * * * * *', '3', '1', '1', 'admin', sysdate(), '', null, ''); +insert into sys_job values(3, '系统默认(多参)', 'default', 'default', 'module_task.scheduler_test.job', 'new', '{\"test\": 111}', '0/20 * * * * * *', '3', '1', '1', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 16、定时任务调度日志表 +-- ---------------------------- +drop table if exists sys_job_log; +create table sys_job_log ( + job_log_id bigint(20) not null auto_increment comment '任务日志ID', + job_name varchar(64) not null comment '任务名称', + job_group varchar(64) not null comment '任务组名', + job_executor varchar(64) not null comment '任务执行器', + invoke_target varchar(500) not null comment '调用目标字符串', + job_args varchar(255) default '' comment '位置参数', + job_kwargs varchar(255) default '' comment '关键字参数', + job_trigger varchar(255) default '' comment '任务触发器', + job_message varchar(500) comment '日志信息', + status char(1) default '0' comment '执行状态(0正常 1失败)', + exception_info varchar(2000) default '' comment '异常信息', + create_time datetime comment '创建时间', + primary key (job_log_id) +) engine=innodb comment = '定时任务调度日志表'; + + +-- ---------------------------- +-- 17、通知公告表 +-- ---------------------------- +drop table if exists sys_notice; +create table sys_notice ( + notice_id int(4) not null auto_increment comment '公告ID', + notice_title varchar(50) not null comment '公告标题', + notice_type char(1) not null comment '公告类型(1通知 2公告)', + notice_content longblob default null comment '公告内容', + status char(1) default '0' comment '公告状态(0正常 1关闭)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(255) default null comment '备注', + primary key (notice_id) +) engine=innodb auto_increment=10 comment = '通知公告表'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '温馨提醒:2018-07-01 vfadmin新版本发布啦', '2', '新版本内容', '0', 'admin', sysdate(), '', null, '管理员'); +insert into sys_notice values('2', '维护通知:2018-07-01 vfadmin系统凌晨维护', '1', '维护内容', '0', 'admin', sysdate(), '', null, '管理员'); + + +-- ---------------------------- +-- 18、代码生成业务表 +-- ---------------------------- +drop table if exists gen_table; +create table gen_table ( + table_id bigint(20) not null auto_increment comment '编号', + table_name varchar(200) default '' comment '表名称', + table_comment varchar(500) default '' comment '表描述', + sub_table_name varchar(64) default null comment '关联子表的表名', + sub_table_fk_name varchar(64) default null comment '子表关联的外键名', + class_name varchar(100) default '' comment '实体类名称', + tpl_category varchar(200) default 'crud' comment '使用的模板(crud单表操作 tree树表操作)', + tpl_web_type varchar(30) default '' comment '前端模板类型(element-ui模版 element-plus模版)', + package_name varchar(100) comment '生成包路径', + module_name varchar(30) comment '生成模块名', + business_name varchar(30) comment '生成业务名', + function_name varchar(50) comment '生成功能名', + function_author varchar(50) comment '生成功能作者', + gen_type char(1) default '0' comment '生成代码方式(0zip压缩包 1自定义路径)', + gen_path varchar(200) default '/' comment '生成路径(不填默认项目路径)', + options varchar(1000) comment '其它生成选项', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (table_id) +) engine=innodb auto_increment=1 comment = '代码生成业务表'; + + +-- ---------------------------- +-- 19、代码生成业务表字段 +-- ---------------------------- +drop table if exists gen_table_column; +create table gen_table_column ( + column_id bigint(20) not null auto_increment comment '编号', + table_id bigint(20) comment '归属表编号', + column_name varchar(200) comment '列名称', + column_comment varchar(500) comment '列描述', + column_type varchar(100) comment '列类型', + java_type varchar(500) comment 'JAVA类型', + java_field varchar(200) comment 'JAVA字段名', + is_pk char(1) comment '是否主键(1是)', + is_increment char(1) comment '是否自增(1是)', + is_required char(1) comment '是否必填(1是)', + is_insert char(1) comment '是否为插入字段(1是)', + is_edit char(1) comment '是否编辑字段(1是)', + is_list char(1) comment '是否列表字段(1是)', + is_query char(1) comment '是否查询字段(1是)', + query_type varchar(200) default 'EQ' comment '查询方式(等于、不等于、大于、小于、范围)', + html_type varchar(200) comment '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)', + dict_type varchar(200) default '' comment '字典类型', + sort int comment '排序', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + primary key (column_id) +) engine=innodb auto_increment=1 comment = '代码生成业务表字段'; \ No newline at end of file diff --git a/ruoyi-fastapi-backend/utils/common_util.py b/ruoyi-fastapi-backend/utils/common_util.py new file mode 100644 index 0000000..a45d266 --- /dev/null +++ b/ruoyi-fastapi-backend/utils/common_util.py @@ -0,0 +1,184 @@ +import pandas as pd +import io +import os +from openpyxl import Workbook +from openpyxl.styles import Alignment, PatternFill +from openpyxl.utils import get_column_letter +from openpyxl.worksheet.datavalidation import DataValidation +from typing import List +from config.env import CachePathConfig + + +def worship(): + print(""" +//////////////////////////////////////////////////////////////////// +// _ooOoo_ // +// o8888888o // +// 88" . "88 // +// (| ^_^ |) // +// O\ = /O // +// ____/`---'\____ // +// .' \\| |// `. // +// / \\||| : |||// \ // +// / _||||| -:- |||||- \ // +// | | \\\ - /// | | // +// | \_| ''\---/'' | | // +// \ .-\__ `-` ___/-. / // +// ___`. .' /--.--\ `. . ___ // +// ."" '< `.___\_<|>_/___.' >'"". // +// | | : `- \`.;`\ _ /`;.`/ - ` : | | // +// \ \ `-. \_ __\ /__ _/ .-` / / // +// ========`-.____`-.___\_____/___.-`____.-'======== // +// `=---=' // +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // +// 佛祖保佑 永不宕机 永无BUG // +//////////////////////////////////////////////////////////////////// + """) + + +class CamelCaseUtil: + """ + 下划线形式(snake_case)转换为小驼峰形式(camelCase)工具方法 + """ + @classmethod + def __to_camel_case(cls, snake_str): + """ + 下划线形式字符串(snake_case)转换为小驼峰形式字符串(camelCase) + :param snake_str: 下划线形式字符串 + :return: 小驼峰形式字符串 + """ + # 分割字符串 + words = snake_str.split('_') + # 小驼峰命名,第一个词首字母小写,其余词首字母大写 + return words[0] + ''.join(word.capitalize() for word in words[1:]) + + @classmethod + def transform_result(cls, result): + """ + 针对不同类型将下划线形式(snake_case)批量转换为小驼峰形式(camelCase)方法 + :param result: 输入数据 + :return: 小驼峰形式结果 + """ + if result is None: + return result + # 如果是字典,直接转换键 + elif isinstance(result, dict): + return {cls.__to_camel_case(k): v for k, v in result.items()} + # 如果是一组字典或其他类型的列表,遍历列表进行转换 + elif isinstance(result, list): + return [cls.transform_result(row) if isinstance(row, dict) else cls.transform_result({c.name: getattr(row, c.name) for c in row.__table__.columns}) for row in result] + # 如果是其他类型,如模型实例,先转换为字典 + else: + return cls.transform_result({c.name: getattr(result, c.name) for c in result.__table__.columns}) + + +def bytes2human(n, format_str="%(value).1f%(symbol)s"): + """Used by various scripts. See: + http://goo.gl/zeJZl + + >>> bytes2human(10000) + '9.8K' + >>> bytes2human(100001221) + '95.4M' + """ + symbols = ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB') + prefix = {} + for i, s in enumerate(symbols[1:]): + prefix[s] = 1 << (i + 1) * 10 + for symbol in reversed(symbols[1:]): + if n >= prefix[symbol]: + value = float(n) / prefix[symbol] + return format_str % locals() + return format_str % dict(symbol=symbols[0], value=n) + + +def bytes2file_response(bytes_info): + yield bytes_info + + +def export_list2excel(list_data: List): + """ + 工具方法:将需要导出的list数据转化为对应excel的二进制数据 + :param list_data: 数据列表 + :return: 字典信息对应excel的二进制数据 + """ + df = pd.DataFrame(list_data) + binary_data = io.BytesIO() + df.to_excel(binary_data, index=False, engine='openpyxl') + binary_data = binary_data.getvalue() + + return binary_data + + +def get_excel_template(header_list: List, selector_header_list: List, option_list: List[dict]): + """ + 工具方法:将需要导出的list数据转化为对应excel的二进制数据 + :param header_list: 表头数据列表 + :param selector_header_list: 需要设置为选择器格式的表头数据列表 + :param option_list: 选择器格式的表头预设的选项列表 + :return: 模板excel的二进制数据 + """ + # 创建Excel工作簿 + wb = Workbook() + # 选择默认的活动工作表 + ws = wb.active + + # 设置表头文字 + headers = header_list + + # 设置表头背景样式为灰色,前景色为白色 + header_fill = PatternFill(start_color="ababab", end_color="ababab", fill_type="solid") + + # 将表头写入第一行 + for col_num, header in enumerate(headers, 1): + cell = ws.cell(row=1, column=col_num) + cell.value = header + cell.fill = header_fill + # 设置列宽度为16 + ws.column_dimensions[chr(64 + col_num)].width = 12 + # 设置水平居中对齐 + cell.alignment = Alignment(horizontal='center') + + # 设置选择器的预设选项 + options = option_list + + # 获取selector_header的字母索引 + for selector_header in selector_header_list: + column_selector_header_index = headers.index(selector_header) + 1 + + # 创建数据有效性规则 + header_option = [] + for option in options: + if option.get(selector_header): + header_option = option.get(selector_header) + dv = DataValidation(type="list", formula1=f'"{",".join(header_option)}"') + # 设置数据有效性规则的起始单元格和结束单元格 + dv.add( + f'{get_column_letter(column_selector_header_index)}2:{get_column_letter(column_selector_header_index)}1048576') + # 添加数据有效性规则到工作表 + ws.add_data_validation(dv) + + # 保存Excel文件为字节类型的数据 + file = io.BytesIO() + wb.save(file) + file.seek(0) + + # 读取字节数据 + excel_data = file.getvalue() + + return excel_data + + +def get_filepath_from_url(url: str): + """ + 工具方法:根据请求参数获取文件路径 + :param url: 请求参数中的url参数 + :return: 文件路径 + """ + file_info = url.split("?")[1].split("&") + task_id = file_info[0].split("=")[1] + file_name = file_info[1].split("=")[1] + task_path = file_info[2].split("=")[1] + filepath = os.path.join(CachePathConfig.PATH, task_path, task_id, file_name) + + return filepath diff --git a/ruoyi-fastapi-backend/utils/log_util.py b/ruoyi-fastapi-backend/utils/log_util.py new file mode 100644 index 0000000..e904653 --- /dev/null +++ b/ruoyi-fastapi-backend/utils/log_util.py @@ -0,0 +1,11 @@ +import os +import time +from loguru import logger + +log_path = os.path.join(os.getcwd(), 'logs') +if not os.path.exists(log_path): + os.mkdir(log_path) + +log_path_error = os.path.join(log_path, f'{time.strftime("%Y-%m-%d")}_error.log') + +logger.add(log_path_error, rotation="50MB", encoding="utf-8", enqueue=True, compression="zip") diff --git a/ruoyi-fastapi-backend/utils/message_util.py b/ruoyi-fastapi-backend/utils/message_util.py new file mode 100644 index 0000000..6a21f88 --- /dev/null +++ b/ruoyi-fastapi-backend/utils/message_util.py @@ -0,0 +1,5 @@ +from utils.log_util import logger + + +def message_service(sms_code: str): + logger.info(f"短信验证码为{sms_code}") diff --git a/ruoyi-fastapi-backend/utils/page_util.py b/ruoyi-fastapi-backend/utils/page_util.py new file mode 100644 index 0000000..341f80f --- /dev/null +++ b/ruoyi-fastapi-backend/utils/page_util.py @@ -0,0 +1,98 @@ +import math +from typing import Optional, List + +from pydantic import BaseModel, ConfigDict +from pydantic.alias_generators import to_camel + + +class PageModel(BaseModel): + """ + 分页模型 + """ + offset: int + page_num: int + page_size: int + total: int + has_next: bool + + +class PageObjectResponse(BaseModel): + """ + 用户管理列表分页查询返回模型 + """ + rows: List = [] + page_num: int + page_size: int + total: int + has_next: bool + + +class PageResponseModel(BaseModel): + """ + 列表分页查询返回模型 + """ + model_config = ConfigDict(alias_generator=to_camel) + + rows: List = [] + page_num: Optional[int] = None + page_size: Optional[int] = None + total: int + has_next: Optional[bool] = None + + +def get_page_info(offset: int, page_num: int, page_size: int, count: int): + """ + 根据分页参数获取分页信息 + :param offset: 起始数据位置 + :param page_num: 当前页码 + :param page_size: 当前页面数据量 + :param count: 数据总数 + :return: 分页信息对象 + """ + has_next = False + if offset >= count: + res_offset_1 = (page_num - 2) * page_size + if res_offset_1 < 0: + res_offset = 0 + res_page_num = 1 + else: + res_offset = res_offset_1 + res_page_num = page_num - 1 + else: + res_offset = offset + if (res_offset + page_size) < count: + has_next = True + res_page_num = page_num + + result = dict(offset=res_offset, page_num=res_page_num, page_size=page_size, total=count, has_next=has_next) + + return PageModel(**result) + + +def get_page_obj(data_list: List, page_num: int, page_size: int): + """ + 输入数据列表data_list和分页信息,返回分页数据列表结果 + :param data_list: 原始数据列表 + :param page_num: 当前页码 + :param page_size: 当前页面数据量 + :return: 分页数据对象 + """ + # 计算起始索引和结束索引 + start = (page_num - 1) * page_size + end = page_num * page_size + + # 根据计算得到的起始索引和结束索引对数据列表进行切片 + paginated_data = data_list[start:end] + has_next = True if math.ceil(len(data_list) / page_size) > page_num else False + + result = PageResponseModel( + rows=paginated_data, + pageNum=page_num, + pageSize=page_size, + total=len(data_list), + hasNext=has_next + ) + + return result + + diff --git a/ruoyi-fastapi-backend/utils/pwd_util.py b/ruoyi-fastapi-backend/utils/pwd_util.py new file mode 100644 index 0000000..2e9a2f9 --- /dev/null +++ b/ruoyi-fastapi-backend/utils/pwd_util.py @@ -0,0 +1,28 @@ +from passlib.context import CryptContext + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + + +class PwdUtil: + """ + 密码工具类 + """ + + @classmethod + def verify_password(cls, plain_password, hashed_password): + """ + 工具方法:校验当前输入的密码与数据库存储的密码是否一致 + :param plain_password: 当前输入的密码 + :param hashed_password: 数据库存储的密码 + :return: 校验结果 + """ + return pwd_context.verify(plain_password, hashed_password) + + @classmethod + def get_password_hash(cls, input_password): + """ + 工具方法:对当前输入的密码进行加密 + :param input_password: 输入的密码 + :return: 加密成功的密码 + """ + return pwd_context.hash(input_password) diff --git a/ruoyi-fastapi-backend/utils/response_util.py b/ruoyi-fastapi-backend/utils/response_util.py new file mode 100644 index 0000000..4dab253 --- /dev/null +++ b/ruoyi-fastapi-backend/utils/response_util.py @@ -0,0 +1,301 @@ +from fastapi import status +from fastapi.responses import JSONResponse, Response, StreamingResponse +from fastapi.encoders import jsonable_encoder +from typing import Any, Dict, Optional +from pydantic import BaseModel +from datetime import datetime + + +class ResponseUtil: + """ + 响应工具类 + """ + + @classmethod + def success(cls, msg: str = '操作成功', data: Optional[Any] = None, rows: Optional[Any] = None, + dict_content: Optional[Dict] = None, model_content: Optional[BaseModel] = None) -> Response: + """ + 成功响应方法 + :param msg: 可选,自定义成功响应信息 + :param data: 可选,成功响应结果中属性为data的值 + :param rows: 可选,成功响应结果中属性为rows的值 + :param dict_content: 可选,dict类型,成功响应结果中自定义属性的值 + :param model_content: 可选,BaseModel类型,成功响应结果中自定义属性的值 + :return: 成功响应结果 + """ + result = { + 'code': 200, + 'msg': msg + } + + if data is not None: + result['data'] = data + if rows is not None: + result['rows'] = rows + if dict_content is not None: + result.update(dict_content) + if model_content is not None: + result.update(model_content.model_dump(by_alias=True)) + + result.update({'success': True, 'time': datetime.now()}) + + return JSONResponse( + status_code=status.HTTP_200_OK, + content=jsonable_encoder(result) + ) + + @classmethod + def failure(cls, msg: str = '操作失败', data: Optional[Any] = None, rows: Optional[Any] = None, + dict_content: Optional[Dict] = None, model_content: Optional[BaseModel] = None) -> Response: + """ + 失败响应方法 + :param msg: 可选,自定义失败响应信息 + :param data: 可选,失败响应结果中属性为data的值 + :param rows: 可选,失败响应结果中属性为rows的值 + :param dict_content: 可选,dict类型,失败响应结果中自定义属性的值 + :param model_content: 可选,BaseModel类型,失败响应结果中自定义属性的值 + :return: 失败响应结果 + """ + result = { + 'code': 601, + 'msg': msg + } + + if data is not None: + result['data'] = data + if rows is not None: + result['rows'] = rows + if dict_content is not None: + result.update(dict_content) + if model_content is not None: + result.update(model_content.model_dump(by_alias=True)) + + result.update({'success': False, 'time': datetime.now()}) + + return JSONResponse( + status_code=status.HTTP_200_OK, + content=jsonable_encoder(result) + ) + + @classmethod + def unauthorized(cls, msg: str = '登录信息已过期,访问系统资源失败', data: Optional[Any] = None, rows: Optional[Any] = None, + dict_content: Optional[Dict] = None, model_content: Optional[BaseModel] = None) -> Response: + """ + 未认证响应方法 + :param msg: 可选,自定义未认证响应信息 + :param data: 可选,未认证响应结果中属性为data的值 + :param rows: 可选,未认证响应结果中属性为rows的值 + :param dict_content: 可选,dict类型,未认证响应结果中自定义属性的值 + :param model_content: 可选,BaseModel类型,未认证响应结果中自定义属性的值 + :return: 未认证响应结果 + """ + result = { + 'code': 401, + 'msg': msg + } + + if data is not None: + result['data'] = data + if rows is not None: + result['rows'] = rows + if dict_content is not None: + result.update(dict_content) + if model_content is not None: + result.update(model_content.model_dump(by_alias=True)) + + result.update({'success': False, 'time': datetime.now()}) + + return JSONResponse( + status_code=status.HTTP_200_OK, + content=jsonable_encoder(result) + ) + + @classmethod + def forbidden(cls, msg: str = '该用户无此接口权限', data: Optional[Any] = None, rows: Optional[Any] = None, + dict_content: Optional[Dict] = None, model_content: Optional[BaseModel] = None) -> Response: + """ + 未认证响应方法 + :param msg: 可选,自定义未认证响应信息 + :param data: 可选,未认证响应结果中属性为data的值 + :param rows: 可选,未认证响应结果中属性为rows的值 + :param dict_content: 可选,dict类型,未认证响应结果中自定义属性的值 + :param model_content: 可选,BaseModel类型,未认证响应结果中自定义属性的值 + :return: 未认证响应结果 + """ + result = { + 'code': 403, + 'msg': msg + } + + if data is not None: + result['data'] = data + if rows is not None: + result['rows'] = rows + if dict_content is not None: + result.update(dict_content) + if model_content is not None: + result.update(model_content.model_dump(by_alias=True)) + + result.update({'success': False, 'time': datetime.now()}) + + return JSONResponse( + status_code=status.HTTP_200_OK, + content=jsonable_encoder(result) + ) + + @classmethod + def error(cls, msg: str = '接口异常', data: Optional[Any] = None, rows: Optional[Any] = None, + dict_content: Optional[Dict] = None, model_content: Optional[BaseModel] = None) -> Response: + """ + 错误响应方法 + :param msg: 可选,自定义错误响应信息 + :param data: 可选,错误响应结果中属性为data的值 + :param rows: 可选,错误响应结果中属性为rows的值 + :param dict_content: 可选,dict类型,错误响应结果中自定义属性的值 + :param model_content: 可选,BaseModel类型,错误响应结果中自定义属性的值 + :return: 错误响应结果 + """ + result = { + 'code': 500, + 'msg': msg + } + + if data is not None: + result['data'] = data + if rows is not None: + result['rows'] = rows + if dict_content is not None: + result.update(dict_content) + if model_content is not None: + result.update(model_content.model_dump(by_alias=True)) + + result.update({'success': False, 'time': datetime.now()}) + + return JSONResponse( + status_code=status.HTTP_200_OK, + content=jsonable_encoder(result) + ) + + @classmethod + def streaming(cls, *, data: Any = None): + """ + 流式响应方法 + :param data: 流式传输的内容 + :return: 流式响应结果 + """ + return StreamingResponse( + status_code=status.HTTP_200_OK, + content=data + ) + + +def response_200(*, data: Any = None, message="获取成功") -> Response: + return JSONResponse( + status_code=status.HTTP_200_OK, + content=jsonable_encoder( + { + 'code': 200, + 'message': message, + 'data': data, + 'success': 'true', + 'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S") + } + ) + ) + + +def response_400(*, data: Any = None, message: str = "获取失败") -> Response: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content=jsonable_encoder( + { + 'code': 400, + 'message': message, + 'data': data, + 'success': 'false', + 'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S") + } + ) + ) + + +def response_401(*, data: Any = None, message: str = "获取失败") -> Response: + return JSONResponse( + status_code=status.HTTP_401_UNAUTHORIZED, + content=jsonable_encoder( + { + 'code': 401, + 'message': message, + 'data': data, + 'success': 'false', + 'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S") + } + ) + ) + + +def response_403(*, data: Any = None, message: str = "获取失败") -> Response: + return JSONResponse( + status_code=status.HTTP_403_FORBIDDEN, + content=jsonable_encoder( + { + 'code': 403, + 'message': message, + 'data': data, + 'success': 'false', + 'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S") + } + ) + ) + + +def response_500(*, data: Any = None, message: str = "接口异常") -> Response: + return JSONResponse( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + content=jsonable_encoder( + { + 'code': 500, + 'message': message, + 'data': data, + 'success': 'false', + 'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S") + } + ) + ) + + +def streaming_response_200(*, data: Any = None): + return StreamingResponse( + status_code=status.HTTP_200_OK, + content=data, + ) + + +class AuthException(Exception): + """ + 自定义令牌异常AuthException + """ + + def __init__(self, data: str = None, message: str = None): + self.data = data + self.message = message + + +class PermissionException(Exception): + """ + 自定义权限异常PermissionException + """ + + def __init__(self, data: str = None, message: str = None): + self.data = data + self.message = message + + +class LoginException(Exception): + """ + 自定义登录异常LoginException + """ + + def __init__(self, data: str = None, message: str = None): + self.data = data + self.message = message diff --git a/ruoyi-fastapi-backend/utils/time_format_util.py b/ruoyi-fastapi-backend/utils/time_format_util.py new file mode 100644 index 0000000..ba19489 --- /dev/null +++ b/ruoyi-fastapi-backend/utils/time_format_util.py @@ -0,0 +1,51 @@ +import datetime + + +def object_format_datetime(obj): + """ + :param obj: 输入一个对象 + :return:对目标对象所有datetime类型的属性格式化 + """ + for attr in dir(obj): + value = getattr(obj, attr) + if isinstance(value, datetime.datetime): + setattr(obj, attr, value.strftime('%Y-%m-%d %H:%M:%S')) + return obj + + +def list_format_datetime(lst): + """ + :param lst: 输入一个嵌套对象的列表 + :return: 对目标列表中所有对象的datetime类型的属性格式化 + """ + for obj in lst: + for attr in dir(obj): + value = getattr(obj, attr) + if isinstance(value, datetime.datetime): + setattr(obj, attr, value.strftime('%Y-%m-%d %H:%M:%S')) + return lst + + +def format_datetime_dict_list(dicts): + """ + 递归遍历嵌套字典,并将 datetime 值转换为字符串格式 + :param dicts: 输入一个嵌套字典的列表 + :return: 对目标列表中所有字典的datetime类型的属性格式化 + """ + result = [] + + for item in dicts: + new_item = {} + for k, v in item.items(): + if isinstance(v, dict): + # 递归遍历子字典 + new_item[k] = format_datetime_dict_list([v])[0] + elif isinstance(v, datetime.datetime): + # 如果值是 datetime 类型,则格式化为字符串 + new_item[k] = v.strftime('%Y-%m-%d %H:%M:%S') + else: + # 否则保留原始值 + new_item[k] = v + result.append(new_item) + + return result diff --git a/ruoyi-fastapi-frontend/.env.development b/ruoyi-fastapi-frontend/.env.development new file mode 100644 index 0000000..af9ba00 --- /dev/null +++ b/ruoyi-fastapi-frontend/.env.development @@ -0,0 +1,8 @@ +# 页面标题 +VITE_APP_TITLE = 若依管理系统 + +# 开发环境配置 +VITE_APP_ENV = 'development' + +# 若依管理系统/开发环境 +VITE_APP_BASE_API = '/dev-api' diff --git a/ruoyi-fastapi-frontend/.env.production b/ruoyi-fastapi-frontend/.env.production new file mode 100644 index 0000000..cbabf89 --- /dev/null +++ b/ruoyi-fastapi-frontend/.env.production @@ -0,0 +1,11 @@ +# 页面标题 +VITE_APP_TITLE = 若依管理系统 + +# 生产环境配置 +VITE_APP_ENV = 'production' + +# 若依管理系统/生产环境 +VITE_APP_BASE_API = '/prod-api' + +# 是否在打包时开启压缩,支持 gzip 和 brotli +VITE_BUILD_COMPRESS = gzip \ No newline at end of file diff --git a/ruoyi-fastapi-frontend/.env.staging b/ruoyi-fastapi-frontend/.env.staging new file mode 100644 index 0000000..b11336d --- /dev/null +++ b/ruoyi-fastapi-frontend/.env.staging @@ -0,0 +1,11 @@ +# 页面标题 +VITE_APP_TITLE = 若依管理系统 + +# 生产环境配置 +VITE_APP_ENV = 'staging' + +# 若依管理系统/生产环境 +VITE_APP_BASE_API = '/stage-api' + +# 是否在打包时开启压缩,支持 gzip 和 brotli +VITE_BUILD_COMPRESS = gzip \ No newline at end of file diff --git a/ruoyi-fastapi-frontend/.gitignore b/ruoyi-fastapi-frontend/.gitignore new file mode 100644 index 0000000..78a752d --- /dev/null +++ b/ruoyi-fastapi-frontend/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + +tests/**/coverage/ +tests/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock diff --git a/ruoyi-fastapi-frontend/LICENSE b/ruoyi-fastapi-frontend/LICENSE new file mode 100644 index 0000000..8564f29 --- /dev/null +++ b/ruoyi-fastapi-frontend/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018 RuoYi + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/ruoyi-fastapi-frontend/README.md b/ruoyi-fastapi-frontend/README.md new file mode 100644 index 0000000..d420cc3 --- /dev/null +++ b/ruoyi-fastapi-frontend/README.md @@ -0,0 +1,109 @@ +
+
+
![]() |
+ ![]() |
+
![]() |
+ ![]() |
+
![]() |
+ ![]() |
+
![]() |
+ ![]() |
+
![]() |
+ ![]() |
+
![]() |
+ ![]() |
+
![]() |
+ ![]() |
+
![]() |
+ ![]() |
+
您正在使用 Internet Explorer 的早期版本(IE11以下版本或使用该内核的浏览器)。这意味着在升级浏览器前,您将无法访问此网站。
+自 2016 年 1 月 12 日起,Microsoft 不再为 IE 11 以下版本提供相应支持和更新。没有关键的浏览器安全更新,您的电脑可能易受有害病毒、间谍软件和其他恶意软件的攻击,它们可以窃取或损害您的业务数据和信息。请参阅 微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明 。
+推荐使用以下浏览器的最新版本。如果您的电脑已有以下浏览器的最新版本则直接使用该浏览器访问即可。
+ +时间表达式
+{{item}} | +Cron 表达式 | + + +
+ {{crontabValueObj.second}}
+ |
+
+ {{crontabValueObj.min}}
+ |
+
+ {{crontabValueObj.hour}}
+ |
+
+ {{crontabValueObj.day}}
+ |
+
+ {{crontabValueObj.month}}
+ |
+
+ {{crontabValueObj.week}}
+ |
+
+ {{crontabValueObj.year}}
+ |
+
+ {{crontabValueString}}
+ |
+
+
---|
最近5次运行时间
++ 领取阿里云通用云产品1888优惠券 ++ +
+https://www.aliyun.com/minisite/goods?userCode=brki8iof +
+ 领取腾讯云通用云产品2860优惠券 +
+https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console +
+ 阿里云服务器折扣区 +>☛☛点我进入☚☚ + 腾讯云服务器秒杀区 +>☛☛点我进入☚☚
++ 云产品通用红包,可叠加官网常规优惠使用。(仅限新用户) +
+
+ 一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适自己的。于是利用空闲休息时间开始自己写一套后台系统。如此有了若依管理系统,她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA等等,当然,您也可以对她进行深度定制,以做出更强系统。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。 +
++ 当前版本: v{{ version }} +
+
+
+
Redis版本 |
+ {{ cache.info.redis_version }} |
+ 运行模式 |
+ {{ cache.info.redis_mode == "standalone" ? "单机" : "集群" }} |
+ 端口 |
+ {{ cache.info.tcp_port }} |
+ 客户端数 |
+ {{ cache.info.connected_clients }} |
+
运行时间(天) |
+ {{ cache.info.uptime_in_days }} |
+ 使用内存 |
+ {{ cache.info.used_memory_human }} |
+ 使用CPU |
+ {{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }} |
+ 内存配置 |
+ {{ cache.info.maxmemory_human }} |
+
AOF是否开启 |
+ {{ cache.info.aof_enabled == "0" ? "否" : "是" }} |
+ RDB是否成功 |
+ {{ cache.info.rdb_last_bgsave_status }} |
+ Key数量 |
+ {{ cache.dbSize }} |
+ 网络入口/出口 |
+ {{ cache.info.instantaneous_input_kbps }}kps/{{cache.info.instantaneous_output_kbps}}kps |
+
属性 |
+ 值 |
+
---|---|
核心数 |
+ {{ server.cpu.cpuNum }} |
+
用户使用率 |
+ {{ server.cpu.used }}% |
+
系统使用率 |
+ {{ server.cpu.sys }}% |
+
当前空闲率 |
+ {{ server.cpu.free }}% |
+
属性 |
+ 内存 |
+ JVM |
+
---|---|---|
总内存 |
+ {{ server.mem.total }}G |
+ {{ server.jvm.total }}M |
+
已用内存 |
+ {{ server.mem.used}}G |
+ {{ server.jvm.used}}M |
+
剩余内存 |
+ {{ server.mem.free }}G |
+ {{ server.jvm.free }}M |
+
使用率 |
+ {{ server.mem.usage }}% |
+ {{ server.jvm.usage }}% |
+
服务器名称 |
+ {{ server.sys.computerName }} |
+ 操作系统 |
+ {{ server.sys.osName }} |
+
服务器IP |
+ {{ server.sys.computerIp }} |
+ 系统架构 |
+ {{ server.sys.osArch }} |
+
Java名称 |
+ {{ server.jvm.name }} |
+ Java版本 |
+ {{ server.jvm.version }} |
+
启动时间 |
+ {{ server.jvm.startTime }} |
+ 运行时长 |
+ {{ server.jvm.runTime }} |
+
安装路径 |
+ {{ server.jvm.home }} |
+ ||
项目路径 |
+ {{ server.sys.userDir }} |
+ ||
运行参数 |
+ {{ server.jvm.inputArgs }} |
+
盘符路径 |
+ 文件系统 |
+ 盘符类型 |
+ 总大小 |
+ 可用大小 |
+ 已用大小 |
+ 已用百分比 |
+
---|---|---|---|---|---|---|
{{ sysFile.dirName }} |
+ {{ sysFile.sysTypeName }} |
+ {{ sysFile.typeName }} |
+ {{ sysFile.total }} |
+ {{ sysFile.free }} |
+ {{ sysFile.used }} |
+ {{ sysFile.usage }}% |
+
{{ value }}+