diff --git a/ruoyi-fastapi-backend b/ruoyi-fastapi-backend deleted file mode 160000 index 443d4d7..0000000 --- a/ruoyi-fastapi-backend +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 443d4d7159d02f696efe2b26808b582bf8738160 diff --git a/ruoyi-fastapi-backend/.env.dev b/ruoyi-fastapi-backend/.env.dev new file mode 100644 index 0000000..940e203 --- /dev/null +++ b/ruoyi-fastapi-backend/.env.dev @@ -0,0 +1,150 @@ +# -------- 应用配置 -------- +# 应用运行环境 +APP_ENV = 'dev' +# 应用名称 +APP_NAME = 'Kangda_robot' +# 应用代理路径 +APP_ROOT_PATH = '/dev-api' +# 应用主机 +APP_HOST = '0.0.0.0' +# 应用端口 +APP_PORT = 9099 +# 应用版本 +APP_VERSION= '1.6.2' +# 应用是否开启热重载 +APP_RELOAD = false +# 应用是否开启IP归属区域查询 +APP_IP_LOCATION_QUERY = true +# 应用是否允许账号同时登录 +APP_SAME_TIME_LOGIN = true + +APP_DOCS_URL = +APP_REDOC_URL = +APP_OPENAPI_URL = + + +# -------- Jwt配置 -------- +# Jwt秘钥 +JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55' +# Jwt算法 +JWT_ALGORITHM = 'HS256' +# 令牌过期时间 +JWT_EXPIRE_MINUTES = 43200 +# redis中令牌过期时间 +JWT_REDIS_EXPIRE_MINUTES = 43100 + + +# -------- 数据库配置 -------- +# 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql' +DB_TYPE = 'mysql' +# 数据库主机 +DB_HOST = '10.0.0.17' +# 数据库端口 +DB_PORT = 3306 +# 数据库用户名 +DB_USERNAME = 'root' +# 数据库密码 +DB_PASSWORD = 'root' +# 数据库名称 +DB_DATABASE = 'ruoyi-fastapi' +# 是否开启sqlalchemy日志 +DB_ECHO = true +# 允许溢出连接池大小的最大连接数 +DB_MAX_OVERFLOW = 10 +# 连接池大小,0表示连接数无限制 +DB_POOL_SIZE = 50 +# 连接回收时间(单位:秒) +DB_POOL_RECYCLE = 3600 +# 连接池中没有线程可用时,最多等待的时间(单位:秒) +DB_POOL_TIMEOUT = 30 + +# -------- Redis配置 -------- +# Redis主机 +REDIS_HOST = '10.0.0.58' +# Redis端口 +REDIS_PORT = 6379 +# Redis用户名 +REDIS_USERNAME = '' +# Redis密码 +REDIS_PASSWORD = '' +# Redis数据库 +REDIS_DATABASE = 3 + + +#------------------海康平台配置------------------ +HAIKANG_URL = 'https://192.168.89.230' +HAIKANG_PORT = 443 + +HAIKANG_AK = '29862915' +HAIKANG_SK = '3QlsmcyadBqu4OpVoxsJ' +HAIKANG_ACCESS_TOKEN_URL = '/api/v1/oauth/token' +# HAIKANG_DOOR_STATES_URL = '/api/v1/door/states' +HAIKANG_DOOR_STATES_URL = '/api/acs/v1/door/states' +HAIKANG_DOOR_DOCONTROL_URL = '/api/acs/v1/door/doControl' +HAIKANG_DOOR_ENVENTS_URL = '/api/acs/v2/door/events' +HAIKANG_DOOR_SEARCH = '/api/resource/v2/door/search' +HAIKANG_DOOR_ONLINE_STATUS = '/api/nms/v1/online/acs_device/get' +HAIKANG_APPLICATION_ONETOMANY_URL = '/api/frs/v1/application/oneToMany' +HAIKANG_PICTURE_CHECK_URL = '/api/frs/v1/face/picture/check' +HAIKANG_FACECAPATURE_SEARCH = '/api/frs/v1/event/face_capture/search' +HAIKANG_FACE_GROUP_URL = '/api/frs/v1/face/group' + +HAIKANG_VISITOR_RECORD_SEARCH = '/api/visitor/v2/appointment/records' +HAIKANG_VISITOR_RECORD_PICTURES = '/api/visitor/v1/record/pictures' + +HAIKANG_PERSON_LIST = '/api/resource/v2/person/personList' +HAIKANG_GET_PERSON_PICTURE = '/api/resource/v1/person/picture' + +HAIKANG_FACE_GROUP_ADDITION = '/api/frs/v1/face/group/single/addition' + +HAIKANG_FACE_SIGLE_ADDITION = '/api/frs/v1/face/single/addition' +HAIKANG_FACE_DELETE = '/api/frs/v1/face/deletion' + +HAIKANG_FACE_SAVE_PATH = "./face_images" + +HAIKANG_VISITOR_PICTURES_SAVE_PATH = "./visitor/face_images/" +HAIKANG_VISITOR_RECORD_TXT = "./visitor/visitorIds.txt" +#---------------------------------海康平台配置end---------------------------------- + + +# # 人脸分组1vN检索 +# HAIKANG_APPLICATION_ONETOMANY_URL = '/api/frs/v1/application/oneToMany' + +# # 人脸评分 +# HAIKANG_PICTURE_CHECK_URL = '/api/frs/v1/face/picture/check' + +# # 按条件查询人脸分组 +# HAIKANG_FACE_GROUP_URL = '/api/frs/v1/face/group' + +# # 查询人脸识别事件 +# HAIKANG_FACECAPATURE_SEARCH = '/api/frs/v1/event/face_capture/search' + +# # 查询访客预约记录 +# HAIKANG_VISITOR_RECORD_SEARCH = '/api/visitor/v2/appointment/records' + + + +#-------------------RAGFlow配置--------------------- + +RAGFLOW_BASE_URL = "http://10.0.0.202:82" +RAGFLOW_API_KEY = "ragflow-hlMjRmNzE2ODNiNTExZjA4ZTNlMDI0Mm" + +#-------------------RAGFlow配置end------------------ + +#-------------------compreface配置------------------- +COMPREFACE_BASE_URL = "http://10.0.0.202" +COMPERFACE_BASE_PORT = "8000" +COMPREFACE_API_KEY = "a5924457-62c9-47dc-a6e7-15462c502d2c" +COMPREFACE_API_KEY_DETECTION = "070283a2-faa3-423b-9772-2cd48ecc5362" +COMPREFACE_FACE_FILE = "./image_face" +COMPREFACE_SIMILARITY_THRESHOLD = 0.995 +#-------------------compreface配置end------------------- + +#-------------------天气服务配置------------------- +WEATHER_API_BASE = 'https://api.openweathermap.org/data/2.5' +WEATHER_API_KEY = '' +WEATHER_DEFAULT_CITY = '北京' +WEATHER_UNITS = 'metric' +WEATHER_LANG = 'zh_cn' +WEATHER_CACHE_TTL = 600 +#-------------------天气服务配置end------------------- \ No newline at end of file diff --git a/ruoyi-fastapi-backend/.env.prod b/ruoyi-fastapi-backend/.env.prod new file mode 100644 index 0000000..28c8919 --- /dev/null +++ b/ruoyi-fastapi-backend/.env.prod @@ -0,0 +1,75 @@ +# -------- 应用配置 -------- +# 应用运行环境 +APP_ENV = 'prod' +# 应用名称 +APP_NAME = 'RuoYi-FastAPI' +# 应用代理路径 +APP_ROOT_PATH = '/prod-api' +# 应用主机 +APP_HOST = '0.0.0.0' +# 应用端口 +APP_PORT = 9099 +# 应用版本 +APP_VERSION= '1.6.2' +# 应用是否开启热重载 +APP_RELOAD = false +# 应用是否开启IP归属区域查询 +APP_IP_LOCATION_QUERY = true +# 应用是否允许账号同时登录 +APP_SAME_TIME_LOGIN = true + +# -------- Jwt配置 -------- +# Jwt秘钥 +JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55' +# Jwt算法 +JWT_ALGORITHM = 'HS256' +# 令牌过期时间 +JWT_EXPIRE_MINUTES = 1440 +# redis中令牌过期时间 +JWT_REDIS_EXPIRE_MINUTES = 30 + + +# -------- 数据库配置 -------- +# 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql' +DB_TYPE = 'mysql' +# 数据库主机 +DB_HOST = '127.0.0.1' +# 数据库端口 +DB_PORT = 3306 +# 数据库用户名 +DB_USERNAME = 'root' +# 数据库密码 +DB_PASSWORD = 'root' +# 数据库名称 +DB_DATABASE = 'ruoyi-fastapi' +# 是否开启sqlalchemy日志 +DB_ECHO = true +# 允许溢出连接池大小的最大连接数 +DB_MAX_OVERFLOW = 10 +# 连接池大小,0表示连接数无限制 +DB_POOL_SIZE = 50 +# 连接回收时间(单位:秒) +DB_POOL_RECYCLE = 3600 +# 连接池中没有线程可用时,最多等待的时间(单位:秒) +DB_POOL_TIMEOUT = 30 + +# -------- Redis配置 -------- +# Redis主机 +REDIS_HOST = '127.0.0.1' +# Redis端口 +REDIS_PORT = 6379 +# Redis用户名 +REDIS_USERNAME = '' +# Redis密码 +REDIS_PASSWORD = '' +# Redis数据库 +REDIS_DATABASE = 2 + +#-------------------天气服务配置------------------- +WEATHER_API_BASE = 'https://api.openweathermap.org/data/2.5' +WEATHER_API_KEY = '' +WEATHER_DEFAULT_CITY = '北京' +WEATHER_UNITS = 'metric' +WEATHER_LANG = 'zh_cn' +WEATHER_CACHE_TTL = 600 +#-------------------天气服务配置end------------------- \ No newline at end of file diff --git a/ruoyi-fastapi-backend/.gitignore b/ruoyi-fastapi-backend/.gitignore new file mode 100644 index 0000000..5686ba6 --- /dev/null +++ b/ruoyi-fastapi-backend/.gitignore @@ -0,0 +1,145 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +vf_admin/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +.idea/ + +# VSCode +.vscode/ + diff --git a/ruoyi-fastapi-backend/001测试海康api.py b/ruoyi-fastapi-backend/001测试海康api.py new file mode 100644 index 0000000..9840850 --- /dev/null +++ b/ruoyi-fastapi-backend/001测试海康api.py @@ -0,0 +1,96 @@ +from utils.haikang_util import HaikangUtil +import asyncio +import base64 + + + + + + +# 查询门禁点列表 +async def get_door_list_service(pageNo: int = 1, pageSize: int = 10): + result = await HaikangUtil.get_door_list_v2(pageNo, pageSize) + print(result) + +# 查询门禁状态 +async def get_door_status_service(door_index_codes): + result = await HaikangUtil.get_door_status(door_index_codes) + print(result) + +# 门禁控制 +async def door_do_control_service(door_index_codes, control_type): + result = await HaikangUtil.door_do_control(door_index_codes, control_type) + print(result) + +# 查询门禁点事件 +async def query_door_events_service(door_index_code,pageNo, pageSize, startTime, endTime): + result = await HaikangUtil.query_door_events_v2(door_index_code, pageNo=pageNo, pageSize=pageSize ,startTime=startTime, endTime=endTime) + print(result) + +# 查看门禁点在线状态 +async def door_online_status_service(door_index_codes): + result = await HaikangUtil.door_online_status(door_index_codes) + print(result) + +# 按条件查询人脸分组, 很重要 +async def get_face_group_service(): + result = await HaikangUtil.get_face_group() + print(result) + +# 人脸分组1vN搜索 +async def face_group_1vN_search_service(image_path): + + with open(image_path, 'rb') as f: + image_data = f.read() + + encoded_image = base64.b64encode(image_data).decode('utf-8') + + result = await HaikangUtil.face_group_1vN_search( + facePicBinaryData=encoded_image, + pageNo=1, + pageSize=10, + searchNum=99, + minSimilarity=50, + faceGroupIndexCodes=['5dc82633-a4cb-4107-b55e-f21bf952f9'] + ) + print(result) + +# 人脸评分 +async def face_picture_check(image_path): + + with open(image_path, 'rb') as f: + image_data = f.read() + + encoded_image = base64.b64encode(image_data).decode('utf-8') + + + result = await HaikangUtil.face_picture_check( + facePicBinaryData=encoded_image + ) + print(result) + +# 查询访客预约记录 +async def query_visitor_record(): + result = await HaikangUtil.query_visitor_record() + print(result) + +if __name__ == '__main__': + # asyncio.run(get_door_list_service()) + # print("*"*100) + # asyncio.run(get_door_status_service(['D01'])) + # print("*"*100) + + # asyncio.run(door_do_control_service(['D01'], 1)) + + # asyncio.run(query_door_events_service('D01',1,10,1640995200,1640995200)) + + # asyncio.run(get_face_group_service()) + + # image_path = "75c03e462769c81b6a8513d90ff2a27d.jpg" + # asyncio.run(face_group_1vN_search_service(image_path)) + # asyncio.run(face_picture_check(image_path)) + + # asyncio.run(query_visitor_record()) + + asyncio.run(door_online_status_service(["xxxxxxxx"])) + diff --git a/ruoyi-fastapi-backend/002简单http服务.py b/ruoyi-fastapi-backend/002简单http服务.py new file mode 100644 index 0000000..a429e5d --- /dev/null +++ b/ruoyi-fastapi-backend/002简单http服务.py @@ -0,0 +1,536 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from http.server import HTTPServer, BaseHTTPRequestHandler +import json +import urllib.parse +import logging + +# 配置日志 +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class CustomHTTPRequestHandler(BaseHTTPRequestHandler): + """自定义HTTP请求处理器""" + + def do_GET(self): + """处理GET请求""" + # 解析URL和查询参数 + parsed_path = urllib.parse.urlparse(self.path) + query_params = urllib.parse.parse_qs(parsed_path.query) + + logger.info(f"GET 请求: {self.path}") + + # 根据路径返回不同的响应 + if parsed_path.path == "/": + self._send_response(200, "text/html", self._get_home_page()) + elif parsed_path.path == "/api/user": + self._handle_user_api(query_params) + elif parsed_path.path == "/api/status": + self._send_json_response(200, {"status": "ok", "message": "服务运行正常"}) + elif parsed_path.path == "/api/time": + import datetime + + current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + self._send_json_response(200, {"time": current_time}) + else: + self._send_json_response(404, {"error": "页面未找到"}) + + def do_POST(self): + """处理POST请求""" + content_length = int(self.headers.get("Content-Length", 0)) + post_data = self.rfile.read(content_length) + + logger.info(f"POST 请求: {self.path}") + + try: + # 尝试解析JSON数据 + if content_length == 0: + logger.info("POST 请求数据为空") + elif self.headers.get("Content-Type") == "application/json": + data = json.loads(post_data.decode("utf-8")) + logger.info(f"接收到JSON数据: {data}") + else: + # 解析表单数据 + data = urllib.parse.parse_qs(post_data.decode("utf-8")) + logger.info(f"接收到表单数据: {data}") + + # print("x"*100) + # 根据路径处理不同的POST请求 + # if self.path == '/api/login': + # self._handle_login(data) + # elif self.path == '/api/echo': + # self._send_json_response(200, {'echo': data, 'message': '数据已接收'}) + # else: + # self._send_json_response(404, {'error': '接口未找到'}) + if self.path == "/api/resource/v2/door/search": + self._send_json_response( + 200, + { + "code": "0", + "msg": "SUCCESS", + "data": { + "total": 3, + "pageNo": 1, + "pageSize": 1, + "list": [ + { + "indexCode": "df8w8cr800283c24c", + "resourceType": "door", + "name": "资源 1", + "doorNo": "123", + "channelNo": "1", + "parentIndexCode": "80d9099q9e991231", + "controlOneId": "11111111", + "controlTwoId": "2222222222", + "readerInId": "ac789y2c0019c", + "readerOutId": "arcew78c710", + "doorSerial": 1, + "treatyType": "hiksdk_net", + "regionIndexCode": "d8a5476e-25c0-4aa2-b7e3-db3788ba1f77", + "regionPath": "@root000000@", + "createTime": "2018-11-28T16:47:27:358+08:00", + "updateTime": "2018-11-28T16:48:34:011+08:00", + "description": "Test", + "channelType": "door", + "regionName": "acs_setUp_42054", + "regionPathName": "@root000000@9ca1eef0-4579-4e7e-a601-caf486442d54@", + "installLocation": "位置 1", + } + ], + }, + }, + ) + elif self.path == "/api/v1/door/states": + self._send_json_response( + 200, + { + "code": "0", + "msg": "success", + "data": { + "authDoorList": [ + { + "doorIndexCode": "e8e3ef5c149243abb4341124ab38fcfc", + "doorState": 0, + } + ], + "noAuthDoorIndexCodeList": [ + "e8e3ef5c149243abb4341124ab38fcfc" + ], + }, + }, + ) + elif self.path == "/api/acs/v1/door/doControl": + self._send_json_response( + 200, + { + "code": "0", + "msg": "success", + "data": [ + { + "doorIndexCode": "2c95c028a809448f962a969e3ab34f", + "controlResultCode": 0, # 0表示反控成功, 其他表示失败 + "controlResultDesc": "success", + } + ], + }, + ) + elif self.path == "/api/acs/v2/door/events": + self._send_json_response( + 200, + { + "code": "0", + "msg": "success", + "data": { + "total": 1, + "totalPage": 1, + "pageNo": 1, + "pageSize": 100, + "list": [ + { + "eventId": "207dd3b1-37a7-4d6c-8e4d-c8bfd343051b", + "eventName": "acs.acs.eventType.successCard", + "eventTime": "2019-11-16T15:44:33+08:00", + "personId": "216e2ba145824269a1cbb423cdc85cb1", + "cardNo": "3891192334", + "personName": "sdk 人员 1zzzcb", + "orgIndexCode": "root000000", + "orgName": "默认组织", + "doorName": "10.40.239.69new_test2_门_1", + "doorIndexCode": "f0b50050d3434f15b4e34f885d5dacfe", + "doorRegionIndexCode": "fd2df06b-1afb-4c9b-b058-5740c2c00076", + "picUri": "no-pcnvr", + "svrIndexCode": "/pic?=d62i7f6e*6a7i125-c838b9--a8c67dea96e65icb1*=sd*=5dpi*=1dpi*m2i1t=4ed35444bb4s=-39", + "eventType": 198914, + "inAndOutType": 1, + "readerDevIndexCode": "378e563bf3e84d5ba6ef5742bbaa8933", + "readerDevName": "读卡器_1", + "devIndexCode": "dcff422aad9c4d60a47b8b2fe2757b71", + "devName": "10.40.239.69new_test2", + "identityCardUri": "/pic?=d62i7f6e*6a7i125-c838b9--a8c67dea96e65icb1*=sd*=5dpi*=1dpi*m2i1t=4ed35444bb4s=-39z422d3", + "receiveTime": "2019-11-16T15:45:13.525+08:00", + "jobNo": "23333", + "studentId": "201900001", + "certNo": "320826199012110005", + } + ], + }, + }, + ) + elif self.path == "/api/frs/v1/face/group": + self._send_json_response( + 200, + { + "code": "0", + "msg": "Success", + "data": [ + { + "indexCode": "5dc82633-a4cb-4107-b55e-f21bf952f9", + "name": "仓库值守人员", + "description": "仓库值守人员是指守着仓库的人", + } + ], + }, + ) + elif self.path == "/api/frs/v1/application/oneToMany": + self._send_json_response( + 200, + { + "code": "0", + "msg": "Success.", + "data": { + "total": 500, + "pageNo": 1, + "pageSize": 10, + "list": [ + { + "similarity": 80, + "indexCode": "7cc0adb2-a3c3-48fd-b432-718103e85c28", + "faceInfo": { + "name": "张三", + "sex": "1", + "certificateType": "111", + "certificateNum": "420204199605121656", + }, + "facePic": { + "faceUrl": "http://10.166.165.121:8080/frs/facepicturetemp/test.jpg" + }, + } + ], + }, + }, + ) + elif self.path == "/api/frs/v1/face/picture/check": + self._send_json_response( + 200, + { + "code": "0", + "msg": "Success", + "data": { + "checkResult": True, + "faceScore": 90, + "facePicAnalysisResult": { + "id": 5566, + "age": 16, + "ageRange": 1, + "ageGroup": "TEENAGER", + "": "male", + "glasses": "NO", + "smile": "NO", + "facePose": { + "pitch": 45, + "yaw": 25, + "roll": 10, + "clearityScore": 0.5, + "colorConfidence": 0.5, + "eyeDistance": 300, + "grayMean": 120, + "visibleScore": 0.5, + }, + "targetModelData": "DD", + "faceRect": { + "height": 12.1, + "width": 16, + "x": 15, + "y": 3, + }, + "recommendFaceRect": { + "height": 4, + "width": 6, + "x": 2, + "y": 1, + }, + "faceMark": { + "leftEye": {"x": 33, "y": 22}, + "rightEye": {"x": 44, "y": 33}, + "noseTip": {"x": 43, "y": 12}, + "leftMouth": {"x": 32, "y": 54}, + "rightMouth": {"x": 67, "y": 12}, + }, + "mask": "NO", + "faceScore": 90, + }, + }, + }, + ) + elif self.path == "/api/visitor/v2/appointment/records": + self._send_json_response( + 200, + { + "code": "0", + "msg": "success", + "data": { + "total": 1, + "pageNo": 1, + "pageSize": 20, + "list": [ + { + "appointRecordId": "321654987", + "receptionistId": "3124126241412", + "receptionistName": "王五", + "receptionistCode": "323JH234KJH23", + "visitStartTime": "2018-07-26T15:00:00 + 08:00", + "visitEndTime": "2018-07-26T19:00:00 + 08:00", + "visitPurpose": "参考", + "visitorName": "张三", + "visitorId": "ASDF454SDAF565613JHU7712332", + "verificationCode": "1234", + "QRCode": "2015468421", + "": 1, + "phoneNo": "13576361254", + "plateNo": "浙 A12345", + "certificateType": 111, + "certificateNo": "311256196602145692", + "picUri": "/pic?adsdqwe21-asafdd-12sfsdfsdf", + "svrIndexCode": "sadsa123-asd21edsfhgsd-23rfdvsr", + "visitorStatus": 1, + "certAddr": "杭州滨江", + "certIssuer": "滨江分局", + "nation": 1, + "birthplace": "杭州", + "visitorWorkUnit": "中国工商银行", + "visitorAddress": "杭州滨江", + "orderId": "d089ady8a0dud87018d0y90ay9d901", + "designatedResources": { + "paramKey": "1", + "paramValues": ["52v72v35762587n75b26"], + }, + "privilegeGroupNames": ["one"], + "identityUri": "/pic?123-scccdf334-3216516516516", + "identitySvrCode": "12ddf53ggg56sss6554", + } + ], + }, + }, + ) + elif self.path == "/api/nms/v1/online/acs_device/get": + self._send_json_response( + 200, + { + "code": "0", + "msg": "success", + "data": { + "pageNo": 1, + "pageSize": 10, + "totalPage": 0, + "total": 1, + "list": [ + { + "deviceType": "HIK%2FDS-9116HW-ST%2F-AF-DVR", + "deviceIndexCode": "null", + "regionIndexCode": "ce91c758-5af4-4539-845a", + "collectTime": "2018-12-28T10:21:40.000+08:00", + "regionName": "NMS 自动化", + "indexCode": "82896441ced946d5a51c6d6ca8e65851", + "cn": "Onvif-IPC(10.67.172.13 )", + "treatyType": "onvif_net", + "manufacturer": "hikvision", + "ip": "10.67.172.13", + "port": 80, + "online": 1, + } + ], + }, + }, + ) + except json.JSONDecodeError: + self._send_json_response(400, {"error": "无效的JSON数据"}) + except Exception as e: + self._send_json_response(500, {"code": 0, "error": f"服务器错误: {str(e)}"}) + + def do_PUT(self): + """处理PUT请求""" + content_length = int(self.headers.get("Content-Length", 0)) + put_data = self.rfile.read(content_length) + + logger.info(f"PUT 请求: {self.path}") + + try: + data = json.loads(put_data.decode("utf-8")) + self._send_json_response(200, {"message": "数据已更新", "data": data}) + except json.JSONDecodeError: + self._send_json_response(400, {"error": "无效的JSON数据"}) + + def do_DELETE(self): + """处理DELETE请求""" + logger.info(f"DELETE 请求: {self.path}") + self._send_json_response(200, {"message": "删除成功", "path": self.path}) + + def _handle_user_api(self, query_params): + """处理用户API请求""" + user_id = query_params.get("id", [""])[0] + if user_id: + user_data = { + "id": user_id, + "name": f"用户{user_id}", + "email": f"user{user_id}@example.com", + "status": "active", + } + self._send_json_response(200, user_data) + else: + # 返回用户列表 + users = [ + {"id": "1", "name": "张三", "email": "zhangsan@example.com"}, + {"id": "2", "name": "李四", "email": "lisi@example.com"}, + {"id": "3", "name": "王五", "email": "wangwu@example.com"}, + ] + self._send_json_response(200, {"users": users}) + + def _handle_login(self, data): + """处理登录请求""" + username = ( + data.get("username", [""])[0] + if isinstance(data, dict) + else data.get("username", "") + ) + password = ( + data.get("password", [""])[0] + if isinstance(data, dict) + else data.get("password", "") + ) + + # 简单的用户验证(仅做演示) + if username == "admin" and password == "123456": + response_data = { + "success": True, + "message": "登录成功", + "token": "fake_jwt_token_here", + "user": {"username": username, "role": "admin"}, + } + self._send_json_response(200, response_data) + else: + self._send_json_response( + 401, {"success": False, "message": "用户名或密码错误"} + ) + + def _send_response(self, status_code, content_type, content): + """发送HTTP响应""" + self.send_response(status_code) + self.send_header("Content-Type", f"{content_type}; charset=utf-8") + self.send_header("Access-Control-Allow-Origin", "*") # 允许跨域 + self.send_header( + "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" + ) + self.send_header("Access-Control-Allow-Headers", "Content-Type") + self.end_headers() + self.wfile.write(content.encode("utf-8")) + + def _send_json_response(self, status_code, data): + """发送JSON响应""" + json_data = json.dumps(data, ensure_ascii=False, indent=2) + self._send_response(status_code, "application/json", json_data) + + def _get_home_page(self): + """获取首页HTML""" + return """ + + +
+ + +服务运行成功!以下是可用的API接口:
+ +
+# 获取状态
+curl http://localhost:8080/api/status
+
+# 用户登录
+curl -X POST -H "Content-Type: application/json" \\
+ -d '{"username":"admin","password":"123456"}' \\
+ http://localhost:8080/api/login
+
+# 获取用户信息
+curl http://localhost:8080/api/user?id=1
+
+
+
+ """
+
+ def do_OPTIONS(self):
+ """处理预检请求(CORS)"""
+ self.send_response(200)
+ self.send_header("Access-Control-Allow-Origin", "*")
+ self.send_header(
+ "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"
+ )
+ self.send_header("Access-Control-Allow-Headers", "Content-Type")
+ self.end_headers()
+
+ def log_message(self, format, *args):
+ """自定义日志格式"""
+ logger.info(f"{self.address_string()} - {format % args}")
+
+
+def run_server(host="localhost", port=8080):
+ """启动HTTP服务器"""
+ server_address = (host, port)
+ httpd = HTTPServer(server_address, CustomHTTPRequestHandler)
+
+ print(f"🌟 HTTP服务器启动成功!")
+ print(f"📍 地址: http://{host}:{port}")
+ print(f"🔗 在浏览器中访问: http://{host}:{port}")
+ print(f"⏹️ 按 Ctrl+C 停止服务器\n")
+
+ try:
+ httpd.serve_forever()
+ except KeyboardInterrupt:
+ print("\n🛑 服务器已停止")
+ httpd.server_close()
+
+
+if __name__ == "__main__":
+ # 启动服务器
+ run_server(host="10.0.0.202", port=9909)
diff --git a/ruoyi-fastapi-backend/003测试海康url.py b/ruoyi-fastapi-backend/003测试海康url.py
new file mode 100644
index 0000000..be27311
--- /dev/null
+++ b/ruoyi-fastapi-backend/003测试海康url.py
@@ -0,0 +1,11 @@
+from urllib.parse import urlparse
+
+url = "https://192.168.10.251:8001/artemis/test_v1"
+
+parsed = urlparse(url)
+
+path_and_query = parsed.path
+if parsed.query:
+ path_and_query += "?" + parsed.query
+
+print(path_and_query)
\ No newline at end of file
diff --git a/ruoyi-fastapi-backend/004测试compreface方法.py b/ruoyi-fastapi-backend/004测试compreface方法.py
new file mode 100644
index 0000000..aba0084
--- /dev/null
+++ b/ruoyi-fastapi-backend/004测试compreface方法.py
@@ -0,0 +1,57 @@
+from utils.compreface_util import ComprefaceUtil
+import asyncio
+import time
+import json
+
+image_path = "./haotian1.jpg"
+
+with open(image_path, "rb") as f:
+ image_bytes = f.read() # 获取图片的字节流
+
+# # 测试上传人脸图片
+# print(asyncio.run(ComprefaceUtil.face_addition(image_path, '刘昊天_访客')))
+
+# #-----------------------------------------测试人脸识别--------------------------------------------
+# start_time = time.time()
+# result = asyncio.run(
+# ComprefaceUtil.face_recognition(
+# image_bytes,
+# options={
+# "limit": 0,
+# "det_prob_threshold": 0.8,
+# "prediction_count": 1,
+# # 可选参数 age,gender,landmarks,calculator
+# "face_plugins": "gender",
+# "status": "true",
+# },
+# )
+# )
+# print(result)
+# with open("compreface_face_recognition.json", "w", encoding="utf-8") as f:
+# f.write(json.dumps(result, ensure_ascii=False, indent=4))
+
+# print("spend time:", time.time() - start_time)
+
+# #-----------------------------------------测试人脸识别end------------------------------------------
+
+# -----------------------------------------测试人脸检测--------------------------------------------
+start_time = time.time()
+result = asyncio.run(
+ ComprefaceUtil.face_detection(
+ image_bytes,
+ options={
+ "limit": 0,
+ "det_prob_threshold": 0.8,
+ "prediction_count": 1,
+ # 可选参数 age,gender,landmarks,calculator
+ "face_plugins": "pose",
+ "status": "false",
+ }
+ )
+)
+print(result)
+with open("compreface_face_detection.json", "w", encoding="utf-8") as f:
+ f.write(json.dumps(result, ensure_ascii=False, indent=4))
+
+print("spend time:", time.time() - start_time)
+# -----------------------------------------测试人脸检测end-----------------------------------------
diff --git a/ruoyi-fastapi-backend/005测试compreface人脸识别服务.py b/ruoyi-fastapi-backend/005测试compreface人脸识别服务.py
new file mode 100644
index 0000000..195a2c9
--- /dev/null
+++ b/ruoyi-fastapi-backend/005测试compreface人脸识别服务.py
@@ -0,0 +1,56 @@
+import requests
+from compreface import CompreFace
+from compreface.service import RecognitionService, DetectionService
+from compreface.collections import FaceCollection
+from compreface.collections.face_collections import Subjects
+import time
+
+DOMAIN: str = 'http://localhost'
+PORT: str = '8000'
+
+def face_recognition_stream(image_path, url):
+ with open(image_path, 'rb') as f:
+ response = requests.post(url, data=f.read())
+ return response.json()
+
+def face_recognition_file(image_path, url):
+
+ files = {"file": ("test.jpg", open(image_path, "rb"), "image/jpeg")}
+ response = requests.post(url, files=files)
+ return response.json()
+def face_recognition_compreface(image_path):
+
+
+ API_KEY: str = 'a5924457-62c9-47dc-a6e7-15462c502d2c'
+
+ compre_face: CompreFace = CompreFace(DOMAIN, PORT)
+
+ recognition: RecognitionService = compre_face.init_face_recognition(API_KEY)
+
+ start_time = time.time()
+ result = recognition.recognize(image_path=image_path)
+ print("--- %s seconds ---" % (time.time() - start_time))
+ print(result)
+
+def face_detection_compreface(image_path):
+
+
+ API_KEY: str = '070283a2-faa3-423b-9772-2cd48ecc5362'
+
+ compre_face: CompreFace = CompreFace(DOMAIN, PORT)
+
+ detection: DetectionService = compre_face.init_face_detection(API_KEY)
+
+ start_time = time.time()
+ result = detection.detect(image_path=image_path)
+ print("--- %s seconds ---" % (time.time() - start_time))
+ print(result)
+
+
+if __name__ == '__main__':
+ image_path = './moshengren.jpg'
+ url = 'http://10.0.0.202:9099/system/compreface/face_recognition'
+ # print(face_recognition_stream(image_path, url))
+ # print(face_recognition_file(image_path, url))
+ # face_recognition_compreface(image_path)
+ face_detection_compreface(image_path)
\ No newline at end of file
diff --git a/ruoyi-fastapi-backend/006测试定时任务.py b/ruoyi-fastapi-backend/006测试定时任务.py
new file mode 100644
index 0000000..1bd35ad
--- /dev/null
+++ b/ruoyi-fastapi-backend/006测试定时任务.py
@@ -0,0 +1,25 @@
+from apscheduler.schedulers.blocking import BlockingScheduler
+from apscheduler.triggers.cron import CronTrigger
+import datetime
+import time
+
+def my_job():
+ print("任务执行时间:", datetime.datetime.now())
+
+if __name__ == '__main__':
+ scheduler = BlockingScheduler(timezone="Asia/Shanghai")
+
+ # # 每天凌晨 1 点执行
+ # scheduler.add_job(my_job, trigger="cron", hour=1, minute=0)
+
+ # 或者用 CronTrigger
+ trigger = CronTrigger(minute='*', second=0)
+ scheduler.add_job(my_job, trigger=trigger)
+
+ scheduler.start()
+
+ try:
+ while True:
+ time.sleep(1)
+ except (KeyboardInterrupt, SystemExit):
+ scheduler.shutdown()
\ No newline at end of file
diff --git a/ruoyi-fastapi-backend/007测试python文件.py b/ruoyi-fastapi-backend/007测试python文件.py
new file mode 100644
index 0000000..9b53e56
--- /dev/null
+++ b/ruoyi-fastapi-backend/007测试python文件.py
@@ -0,0 +1,5 @@
+import os
+
+txt_path = "./test/image_base64.txt"
+print(os.path.dirname(txt_path))
+# os.makedirs(, exist_ok=True)
\ No newline at end of file
diff --git a/ruoyi-fastapi-backend/75c03e462769c81b6a8513d90ff2a27d.jpg b/ruoyi-fastapi-backend/75c03e462769c81b6a8513d90ff2a27d.jpg
new file mode 100644
index 0000000..94a8bd4
Binary files /dev/null and b/ruoyi-fastapi-backend/75c03e462769c81b6a8513d90ff2a27d.jpg differ
diff --git a/ruoyi-fastapi-backend/app.py b/ruoyi-fastapi-backend/app.py
new file mode 100644
index 0000000..1ee7695
--- /dev/null
+++ b/ruoyi-fastapi-backend/app.py
@@ -0,0 +1,12 @@
+import uvicorn
+from server import app, AppConfig # noqa: F401
+
+
+if __name__ == '__main__':
+ uvicorn.run(
+ app='app:app',
+ host=AppConfig.app_host,
+ port=AppConfig.app_port,
+ root_path=AppConfig.app_root_path,
+ reload=AppConfig.app_reload,
+ )
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/compreface_face_detection.json b/ruoyi-fastapi-backend/compreface_face_detection.json
new file mode 100644
index 0000000..58b1125
--- /dev/null
+++ b/ruoyi-fastapi-backend/compreface_face_detection.json
@@ -0,0 +1,18 @@
+{
+ "result": [
+ {
+ "pose": {
+ "pitch": -13.898311223378556,
+ "roll": 0.8241647740114217,
+ "yaw": 0.30893387084981927
+ },
+ "box": {
+ "probability": 0.9989770650863647,
+ "x_max": 874,
+ "y_max": 1127,
+ "x_min": 375,
+ "y_min": 418
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ruoyi-fastapi-backend/compreface_face_recognition.json b/ruoyi-fastapi-backend/compreface_face_recognition.json
new file mode 100644
index 0000000..aed763c
--- /dev/null
+++ b/ruoyi-fastapi-backend/compreface_face_recognition.json
@@ -0,0 +1,33 @@
+{
+ "result": [
+ {
+ "gender": {
+ "probability": 1.0,
+ "value": "male"
+ },
+ "box": {
+ "probability": 0.99898,
+ "x_max": 874,
+ "y_max": 1127,
+ "x_min": 375,
+ "y_min": 418
+ },
+ "subjects": [
+ {
+ "subject": "刘昊天_访客",
+ "similarity": 0.99991
+ }
+ ],
+ "execution_time": {
+ "gender": 3.0,
+ "detector": 32.0,
+ "calculator": 9.0
+ }
+ }
+ ],
+ "plugins_versions": {
+ "gender": "insightface.GenderDetector",
+ "detector": "insightface.FaceDetector@retinaface_r50_v1",
+ "calculator": "insightface.Calculator@arcface-r100-msfdrop75"
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-fastapi-backend/config/__init__.py b/ruoyi-fastapi-backend/config/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ruoyi-fastapi-backend/config/constant.py b/ruoyi-fastapi-backend/config/constant.py
new file mode 100644
index 0000000..eb77464
--- /dev/null
+++ b/ruoyi-fastapi-backend/config/constant.py
@@ -0,0 +1,483 @@
+from config.env import DataBaseConfig
+
+
+class CommonConstant:
+ """
+ 常用常量
+
+ WWW: www主域
+ HTTP: http请求
+ HTTPS: https请求
+ LOOKUP_RMI: RMI远程方法调用
+ LOOKUP_LDAP: LDAP远程方法调用
+ LOOKUP_LDAPS: LDAPS远程方法调用
+ YES: 是否为系统默认(是)
+ NO: 是否为系统默认(否)
+ DEPT_NORMAL: 部门正常状态
+ DEPT_DISABLE: 部门停用状态
+ UNIQUE: 校验是否唯一的返回标识(是)
+ NOT_UNIQUE: 校验是否唯一的返回标识(否)
+ """
+
+ WWW = 'www.'
+ HTTP = 'http://'
+ HTTPS = 'https://'
+ LOOKUP_RMI = 'rmi:'
+ LOOKUP_LDAP = 'ldap:'
+ LOOKUP_LDAPS = 'ldaps:'
+ YES = 'Y'
+ NO = 'N'
+ DEPT_NORMAL = '0'
+ DEPT_DISABLE = '1'
+ UNIQUE = True
+ NOT_UNIQUE = False
+
+
+class HttpStatusConstant:
+ """
+ 返回状态码
+
+ SUCCESS: 操作成功
+ CREATED: 对象创建成功
+ ACCEPTED: 请求已经被接受
+ NO_CONTENT: 操作已经执行成功,但是没有返回数据
+ MOVED_PERM: 资源已被移除
+ SEE_OTHER: 重定向
+ NOT_MODIFIED: 资源没有被修改
+ BAD_REQUEST: 参数列表错误(缺少,格式不匹配)
+ UNAUTHORIZED: 未授权
+ FORBIDDEN: 访问受限,授权过期
+ NOT_FOUND: 资源,服务未找到
+ BAD_METHOD: 不允许的http方法
+ CONFLICT: 资源冲突,或者资源被锁
+ UNSUPPORTED_TYPE: 不支持的数据,媒体类型
+ ERROR: 系统内部错误
+ NOT_IMPLEMENTED: 接口未实现
+ WARN: 系统警告消息
+ """
+
+ SUCCESS = 200
+ CREATED = 201
+ ACCEPTED = 202
+ NO_CONTENT = 204
+ MOVED_PERM = 301
+ SEE_OTHER = 303
+ NOT_MODIFIED = 304
+ BAD_REQUEST = 400
+ UNAUTHORIZED = 401
+ FORBIDDEN = 403
+ NOT_FOUND = 404
+ BAD_METHOD = 405
+ CONFLICT = 409
+ UNSUPPORTED_TYPE = 415
+ ERROR = 500
+ NOT_IMPLEMENTED = 501
+ WARN = 601
+
+
+class JobConstant:
+ """
+ 定时任务常量
+
+ JOB_ERROR_LIST: 定时任务禁止调用模块及违规字符串列表
+ JOB_WHITE_LIST: 定时任务允许调用模块列表
+ """
+
+ JOB_ERROR_LIST = [
+ 'app',
+ 'config',
+ 'exceptions',
+ 'import ',
+ 'middlewares',
+ 'module_admin',
+ 'open(',
+ 'os.',
+ 'server',
+ 'sub_applications',
+ 'subprocess.',
+ 'sys.',
+ 'utils',
+ 'while ',
+ '__import__',
+ '"',
+ "'",
+ ',',
+ '?',
+ ':',
+ ';',
+ '/',
+ '|',
+ '+',
+ '-',
+ '=',
+ '~',
+ '!',
+ '#',
+ '$',
+ '%',
+ '^',
+ '&',
+ '*',
+ '<',
+ '>',
+ '(',
+ ')',
+ '[',
+ ']',
+ '{',
+ '}',
+ ' ',
+ ]
+ JOB_WHITE_LIST = ['module_task']
+
+
+class MenuConstant:
+ """
+ 菜单常量
+
+ TYPE_DIR: 菜单类型(目录)
+ TYPE_MENU: 菜单类型(菜单)
+ TYPE_BUTTON: 菜单类型(按钮)
+ YES_FRAME: 是否菜单外链(是)
+ NO_FRAME: 是否菜单外链(否)
+ LAYOUT: Layout组件标识
+ PARENT_VIEW: ParentView组件标识
+ INNER_LINK: InnerLink组件标识
+ """
+
+ TYPE_DIR = 'M'
+ TYPE_MENU = 'C'
+ TYPE_BUTTON = 'F'
+ YES_FRAME = 0
+ NO_FRAME = 1
+ LAYOUT = 'Layout'
+ PARENT_VIEW = 'ParentView'
+ INNER_LINK = 'InnerLink'
+
+
+class GenConstant:
+ """
+ 代码生成常量
+
+ TPL_CRUD: 单表(增删改查
+ TPL_TREE: 树表(增删改查)
+ TPL_SUB: 主子表(增删改查)
+ TREE_CODE: 树编码字段
+ TREE_PARENT_CODE: 树父编码字段
+ TREE_NAME: 树名称字段
+ PARENT_MENU_ID: 上级菜单ID字段
+ PARENT_MENU_NAME: 上级菜单名称字段
+ COLUMNTYPE_STR: 数据库字符串类型
+ COLUMNTYPE_TEXT: 数据库文本类型
+ COLUMNTYPE_TIME: 数据库时间类型
+ COLUMNTYPE_GEOMETRY: 数据库字空间类型
+ COLUMNTYPE_NUMBER: 数据库数字类型
+ COLUMNNAME_NOT_EDIT: 页面不需要编辑字段
+ COLUMNNAME_NOT_LIST: 页面不需要显示的列表字段
+ COLUMNNAME_NOT_QUERY: 页面不需要查询字段
+ BASE_ENTITY: Entity基类字段
+ TREE_ENTITY: Tree基类字段
+ HTML_INPUT: 文本框
+ HTML_TEXTAREA: 文本域
+ HTML_SELECT: 下拉框
+ HTML_RADIO: 单选框
+ HTML_CHECKBOX: 复选框
+ HTML_DATETIME: 日期控件
+ HTML_IMAGE_UPLOAD: 图片上传控件
+ HTML_FILE_UPLOAD: 文件上传控件
+ HTML_EDITOR: 富文本控件
+ TYPE_DECIMAL: 高精度计算类型
+ TYPE_DATE: 时间类型
+ QUERY_LIKE: 模糊查询
+ QUERY_EQ: 相等查询
+ REQUIRE: 需要
+ DB_TO_SQLALCHEMY_TYPE_MAPPING: 数据库类型与sqlalchemy类型映射
+ DB_TO_PYTHON_TYPE_MAPPING: 数据库类型与python类型映射
+ """
+
+ TPL_CRUD = 'crud'
+ TPL_TREE = 'tree'
+ TPL_SUB = 'sub'
+ TREE_CODE = 'treeCode'
+ TREE_PARENT_CODE = 'treeParentCode'
+ TREE_NAME = 'treeName'
+ PARENT_MENU_ID = 'parentMenuId'
+ PARENT_MENU_NAME = 'parentMenuName'
+ COLUMNTYPE_STR = (
+ ['character varying', 'varchar', 'character', 'char']
+ if DataBaseConfig.db_type == 'postgresql'
+ else ['char', 'varchar', 'nvarchar', 'varchar2']
+ )
+ COLUMNTYPE_TEXT = (
+ ['text', 'citext'] if DataBaseConfig.db_type == 'postgresql' else ['tinytext', 'text', 'mediumtext', 'longtext']
+ )
+ COLUMNTYPE_TIME = (
+ [
+ 'date',
+ 'time',
+ 'time with time zone',
+ 'time without time zone',
+ 'timestamp',
+ 'timestamp with time zone',
+ 'timestamp without time zone',
+ 'interval',
+ ]
+ if DataBaseConfig.db_type == 'postgresql'
+ else ['datetime', 'time', 'date', 'timestamp']
+ )
+ COLUMNTYPE_GEOMETRY = (
+ ['point', 'line', 'lseg', 'box', 'path', 'polygon', 'circle']
+ if DataBaseConfig.db_type == 'postgresql'
+ else [
+ 'geometry',
+ 'point',
+ 'linestring',
+ 'polygon',
+ 'multipoint',
+ 'multilinestring',
+ 'multipolygon',
+ 'geometrycollection',
+ ]
+ )
+ COLUMNTYPE_NUMBER = [
+ 'tinyint',
+ 'smallint',
+ 'mediumint',
+ 'int',
+ 'number',
+ 'integer',
+ 'bit',
+ 'bigint',
+ 'float',
+ 'double',
+ 'decimal',
+ ]
+ COLUMNNAME_NOT_ADD_SHOW = ['create_by', 'create_time']
+ COLUMNNAME_NOT_EDIT_SHOW = ['update_by', 'update_time']
+ COLUMNNAME_NOT_EDIT = ['id', 'create_by', 'create_time', 'del_flag']
+ COLUMNNAME_NOT_LIST = ['id', 'create_by', 'create_time', 'del_flag', 'update_by', 'update_time']
+ COLUMNNAME_NOT_QUERY = ['id', 'create_by', 'create_time', 'del_flag', 'update_by', 'update_time', 'remark']
+ BASE_ENTITY = ['createBy', 'createTime', 'updateBy', 'updateTime', 'remark']
+ TREE_ENTITY = ['parentName', 'parentId', 'orderNum', 'ancestors', 'children']
+ HTML_INPUT = 'input'
+ HTML_TEXTAREA = 'textarea'
+ HTML_SELECT = 'select'
+ HTML_RADIO = 'radio'
+ HTML_CHECKBOX = 'checkbox'
+ HTML_DATETIME = 'datetime'
+ HTML_IMAGE_UPLOAD = 'imageUpload'
+ HTML_FILE_UPLOAD = 'fileUpload'
+ HTML_EDITOR = 'editor'
+ TYPE_DECIMAL = 'Decimal'
+ TYPE_DATE = ['date', 'time', 'datetime']
+ QUERY_LIKE = 'LIKE'
+ QUERY_EQ = 'EQ'
+ REQUIRE = '1'
+ DB_TO_SQLALCHEMY_TYPE_MAPPING = (
+ {
+ 'boolean': 'Boolean',
+ 'smallint': 'SmallInteger',
+ 'integer': 'Integer',
+ 'bigint': 'BigInteger',
+ 'real': 'Float',
+ 'double precision': 'Float',
+ 'numeric': 'Numeric',
+ 'character varying': 'String',
+ 'character': 'String',
+ 'text': 'Text',
+ 'bytea': 'LargeBinary',
+ 'date': 'Date',
+ 'time': 'Time',
+ 'time with time zone': 'Time',
+ 'time without time zone': 'Time',
+ 'timestamp': 'DateTime',
+ 'timestamp with time zone': 'DateTime',
+ 'timestamp without time zone': 'DateTime',
+ 'interval': 'Interval',
+ 'json': 'JSON',
+ 'jsonb': 'JSONB',
+ 'uuid': 'Uuid',
+ 'inet': 'INET',
+ 'cidr': 'CIDR',
+ 'macaddr': 'MACADDR',
+ 'point': 'Geometry',
+ 'line': 'Geometry',
+ 'lseg': 'Geometry',
+ 'box': 'Geometry',
+ 'path': 'Geometry',
+ 'polygon': 'Geometry',
+ 'circle': 'Geometry',
+ 'bit': 'Bit',
+ 'bit varying': 'Bit',
+ 'tsvector': 'TSVECTOR',
+ 'tsquery': 'TSQUERY',
+ 'xml': 'String',
+ 'array': 'ARRAY',
+ 'composite': 'JSON',
+ 'enum': 'Enum',
+ 'range': 'Range',
+ 'money': 'Numeric',
+ 'pg_lsn': 'BigInteger',
+ 'txid_snapshot': 'String',
+ 'oid': 'BigInteger',
+ 'regproc': 'String',
+ 'regclass': 'String',
+ 'regtype': 'String',
+ 'regrole': 'String',
+ 'regnamespace': 'String',
+ 'int2vector': 'ARRAY',
+ 'oidvector': 'ARRAY',
+ 'pg_node_tree': 'Text',
+ }
+ if DataBaseConfig.db_type == 'postgresql'
+ else {
+ # 数值类型
+ 'TINYINT': 'SmallInteger',
+ 'SMALLINT': 'SmallInteger',
+ 'MEDIUMINT': 'Integer',
+ 'INT': 'Integer',
+ 'INTEGER': 'Integer',
+ 'BIGINT': 'BigInteger',
+ 'FLOAT': 'Float',
+ 'DOUBLE': 'Float',
+ 'DECIMAL': 'DECIMAL',
+ 'BIT': 'Integer',
+ # 日期和时间类型
+ 'DATE': 'Date',
+ 'TIME': 'Time',
+ 'DATETIME': 'DateTime',
+ 'TIMESTAMP': 'TIMESTAMP',
+ 'YEAR': 'Integer',
+ # 字符串类型
+ 'CHAR': 'CHAR',
+ 'VARCHAR': 'String',
+ 'TINYTEXT': 'Text',
+ 'TEXT': 'Text',
+ 'MEDIUMTEXT': 'Text',
+ 'LONGTEXT': 'Text',
+ 'BINARY': 'BINARY',
+ 'VARBINARY': 'VARBINARY',
+ 'TINYBLOB': 'LargeBinary',
+ 'BLOB': 'LargeBinary',
+ 'MEDIUMBLOB': 'LargeBinary',
+ 'LONGBLOB': 'LargeBinary',
+ # 枚举和集合类型
+ 'ENUM': 'Enum',
+ 'SET': 'String',
+ # JSON 类型
+ 'JSON': 'JSON',
+ # 空间数据类型(需要扩展支持,如 GeoAlchemy2)
+ 'GEOMETRY': 'Geometry', # 需要安装 geoalchemy2
+ 'POINT': 'Geometry',
+ 'LINESTRING': 'Geometry',
+ 'POLYGON': 'Geometry',
+ 'MULTIPOINT': 'Geometry',
+ 'MULTILINESTRING': 'Geometry',
+ 'MULTIPOLYGON': 'Geometry',
+ 'GEOMETRYCOLLECTION': 'Geometry',
+ }
+ )
+ DB_TO_PYTHON_TYPE_MAPPING = (
+ {
+ 'boolean': 'bool',
+ 'smallint': 'int',
+ 'integer': 'int',
+ 'bigint': 'int',
+ 'real': 'float',
+ 'double precision': 'float',
+ 'numeric': 'Decimal',
+ 'character varying': 'str',
+ 'character': 'str',
+ 'text': 'str',
+ 'bytea': 'bytes',
+ 'date': 'date',
+ 'time': 'time',
+ 'time with time zone': 'time',
+ 'time without time zone': 'time',
+ 'timestamp': 'datetime',
+ 'timestamp with time zone': 'datetime',
+ 'timestamp without time zone': 'datetime',
+ 'interval': 'timedelta',
+ 'json': 'dict',
+ 'jsonb': 'dict',
+ 'uuid': 'str',
+ 'inet': 'str',
+ 'cidr': 'str',
+ 'macaddr': 'str',
+ 'point': 'list',
+ 'line': 'list',
+ 'lseg': 'list',
+ 'box': 'list',
+ 'path': 'list',
+ 'polygon': 'list',
+ 'circle': 'list',
+ 'bit': 'int',
+ 'bit varying': 'int',
+ 'tsvector': 'str',
+ 'tsquery': 'str',
+ 'xml': 'str',
+ 'array': 'list',
+ 'composite': 'dict',
+ 'enum': 'str',
+ 'range': 'list',
+ 'money': 'Decimal',
+ 'pg_lsn': 'int',
+ 'txid_snapshot': 'str',
+ 'oid': 'int',
+ 'regproc': 'str',
+ 'regclass': 'str',
+ 'regtype': 'str',
+ 'regrole': 'str',
+ 'regnamespace': 'str',
+ 'int2vector': 'list',
+ 'oidvector': 'list',
+ 'pg_node_tree': 'str',
+ }
+ if DataBaseConfig.db_type == 'postgresql'
+ else {
+ # 数值类型
+ 'TINYINT': 'int',
+ 'SMALLINT': 'int',
+ 'MEDIUMINT': 'int',
+ 'INT': 'int',
+ 'INTEGER': 'int',
+ 'BIGINT': 'int',
+ 'FLOAT': 'float',
+ 'DOUBLE': 'float',
+ 'DECIMAL': 'Decimal',
+ 'BIT': 'int',
+ # 日期和时间类型
+ 'DATE': 'date',
+ 'TIME': 'time',
+ 'DATETIME': 'datetime',
+ 'TIMESTAMP': 'datetime',
+ 'YEAR': 'int',
+ # 字符串类型
+ 'CHAR': 'str',
+ 'VARCHAR': 'str',
+ 'TINYTEXT': 'str',
+ 'TEXT': 'str',
+ 'MEDIUMTEXT': 'str',
+ 'LONGTEXT': 'str',
+ 'BINARY': 'bytes',
+ 'VARBINARY': 'bytes',
+ 'TINYBLOB': 'bytes',
+ 'BLOB': 'bytes',
+ 'MEDIUMBLOB': 'bytes',
+ 'LONGBLOB': 'bytes',
+ # 枚举和集合类型
+ 'ENUM': 'str',
+ 'SET': 'str',
+ # JSON 类型
+ 'JSON': 'dict',
+ # 空间数据类型(通常需要特殊处理)
+ 'GEOMETRY': 'bytes',
+ 'POINT': 'bytes',
+ 'LINESTRING': 'bytes',
+ 'POLYGON': 'bytes',
+ 'MULTIPOINT': 'bytes',
+ 'MULTILINESTRING': 'bytes',
+ 'MULTIPOLYGON': 'bytes',
+ 'GEOMETRYCOLLECTION': 'bytes',
+ }
+ )
diff --git a/ruoyi-fastapi-backend/config/database.py b/ruoyi-fastapi-backend/config/database.py
new file mode 100644
index 0000000..006b6a5
--- /dev/null
+++ b/ruoyi-fastapi-backend/config/database.py
@@ -0,0 +1,30 @@
+from sqlalchemy.ext.asyncio import create_async_engine
+from sqlalchemy.ext.asyncio import async_sessionmaker
+from sqlalchemy.ext.asyncio import AsyncAttrs
+from sqlalchemy.orm import DeclarativeBase
+from urllib.parse import quote_plus
+from config.env import DataBaseConfig
+
+ASYNC_SQLALCHEMY_DATABASE_URL = (
+ f'mysql+asyncmy://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@'
+ f'{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}'
+)
+if DataBaseConfig.db_type == 'postgresql':
+ ASYNC_SQLALCHEMY_DATABASE_URL = (
+ f'postgresql+asyncpg://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@'
+ f'{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}'
+ )
+
+async_engine = create_async_engine(
+ ASYNC_SQLALCHEMY_DATABASE_URL,
+ echo=DataBaseConfig.db_echo,
+ max_overflow=DataBaseConfig.db_max_overflow,
+ pool_size=DataBaseConfig.db_pool_size,
+ pool_recycle=DataBaseConfig.db_pool_recycle,
+ pool_timeout=DataBaseConfig.db_pool_timeout,
+)
+AsyncSessionLocal = async_sessionmaker(autocommit=False, autoflush=False, bind=async_engine)
+
+
+class Base(AsyncAttrs, DeclarativeBase):
+ pass
diff --git a/ruoyi-fastapi-backend/config/enums.py b/ruoyi-fastapi-backend/config/enums.py
new file mode 100644
index 0000000..6b62ce7
--- /dev/null
+++ b/ruoyi-fastapi-backend/config/enums.py
@@ -0,0 +1,78 @@
+from enum import Enum
+
+
+class GuideWordType(Enum):
+ """
+ 引导词类型
+ NEWVISITOR 新访客 0
+ BOOKEDVISITOR 已预约访客 1
+ STAFF 员工 2
+ CHAT 聊天关键词 3
+ """
+ NEWVISITOR = 0
+ BOOKEDVISITOR = 1
+ STAFF = 2
+ CHAT = 3
+
+class ExplanationContentStatus(Enum):
+ """
+ 讲解内容状态
+
+ NOTSTART: 未开始
+ READY: 已就绪
+ PLAYING: 播放中
+ FINISH: 已完成
+ """
+ NOTSTART = '0'
+ READY = '1'
+ PLAYING = '2'
+ FINISH = '3'
+
+class BusinessType(Enum):
+ """
+ 业务操作类型
+
+ OTHER: 其它
+ INSERT: 新增
+ UPDATE: 修改
+ DELETE: 删除
+ GRANT: 授权
+ EXPORT: 导出
+ IMPORT: 导入
+ FORCE: 强退
+ GENCODE: 生成代码
+ CLEAN: 清空数据
+ """
+
+ OTHER = 0
+ INSERT = 1
+ UPDATE = 2
+ DELETE = 3
+ GRANT = 4
+ EXPORT = 5
+ IMPORT = 6
+ FORCE = 7
+ GENCODE = 8
+ CLEAN = 9
+
+
+class RedisInitKeyConfig(Enum):
+ """
+ 系统内置Redis键名
+ """
+
+ @property
+ def key(self):
+ return self.value.get('key')
+
+ @property
+ def remark(self):
+ return self.value.get('remark')
+
+ 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/env.py b/ruoyi-fastapi-backend/config/env.py
new file mode 100644
index 0000000..5840641
--- /dev/null
+++ b/ruoyi-fastapi-backend/config/env.py
@@ -0,0 +1,353 @@
+import argparse
+import os
+import sys
+from dotenv import load_dotenv
+from functools import lru_cache
+from pydantic import computed_field
+from pydantic_settings import BaseSettings
+from typing import Literal
+
+
+class AppSettings(BaseSettings):
+ """
+ 应用配置
+ """
+
+ app_env: str = 'dev'
+ app_name: str = 'RuoYi-FasAPI'
+ app_root_path: str = '/dev-api'
+ app_host: str = '0.0.0.0'
+ app_port: int = 9099
+ app_version: str = '1.0.0'
+ app_reload: bool = False
+ app_ip_location_query: bool = True
+ app_same_time_login: bool = True
+
+ app_docs_url: str = None
+ app_redoc_url: str = None
+ app_openapi_url: str = None
+
+
+
+class JwtSettings(BaseSettings):
+ """
+ Jwt配置
+ """
+
+ jwt_secret_key: str = os.getenv('JWT_SECRET_KEY', '')
+ jwt_algorithm: str = os.getenv('JWT_ALGORITHM', 'HS256')
+ jwt_expire_minutes: int = int(os.getenv('JWT_EXPIRE_MINUTES', '1440'))
+ jwt_redis_expire_minutes: int = int(os.getenv('JWT_REDIS_EXPIRE_MINUTES', '30'))
+
+
+class DataBaseSettings(BaseSettings):
+ """
+ 数据库配置
+ """
+
+ db_type: Literal['mysql', 'postgresql'] = os.getenv('DB_TYPE', 'mysql')
+ db_host: str = os.getenv('DB_HOST', '127.0.0.1')
+ db_port: int = int(os.getenv('DB_PORT', '3306'))
+ db_username: str = os.getenv('DB_USERNAME', '')
+ db_password: str = os.getenv('DB_PASSWORD', '')
+ db_database: str = os.getenv('DB_DATABASE', '')
+ db_echo: bool = os.getenv('DB_ECHO', 'True').lower() == 'true'
+ db_max_overflow: int = int(os.getenv('DB_MAX_OVERFLOW', '10'))
+ db_pool_size: int = int(os.getenv('DB_POOL_SIZE', '50'))
+ db_pool_recycle: int = int(os.getenv('DB_POOL_RECYCLE', '3600'))
+ db_pool_timeout: int = int(os.getenv('DB_POOL_TIMEOUT', '30'))
+
+ @computed_field
+ @property
+ def sqlglot_parse_dialect(self) -> str:
+ if self.db_type == 'postgresql':
+ return 'postgres'
+ return self.db_type
+
+
+class RedisSettings(BaseSettings):
+ """
+ Redis配置
+ """
+
+ redis_host: str = os.getenv('REDIS_HOST', '127.0.0.1')
+ redis_port: int = int(os.getenv('REDIS_PORT', '6379'))
+ redis_username: str = os.getenv('REDIS_USERNAME', '')
+ redis_password: str = os.getenv('REDIS_PASSWORD', '')
+ redis_database: int = int(os.getenv('REDIS_DATABASE', '2'))
+
+
+class GenSettings:
+ """
+ 代码生成配置
+ """
+
+ author = 'insistence'
+ package_name = 'module_admin.system'
+ auto_remove_pre = False
+ table_prefix = 'sys_'
+ allow_overwrite = False
+
+ GEN_PATH = 'vf_admin/gen_path'
+
+ def __init__(self):
+ if not os.path.exists(self.GEN_PATH):
+ os.makedirs(self.GEN_PATH)
+
+
+class UploadSettings:
+ """
+ 上传配置
+ """
+
+ UPLOAD_PREFIX = '/profile'
+ UPLOAD_PATH = 'vf_admin/upload_path'
+ UPLOAD_MACHINE = 'A'
+ DEFAULT_ALLOWED_EXTENSION = [
+ # 图片
+ 'bmp',
+ 'gif',
+ 'jpg',
+ 'jpeg',
+ 'png',
+ # word excel powerpoint
+ 'doc',
+ 'docx',
+ 'xls',
+ 'xlsx',
+ 'ppt',
+ 'pptx',
+ 'html',
+ 'htm',
+ 'txt',
+ # 压缩文件
+ 'rar',
+ 'zip',
+ 'gz',
+ 'bz2',
+ # 视频格式
+ 'mp4',
+ 'avi',
+ 'rmvb',
+ # pdf
+ 'pdf',
+ ]
+ DOWNLOAD_PATH = 'vf_admin/download_path'
+
+ def __init__(self):
+ if not os.path.exists(self.UPLOAD_PATH):
+ os.makedirs(self.UPLOAD_PATH)
+ if not os.path.exists(self.DOWNLOAD_PATH):
+ os.makedirs(self.DOWNLOAD_PATH)
+
+
+class CachePathConfig:
+ """
+ 缓存目录配置
+ """
+
+ PATH = os.path.join(os.path.abspath(os.getcwd()), 'caches')
+ PATHSTR = 'caches'
+
+class ComprefaceSettings:
+ """
+ Compreface配置
+ """
+ COMPREFACE_BASE_URL = os.getenv("COMPREFACE_BASE_URL", "http://10.0.0.202")
+ COMPERFACE_BASE_PORT = os.getenv("COMPERFACE_BASE_PORT", "8000")
+ COMPREFACE_API_KEY = os.getenv("COMPREFACE_API_KEY", "")
+ COMPREFACE_API_KEY_DETECTION = os.getenv("COMPREFACE_API_KEY_DETECTION", "")
+ COMPREFACE_FACE_FILE = os.getenv("COMPREFACE_FACE_FILE", "./image_face")
+ COMPREFACE_SIMILARITY_THRESHOLD = float(os.getenv("COMPREFACE_SIMILARITY_THRESHOLD", "0.995"))
+ COMPREFACE_POSE_MAX_ANGLE = float(os.getenv("COMPREFACE_POSE_MAX_ANGLE", "10"))
+ COMPREFACE_MIN_FACE_WIDTH = int(os.getenv("COMPREFACE_MIN_FACE_WIDTH", "200"))
+ COMPREFACE_MIN_FACE_HEIGHT = int(os.getenv("COMPREFACE_MIN_FACE_HEIGHT", "200"))
+ COMPREFACE_MIN_DETECTION_PROBABILITY = float(os.getenv("COMPREFACE_MIN_DETECTION_PROBABILITY", "0.9"))
+
+class HaiKangSettings:
+ """
+ 海康平台配置
+ """
+ HAIKANG_URL = os.getenv('HAIKANG_URL', 'https://127.0.0.1')
+ HAIKANG_PORT = int(os.getenv('HAIKANG_PORT', '443'))
+
+ HAIKANG_AK = os.getenv('HAIKANG_AK', '')
+ HAIKANG_SK = os.getenv('HAIKANG_SK', '')
+ HAIKANG_ACCESS_TOKEN_URL = os.getenv('HAIKANG_ACCESS_TOKEN_URL', '/api/v1/oauth/token')
+ # HAIKANG_DOOR_STATES_URL = '/api/v1/door/states'
+ HAIKANG_DOOR_STATES_URL = os.getenv('HAIKANG_DOOR_STATES_URL', '/api/acs/v1/door/states')
+ HAIKANG_DOOR_DOCONTROL_URL = os.getenv('HAIKANG_DOOR_DOCONTROL_URL', '/api/acs/v1/door/doControl')
+ HAIKANG_DOOR_ENVENTS_URL = os.getenv('HAIKANG_DOOR_EVENTS_URL', '/api/acs/v2/door/events')
+ HAIKANG_DOOR_SEARCH = os.getenv('HAIKANG_DOOR_SEARCH', '/api/resource/v2/door/search')
+ HAIKANG_DOOR_ONLINE_STATUS = os.getenv('HAIKANG_DOOR_ONLINE_STATUS', '/api/nms/v1/online/acs_device/get')
+ HAIKANG_APPLICATION_ONETOMANY_URL = os.getenv('HAIKANG_APPLICATION_ONETOMANY_URL', '/api/frs/v1/application/oneToMany')
+ HAIKANG_PICTURE_CHECK_URL = os.getenv('HAIKANG_PICTURE_CHECK_URL', '/api/frs/v1/face/picture/check')
+ HAIKANG_FACECAPATURE_SEARCH = os.getenv('HAIKANG_FACE_CAPTURE_SEARCH', '/api/frs/v1/event/face_capture/search')
+ HAIKANG_FACE_GROUP_URL = os.getenv('HAIKANG_FACE_GROUP_URL', '/api/frs/v1/face/group')
+
+ HAIKANG_VISITOR_RECORD_SEARCH = os.getenv('HAIKANG_VISITOR_RECORD_SEARCH', '/api/visitor/v2/appointment/records')
+ HAIKANG_VISITOR_RECORD_PICTURES = os.getenv('HAIKANG_VISITOR_RECORD_PICTURES', '/api/visitor/v1/record/pictures')
+
+ HAIKANG_PERSON_LIST = os.getenv('HAIKANG_PERSON_LIST', '/api/resource/v2/person/personList')
+ HAIKANG_GET_PERSON_PICTURE = os.getenv('HAIKANG_GET_PERSON_PICTURE', '/api/resource/v1/person/picture')
+
+ HAIKANG_FACE_GROUP_ADDITION = os.getenv('HAIKANG_FACE_GROUP_ADDITION', '/api/frs/v1/face/group/single/addition')
+
+ HAIKANG_FACE_SIGLE_ADDITION = os.getenv('HAIKANG_FACE_SINGLE_ADDITION', '/api/frs/v1/face/single/addition')
+ HAIKANG_FACE_DELETE = os.getenv('HAIKANG_FACE_DELETE', '/api/frs/v1/face/deletion')
+
+ HAIKANG_FACE_SAVE_PATH = os.getenv('HAIKANG_FACE_SAVE_PATH', "./face_images")
+ HAIKANG_VISITOR_PICTURES_SAVE_PATH = os.getenv('HAIKANG_VISITOR_PICTURES_SAVE_PATH', "./visitor/face_images/")
+ HAIKANG_VISITOR_RECORD_TXT = os.getenv('HAIKANG_VISITOR_RECORD_TXT', "./visitor/visitorIds.txt")
+
+
+
+class RAGFlowSettings:
+ """
+ RAGFlowSettings
+ """
+ RAGFLOW_BASE_URL = os.getenv("RAGFLOW_BASE_URL", "http://10.0.0.202:82")
+ RAGFLOW_API_KEY = os.getenv("RAGFLOW_API_KEY", "")
+
+class WeatherSettings:
+ """天气服务配置"""
+
+ WEATHER_API_BASE = os.getenv("WEATHER_API_BASE", "https://api.openweathermap.org/data/2.5")
+ WEATHER_API_KEY = os.getenv("WEATHER_API_KEY", "")
+ WEATHER_DEFAULT_CITY = os.getenv("WEATHER_DEFAULT_CITY", "北京")
+ WEATHER_UNITS = os.getenv("WEATHER_UNITS", "metric")
+ WEATHER_LANG = os.getenv("WEATHER_LANG", "zh_cn")
+ WEATHER_CACHE_TTL = int(os.getenv("WEATHER_CACHE_TTL", "600"))
+
+
+class GetConfig:
+ """
+ 获取配置
+ """
+
+ def __init__(self):
+ self.parse_cli_args()
+
+ @lru_cache()
+ def get_compreface_config(self):
+ """
+ 获取Compreface配置
+ """
+ # 获取Compreface配置
+ return ComprefaceSettings()
+ @lru_cache()
+ def get_app_config(self):
+ """
+ 获取应用配置
+ """
+ # 实例化应用配置模型
+ return AppSettings()
+
+ @lru_cache()
+ def get_jwt_config(self):
+ """
+ 获取Jwt配置
+ """
+ # 实例化Jwt配置模型
+ return JwtSettings()
+
+ @lru_cache()
+ def get_database_config(self):
+ """
+ 获取数据库配置
+ """
+ # 实例化数据库配置模型
+ return DataBaseSettings()
+
+ @lru_cache()
+ def get_redis_config(self):
+ """
+ 获取Redis配置
+ """
+ # 实例化Redis配置模型
+ return RedisSettings()
+
+ @lru_cache()
+ def get_gen_config(self):
+ """
+ 获取代码生成配置
+ """
+ # 实例化代码生成配置
+ return GenSettings()
+
+ @lru_cache()
+ def get_upload_config(self):
+ """
+ 获取数据库配置
+ """
+ # 实例上传配置
+ return UploadSettings()
+
+ @lru_cache()
+ def get_haikang_config(self):
+ """
+ 获取海康平台配置
+ """
+ return HaiKangSettings()
+
+ @lru_cache()
+ def get_ragflow_config(self):
+ """
+ 获取RAGFlow配置
+ """
+ return RAGFlowSettings()
+
+ @lru_cache()
+ def get_weather_config(self):
+ """获取天气配置"""
+ return WeatherSettings()
+
+ @staticmethod
+ def parse_cli_args():
+ """
+ 解析命令行参数
+ """
+ if 'uvicorn' in sys.argv[0]:
+ # 使用uvicorn启动时,命令行参数需要按照uvicorn的文档进行配置,无法自定义参数
+ pass
+ else:
+ # 使用argparse定义命令行参数
+ parser = argparse.ArgumentParser(description='命令行参数')
+ parser.add_argument('--env', type=str, default='dev', help='运行环境')
+ # 解析命令行参数
+ args = parser.parse_args()
+ # 设置环境变量,如果未设置命令行参数,默认APP_ENV为dev
+ os.environ['APP_ENV'] = args.env if args.env else 'dev'
+ # 读取运行环境
+ run_env = os.environ.get('APP_ENV', '')
+ # 运行环境未指定时默认加载.env.dev
+ env_file = '.env.dev'
+ # 运行环境不为空时按命令行参数加载对应.env文件
+ if run_env != '':
+ env_file = f'.env.{run_env}'
+ # 加载配置
+ load_dotenv(env_file)
+
+
+# 实例化获取配置类
+get_config = GetConfig()
+# 应用配置
+AppConfig = get_config.get_app_config()
+# Jwt配置
+JwtConfig = get_config.get_jwt_config()
+# 数据库配置
+DataBaseConfig = get_config.get_database_config()
+# Redis配置
+RedisConfig = get_config.get_redis_config()
+# 代码生成配置
+GenConfig = get_config.get_gen_config()
+# 上传配置
+UploadConfig = get_config.get_upload_config()
+# 海康平台配置
+HaiKangConfig = get_config.get_haikang_config()
+# RAGFlow配置
+RAGFlowConfig = get_config.get_ragflow_config()
+# compreface配置
+ComprefaceConfig = get_config.get_compreface_config()
+# 天气配置
+WeatherConfig = get_config.get_weather_config()
\ No newline at end of file
diff --git a/ruoyi-fastapi-backend/config/get_db.py b/ruoyi-fastapi-backend/config/get_db.py
new file mode 100644
index 0000000..20986ae
--- /dev/null
+++ b/ruoyi-fastapi-backend/config/get_db.py
@@ -0,0 +1,24 @@
+from config.database import async_engine, AsyncSessionLocal, Base
+from utils.log_util import logger
+
+
+async def get_db():
+ """
+ 每一个请求处理完毕后会关闭当前连接,不同的请求使用不同的连接
+
+ :return:
+ """
+ async with AsyncSessionLocal() as current_db:
+ yield current_db
+
+
+async def init_create_table():
+ """
+ 应用启动时初始化数据库连接
+
+ :return:
+ """
+ logger.info('初始化数据库连接...')
+ async with async_engine.begin() as conn:
+ await conn.run_sync(Base.metadata.create_all)
+ logger.info('数据库连接成功')
diff --git a/ruoyi-fastapi-backend/config/get_redis.py b/ruoyi-fastapi-backend/config/get_redis.py
new file mode 100644
index 0000000..9d78cad
--- /dev/null
+++ b/ruoyi-fastapi-backend/config/get_redis.py
@@ -0,0 +1,77 @@
+from redis import asyncio as aioredis
+from redis.exceptions import AuthenticationError, TimeoutError, RedisError
+from config.database import AsyncSessionLocal
+from config.env import RedisConfig
+from module_admin.service.config_service import ConfigService
+from module_admin.service.dict_service import DictDataService
+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.redis_host}',
+ port=RedisConfig.redis_port,
+ username=RedisConfig.redis_username,
+ password=RedisConfig.redis_password,
+ db=RedisConfig.redis_database,
+ encoding='utf-8',
+ decode_responses=True,
+ )
+ try:
+ connection = await redis.ping()
+ if connection:
+ logger.info('redis连接成功')
+ else:
+ logger.error('redis连接失败')
+ except AuthenticationError as e:
+ logger.error(f'redis用户名或密码错误,详细错误信息:{e}')
+ except TimeoutError as e:
+ logger.error(f'redis连接超时,详细错误信息:{e}')
+ except RedisError as e:
+ logger.error(f'redis连接错误,详细错误信息:{e}')
+ 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:
+ """
+ async with AsyncSessionLocal() as session:
+ await DictDataService.init_cache_sys_dict_services(session, redis)
+
+ @classmethod
+ async def init_sys_config(cls, redis):
+ """
+ 应用启动时缓存参数配置表
+
+ :param redis: redis对象
+ :return:
+ """
+ async with AsyncSessionLocal() as session:
+ await ConfigService.init_cache_sys_config_services(session, redis)
diff --git a/ruoyi-fastapi-backend/config/get_scheduler.py b/ruoyi-fastapi-backend/config/get_scheduler.py
new file mode 100644
index 0000000..4b0c298
--- /dev/null
+++ b/ruoyi-fastapi-backend/config/get_scheduler.py
@@ -0,0 +1,279 @@
+import json
+from apscheduler.events import EVENT_ALL
+from apscheduler.executors.asyncio import AsyncIOExecutor
+from apscheduler.executors.pool import ProcessPoolExecutor
+from apscheduler.jobstores.memory import MemoryJobStore
+from apscheduler.jobstores.redis import RedisJobStore
+from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
+from apscheduler.schedulers.asyncio import AsyncIOScheduler
+from apscheduler.triggers.combining import OrTrigger
+from apscheduler.triggers.cron import CronTrigger
+from apscheduler.triggers.date import DateTrigger
+from asyncio import iscoroutinefunction
+from datetime import datetime, timedelta
+from sqlalchemy.engine import create_engine
+from sqlalchemy.orm import sessionmaker
+from typing import Union
+from config.database import AsyncSessionLocal, quote_plus
+from config.env import DataBaseConfig, RedisConfig
+from module_admin.dao.job_dao import JobDao
+from module_admin.entity.vo.job_vo import JobLogModel, JobModel
+from module_admin.service.job_log_service import JobLogService
+from utils.log_util import logger
+import module_task # noqa: F401
+
+
+# 重写Cron定时
+class MyCronTrigger(CronTrigger):
+ @classmethod
+ def from_crontab(cls, expr: str, 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: int):
+ 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
+
+
+SQLALCHEMY_DATABASE_URL = (
+ f'mysql+pymysql://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@'
+ f'{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}'
+)
+if DataBaseConfig.db_type == 'postgresql':
+ SQLALCHEMY_DATABASE_URL = (
+ f'postgresql+psycopg2://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@'
+ f'{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}'
+ )
+engine = create_engine(
+ SQLALCHEMY_DATABASE_URL,
+ echo=DataBaseConfig.db_echo,
+ max_overflow=DataBaseConfig.db_max_overflow,
+ pool_size=DataBaseConfig.db_pool_size,
+ pool_recycle=DataBaseConfig.db_pool_recycle,
+ pool_timeout=DataBaseConfig.db_pool_timeout,
+)
+SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
+job_stores = {
+ 'default': MemoryJobStore(),
+ 'sqlalchemy': SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URL, engine=engine),
+ 'redis': RedisJobStore(
+ **dict(
+ host=RedisConfig.redis_host,
+ port=RedisConfig.redis_port,
+ username=RedisConfig.redis_username,
+ password=RedisConfig.redis_password,
+ db=RedisConfig.redis_database,
+ )
+ ),
+}
+executors = {'default': AsyncIOExecutor(), 'processpool': ProcessPoolExecutor(5)}
+job_defaults = {'coalesce': False, 'max_instance': 1}
+scheduler = AsyncIOScheduler()
+scheduler.configure(jobstores=job_stores, executors=executors, job_defaults=job_defaults)
+
+
+class SchedulerUtil:
+ """
+ 定时任务相关方法
+ """
+
+ @classmethod
+ async def init_system_scheduler(cls):
+ """
+ 应用启动时初始化定时任务
+
+ :return:
+ """
+ logger.info('开始启动定时任务...')
+ scheduler.start()
+ async with AsyncSessionLocal() as session:
+ job_list = await JobDao.get_job_list_for_scheduler(session)
+ for item in job_list:
+ cls.remove_scheduler_job(job_id=str(item.job_id))
+ cls.add_scheduler_job(item)
+ 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: Union[str, int]):
+ """
+ 根据任务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: JobModel):
+ """
+ 根据输入的任务对象信息添加任务
+
+ :param job_info: 任务对象信息
+ :return:
+ """
+ job_func = eval(job_info.invoke_target)
+ job_executor = job_info.job_executor
+ if iscoroutinefunction(job_func):
+ job_executor = 'default'
+ 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_executor,
+ )
+
+ @classmethod
+ def execute_scheduler_job_once(cls, job_info: JobModel):
+ """
+ 根据输入的任务对象执行一次任务
+
+ :param job_info: 任务对象信息
+ :return:
+ """
+ job_func = eval(job_info.invoke_target)
+ job_executor = job_info.job_executor
+ if iscoroutinefunction(job_func):
+ job_executor = 'default'
+ job_trigger = DateTrigger()
+ if job_info.status == '0':
+ job_trigger = OrTrigger(triggers=[DateTrigger(), MyCronTrigger.from_crontab(job_info.cron_expression)])
+ scheduler.add_job(
+ func=eval(job_info.invoke_target),
+ trigger=job_trigger,
+ 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_executor,
+ )
+
+ @classmethod
+ def remove_scheduler_job(cls, job_id: Union[str, int]):
+ """
+ 根据任务id移除任务
+
+ :param job_id: 任务id
+ :return:
+ """
+ query_job = cls.get_scheduler_job(job_id=job_id)
+ if query_job:
+ 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'
+ if hasattr(event, 'job_id'):
+ 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,
+ createTime=datetime.now(),
+ )
+ session = SessionLocal()
+ JobLogService.add_job_log_services(session, job_log)
+ session.close()
diff --git a/ruoyi-fastapi-backend/exceptions/__init__.py b/ruoyi-fastapi-backend/exceptions/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ruoyi-fastapi-backend/exceptions/exception.py b/ruoyi-fastapi-backend/exceptions/exception.py
new file mode 100644
index 0000000..b86f50d
--- /dev/null
+++ b/ruoyi-fastapi-backend/exceptions/exception.py
@@ -0,0 +1,58 @@
+class LoginException(Exception):
+ """
+ 自定义登录异常LoginException
+ """
+
+ def __init__(self, data: str = None, message: str = None):
+ self.data = data
+ self.message = message
+
+
+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 ServiceException(Exception):
+ """
+ 自定义服务异常ServiceException
+ """
+
+ def __init__(self, data: str = None, message: str = None):
+ self.data = data
+ self.message = message
+
+
+class ServiceWarning(Exception):
+ """
+ 自定义服务警告ServiceWarning
+ """
+
+ def __init__(self, data: str = None, message: str = None):
+ self.data = data
+ self.message = message
+
+
+class ModelValidatorException(Exception):
+ """
+ 自定义模型校验异常ModelValidatorException
+ """
+
+ def __init__(self, data: str = None, message: str = None):
+ self.data = data
+ self.message = message
diff --git a/ruoyi-fastapi-backend/exceptions/handle.py b/ruoyi-fastapi-backend/exceptions/handle.py
new file mode 100644
index 0000000..dec516a
--- /dev/null
+++ b/ruoyi-fastapi-backend/exceptions/handle.py
@@ -0,0 +1,71 @@
+from fastapi import FastAPI, Request
+from fastapi.exceptions import HTTPException
+from pydantic_validation_decorator import FieldValidationError
+from exceptions.exception import (
+ AuthException,
+ LoginException,
+ ModelValidatorException,
+ PermissionException,
+ ServiceException,
+ ServiceWarning,
+)
+from utils.log_util import logger
+from utils.response_util import jsonable_encoder, JSONResponse, ResponseUtil
+
+
+def handle_exception(app: FastAPI):
+ """
+ 全局异常处理
+ """
+
+ # 自定义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(LoginException)
+ async def login_exception_handler(request: Request, exc: LoginException):
+ return ResponseUtil.failure(data=exc.data, msg=exc.message)
+
+ # 自定义模型检验异常
+ @app.exception_handler(ModelValidatorException)
+ async def model_validator_exception_handler(request: Request, exc: ModelValidatorException):
+ logger.warning(exc.message)
+ return ResponseUtil.failure(data=exc.data, msg=exc.message)
+
+ # 自定义字段检验异常
+ @app.exception_handler(FieldValidationError)
+ async def field_validation_error_handler(request: Request, exc: FieldValidationError):
+ logger.warning(exc.message)
+ return ResponseUtil.failure(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(ServiceException)
+ async def service_exception_handler(request: Request, exc: ServiceException):
+ logger.error(exc.message)
+ return ResponseUtil.error(data=exc.data, msg=exc.message)
+
+ # 自定义服务警告
+ @app.exception_handler(ServiceWarning)
+ async def service_warning_handler(request: Request, exc: ServiceWarning):
+ logger.warning(exc.message)
+ return ResponseUtil.failure(data=exc.data, msg=exc.message)
+
+ # 处理其他http请求异常
+ @app.exception_handler(HTTPException)
+ async def http_exception_handler(request: Request, exc: HTTPException):
+ return JSONResponse(
+ content=jsonable_encoder({'code': exc.status_code, 'msg': exc.detail}), status_code=exc.status_code
+ )
+
+ # 处理其他异常
+ @app.exception_handler(Exception)
+ async def exception_handler(request: Request, exc: Exception):
+ logger.exception(exc)
+ return ResponseUtil.error(msg=str(exc))
diff --git a/ruoyi-fastapi-backend/middlewares/__init__.py b/ruoyi-fastapi-backend/middlewares/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ruoyi-fastapi-backend/middlewares/cors_middleware.py b/ruoyi-fastapi-backend/middlewares/cors_middleware.py
new file mode 100644
index 0000000..55508e7
--- /dev/null
+++ b/ruoyi-fastapi-backend/middlewares/cors_middleware.py
@@ -0,0 +1,25 @@
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+
+
+def add_cors_middleware(app: FastAPI):
+ """
+ 添加跨域中间件
+
+ :param app: FastAPI对象
+ :return:
+ """
+ # 前端页面url
+ origins = [
+ 'http://localhost:80',
+ 'http://127.0.0.1:80',
+ ]
+
+ # 后台api允许跨域
+ app.add_middleware(
+ CORSMiddleware,
+ allow_origins=origins,
+ allow_credentials=True,
+ allow_methods=['*'],
+ allow_headers=['*'],
+ )
diff --git a/ruoyi-fastapi-backend/middlewares/gzip_middleware.py b/ruoyi-fastapi-backend/middlewares/gzip_middleware.py
new file mode 100644
index 0000000..eb371ce
--- /dev/null
+++ b/ruoyi-fastapi-backend/middlewares/gzip_middleware.py
@@ -0,0 +1,12 @@
+from fastapi import FastAPI
+from starlette.middleware.gzip import GZipMiddleware
+
+
+def add_gzip_middleware(app: FastAPI):
+ """
+ 添加gzip压缩中间件
+
+ :param app: FastAPI对象
+ :return:
+ """
+ app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=9)
diff --git a/ruoyi-fastapi-backend/middlewares/handle.py b/ruoyi-fastapi-backend/middlewares/handle.py
new file mode 100644
index 0000000..abb2d0d
--- /dev/null
+++ b/ruoyi-fastapi-backend/middlewares/handle.py
@@ -0,0 +1,16 @@
+from fastapi import FastAPI
+from middlewares.cors_middleware import add_cors_middleware
+from middlewares.gzip_middleware import add_gzip_middleware
+from middlewares.trace_middleware import add_trace_middleware
+
+
+def handle_middleware(app: FastAPI):
+ """
+ 全局中间件处理
+ """
+ # 加载跨域中间件
+ add_cors_middleware(app)
+ # 加载gzip压缩中间件
+ add_gzip_middleware(app)
+ # 加载trace中间件
+ add_trace_middleware(app)
diff --git a/ruoyi-fastapi-backend/middlewares/trace_middleware/__init__.py b/ruoyi-fastapi-backend/middlewares/trace_middleware/__init__.py
new file mode 100644
index 0000000..76f8d85
--- /dev/null
+++ b/ruoyi-fastapi-backend/middlewares/trace_middleware/__init__.py
@@ -0,0 +1,17 @@
+from fastapi import FastAPI
+from .ctx import TraceCtx
+from .middle import TraceASGIMiddleware
+
+__all__ = ('TraceASGIMiddleware', 'TraceCtx')
+
+__version__ = '0.1.0'
+
+
+def add_trace_middleware(app: FastAPI):
+ """
+ 添加trace中间件
+
+ :param app: FastAPI对象
+ :return:
+ """
+ app.add_middleware(TraceASGIMiddleware)
diff --git a/ruoyi-fastapi-backend/middlewares/trace_middleware/ctx.py b/ruoyi-fastapi-backend/middlewares/trace_middleware/ctx.py
new file mode 100644
index 0000000..558a5c9
--- /dev/null
+++ b/ruoyi-fastapi-backend/middlewares/trace_middleware/ctx.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+"""
+@author: peng
+@file: ctx.py
+@time: 2025/1/17 16:57
+"""
+
+import contextvars
+from uuid import uuid4
+
+CTX_REQUEST_ID: contextvars.ContextVar[str] = contextvars.ContextVar('request-id', default='')
+
+
+class TraceCtx:
+ @staticmethod
+ def set_id():
+ _id = uuid4().hex
+ CTX_REQUEST_ID.set(_id)
+ return _id
+
+ @staticmethod
+ def get_id():
+ return CTX_REQUEST_ID.get()
diff --git a/ruoyi-fastapi-backend/middlewares/trace_middleware/middle.py b/ruoyi-fastapi-backend/middlewares/trace_middleware/middle.py
new file mode 100644
index 0000000..a071692
--- /dev/null
+++ b/ruoyi-fastapi-backend/middlewares/trace_middleware/middle.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+"""
+@author: peng
+@file: middle.py
+@time: 2025/1/17 16:57
+"""
+
+from functools import wraps
+from starlette.types import ASGIApp, Message, Receive, Scope, Send
+from .span import get_current_span, Span
+
+
+class TraceASGIMiddleware:
+ """
+ fastapi-example:
+ app = FastAPI()
+ app.add_middleware(TraceASGIMiddleware)
+ """
+
+ def __init__(self, app: ASGIApp) -> None:
+ self.app = app
+
+ @staticmethod
+ async def my_receive(receive: Receive, span: Span):
+ await span.request_before()
+
+ @wraps(receive)
+ async def my_receive():
+ message = await receive()
+ await span.request_after(message)
+ return message
+
+ return my_receive
+
+ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
+ if scope['type'] != 'http':
+ await self.app(scope, receive, send)
+ return
+
+ async with get_current_span(scope) as span:
+ handle_outgoing_receive = await self.my_receive(receive, span)
+
+ async def handle_outgoing_request(message: 'Message') -> None:
+ await span.response(message)
+ await send(message)
+
+ await self.app(scope, handle_outgoing_receive, handle_outgoing_request)
diff --git a/ruoyi-fastapi-backend/middlewares/trace_middleware/span.py b/ruoyi-fastapi-backend/middlewares/trace_middleware/span.py
new file mode 100644
index 0000000..1e38eab
--- /dev/null
+++ b/ruoyi-fastapi-backend/middlewares/trace_middleware/span.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+"""
+@author: peng
+@file: span.py
+@time: 2025/1/17 16:57
+"""
+
+from contextlib import asynccontextmanager
+from starlette.types import Scope, Message
+from .ctx import TraceCtx
+
+
+class Span:
+ """
+ 整个http生命周期:
+ request(before) --> request(after) --> response(before) --> response(after)
+ """
+
+ def __init__(self, scope: Scope):
+ self.scope = scope
+
+ async def request_before(self):
+ """
+ request_before: 处理header信息等, 如记录请求体信息
+ """
+ TraceCtx.set_id()
+
+ async def request_after(self, message: Message):
+ """
+ request_after: 处理请求bytes, 如记录请求参数
+
+ example:
+ message: {'type': 'http.request', 'body': b'{\r\n "name": "\xe8\x8b\x8f\xe8\x8b\x8f\xe8\x8b\x8f"\r\n}', 'more_body': False}
+ """
+ return message
+
+ async def response(self, message: Message):
+ """
+ if message['type'] == "http.response.start": -----> request-before
+ pass
+ if message['type'] == "http.response.body": -----> request-after
+ message.get('body', b'')
+ pass
+ """
+ if message['type'] == 'http.response.start':
+ message['headers'].append((b'request-id', TraceCtx.get_id().encode()))
+ return message
+
+
+@asynccontextmanager
+async def get_current_span(scope: Scope):
+ yield Span(scope)
diff --git a/ruoyi-fastapi-backend/module_admin/__init__.py b/ruoyi-fastapi-backend/module_admin/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ruoyi-fastapi-backend/module_admin/annotation/__init__.py b/ruoyi-fastapi-backend/module_admin/annotation/__init__.py
new file mode 100644
index 0000000..e69de29
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..19ac84e
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/annotation/log_annotation.py
@@ -0,0 +1,266 @@
+import inspect
+import json
+import os
+import requests
+import time
+from datetime import datetime
+from fastapi import Request
+from fastapi.responses import JSONResponse, ORJSONResponse, UJSONResponse
+from functools import lru_cache, wraps
+from sqlalchemy.ext.asyncio import AsyncSession
+from typing import Any, Callable, Literal, Optional
+from user_agents import parse
+from config.enums import BusinessType
+from config.env import AppConfig
+from exceptions.exception import LoginException, ServiceException, ServiceWarning
+from module_admin.entity.vo.log_vo import LogininforModel, OperLogModel
+from module_admin.service.log_service import LoginLogService, OperationLogService
+from module_admin.service.login_service import LoginService
+from utils.log_util import logger
+from utils.response_util import ResponseUtil
+
+
+class Log:
+ """
+ 日志装饰器
+ """
+
+ def __init__(
+ self,
+ title: str,
+ business_type: BusinessType,
+ log_type: Optional[Literal['login', 'operation']] = 'operation',
+ ):
+ """
+ 日志装饰器
+
+ :param title: 当前日志装饰器装饰的模块标题
+ :param business_type: 业务类型(OTHER其它 INSERT新增 UPDATE修改 DELETE删除 GRANT授权 EXPORT导出 IMPORT导入 FORCE强退 GENCODE生成代码 CLEAN清空数据)
+ :param log_type: 日志类型(login表示登录日志,operation表示为操作日志)
+ :return:
+ """
+ self.title = title
+ self.business_type = business_type.value
+ self.log_type = log_type
+
+ def __call__(self, 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('\\', '.').replace('/', '.')
+ # 获取当前被装饰函数所在路径
+ func_path = f'{relative_path}{func.__name__}()'
+ # 获取上下文信息
+ request_name_list = get_function_parameters_name_by_type(func, Request)
+ request = get_function_parameters_value_by_name(func, request_name_list[0], *args, **kwargs)
+ token = request.headers.get('Authorization')
+ session_name_list = get_function_parameters_name_by_type(func, AsyncSession)
+ query_db = get_function_parameters_value_by_name(func, session_name_list[0], *args, **kwargs)
+ 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'
+ if AppConfig.app_ip_location_query:
+ oper_location = get_ip_location(oper_ip)
+ # 根据不同的请求类型使用不同的方法获取请求参数
+ 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 self.log_type == 'login':
+ user_agent_info = parse(user_agent)
+ browser = f'{user_agent_info.browser.family}'
+ system_os = f'{user_agent_info.os.family}'
+ if user_agent_info.browser.version != ():
+ browser += f' {user_agent_info.browser.version[0]}'
+ if user_agent_info.os.version != ():
+ system_os += f' {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
+ try:
+ # 调用原始函数
+ result = await func(*args, **kwargs)
+ except (LoginException, ServiceWarning) as e:
+ logger.warning(e.message)
+ result = ResponseUtil.failure(data=e.data, msg=e.message)
+ except ServiceException as e:
+ logger.error(e.message)
+ result = ResponseUtil.error(data=e.data, msg=e.message)
+ except Exception as e:
+ logger.exception(e)
+ result = ResponseUtil.error(msg=str(e))
+ # 获取请求耗时
+ 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 self.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')
+
+ await LoginLogService.add_login_log_services(query_db, LogininforModel(**login_log))
+ else:
+ try:
+ current_user = await LoginService.get_current_user(request, token, query_db)
+ oper_name = current_user.user.user_name if current_user.user else None
+ dept_name = current_user.user.dept.dept_name if current_user.user.dept else None
+ except:
+ oper_name = None
+ dept_name = None
+
+ operation_log = OperLogModel(
+ title=self.title,
+ businessType=self.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),
+ )
+ await OperationLogService.add_operation_log_services(query_db, operation_log)
+
+ return result
+
+ return wrapper
+
+
+@lru_cache()
+def get_ip_location(oper_ip: str):
+ """
+ 查询ip归属区域
+
+ :param oper_ip: 需要查询的ip
+ :return: ip归属区域
+ """
+ oper_location = '内网IP'
+ try:
+ if oper_ip != '127.0.0.1' and oper_ip != 'localhost':
+ oper_location = '未知'
+ 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}'
+ except Exception as e:
+ oper_location = '未知'
+ print(e)
+ return oper_location
+
+
+def get_function_parameters_name_by_type(func: Callable, param_type: Any):
+ """
+ 获取函数指定类型的参数名称
+
+ :param func: 函数
+ :param arg_type: 参数类型
+ :return: 函数指定类型的参数名称
+ """
+ # 获取函数的参数信息
+ parameters = inspect.signature(func).parameters
+ # 找到指定类型的参数名称
+ parameters_name_list = []
+ for name, param in parameters.items():
+ if param.annotation == param_type:
+ parameters_name_list.append(name)
+ return parameters_name_list
+
+
+def get_function_parameters_value_by_name(func: Callable, name: str, *args, **kwargs):
+ """
+ 获取函数指定参数的值
+
+ :param func: 函数
+ :param name: 参数名
+ :return: 参数值
+ """
+ # 获取参数值
+ bound_parameters = inspect.signature(func).bind(*args, **kwargs)
+ bound_parameters.apply_defaults()
+ parameters_value = bound_parameters.arguments.get(name)
+
+ return parameters_value
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..11e8d7f
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/annotation/pydantic_annotation.py
@@ -0,0 +1,84 @@
+import inspect
+from fastapi import Form, Query
+from pydantic import BaseModel
+from pydantic.fields import FieldInfo
+from typing import Type, TypeVar
+
+
+BaseModelVar = TypeVar('BaseModelVar', bound=BaseModel)
+
+
+def as_query(cls: Type[BaseModelVar]) -> Type[BaseModelVar]:
+ """
+ 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(default=model_field.default, description=model_field.description),
+ annotation=model_field.annotation,
+ )
+ )
+ else:
+ new_parameters.append(
+ inspect.Parameter(
+ model_field.alias,
+ inspect.Parameter.POSITIONAL_ONLY,
+ default=Query(..., description=model_field.description),
+ 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[BaseModelVar]) -> Type[BaseModelVar]:
+ """
+ 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(default=model_field.default, description=model_field.description),
+ annotation=model_field.annotation,
+ )
+ )
+ else:
+ new_parameters.append(
+ inspect.Parameter(
+ model_field.alias,
+ inspect.Parameter.POSITIONAL_ONLY,
+ default=Form(..., description=model_field.description),
+ 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/__init__.py b/ruoyi-fastapi-backend/module_admin/aspect/__init__.py
new file mode 100644
index 0000000..e69de29
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..5a7afbb
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/aspect/data_scope.py
@@ -0,0 +1,75 @@
+from fastapi import Depends
+from typing import Optional
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+
+
+class GetDataScope:
+ """
+ 获取当前用户数据权限对应的查询sql语句
+ """
+
+ DATA_SCOPE_ALL = '1'
+ DATA_SCOPE_CUSTOM = '2'
+ DATA_SCOPE_DEPT = '3'
+ DATA_SCOPE_DEPT_AND_CHILD = '4'
+ DATA_SCOPE_SELF = '5'
+
+ def __init__(
+ self,
+ query_alias: Optional[str] = '',
+ db_alias: Optional[str] = 'db',
+ user_alias: Optional[str] = 'user_id',
+ dept_alias: Optional[str] = 'dept_id',
+ ):
+ """
+ 获取当前用户数据权限对应的查询sql语句
+
+ :param query_alias: 所要查询表对应的sqlalchemy模型名称,默认为''
+ :param db_alias: orm对象别名,默认为'db'
+ :param user_alias: 用户id字段别名,默认为'user_id'
+ :param dept_alias: 部门id字段别名,默认为'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
+ custom_data_scope_role_id_list = [
+ item.role_id for item in current_user.user.role if item.data_scope == self.DATA_SCOPE_CUSTOM
+ ]
+ param_sql_list = []
+ for role in current_user.user.role:
+ if current_user.user.admin or role.data_scope == self.DATA_SCOPE_ALL:
+ param_sql_list = ['1 == 1']
+ break
+ elif role.data_scope == self.DATA_SCOPE_CUSTOM:
+ if len(custom_data_scope_role_id_list) > 1:
+ param_sql_list.append(
+ f"{self.query_alias}.{self.dept_alias}.in_(select(SysRoleDept.dept_id).where(SysRoleDept.role_id.in_({custom_data_scope_role_id_list}))) if hasattr({self.query_alias}, '{self.dept_alias}') else 1 == 0"
+ )
+ else:
+ param_sql_list.append(
+ f"{self.query_alias}.{self.dept_alias}.in_(select(SysRoleDept.dept_id).where(SysRoleDept.role_id == {role.role_id})) if hasattr({self.query_alias}, '{self.dept_alias}') else 1 == 0"
+ )
+ elif role.data_scope == self.DATA_SCOPE_DEPT:
+ param_sql_list.append(
+ f"{self.query_alias}.{self.dept_alias} == {dept_id} if hasattr({self.query_alias}, '{self.dept_alias}') else 1 == 0"
+ )
+ elif role.data_scope == self.DATA_SCOPE_DEPT_AND_CHILD:
+ param_sql_list.append(
+ f"{self.query_alias}.{self.dept_alias}.in_(select(SysDept.dept_id).where(or_(SysDept.dept_id == {dept_id}, func.find_in_set({dept_id}, SysDept.ancestors)))) if hasattr({self.query_alias}, '{self.dept_alias}') else 1 == 0"
+ )
+ elif role.data_scope == self.DATA_SCOPE_SELF:
+ param_sql_list.append(
+ f"{self.query_alias}.{self.user_alias} == {user_id} if hasattr({self.query_alias}, '{self.user_alias}') else 1 == 0"
+ )
+ else:
+ param_sql_list.append('1 == 0')
+ param_sql_list = list(dict.fromkeys(param_sql_list))
+ param_sql = f"or_({', '.join(param_sql_list)})"
+
+ 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..8f8349d
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/aspect/interface_auth.py
@@ -0,0 +1,68 @@
+from fastapi import Depends
+from typing import List, Union
+from exceptions.exception import PermissionException
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+
+
+class CheckUserInterfaceAuth:
+ """
+ 校验当前用户是否具有相应的接口权限
+ """
+
+ def __init__(self, perm: Union[str, List], is_strict: bool = False):
+ """
+ 校验当前用户是否具有相应的接口权限
+
+ :param perm: 权限标识
+ :param is_strict: 当传入的权限标识是list类型时,是否开启严格模式,开启表示会校验列表中的每一个权限标识,所有的校验结果都需要为True才会通过
+ """
+ self.perm = perm
+ self.is_strict = is_strict
+
+ def __call__(self, current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
+ user_auth_list = current_user.permissions
+ if '*:*:*' in user_auth_list:
+ return True
+ if isinstance(self.perm, str):
+ if self.perm in user_auth_list:
+ return True
+ if isinstance(self.perm, list):
+ if self.is_strict:
+ if all([perm_str in user_auth_list for perm_str in self.perm]):
+ return True
+ else:
+ if any([perm_str in user_auth_list for perm_str in self.perm]):
+ return True
+ raise PermissionException(data='', message='该用户无此接口权限')
+
+
+class CheckRoleInterfaceAuth:
+ """
+ 根据角色校验当前用户是否具有相应的接口权限
+ """
+
+ def __init__(self, role_key: Union[str, List], is_strict: bool = False):
+ """
+ 根据角色校验当前用户是否具有相应的接口权限
+
+ :param role_key: 角色标识
+ :param is_strict: 当传入的角色标识是list类型时,是否开启严格模式,开启表示会校验列表中的每一个角色标识,所有的校验结果都需要为True才会通过
+ """
+ self.role_key = role_key
+ self.is_strict = is_strict
+
+ def __call__(self, current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
+ user_role_list = current_user.user.role
+ user_role_key_list = [role.role_key for role in user_role_list]
+ if isinstance(self.role_key, str):
+ if self.role_key in user_role_key_list:
+ return True
+ if isinstance(self.role_key, list):
+ if self.is_strict:
+ if all([role_key_str in user_role_key_list for role_key_str in self.role_key]):
+ return True
+ else:
+ if any([role_key_str in user_role_key_list for role_key_str in self.role_key]):
+ return True
+ raise PermissionException(data='', message='该用户无此接口权限')
diff --git a/ruoyi-fastapi-backend/module_admin/controller/__init__.py b/ruoyi-fastapi-backend/module_admin/controller/__init__.py
new file mode 100644
index 0000000..e69de29
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..9e72713
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/cache_controller.py
@@ -0,0 +1,89 @@
+from fastapi import APIRouter, Depends, Request
+from typing import List
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.cache_vo import CacheInfoModel, CacheMonitorModel
+from module_admin.service.cache_service import CacheService
+from module_admin.service.login_service import LoginService
+from utils.log_util import logger
+from utils.response_util import ResponseUtil
+
+
+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):
+ # 获取全量数据
+ cache_info_query_result = await CacheService.get_cache_monitor_statistical_info_services(request)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=cache_info_query_result)
+
+
+@cacheController.get(
+ '/getNames',
+ response_model=List[CacheInfoModel],
+ dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))],
+)
+async def get_monitor_cache_name(request: Request):
+ # 获取全量数据
+ cache_name_list_result = await CacheService.get_cache_monitor_cache_name_services()
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=cache_name_list_result)
+
+
+@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):
+ # 获取全量数据
+ 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)
+
+
+@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):
+ # 获取全量数据
+ 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)
+
+
+@cacheController.delete(
+ '/clearCacheName/{cache_name}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))]
+)
+async def clear_monitor_cache_name(request: Request, cache_name: str):
+ clear_cache_name_result = await CacheService.clear_cache_monitor_cache_name_services(request, cache_name)
+ logger.info(clear_cache_name_result.message)
+
+ return ResponseUtil.success(msg=clear_cache_name_result.message)
+
+
+@cacheController.delete(
+ '/clearCacheKey/{cache_key}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))]
+)
+async def clear_monitor_cache_key(request: Request, cache_key: str):
+ clear_cache_key_result = await CacheService.clear_cache_monitor_cache_key_services(request, cache_key)
+ logger.info(clear_cache_key_result.message)
+
+ return ResponseUtil.success(msg=clear_cache_key_result.message)
+
+
+@cacheController.delete('/clearCacheAll', dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))])
+async def clear_monitor_cache_all(request: Request):
+ clear_cache_all_result = await CacheService.clear_cache_monitor_all_services(request)
+ logger.info(clear_cache_all_result.message)
+
+ return ResponseUtil.success(msg=clear_cache_all_result.message)
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..83d35b8
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/captcha_controller.py
@@ -0,0 +1,40 @@
+import uuid
+from datetime import timedelta
+from fastapi import APIRouter, Request
+from config.enums import RedisInitKeyConfig
+from module_admin.entity.vo.login_vo import CaptchaCode
+from module_admin.service.captcha_service import CaptchaService
+from utils.response_util import ResponseUtil
+from utils.log_util import logger
+
+
+captchaController = APIRouter()
+
+
+@captchaController.get('/captchaImage')
+async def get_captcha_image(request: Request):
+ captcha_enabled = (
+ True
+ if await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.captchaEnabled')
+ == 'true'
+ else False
+ )
+ register_enabled = (
+ True
+ if await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.registerUser') == 'true'
+ else False
+ )
+ session_id = str(uuid.uuid4())
+ captcha_result = await CaptchaService.create_captcha_image_service()
+ image = captcha_result[0]
+ computed_result = captcha_result[1]
+ await request.app.state.redis.set(
+ f'{RedisInitKeyConfig.CAPTCHA_CODES.key}:{session_id}', computed_result, ex=timedelta(minutes=2)
+ )
+ logger.info(f'编号为{session_id}的会话获取图片验证码成功')
+
+ return ResponseUtil.success(
+ model_content=CaptchaCode(
+ captchaEnabled=captcha_enabled, registerEnabled=register_enabled, img=image, uuid=session_id
+ )
+ )
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..d2fd621
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/common_controller.py
@@ -0,0 +1,36 @@
+from fastapi import APIRouter, BackgroundTasks, Depends, File, Query, Request, UploadFile
+from module_admin.service.common_service import CommonService
+from module_admin.service.login_service import LoginService
+from utils.log_util import logger
+from utils.response_util import ResponseUtil
+
+commonController = APIRouter(prefix='/common', dependencies=[Depends(LoginService.get_current_user)])
+
+
+@commonController.post('/upload')
+async def common_upload(request: Request, file: UploadFile = File(...)):
+ upload_result = await CommonService.upload_service(request, file)
+ logger.info('上传成功')
+
+ return ResponseUtil.success(model_content=upload_result.result)
+
+
+@commonController.get('/download')
+async def common_download(
+ request: Request,
+ background_tasks: BackgroundTasks,
+ file_name: str = Query(alias='fileName'),
+ delete: bool = Query(),
+):
+ download_result = await CommonService.download_services(background_tasks, file_name, delete)
+ logger.info(download_result.message)
+
+ return ResponseUtil.streaming(data=download_result.result)
+
+
+@commonController.get('/download/resource')
+async def common_download_resource(request: Request, resource: str = Query()):
+ download_resource_result = await CommonService.download_resource_services(resource)
+ logger.info(download_resource_result.message)
+
+ return ResponseUtil.streaming(data=download_resource_result.result)
diff --git a/ruoyi-fastapi-backend/module_admin/controller/compreface_controller.py b/ruoyi-fastapi-backend/module_admin/controller/compreface_controller.py
new file mode 100644
index 0000000..8f08b7a
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/compreface_controller.py
@@ -0,0 +1,58 @@
+from fastapi import APIRouter, Depends, Form, Request, Body, UploadFile, File
+from module_admin.service.login_service import LoginService
+from module_admin.annotation.log_annotation import Log
+from module_admin.service.compreface_service import ComprefaceService
+from config.enums import BusinessType
+from utils.response_util import ResponseUtil
+from utils.log_util import logger
+import datetime
+
+
+
+comprefaceController = APIRouter(prefix='/system/compreface'
+ , dependencies=[Depends(LoginService.get_current_user)]
+ )
+
+# 人脸检测
+@comprefaceController.post('/face_detection')
+async def face_detection(request: Request, file: UploadFile = File(None)) -> dict:
+ """
+ 人脸检测
+
+ """
+ if file: # 说明是表单上传
+ image = await file.read()
+
+ else: # 尝试按字节流读取
+ image = await request.body()
+
+ result = await ComprefaceService.face_detection_service(image)
+ print(result)
+ return ResponseUtil.success(data=result)
+
+# 人脸识别
+@comprefaceController.post('/face_recognition')
+# @Log(title='人脸识别', business_type=BusinessType.OTHER)
+async def face_recognition(request: Request, file: UploadFile = File(None)) -> dict:
+ """
+ 人脸识别
+
+ """
+ if file: # 说明是表单上传
+ image = await file.read()
+
+ else: # 尝试按字节流读取
+ image = await request.body()
+
+ result = await ComprefaceService.face_recognition_service(image)
+ print(result)
+ return ResponseUtil.success(data=result)
+
+
+# def my_job():
+# print("任务执行时间:", datetime.datetime.now())
+
+# from config.get_scheduler import scheduler
+# from apscheduler.triggers.cron import CronTrigger
+# trigger = CronTrigger(minute='*', second=0)
+# scheduler.add_job(my_job, trigger=trigger)
\ No newline at end of file
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..9b0184e
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/config_controller.py
@@ -0,0 +1,123 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.config_vo import ConfigModel, ConfigPageQueryModel, DeleteConfigModel
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.config_service import ConfigService
+from module_admin.service.login_service import LoginService
+from utils.common_util import bytes2file_response, export_list2excel
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+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: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ config_page_query_result = await ConfigService.get_config_list_services(query_db, config_page_query, is_page=True)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=config_page_query_result)
+
+
+@configController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:config:add'))])
+@ValidateFields(validate_model='add_config')
+@Log(title='参数管理', business_type=BusinessType.INSERT)
+async def add_system_config(
+ request: Request,
+ add_config: ConfigModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_config.create_by = current_user.user.user_name
+ add_config.create_time = datetime.now()
+ add_config.update_by = current_user.user.user_name
+ add_config.update_time = datetime.now()
+ add_config_result = await ConfigService.add_config_services(request, query_db, add_config)
+ logger.info(add_config_result.message)
+
+ return ResponseUtil.success(msg=add_config_result.message)
+
+
+@configController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:config:edit'))])
+@ValidateFields(validate_model='edit_config')
+@Log(title='参数管理', business_type=BusinessType.UPDATE)
+async def edit_system_config(
+ request: Request,
+ edit_config: ConfigModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_config.update_by = current_user.user.user_name
+ edit_config.update_time = datetime.now()
+ edit_config_result = await ConfigService.edit_config_services(request, query_db, edit_config)
+ logger.info(edit_config_result.message)
+
+ return ResponseUtil.success(msg=edit_config_result.message)
+
+
+@configController.delete('/refreshCache', dependencies=[Depends(CheckUserInterfaceAuth('system:config:remove'))])
+@Log(title='参数管理', business_type=BusinessType.UPDATE)
+async def refresh_system_config(request: Request, query_db: AsyncSession = Depends(get_db)):
+ refresh_config_result = await ConfigService.refresh_sys_config_services(request, query_db)
+ logger.info(refresh_config_result.message)
+
+ return ResponseUtil.success(msg=refresh_config_result.message)
+
+
+@configController.delete('/{config_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:config:remove'))])
+@Log(title='参数管理', business_type=BusinessType.DELETE)
+async def delete_system_config(request: Request, config_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_config = DeleteConfigModel(configIds=config_ids)
+ delete_config_result = await ConfigService.delete_config_services(request, query_db, delete_config)
+ logger.info(delete_config_result.message)
+
+ return ResponseUtil.success(msg=delete_config_result.message)
+
+
+@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: AsyncSession = Depends(get_db)):
+ config_detail_result = await ConfigService.config_detail_services(query_db, config_id)
+ logger.info(f'获取config_id为{config_id}的信息成功')
+
+ return ResponseUtil.success(data=config_detail_result)
+
+
+@configController.get('/configKey/{config_key}')
+async def query_system_config(request: Request, config_key: str):
+ # 获取全量数据
+ 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)
+
+
+@configController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:config:export'))])
+@Log(title='参数管理', business_type=BusinessType.EXPORT)
+async def export_system_config_list(
+ request: Request,
+ config_page_query: ConfigPageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取全量数据
+ config_query_result = await ConfigService.get_config_list_services(query_db, config_page_query, is_page=False)
+ config_export_result = await ConfigService.export_config_list_services(config_query_result)
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=export_list2excel(config_export_result))
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..2943206
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/dept_controller.py
@@ -0,0 +1,132 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from typing import List
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.data_scope import GetDataScope
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.dept_vo import DeleteDeptModel, DeptModel, DeptQueryModel
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.dept_service import DeptService
+from module_admin.service.login_service import LoginService
+from utils.log_util import logger
+from utils.response_util import ResponseUtil
+
+
+deptController = APIRouter(prefix='/system/dept', dependencies=[Depends(LoginService.get_current_user)])
+
+
+@deptController.get(
+ '/list/exclude/{dept_id}',
+ response_model=List[DeptModel],
+ dependencies=[Depends(CheckUserInterfaceAuth('system:dept:list'))],
+)
+async def get_system_dept_tree_for_edit_option(
+ request: Request,
+ dept_id: int,
+ query_db: AsyncSession = Depends(get_db),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ dept_query = DeptModel(deptId=dept_id)
+ dept_query_result = await DeptService.get_dept_for_edit_option_services(query_db, dept_query, data_scope_sql)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=dept_query_result)
+
+
+@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: AsyncSession = Depends(get_db),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ dept_query_result = await DeptService.get_dept_list_services(query_db, dept_query, data_scope_sql)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=dept_query_result)
+
+
+@deptController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:dept:add'))])
+@ValidateFields(validate_model='add_dept')
+@Log(title='部门管理', business_type=BusinessType.INSERT)
+async def add_system_dept(
+ request: Request,
+ add_dept: DeptModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_dept.create_by = current_user.user.user_name
+ add_dept.create_time = datetime.now()
+ add_dept.update_by = current_user.user.user_name
+ add_dept.update_time = datetime.now()
+ add_dept_result = await DeptService.add_dept_services(query_db, add_dept)
+ logger.info(add_dept_result.message)
+
+ return ResponseUtil.success(data=add_dept_result)
+
+
+@deptController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:dept:edit'))])
+@ValidateFields(validate_model='edit_dept')
+@Log(title='部门管理', business_type=BusinessType.UPDATE)
+async def edit_system_dept(
+ request: Request,
+ edit_dept: DeptModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ if not current_user.user.admin:
+ await DeptService.check_dept_data_scope_services(query_db, edit_dept.dept_id, data_scope_sql)
+ edit_dept.update_by = current_user.user.user_name
+ edit_dept.update_time = datetime.now()
+ edit_dept_result = await DeptService.edit_dept_services(query_db, edit_dept)
+ logger.info(edit_dept_result.message)
+
+ return ResponseUtil.success(msg=edit_dept_result.message)
+
+
+@deptController.delete('/{dept_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:dept:remove'))])
+@Log(title='部门管理', business_type=BusinessType.DELETE)
+async def delete_system_dept(
+ request: Request,
+ dept_ids: str,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ dept_id_list = dept_ids.split(',') if dept_ids else []
+ if dept_id_list:
+ for dept_id in dept_id_list:
+ if not current_user.user.admin:
+ await DeptService.check_dept_data_scope_services(query_db, int(dept_id), data_scope_sql)
+ delete_dept = DeleteDeptModel(deptIds=dept_ids)
+ delete_dept.update_by = current_user.user.user_name
+ delete_dept.update_time = datetime.now()
+ delete_dept_result = await DeptService.delete_dept_services(query_db, delete_dept)
+ logger.info(delete_dept_result.message)
+
+ return ResponseUtil.success(msg=delete_dept_result.message)
+
+
+@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: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ if not current_user.user.admin:
+ await DeptService.check_dept_data_scope_services(query_db, dept_id, data_scope_sql)
+ detail_dept_result = await DeptService.dept_detail_services(query_db, dept_id)
+ logger.info(f'获取dept_id为{dept_id}的信息成功')
+
+ return ResponseUtil.success(data=detail_dept_result)
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..27fa24c
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/dict_controller.py
@@ -0,0 +1,239 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from typing import List
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.dict_vo import (
+ DeleteDictDataModel,
+ DeleteDictTypeModel,
+ DictDataModel,
+ DictDataPageQueryModel,
+ DictTypeModel,
+ DictTypePageQueryModel,
+)
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.dict_service import DictDataService, DictTypeService
+from module_admin.service.login_service import LoginService
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+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: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ dict_type_page_query_result = await DictTypeService.get_dict_type_list_services(
+ query_db, dict_type_page_query, is_page=True
+ )
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=dict_type_page_query_result)
+
+
+@dictController.post('/type', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:add'))])
+@ValidateFields(validate_model='add_dict_type')
+@Log(title='字典类型', business_type=BusinessType.INSERT)
+async def add_system_dict_type(
+ request: Request,
+ add_dict_type: DictTypeModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_dict_type.create_by = current_user.user.user_name
+ add_dict_type.create_time = datetime.now()
+ add_dict_type.update_by = current_user.user.user_name
+ add_dict_type.update_time = datetime.now()
+ add_dict_type_result = await DictTypeService.add_dict_type_services(request, query_db, add_dict_type)
+ logger.info(add_dict_type_result.message)
+
+ return ResponseUtil.success(msg=add_dict_type_result.message)
+
+
+@dictController.put('/type', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:edit'))])
+@ValidateFields(validate_model='edit_dict_type')
+@Log(title='字典类型', business_type=BusinessType.UPDATE)
+async def edit_system_dict_type(
+ request: Request,
+ edit_dict_type: DictTypeModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_dict_type.update_by = current_user.user.user_name
+ edit_dict_type.update_time = datetime.now()
+ edit_dict_type_result = await DictTypeService.edit_dict_type_services(request, query_db, edit_dict_type)
+ logger.info(edit_dict_type_result.message)
+
+ return ResponseUtil.success(msg=edit_dict_type_result.message)
+
+
+@dictController.delete('/type/refreshCache', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:remove'))])
+@Log(title='字典类型', business_type=BusinessType.UPDATE)
+async def refresh_system_dict(request: Request, query_db: AsyncSession = Depends(get_db)):
+ refresh_dict_result = await DictTypeService.refresh_sys_dict_services(request, query_db)
+ logger.info(refresh_dict_result.message)
+
+ return ResponseUtil.success(msg=refresh_dict_result.message)
+
+
+@dictController.delete('/type/{dict_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:remove'))])
+@Log(title='字典类型', business_type=BusinessType.DELETE)
+async def delete_system_dict_type(request: Request, dict_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_dict_type = DeleteDictTypeModel(dictIds=dict_ids)
+ delete_dict_type_result = await DictTypeService.delete_dict_type_services(request, query_db, delete_dict_type)
+ logger.info(delete_dict_type_result.message)
+
+ return ResponseUtil.success(msg=delete_dict_type_result.message)
+
+
+@dictController.get('/type/optionselect', response_model=List[DictTypeModel])
+async def query_system_dict_type_options(request: Request, query_db: AsyncSession = Depends(get_db)):
+ dict_type_query_result = await DictTypeService.get_dict_type_list_services(
+ query_db, DictTypePageQueryModel(**dict()), is_page=False
+ )
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=dict_type_query_result)
+
+
+@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: AsyncSession = Depends(get_db)):
+ dict_type_detail_result = await DictTypeService.dict_type_detail_services(query_db, dict_id)
+ logger.info(f'获取dict_id为{dict_id}的信息成功')
+
+ return ResponseUtil.success(data=dict_type_detail_result)
+
+
+@dictController.post('/type/export', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:export'))])
+@Log(title='字典类型', business_type=BusinessType.EXPORT)
+async def export_system_dict_type_list(
+ request: Request,
+ dict_type_page_query: DictTypePageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取全量数据
+ dict_type_query_result = await DictTypeService.get_dict_type_list_services(
+ query_db, dict_type_page_query, is_page=False
+ )
+ dict_type_export_result = await DictTypeService.export_dict_type_list_services(dict_type_query_result)
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(dict_type_export_result))
+
+
+@dictController.get('/data/type/{dict_type}')
+async def query_system_dict_type_data(request: Request, dict_type: str, query_db: AsyncSession = Depends(get_db)):
+ # 获取全量数据
+ 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)
+
+
+@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: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ dict_data_page_query_result = await DictDataService.get_dict_data_list_services(
+ query_db, dict_data_page_query, is_page=True
+ )
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=dict_data_page_query_result)
+
+
+@dictController.post('/data', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:add'))])
+@ValidateFields(validate_model='add_dict_data')
+@Log(title='字典数据', business_type=BusinessType.INSERT)
+async def add_system_dict_data(
+ request: Request,
+ add_dict_data: DictDataModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_dict_data.create_by = current_user.user.user_name
+ add_dict_data.create_time = datetime.now()
+ add_dict_data.update_by = current_user.user.user_name
+ add_dict_data.update_time = datetime.now()
+ add_dict_data_result = await DictDataService.add_dict_data_services(request, query_db, add_dict_data)
+ logger.info(add_dict_data_result.message)
+
+ return ResponseUtil.success(msg=add_dict_data_result.message)
+
+
+@dictController.put('/data', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:edit'))])
+@ValidateFields(validate_model='edit_dict_data')
+@Log(title='字典数据', business_type=BusinessType.UPDATE)
+async def edit_system_dict_data(
+ request: Request,
+ edit_dict_data: DictDataModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_dict_data.update_by = current_user.user.user_name
+ edit_dict_data.update_time = datetime.now()
+ edit_dict_data_result = await DictDataService.edit_dict_data_services(request, query_db, edit_dict_data)
+ logger.info(edit_dict_data_result.message)
+
+ return ResponseUtil.success(msg=edit_dict_data_result.message)
+
+
+@dictController.delete('/data/{dict_codes}', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:remove'))])
+@Log(title='字典数据', business_type=BusinessType.DELETE)
+async def delete_system_dict_data(request: Request, dict_codes: str, query_db: AsyncSession = Depends(get_db)):
+ delete_dict_data = DeleteDictDataModel(dictCodes=dict_codes)
+ delete_dict_data_result = await DictDataService.delete_dict_data_services(request, query_db, delete_dict_data)
+ logger.info(delete_dict_data_result.message)
+
+ return ResponseUtil.success(msg=delete_dict_data_result.message)
+
+
+@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: AsyncSession = Depends(get_db)):
+ detail_dict_data_result = await DictDataService.dict_data_detail_services(query_db, dict_code)
+ logger.info(f'获取dict_code为{dict_code}的信息成功')
+
+ return ResponseUtil.success(data=detail_dict_data_result)
+
+
+@dictController.post('/data/export', dependencies=[Depends(CheckUserInterfaceAuth('system:dict:export'))])
+@Log(title='字典数据', business_type=BusinessType.EXPORT)
+async def export_system_dict_data_list(
+ request: Request,
+ dict_data_page_query: DictDataPageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取全量数据
+ dict_data_query_result = await DictDataService.get_dict_data_list_services(
+ query_db, dict_data_page_query, is_page=False
+ )
+ dict_data_export_result = await DictDataService.export_dict_data_list_services(dict_data_query_result)
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(dict_data_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/door_controller.py b/ruoyi-fastapi-backend/module_admin/controller/door_controller.py
new file mode 100644
index 0000000..362f500
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/door_controller.py
@@ -0,0 +1,162 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request, Body
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.door_service import DoorService
+from module_admin.service.haikang_service import HaiKangService
+from module_admin.entity.vo.door_vo import DeleteDoorModel, DoorModel, DoorPageQueryModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+import time
+
+
+
+doorController = APIRouter(prefix='/system/door', dependencies=[Depends(LoginService.get_current_user)])
+
+"""
+ 实时监控页面权限 access:monitor:view ,dependencies=[Depends(CheckUserInterfaceAuth('access:monitor:view'))]
+ 识别记录表权限 access:record:list , dependencies=[Depends(CheckUserInterfaceAuth('access:record:list'))]
+ 门禁设备管理 access:device:list, dependencies=[Depends(CheckUserInterfaceAuth('access:device:list'))]
+"""
+
+# 机器人控制开门
+@doorController.post('/control_door')
+async def control_door(request: Request, door_index_code: str = Body(), control_type: int = Body()):
+ print("door_index_code: ", door_index_code)
+ print("control_type: ", control_type)
+
+ start_time = time.time()
+ result = await HaiKangService.door_do_control_service(door_index_code, control_type)
+ print("end_time: ", time.time() - start_time)
+ if result[0]:
+ logger.info('门控制成功')
+ return ResponseUtil.success(data=result[1])
+ logger.error('门控制失败')
+ return ResponseUtil.error(msg=result[1])
+
+# 获取门禁设备列表
+@doorController.get(
+ '/list', response_model=PageResponseModel
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:door:list'))]
+ , dependencies=[Depends(CheckUserInterfaceAuth('access:device:list'))]
+)
+async def get_system_door_list(
+ request: Request,
+ door_page_query: DoorPageQueryModel = Depends(DoorPageQueryModel.as_query),
+ query_db: AsyncSession = Depends(get_db),
+):
+
+ # 获取分页数据
+ door_page_query_result = await DoorService.get_door_list_services(query_db, door_page_query, is_page=True)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=door_page_query_result)
+
+
+# 添加门禁设备
+@doorController.post('/add'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:door:add'))]
+ , dependencies=[Depends(CheckUserInterfaceAuth('access:device:list'))]
+ )
+@ValidateFields(validate_model='add_door')
+@Log(title='门禁设备', business_type=BusinessType.INSERT)
+async def add_system_door(
+ request: Request,
+ add_door: DoorModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_door.create_time = datetime.now()
+ add_door.create_by = current_user.user.user_name
+ add_door.update_time = datetime.now()
+ add_door.update_by = current_user.user.user_name
+ add_door_result = await DoorService.add_door_services(query_db, add_door)
+ logger.info(add_door_result.message)
+
+ return ResponseUtil.success(msg=add_door_result.message)
+
+
+@doorController.put('/edit'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:door:edit'))]
+ , dependencies=[Depends(CheckUserInterfaceAuth('access:device:list'))]
+ )
+@ValidateFields(validate_model='edit_door')
+@Log(title='门禁设备', business_type=BusinessType.UPDATE)
+async def edit_system_door(
+ request: Request,
+ edit_door: DoorModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_door.update_by = current_user.user.user_name
+ edit_door.update_time = datetime.now()
+ edit_door_result = await DoorService.edit_door_services(query_db, edit_door)
+ logger.info(edit_door_result.message)
+
+ return ResponseUtil.success(msg=edit_door_result.message)
+
+
+@doorController.delete('/{ids}'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:door:remove'))]
+ , dependencies=[Depends(CheckUserInterfaceAuth('access:device:list'))]
+ )
+@Log(title='门禁设备', business_type=BusinessType.DELETE)
+async def delete_system_door(request: Request, ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_door = DeleteDoorModel(ids=ids)
+ delete_door_result = await DoorService.delete_door_services(query_db, delete_door)
+ logger.info(delete_door_result.message)
+
+ return ResponseUtil.success(msg=delete_door_result.message)
+
+# 获取设备状态
+@doorController.get('/door_status', dependencies=[Depends(CheckUserInterfaceAuth('access:monitor:view'))])
+async def get_door_status(request: Request, query_db: AsyncSession = Depends(get_db)):
+ result = await DoorService.get_door_status_service(query_db)
+
+ if result[0]:
+ return ResponseUtil.success(data=result[1])
+
+ return ResponseUtil.error(msg=result[1])
+
+# 获取视频流地址
+@doorController.get('/video_uri/{robot_id}' ,dependencies=[Depends(CheckUserInterfaceAuth('access:monitor:view'))])
+async def get_video_uri(request: Request
+ , robot_id: int
+ , query_db: AsyncSession = Depends(get_db)
+ ):
+ result = await DoorService.get_video_uri_service(robot_id, query_db)
+ return ResponseUtil.success(data=result)
+
+
+# @doorController.get(
+# '/{id}', response_model=DoorModel, dependencies=[Depends(CheckUserInterfaceAuth('system:door:query'))]
+# )
+# async def query_detail_system_door(request: Request, id: int, query_db: AsyncSession = Depends(get_db)):
+# door_detail_result = await DoorService.door_detail_services(query_db, id)
+# logger.info(f'获取id为{id}的信息成功')
+
+# return ResponseUtil.success(data=door_detail_result)
+
+
+# @doorController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:door:export'))])
+# @Log(title='门禁设备', business_type=BusinessType.EXPORT)
+# async def export_system_door_list(
+# request: Request,
+# door_page_query: DoorPageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# door_query_result = await DoorService.get_door_list_services(query_db, door_page_query, is_page=False)
+# door_export_result = await DoorService.export_door_list_services(door_query_result)
+# logger.info('导出成功')
+
+# return ResponseUtil.streaming(data=bytes2file_response(door_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/explanation_content_controller.py b/ruoyi-fastapi-backend/module_admin/controller/explanation_content_controller.py
new file mode 100644
index 0000000..19432e7
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/explanation_content_controller.py
@@ -0,0 +1,143 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.explanation_content_type_vo import Explanation_content_typePageQueryModel, \
+ Explanation_content_typeQueryModel
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.explanation_content_type_service import Explanation_content_typeService
+from module_admin.service.login_service import LoginService
+from module_admin.service.explanation_content_service import Explanation_contentService
+from module_admin.entity.vo.explanation_content_vo import DeleteExplanation_contentModel, Explanation_contentModel, Explanation_contentPageQueryModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+from config.enums import ExplanationContentStatus
+
+
+explanationContentController = APIRouter(prefix='/system/explanation_content', dependencies=[Depends(LoginService.get_current_user)])
+
+"""
+ 展厅讲解权限字符 exhibitionhall:guide:query, dependencies=[Depends(CheckUserInterfaceAuth('exhibitionhall:guide:query'))]
+"""
+
+@explanationContentController.get(
+ '/list', response_model=PageResponseModel
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content:list'))]
+)
+async def get_system_explanation_content_list(
+ request: Request,
+explanation_content_page_query: Explanation_contentPageQueryModel = Depends(Explanation_contentPageQueryModel.as_query),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ explanation_content_page_query_result = await Explanation_contentService.get_explanation_content_list_services(query_db, explanation_content_page_query, is_page=False)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=explanation_content_page_query_result)
+
+
+@explanationContentController.post('/add_explanation'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content:add'))]
+ )
+@ValidateFields(validate_model='add_explanation_content')
+@Log(title='讲解内容', business_type=BusinessType.INSERT)
+async def add_system_explanation_content(
+ request: Request,
+ add_explanation_content: Explanation_contentModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_explanation_content.create_time = datetime.now()
+ add_explanation_content.create_by = current_user.user.user_name
+ add_explanation_content.update_time = datetime.now()
+ add_explanation_content.update_by = current_user.user.user_name
+ # 新添加内容状态默认0
+ add_explanation_content.status = ExplanationContentStatus.NOTSTART.value
+ add_explanation_content_result = await Explanation_contentService.add_explanation_content_services(query_db, add_explanation_content)
+ logger.info(add_explanation_content_result.message)
+
+ return ResponseUtil.success(msg=add_explanation_content_result.message)
+
+
+@explanationContentController.put('/edit_explanation'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content:edit'))]
+ )
+@ValidateFields(validate_model='edit_explanation_content')
+@Log(title='讲解内容', business_type=BusinessType.UPDATE)
+async def edit_system_explanation_content(
+ request: Request,
+ edit_explanation_content: Explanation_contentModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_explanation_content.update_by = current_user.user.user_name
+ edit_explanation_content.update_time = datetime.now()
+ edit_explanation_content_result = await Explanation_contentService.edit_explanation_content_services(query_db, edit_explanation_content)
+ logger.info(edit_explanation_content_result.message)
+
+ return ResponseUtil.success(msg=edit_explanation_content_result.message)
+
+
+@explanationContentController.delete('/{explanation_content_ids}'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content:remove'))]
+ )
+@Log(title='讲解内容', business_type=BusinessType.DELETE)
+async def delete_system_explanation_content(request: Request, explanation_content_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_explanation_content = DeleteExplanation_contentModel(explanationContentIds=explanation_content_ids)
+ delete_explanation_content_result = await Explanation_contentService.delete_explanation_content_services(query_db, delete_explanation_content)
+ logger.info(delete_explanation_content_result.message)
+
+ return ResponseUtil.success(msg=delete_explanation_content_result.message)
+
+
+@explanationContentController.get(
+ '/type_list', response_model=PageResponseModel
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content_type:list'))]
+)
+async def get_system_explanation_content_type_list(
+ request: Request,
+# explanation_content_type_page_query: Explanation_content_typePageQueryModel = Depends(Explanation_content_typePageQueryModel.as_query),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ # explanation_content_type_page_query_result = await Explanation_content_typeService.get_explanation_content_type_list_services(query_db, explanation_content_type_page_query, is_page=True)
+
+ explanation_content_type_query_result = await Explanation_content_typeService.get_explanation_content_type_list_services(query_db, Explanation_content_typeQueryModel(), is_page=False)
+
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=explanation_content_type_query_result)
+
+
+# @explanationContentController.get(
+# '/{explanation_content_id}', response_model=Explanation_contentModel
+# # , dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content:query'))]
+# )
+# async def query_detail_system_explanation_content(request: Request, explanation_content_id: int, query_db: AsyncSession = Depends(get_db)):
+# explanation_content_detail_result = await Explanation_contentService.explanation_content_detail_services(query_db, explanation_content_id)
+# logger.info(f'获取explanation_content_id为{explanation_content_id}的信息成功')
+#
+# return ResponseUtil.success(data=explanation_content_detail_result)
+
+
+# @explanationContentController.post('/export'
+# # , dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content:export'))]
+# )
+# @Log(title='讲解内容', business_type=BusinessType.EXPORT)
+# async def export_system_explanation_content_list(
+# request: Request,
+# explanation_content_page_query: Explanation_contentPageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# explanation_content_query_result = await Explanation_contentService.get_explanation_content_list_services(query_db, explanation_content_page_query, is_page=False)
+# explanation_content_export_result = await Explanation_contentService.export_explanation_content_list_services(explanation_content_query_result)
+# logger.info('导出成功')
+#
+# return ResponseUtil.streaming(data=bytes2file_response(explanation_content_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/explanation_content_type_controller.py b/ruoyi-fastapi-backend/module_admin/controller/explanation_content_type_controller.py
new file mode 100644
index 0000000..3c2344c
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/explanation_content_type_controller.py
@@ -0,0 +1,111 @@
+# from datetime import datetime
+# from fastapi import APIRouter, Depends, Form, Request
+# from pydantic_validation_decorator import ValidateFields
+# from sqlalchemy.ext.asyncio import AsyncSession
+# from config.enums import BusinessType
+# from config.get_db import get_db
+# from module_admin.annotation.log_annotation import Log
+# from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+# from module_admin.entity.vo.user_vo import CurrentUserModel
+# from module_admin.service.login_service import LoginService
+# from module_admin.service.explanation_content_type_service import Explanation_content_typeService
+# from module_admin.entity.vo.explanation_content_type_vo import DeleteExplanation_content_typeModel, Explanation_content_typeModel, Explanation_content_typePageQueryModel
+# from utils.common_util import bytes2file_response
+# from utils.log_util import logger
+# from utils.page_util import PageResponseModel
+# from utils.response_util import ResponseUtil
+
+
+# explanation_content_typeController = APIRouter(prefix='/system/explanation_content_type'
+# , dependencies=[Depends(LoginService.get_current_user)]
+# )
+#
+#
+# @explanation_content_typeController.get(
+# '/list', response_model=PageResponseModel
+# # , dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content_type:list'))]
+# )
+# async def get_system_explanation_content_type_list(
+# request: Request,
+# # explanation_content_type_page_query: Explanation_content_typePageQueryModel = Depends(Explanation_content_typePageQueryModel.as_query),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取分页数据
+# # explanation_content_type_page_query_result = await Explanation_content_typeService.get_explanation_content_type_list_services(query_db, explanation_content_type_page_query, is_page=True)
+#
+# explanation_content_type_query_result = await Explanation_content_typeService.get_explanation_content_type_list_services(query_db, is_page=False)
+#
+# logger.info('获取成功')
+#
+# return ResponseUtil.success(model_content=explanation_content_type_query_result)
+
+
+# @explanation_content_typeController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content_type:add'))])
+# @ValidateFields(validate_model='add_explanation_content_type')
+# @Log(title='讲解内容类型', business_type=BusinessType.INSERT)
+# async def add_system_explanation_content_type(
+# request: Request,
+# add_explanation_content_type: Explanation_content_typeModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# add_explanation_content_type.create_time = datetime.now()
+# add_explanation_content_type.create_by = current_user.user.user_name
+# add_explanation_content_type.update_time = datetime.now()
+# add_explanation_content_type.update_by = current_user.user.user_name
+# add_explanation_content_type_result = await Explanation_content_typeService.add_explanation_content_type_services(query_db, add_explanation_content_type)
+# logger.info(add_explanation_content_type_result.message)
+#
+# return ResponseUtil.success(msg=add_explanation_content_type_result.message)
+#
+#
+# @explanation_content_typeController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content_type:edit'))])
+# @ValidateFields(validate_model='edit_explanation_content_type')
+# @Log(title='讲解内容类型', business_type=BusinessType.UPDATE)
+# async def edit_system_explanation_content_type(
+# request: Request,
+# edit_explanation_content_type: Explanation_content_typeModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# edit_explanation_content_type.update_by = current_user.user.user_name
+# edit_explanation_content_type.update_time = datetime.now()
+# edit_explanation_content_type_result = await Explanation_content_typeService.edit_explanation_content_type_services(query_db, edit_explanation_content_type)
+# logger.info(edit_explanation_content_type_result.message)
+#
+# return ResponseUtil.success(msg=edit_explanation_content_type_result.message)
+#
+#
+# @explanation_content_typeController.delete('/{content_type_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content_type:remove'))])
+# @Log(title='讲解内容类型', business_type=BusinessType.DELETE)
+# async def delete_system_explanation_content_type(request: Request, content_type_ids: str, query_db: AsyncSession = Depends(get_db)):
+# delete_explanation_content_type = DeleteExplanation_content_typeModel(contentTypeIds=content_type_ids)
+# delete_explanation_content_type_result = await Explanation_content_typeService.delete_explanation_content_type_services(query_db, delete_explanation_content_type)
+# logger.info(delete_explanation_content_type_result.message)
+#
+# return ResponseUtil.success(msg=delete_explanation_content_type_result.message)
+#
+#
+# @explanation_content_typeController.get(
+# '/{content_type_id}', response_model=Explanation_content_typeModel, dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content_type:query'))]
+# )
+# async def query_detail_system_explanation_content_type(request: Request, content_type_id: int, query_db: AsyncSession = Depends(get_db)):
+# explanation_content_type_detail_result = await Explanation_content_typeService.explanation_content_type_detail_services(query_db, content_type_id)
+# logger.info(f'获取content_type_id为{content_type_id}的信息成功')
+#
+# return ResponseUtil.success(data=explanation_content_type_detail_result)
+#
+#
+# @explanation_content_typeController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_content_type:export'))])
+# @Log(title='讲解内容类型', business_type=BusinessType.EXPORT)
+# async def export_system_explanation_content_type_list(
+# request: Request,
+# explanation_content_type_page_query: Explanation_content_typePageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# explanation_content_type_query_result = await Explanation_content_typeService.get_explanation_content_type_list_services(query_db, explanation_content_type_page_query, is_page=False)
+# explanation_content_type_export_result = await Explanation_content_typeService.export_explanation_content_type_list_services(explanation_content_type_query_result)
+# logger.info('导出成功')
+#
+# return ResponseUtil.streaming(data=bytes2file_response(explanation_content_type_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/explanation_style_controller.py b/ruoyi-fastapi-backend/module_admin/controller/explanation_style_controller.py
new file mode 100644
index 0000000..8cf8454
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/explanation_style_controller.py
@@ -0,0 +1,105 @@
+# from datetime import datetime
+# from fastapi import APIRouter, Depends, Form, Request
+# from pydantic_validation_decorator import ValidateFields
+# from sqlalchemy.ext.asyncio import AsyncSession
+# from config.enums import BusinessType
+# from config.get_db import get_db
+# from module_admin.annotation.log_annotation import Log
+# from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+# from module_admin.entity.vo.user_vo import CurrentUserModel
+# from module_admin.service.login_service import LoginService
+# from module_admin.service.explanation_style_service import Explanation_styleService
+# from module_admin.entity.vo.explanation_style_vo import DeleteExplanation_styleModel, Explanation_styleModel, Explanation_stylePageQueryModel
+# from utils.common_util import bytes2file_response
+# from utils.log_util import logger
+# from utils.page_util import PageResponseModel
+# from utils.response_util import ResponseUtil
+#
+#
+# explanation_styleController = APIRouter(prefix='/system/explanation_style', dependencies=[Depends(LoginService.get_current_user)])
+#
+#
+# @explanation_styleController.get(
+# '/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_style:list'))]
+# )
+# async def get_system_explanation_style_list(
+# request: Request,
+# explanation_style_page_query: Explanation_stylePageQueryModel = Depends(Explanation_stylePageQueryModel.as_query),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取分页数据
+# explanation_style_page_query_result = await Explanation_styleService.get_explanation_style_list_services(query_db, explanation_style_page_query, is_page=True)
+# logger.info('获取成功')
+#
+# return ResponseUtil.success(model_content=explanation_style_page_query_result)
+#
+#
+# @explanation_styleController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_style:add'))])
+# @ValidateFields(validate_model='add_explanation_style')
+# @Log(title='讲解风格', business_type=BusinessType.INSERT)
+# async def add_system_explanation_style(
+# request: Request,
+# add_explanation_style: Explanation_styleModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# add_explanation_style.create_time = datetime.now()
+# add_explanation_style.create_by = current_user.user.user_name
+# add_explanation_style.update_time = datetime.now()
+# add_explanation_style.update_by = current_user.user.user_name
+# add_explanation_style_result = await Explanation_styleService.add_explanation_style_services(query_db, add_explanation_style)
+# logger.info(add_explanation_style_result.message)
+#
+# return ResponseUtil.success(msg=add_explanation_style_result.message)
+#
+#
+# @explanation_styleController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_style:edit'))])
+# @ValidateFields(validate_model='edit_explanation_style')
+# @Log(title='讲解风格', business_type=BusinessType.UPDATE)
+# async def edit_system_explanation_style(
+# request: Request,
+# edit_explanation_style: Explanation_styleModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# edit_explanation_style.update_by = current_user.user.user_name
+# edit_explanation_style.update_time = datetime.now()
+# edit_explanation_style_result = await Explanation_styleService.edit_explanation_style_services(query_db, edit_explanation_style)
+# logger.info(edit_explanation_style_result.message)
+#
+# return ResponseUtil.success(msg=edit_explanation_style_result.message)
+#
+#
+# @explanation_styleController.delete('/{explanation_style_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_style:remove'))])
+# @Log(title='讲解风格', business_type=BusinessType.DELETE)
+# async def delete_system_explanation_style(request: Request, explanation_style_ids: str, query_db: AsyncSession = Depends(get_db)):
+# delete_explanation_style = DeleteExplanation_styleModel(explanationStyleIds=explanation_style_ids)
+# delete_explanation_style_result = await Explanation_styleService.delete_explanation_style_services(query_db, delete_explanation_style)
+# logger.info(delete_explanation_style_result.message)
+#
+# return ResponseUtil.success(msg=delete_explanation_style_result.message)
+#
+#
+# @explanation_styleController.get(
+# '/{explanation_style_id}', response_model=Explanation_styleModel, dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_style:query'))]
+# )
+# async def query_detail_system_explanation_style(request: Request, explanation_style_id: int, query_db: AsyncSession = Depends(get_db)):
+# explanation_style_detail_result = await Explanation_styleService.explanation_style_detail_services(query_db, explanation_style_id)
+# logger.info(f'获取explanation_style_id为{explanation_style_id}的信息成功')
+#
+# return ResponseUtil.success(data=explanation_style_detail_result)
+#
+#
+# @explanation_styleController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_style:export'))])
+# @Log(title='讲解风格', business_type=BusinessType.EXPORT)
+# async def export_system_explanation_style_list(
+# request: Request,
+# explanation_style_page_query: Explanation_stylePageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# explanation_style_query_result = await Explanation_styleService.get_explanation_style_list_services(query_db, explanation_style_page_query, is_page=False)
+# explanation_style_export_result = await Explanation_styleService.export_explanation_style_list_services(explanation_style_query_result)
+# logger.info('导出成功')
+#
+# return ResponseUtil.streaming(data=bytes2file_response(explanation_style_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/explanation_style_robot_pair_controller.py b/ruoyi-fastapi-backend/module_admin/controller/explanation_style_robot_pair_controller.py
new file mode 100644
index 0000000..7356a51
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/explanation_style_robot_pair_controller.py
@@ -0,0 +1,130 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.explanation_style_robot_pair_service import Explanation_style_robot_pairService
+from module_admin.entity.vo.explanation_style_robot_pair_vo import DeleteExplanation_style_robot_pairModel, \
+ Explanation_style_robot_pairModel, Explanation_style_robot_pairPageQueryModel, SwitchExplanationStyleModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+explanation_style_robot_pairController = APIRouter(prefix='/system/explanation_style_robot_pair', dependencies=[Depends(LoginService.get_current_user)])
+
+
+# @explanation_style_robot_pairController.get(
+# '/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_style_robot_pair:list'))]
+# )
+# async def get_system_explanation_style_robot_pair_list(
+# request: Request,
+# robot_id: int,
+# # explanation_style_robot_pair_page_query: Explanation_style_robot_pairPageQueryModel = Depends(Explanation_style_robot_pairPageQueryModel.as_query),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取分页数据
+# # explanation_style_robot_pair_page_query_result = await Explanation_style_robot_pairService.get_explanation_style_robot_pair_list_services(query_db, explanation_style_robot_pair_page_query, is_page=False)
+
+# explanation_style_robot_pair_query_result = await Explanation_style_robot_pairService.get_style_robot_pair_list_services(query_db, robot_id)
+# logger.info('获取成功')
+
+# return ResponseUtil.success(data=explanation_style_robot_pair_query_result)
+
+# 切换风格
+@explanation_style_robot_pairController.post('/switch_style')
+async def switch_explanation_style(
+ request: Request,
+ switch_explanation_style: SwitchExplanationStyleModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ switch_explanation_style.update_by = current_user.user.user_name
+ switch_explanation_style_message = await Explanation_style_robot_pairService.switch_explanation_style_services(query_db, switch_explanation_style)
+
+ if switch_explanation_style_message.is_success:
+ logger.info(switch_explanation_style_message.message)
+
+ return ResponseUtil.success(msg=switch_explanation_style_message.message)
+ else:
+ return ResponseUtil.error(msg=switch_explanation_style_message.message)
+
+
+
+# @explanation_style_robot_pairController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_style_robot_pair:add'))])
+# @ValidateFields(validate_model='add_explanation_style_robot_pair')
+# @Log(title='讲解风格--机器人配对', business_type=BusinessType.INSERT)
+# async def add_system_explanation_style_robot_pair(
+# request: Request,
+# add_explanation_style_robot_pair: Explanation_style_robot_pairModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# add_explanation_style_robot_pair.create_time = datetime.now()
+# add_explanation_style_robot_pair.create_by = current_user.user.user_name
+# add_explanation_style_robot_pair.update_time = datetime.now()
+# add_explanation_style_robot_pair.update_by = current_user.user.user_name
+# add_explanation_style_robot_pair_result = await Explanation_style_robot_pairService.add_explanation_style_robot_pair_services(query_db, add_explanation_style_robot_pair)
+# logger.info(add_explanation_style_robot_pair_result.message)
+#
+# return ResponseUtil.success(msg=add_explanation_style_robot_pair_result.message)
+
+
+
+
+# @explanation_style_robot_pairController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_style_robot_pair:edit'))])
+# @ValidateFields(validate_model='edit_explanation_style_robot_pair')
+# @Log(title='讲解风格--机器人配对', business_type=BusinessType.UPDATE)
+# async def edit_system_explanation_style_robot_pair(
+# request: Request,
+# edit_explanation_style_robot_pair: Explanation_style_robot_pairModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# edit_explanation_style_robot_pair.update_by = current_user.user.user_name
+# edit_explanation_style_robot_pair.update_time = datetime.now()
+# edit_explanation_style_robot_pair_result = await Explanation_style_robot_pairService.edit_explanation_style_robot_pair_services(query_db, edit_explanation_style_robot_pair)
+# logger.info(edit_explanation_style_robot_pair_result.message)
+#
+# return ResponseUtil.success(msg=edit_explanation_style_robot_pair_result.message)
+
+
+# @explanation_style_robot_pairController.delete('/{pairing_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_style_robot_pair:remove'))])
+# @Log(title='讲解风格--机器人配对', business_type=BusinessType.DELETE)
+# async def delete_system_explanation_style_robot_pair(request: Request, pairing_ids: str, query_db: AsyncSession = Depends(get_db)):
+# delete_explanation_style_robot_pair = DeleteExplanation_style_robot_pairModel(pairingIds=pairing_ids)
+# delete_explanation_style_robot_pair_result = await Explanation_style_robot_pairService.delete_explanation_style_robot_pair_services(query_db, delete_explanation_style_robot_pair)
+# logger.info(delete_explanation_style_robot_pair_result.message)
+#
+# return ResponseUtil.success(msg=delete_explanation_style_robot_pair_result.message)
+
+
+# @explanation_style_robot_pairController.get(
+# '/{pairing_id}', response_model=Explanation_style_robot_pairModel, dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_style_robot_pair:query'))]
+# )
+# async def query_detail_system_explanation_style_robot_pair(request: Request, pairing_id: int, query_db: AsyncSession = Depends(get_db)):
+# explanation_style_robot_pair_detail_result = await Explanation_style_robot_pairService.explanation_style_robot_pair_detail_services(query_db, pairing_id)
+# logger.info(f'获取pairing_id为{pairing_id}的信息成功')
+#
+# return ResponseUtil.success(data=explanation_style_robot_pair_detail_result)
+
+
+# @explanation_style_robot_pairController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:explanation_style_robot_pair:export'))])
+# @Log(title='讲解风格--机器人配对', business_type=BusinessType.EXPORT)
+# async def export_system_explanation_style_robot_pair_list(
+# request: Request,
+# explanation_style_robot_pair_page_query: Explanation_style_robot_pairPageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# explanation_style_robot_pair_query_result = await Explanation_style_robot_pairService.get_explanation_style_robot_pair_list_services(query_db, explanation_style_robot_pair_page_query, is_page=False)
+# explanation_style_robot_pair_export_result = await Explanation_style_robot_pairService.export_explanation_style_robot_pair_list_services(explanation_style_robot_pair_query_result)
+# logger.info('导出成功')
+#
+# return ResponseUtil.streaming(data=bytes2file_response(explanation_style_robot_pair_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/haikang_controller.py b/ruoyi-fastapi-backend/module_admin/controller/haikang_controller.py
new file mode 100644
index 0000000..dec22b9
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/haikang_controller.py
@@ -0,0 +1,81 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.service.login_service import LoginService
+from module_admin.service.haikang_service import HaiKangService
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+from module_admin.entity.vo.haikang_vo import VisitorReservationQueryModel
+
+from typing import List
+
+
+haikang_controller = APIRouter(prefix="/system/haikang", dependencies=[Depends(LoginService.get_current_user)])
+
+
+# # 查询门禁点列表
+# @haikang_controller.get("/door_list"
+# # , response_model=PageResponseModel
+# # , dependencies=[Depends(CheckUserInterfaceAuth("system:haikang:list"))]
+# )
+# async def get_system_haikang_list(
+# request: Request,
+# pageNo:int,
+# pageSize:int,
+# ):
+# """
+# 查询门禁点列表
+# """
+# result = await HaiKangService.get_door_list_service(pageNo, pageSize)
+# return parse_result(result)
+
+# # 查询门禁点状态
+# @haikang_controller.post(
+# "/door_status",
+# # dependencies=[Depends(CheckUserInterfaceAuth("system:haikang:list"))]
+# )
+# async def get_door_status(
+# request: Request,
+# door_index_codes: List[str],
+# ):
+# """
+# 查询门禁点列表
+# """
+# result = await HaiKangService.get_door_status_service(door_index_codes)
+# return parse_result(result)
+
+# # 门禁点反控
+# @haikang_controller.post(
+# "/door_do_control",
+# # dependencies=[Depends(CheckUserInterfaceAuth("system:haikang:list"))]
+# )
+# async def door_do_control(
+# request: Request,
+# door_index_codes: List[str],
+# control_type: int,
+# ):
+# pass
+
+# 查询访客预约记录
+@haikang_controller.post(
+ "/get_visitor_list",
+ # dependencies=[Depends(CheckUserInterfaceAuth("system:haikang:list"))]
+)
+async def get_visitor(
+ request: Request,
+ visitor_query: VisitorReservationQueryModel
+):
+ result = await HaiKangService.get_visitor_list_service(visitor_query)
+ return parse_result(result)
+
+
+def parse_result(result):
+ if result[0]:
+ return ResponseUtil.success(data=result[1])
+ return ResponseUtil.error(msg=f"code:{result[1]} msg:{result[2]}")
\ No newline at end of file
diff --git a/ruoyi-fastapi-backend/module_admin/controller/identification_record_controller.py b/ruoyi-fastapi-backend/module_admin/controller/identification_record_controller.py
new file mode 100644
index 0000000..612a71a
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/identification_record_controller.py
@@ -0,0 +1,106 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.identification_record_service import Identification_recordService
+from module_admin.entity.vo.identification_record_vo import DeleteIdentification_recordModel, Identification_recordModel, Identification_recordPageQueryModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+identification_recordController = APIRouter(prefix='/system/identification_record', dependencies=[Depends(LoginService.get_current_user)])
+
+
+@identification_recordController.get(
+ '/list', response_model=PageResponseModel
+ , dependencies=[Depends(CheckUserInterfaceAuth('access:record:list'))]
+)
+async def get_system_identification_record_list(
+ request: Request,
+identification_record_page_query: Identification_recordPageQueryModel = Depends(Identification_recordPageQueryModel.as_query),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ identification_record_page_query_result = await Identification_recordService.get_identification_record_list_services(query_db, identification_record_page_query, is_page=True)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=identification_record_page_query_result)
+
+
+@identification_recordController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:identification_record:add'))])
+@ValidateFields(validate_model='add_identification_record')
+@Log(title='识别记录', business_type=BusinessType.INSERT)
+async def add_system_identification_record(
+ request: Request,
+ add_identification_record: Identification_recordModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_identification_record.create_time = datetime.now()
+ add_identification_record.create_by = current_user.user.user_name
+ add_identification_record_result = await Identification_recordService.add_identification_record_services(query_db, add_identification_record)
+ logger.info(add_identification_record_result.message)
+
+ return ResponseUtil.success(msg=add_identification_record_result.message)
+
+
+# @identification_recordController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:identification_record:edit'))])
+# @ValidateFields(validate_model='edit_identification_record')
+# @Log(title='识别记录', business_type=BusinessType.UPDATE)
+# async def edit_system_identification_record(
+# request: Request,
+# edit_identification_record: Identification_recordModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# edit_identification_record.update_by = current_user.user.user_name
+# edit_identification_record.update_time = datetime.now()
+# edit_identification_record_result = await Identification_recordService.edit_identification_record_services(query_db, edit_identification_record)
+# logger.info(edit_identification_record_result.message)
+
+# return ResponseUtil.success(msg=edit_identification_record_result.message)
+
+
+# @identification_recordController.delete('/{ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:identification_record:remove'))])
+# @Log(title='识别记录', business_type=BusinessType.DELETE)
+# async def delete_system_identification_record(request: Request, ids: str, query_db: AsyncSession = Depends(get_db)):
+# delete_identification_record = DeleteIdentification_recordModel(ids=ids)
+# delete_identification_record_result = await Identification_recordService.delete_identification_record_services(query_db, delete_identification_record)
+# logger.info(delete_identification_record_result.message)
+
+# return ResponseUtil.success(msg=delete_identification_record_result.message)
+
+
+# @identification_recordController.get(
+# '/{id}', response_model=Identification_recordModel, dependencies=[Depends(CheckUserInterfaceAuth('system:identification_record:query'))]
+# )
+# async def query_detail_system_identification_record(request: Request, id: int, query_db: AsyncSession = Depends(get_db)):
+# identification_record_detail_result = await Identification_recordService.identification_record_detail_services(query_db, id)
+# logger.info(f'获取id为{id}的信息成功')
+
+# return ResponseUtil.success(data=identification_record_detail_result)
+
+
+@identification_recordController.post('/export'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:identification_record:export'))]
+ )
+@Log(title='识别记录', business_type=BusinessType.EXPORT)
+async def export_system_identification_record_list(
+ request: Request,
+ identification_record_page_query: Identification_recordPageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取全量数据
+ identification_record_query_result = await Identification_recordService.get_identification_record_list_services(query_db, identification_record_page_query, is_page=False)
+ identification_record_export_result = await Identification_recordService.export_identification_record_list_services(identification_record_query_result)
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(identification_record_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/identification_statistics_controller.py b/ruoyi-fastapi-backend/module_admin/controller/identification_statistics_controller.py
new file mode 100644
index 0000000..b0c7314
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/identification_statistics_controller.py
@@ -0,0 +1,131 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.identification_statistics_service import Identification_statisticsService
+from module_admin.entity.vo.identification_statistics_vo import DeleteIdentification_statisticsModel, Identification_statisticsModel, Identification_statisticsPageQueryModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+identification_statisticsController = APIRouter(prefix='/system/identification_statistics', dependencies=[Depends(LoginService.get_current_user)])
+
+# 获取今天的识别统计数据
+@identification_statisticsController.get('/today')
+async def get_today_identification_statistics(request: Request, query_db: AsyncSession = Depends(get_db)):
+ """
+ 获取今天的识别统计数据
+ :param request:
+ :param query_db:
+ :return:
+ """
+ logger.info('获取今天的识别统计数据')
+ identification_statistics = await Identification_statisticsService.get_today_identification_statistics_services(query_db)
+ return ResponseUtil.success(data = identification_statistics)
+
+# 获取总的识别统计数据
+@identification_statisticsController.get('/total')
+async def get_total_identification_statistics(request: Request, query_db: AsyncSession = Depends(get_db)):
+ """
+ 获取今天的识别统计数据
+ :param request:
+ :param query_db:
+ :return:
+ """
+ logger.info('获取今天的识别统计数据')
+ identification_statistics = await Identification_statisticsService.get_total_identification_statistics_services(query_db)
+ return ResponseUtil.success(data = identification_statistics)
+
+
+
+
+# @identification_statisticsController.get(
+# '/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:identification_statistics:list'))]
+# )
+# async def get_system_identification_statistics_list(
+# request: Request,
+# identification_statistics_page_query: Identification_statisticsPageQueryModel = Depends(Identification_statisticsPageQueryModel.as_query),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取分页数据
+# identification_statistics_page_query_result = await Identification_statisticsService.get_identification_statistics_list_services(query_db, identification_statistics_page_query, is_page=True)
+# logger.info('获取成功')
+
+# return ResponseUtil.success(model_content=identification_statistics_page_query_result)
+
+
+# @identification_statisticsController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:identification_statistics:add'))])
+# @ValidateFields(validate_model='add_identification_statistics')
+# @Log(title='识别统计', business_type=BusinessType.INSERT)
+# async def add_system_identification_statistics(
+# request: Request,
+# add_identification_statistics: Identification_statisticsModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# add_identification_statistics.create_time = datetime.now()
+# add_identification_statistics.create_by = current_user.user.user_name
+# add_identification_statistics_result = await Identification_statisticsService.add_identification_statistics_services(query_db, add_identification_statistics)
+# logger.info(add_identification_statistics_result.message)
+
+# return ResponseUtil.success(msg=add_identification_statistics_result.message)
+
+
+# @identification_statisticsController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:identification_statistics:edit'))])
+# @ValidateFields(validate_model='edit_identification_statistics')
+# @Log(title='识别统计', business_type=BusinessType.UPDATE)
+# async def edit_system_identification_statistics(
+# request: Request,
+# edit_identification_statistics: Identification_statisticsModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# edit_identification_statistics.update_by = current_user.user.user_name
+# edit_identification_statistics.update_time = datetime.now()
+# edit_identification_statistics_result = await Identification_statisticsService.edit_identification_statistics_services(query_db, edit_identification_statistics)
+# logger.info(edit_identification_statistics_result.message)
+
+# return ResponseUtil.success(msg=edit_identification_statistics_result.message)
+
+
+# @identification_statisticsController.delete('/{ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:identification_statistics:remove'))])
+# @Log(title='识别统计', business_type=BusinessType.DELETE)
+# async def delete_system_identification_statistics(request: Request, ids: str, query_db: AsyncSession = Depends(get_db)):
+# delete_identification_statistics = DeleteIdentification_statisticsModel(ids=ids)
+# delete_identification_statistics_result = await Identification_statisticsService.delete_identification_statistics_services(query_db, delete_identification_statistics)
+# logger.info(delete_identification_statistics_result.message)
+
+# return ResponseUtil.success(msg=delete_identification_statistics_result.message)
+
+
+# @identification_statisticsController.get(
+# '/{id}', response_model=Identification_statisticsModel, dependencies=[Depends(CheckUserInterfaceAuth('system:identification_statistics:query'))]
+# )
+# async def query_detail_system_identification_statistics(request: Request, id: int, query_db: AsyncSession = Depends(get_db)):
+# identification_statistics_detail_result = await Identification_statisticsService.identification_statistics_detail_services(query_db, id)
+# logger.info(f'获取id为{id}的信息成功')
+
+# return ResponseUtil.success(data=identification_statistics_detail_result)
+
+
+# @identification_statisticsController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:identification_statistics:export'))])
+# @Log(title='识别统计', business_type=BusinessType.EXPORT)
+# async def export_system_identification_statistics_list(
+# request: Request,
+# identification_statistics_page_query: Identification_statisticsPageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# identification_statistics_query_result = await Identification_statisticsService.get_identification_statistics_list_services(query_db, identification_statistics_page_query, is_page=False)
+# identification_statistics_export_result = await Identification_statisticsService.export_identification_statistics_list_services(identification_statistics_query_result)
+# logger.info('导出成功')
+
+# return ResponseUtil.streaming(data=bytes2file_response(identification_statistics_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/info_controller.py b/ruoyi-fastapi-backend/module_admin/controller/info_controller.py
new file mode 100644
index 0000000..91469a1
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/info_controller.py
@@ -0,0 +1,131 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.info_service import InfoService
+from module_admin.entity.vo.info_vo import DeleteInfoModel, InfoModel, InfoPageQueryModel, InfoUpdateModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+infoController = APIRouter(prefix='/robot/info', dependencies=[Depends(LoginService.get_current_user)])
+
+
+@infoController.get(
+ '/list', response_model=PageResponseModel
+ # , dependencies=[Depends(CheckUserInterfaceAuth('robot:info:list'))]
+)
+async def get_system_info_list(
+ request: Request,
+info_page_query: InfoPageQueryModel = Depends(InfoPageQueryModel.as_query),
+ query_db: AsyncSession = Depends(get_db),
+):
+ print('xxxxxxxxx', info_page_query.begin_create_time)
+ print('xxxxxxxxx',info_page_query.end_create_time)
+ print(info_page_query.name)
+ # 获取分页数据
+ info_page_query_result = await InfoService.get_info_list_services(query_db, info_page_query, is_page=True)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=info_page_query_result)
+
+
+@infoController.post('/add_robot'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('robot:info:add'))]
+ )
+@ValidateFields(validate_model='add_info')
+@Log(title='机器人信息', business_type=BusinessType.INSERT)
+async def add_system_info(
+ request: Request,
+ add_info: InfoModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_info.create_time = datetime.now()
+ add_info.create_by = current_user.user.user_name
+ add_info.update_time = datetime.now()
+ add_info.update_by = current_user.user.user_name
+ add_info.del_flag = '0'
+ add_info.online = '1'
+ add_info.power = '100'
+ add_info_result = await InfoService.add_info_services(query_db, add_info)
+ logger.info(add_info_result.message)
+
+ return ResponseUtil.success(msg=add_info_result.message)
+
+
+@infoController.put('/edit_robot'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('robot:info:edit'))]
+ )
+@ValidateFields(validate_model='edit_info')
+@Log(title='机器人信息', business_type=BusinessType.UPDATE)
+async def edit_system_info(
+ request: Request,
+ edit_info: InfoUpdateModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_info.update_by = current_user.user.user_name
+ edit_info.update_time = datetime.now()
+ edit_info_result = await InfoService.edit_info_services(query_db, edit_info)
+ logger.info(edit_info_result.message)
+
+ return ResponseUtil.success(msg=edit_info_result.message)
+
+
+@infoController.delete('/{robot_ids}'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('robot:info:remove'))]
+ )
+@Log(title='机器人信息', business_type=BusinessType.DELETE)
+async def delete_system_info(request: Request, robot_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_info = DeleteInfoModel(robotIds=robot_ids)
+ delete_info_result = await InfoService.delete_info_services(query_db, delete_info)
+ logger.info(delete_info_result.message)
+
+ return ResponseUtil.success(msg=delete_info_result.message)
+
+
+@infoController.get(
+ '/{robot_id}', response_model=InfoModel
+ # , dependencies=[Depends(CheckUserInterfaceAuth('robot:info:query'))]
+)
+async def query_detail_system_info(request: Request, robot_id: int, query_db: AsyncSession = Depends(get_db)):
+ info_detail_result = await InfoService.info_detail_services(query_db, robot_id)
+ logger.info(f'获取robot_id为{robot_id}的信息成功')
+
+ return ResponseUtil.success(data=info_detail_result)
+
+# 根据机器人id刷新机器人信息
+@infoController.get('/refresh_robot/{robot_ids}'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:info:add'))]
+)
+async def refresh_robot(request: Request,robot_ids: str , query_db: AsyncSession = Depends(get_db)):
+ refresh_result = await InfoService.refresh_robot_services(query_db, robot_ids)
+ logger.info(f'刷新机器人信息成功')
+
+ return ResponseUtil.success(data=refresh_result)
+
+
+@infoController.post('/export'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:info:export'))]
+ )
+@Log(title='机器人信息', business_type=BusinessType.EXPORT)
+async def export_system_info_list(
+ request: Request,
+ info_page_query: InfoPageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取全量数据
+ info_query_result = await InfoService.get_info_list_services(query_db, info_page_query, is_page=False)
+ info_export_result = await InfoService.export_info_list_services(info_query_result)
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(info_export_result))
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..c930569
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/job_controller.py
@@ -0,0 +1,194 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.job_vo import (
+ DeleteJobLogModel,
+ DeleteJobModel,
+ EditJobModel,
+ JobLogPageQueryModel,
+ JobModel,
+ JobPageQueryModel,
+)
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.job_log_service import JobLogService
+from module_admin.service.job_service import JobService
+from module_admin.service.login_service import LoginService
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+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: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ notice_page_query_result = await JobService.get_job_list_services(query_db, job_page_query, is_page=True)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=notice_page_query_result)
+
+
+@jobController.post('/job', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:add'))])
+@ValidateFields(validate_model='add_job')
+@Log(title='定时任务', business_type=BusinessType.INSERT)
+async def add_system_job(
+ request: Request,
+ add_job: JobModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_job.create_by = current_user.user.user_name
+ add_job.create_time = datetime.now()
+ add_job.update_by = current_user.user.user_name
+ add_job.update_time = datetime.now()
+ add_job_result = await JobService.add_job_services(query_db, add_job)
+ logger.info(add_job_result.message)
+
+ return ResponseUtil.success(msg=add_job_result.message)
+
+
+@jobController.put('/job', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:edit'))])
+@ValidateFields(validate_model='edit_job')
+@Log(title='定时任务', business_type=BusinessType.UPDATE)
+async def edit_system_job(
+ request: Request,
+ edit_job: EditJobModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_job.update_by = current_user.user.user_name
+ edit_job.update_time = datetime.now()
+ edit_job_result = await JobService.edit_job_services(query_db, edit_job)
+ logger.info(edit_job_result.message)
+
+ return ResponseUtil.success(msg=edit_job_result.message)
+
+
+@jobController.put('/job/changeStatus', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:changeStatus'))])
+@Log(title='定时任务', business_type=BusinessType.UPDATE)
+async def change_system_job_status(
+ request: Request,
+ change_job: EditJobModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_job = EditJobModel(
+ jobId=change_job.job_id,
+ status=change_job.status,
+ updateBy=current_user.user.user_name,
+ updateTime=datetime.now(),
+ type='status',
+ )
+ edit_job_result = await JobService.edit_job_services(query_db, edit_job)
+ logger.info(edit_job_result.message)
+
+ return ResponseUtil.success(msg=edit_job_result.message)
+
+
+@jobController.put('/job/run', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:changeStatus'))])
+@Log(title='定时任务', business_type=BusinessType.UPDATE)
+async def execute_system_job(request: Request, execute_job: JobModel, query_db: AsyncSession = Depends(get_db)):
+ execute_job_result = await JobService.execute_job_once_services(query_db, execute_job)
+ logger.info(execute_job_result.message)
+
+ return ResponseUtil.success(msg=execute_job_result.message)
+
+
+@jobController.delete('/job/{job_ids}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:remove'))])
+@Log(title='定时任务', business_type=BusinessType.DELETE)
+async def delete_system_job(request: Request, job_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_job = DeleteJobModel(jobIds=job_ids)
+ delete_job_result = await JobService.delete_job_services(query_db, delete_job)
+ logger.info(delete_job_result.message)
+
+ return ResponseUtil.success(msg=delete_job_result.message)
+
+
+@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: AsyncSession = Depends(get_db)):
+ job_detail_result = await JobService.job_detail_services(query_db, job_id)
+ logger.info(f'获取job_id为{job_id}的信息成功')
+
+ return ResponseUtil.success(data=job_detail_result)
+
+
+@jobController.post('/job/export', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:export'))])
+@Log(title='定时任务', business_type=BusinessType.EXPORT)
+async def export_system_job_list(
+ request: Request,
+ job_page_query: JobPageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取全量数据
+ job_query_result = await JobService.get_job_list_services(query_db, job_page_query, is_page=False)
+ job_export_result = await JobService.export_job_list_services(request, job_query_result)
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(job_export_result))
+
+
+@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: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ job_log_page_query_result = await JobLogService.get_job_log_list_services(
+ query_db, job_log_page_query, is_page=True
+ )
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=job_log_page_query_result)
+
+
+@jobController.delete('/jobLog/clean', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:remove'))])
+@Log(title='定时任务调度日志', business_type=BusinessType.CLEAN)
+async def clear_system_job_log(request: Request, query_db: AsyncSession = Depends(get_db)):
+ clear_job_log_result = await JobLogService.clear_job_log_services(query_db)
+ logger.info(clear_job_log_result.message)
+
+ return ResponseUtil.success(msg=clear_job_log_result.message)
+
+
+@jobController.delete('/jobLog/{job_log_ids}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:remove'))])
+@Log(title='定时任务调度日志', business_type=BusinessType.DELETE)
+async def delete_system_job_log(request: Request, job_log_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_job_log = DeleteJobLogModel(jobLogIds=job_log_ids)
+ delete_job_log_result = await JobLogService.delete_job_log_services(query_db, delete_job_log)
+ logger.info(delete_job_log_result.message)
+
+ return ResponseUtil.success(msg=delete_job_log_result.message)
+
+
+@jobController.post('/jobLog/export', dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:export'))])
+@Log(title='定时任务调度日志', business_type=BusinessType.EXPORT)
+async def export_system_job_log_list(
+ request: Request,
+ job_log_page_query: JobLogPageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取全量数据
+ job_log_query_result = await JobLogService.get_job_log_list_services(query_db, job_log_page_query, is_page=False)
+ job_log_export_result = await JobLogService.export_job_log_list_services(request, job_log_query_result)
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(job_log_export_result))
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..a01ba94
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/log_controller.py
@@ -0,0 +1,150 @@
+from fastapi import APIRouter, Depends, Form, Request
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.log_vo import (
+ DeleteLoginLogModel,
+ DeleteOperLogModel,
+ LoginLogPageQueryModel,
+ OperLogPageQueryModel,
+ UnlockUser,
+)
+from module_admin.service.log_service import LoginLogService, OperationLogService
+from module_admin.service.login_service import LoginService
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+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: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ operation_log_page_query_result = await OperationLogService.get_operation_log_list_services(
+ query_db, operation_log_page_query, is_page=True
+ )
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=operation_log_page_query_result)
+
+
+@logController.delete('/operlog/clean', dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:remove'))])
+@Log(title='操作日志', business_type=BusinessType.CLEAN)
+async def clear_system_operation_log(request: Request, query_db: AsyncSession = Depends(get_db)):
+ clear_operation_log_result = await OperationLogService.clear_operation_log_services(query_db)
+ logger.info(clear_operation_log_result.message)
+
+ return ResponseUtil.success(msg=clear_operation_log_result.message)
+
+
+@logController.delete('/operlog/{oper_ids}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:remove'))])
+@Log(title='操作日志', business_type=BusinessType.DELETE)
+async def delete_system_operation_log(request: Request, oper_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_operation_log = DeleteOperLogModel(operIds=oper_ids)
+ delete_operation_log_result = await OperationLogService.delete_operation_log_services(
+ query_db, delete_operation_log
+ )
+ logger.info(delete_operation_log_result.message)
+
+ return ResponseUtil.success(msg=delete_operation_log_result.message)
+
+
+@logController.post('/operlog/export', dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:export'))])
+@Log(title='操作日志', business_type=BusinessType.EXPORT)
+async def export_system_operation_log_list(
+ request: Request,
+ operation_log_page_query: OperLogPageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取全量数据
+ operation_log_query_result = await OperationLogService.get_operation_log_list_services(
+ query_db, operation_log_page_query, is_page=False
+ )
+ operation_log_export_result = await OperationLogService.export_operation_log_list_services(
+ request, operation_log_query_result
+ )
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(operation_log_export_result))
+
+
+@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: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ login_log_page_query_result = await LoginLogService.get_login_log_list_services(
+ query_db, login_log_page_query, is_page=True
+ )
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=login_log_page_query_result)
+
+
+@logController.delete('/logininfor/clean', dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:remove'))])
+@Log(title='登录日志', business_type=BusinessType.CLEAN)
+async def clear_system_login_log(request: Request, query_db: AsyncSession = Depends(get_db)):
+ clear_login_log_result = await LoginLogService.clear_login_log_services(query_db)
+ logger.info(clear_login_log_result.message)
+
+ return ResponseUtil.success(msg=clear_login_log_result.message)
+
+
+@logController.delete(
+ '/logininfor/{info_ids}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:remove'))]
+)
+@Log(title='登录日志', business_type=BusinessType.DELETE)
+async def delete_system_login_log(request: Request, info_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_login_log = DeleteLoginLogModel(infoIds=info_ids)
+ delete_login_log_result = await LoginLogService.delete_login_log_services(query_db, delete_login_log)
+ logger.info(delete_login_log_result.message)
+
+ return ResponseUtil.success(msg=delete_login_log_result.message)
+
+
+@logController.get(
+ '/logininfor/unlock/{user_name}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:unlock'))]
+)
+@Log(title='账户解锁', business_type=BusinessType.OTHER)
+async def unlock_system_user(request: Request, user_name: str, query_db: AsyncSession = Depends(get_db)):
+ unlock_user = UnlockUser(userName=user_name)
+ unlock_user_result = await LoginLogService.unlock_user_services(request, unlock_user)
+ logger.info(unlock_user_result.message)
+
+ return ResponseUtil.success(msg=unlock_user_result.message)
+
+
+@logController.post('/logininfor/export', dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:export'))])
+@Log(title='登录日志', business_type=BusinessType.EXPORT)
+async def export_system_login_log_list(
+ request: Request,
+ login_log_page_query: LoginLogPageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取全量数据
+ login_log_query_result = await LoginLogService.get_login_log_list_services(
+ query_db, login_log_page_query, is_page=False
+ )
+ login_log_export_result = await LoginLogService.export_login_log_list_services(login_log_query_result)
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(login_log_export_result))
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..9a255ef
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/login_controller.py
@@ -0,0 +1,155 @@
+import jwt
+import uuid
+from datetime import datetime, timedelta
+from fastapi import APIRouter, Depends, Request
+from sqlalchemy.ext.asyncio import AsyncSession
+from typing import Optional
+from config.enums import BusinessType, RedisInitKeyConfig
+from config.env import AppConfig, JwtConfig
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.entity.vo.common_vo import CrudResponseModel
+from module_admin.entity.vo.login_vo import UserLogin, UserRegister, Token
+from module_admin.entity.vo.user_vo import CurrentUserModel, EditUserModel
+from module_admin.service.login_service import CustomOAuth2PasswordRequestForm, LoginService, oauth2_scheme
+from module_admin.service.user_service import UserService
+from utils.log_util import logger
+from utils.response_util import ResponseUtil
+
+
+loginController = APIRouter()
+
+
+@loginController.post('/login', response_model=Token)
+@Log(title='用户登录', business_type=BusinessType.OTHER, log_type='login')
+async def login(
+ request: Request, form_data: CustomOAuth2PasswordRequestForm = Depends(), query_db: AsyncSession = Depends(get_db)
+):
+ # 是否启用验证码
+ # captcha_enabled = (
+ # True
+ # if await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.captchaEnabled')
+ # == 'true'
+ # else False
+ # )
+
+ captcha_enabled =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,
+ )
+ result = await LoginService.authenticate_user(request, query_db, user)
+ access_token_expires = timedelta(minutes=JwtConfig.jwt_expire_minutes)
+ session_id = str(uuid.uuid4())
+ access_token = await LoginService.create_access_token(
+ data={
+ 'user_id': str(result[0].user_id),
+ 'user_name': result[0].user_name,
+ 'dept_name': result[1].dept_name if result[1] else None,
+ 'session_id': session_id,
+ 'login_info': user.login_info,
+ },
+ expires_delta=access_token_expires,
+ )
+ if AppConfig.app_same_time_login:
+ await request.app.state.redis.set(
+ f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{session_id}',
+ access_token,
+ ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes),
+ )
+ else:
+ # 此方法可实现同一账号同一时间只能登录一次
+ await request.app.state.redis.set(
+ f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{result[0].user_id}',
+ access_token,
+ ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes),
+ )
+ await UserService.edit_user_services(
+ query_db, EditUserModel(userId=result[0].user_id, loginDate=datetime.now(), type='status')
+ )
+ logger.info('登录成功')
+ # 判断请求是否来自于api文档,如果是返回指定格式的结果,用于修复api文档认证成功后token显示undefined的bug
+ request_from_swagger = request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False
+ request_from_redoc = request.headers.get('referer').endswith('redoc') if request.headers.get('referer') else False
+ if request_from_swagger or request_from_redoc:
+ return {'access_token': access_token, 'token_type': 'Bearer'}
+ return ResponseUtil.success(msg='登录成功', dict_content={'token': access_token})
+
+
+@loginController.get('/getInfo', response_model=CurrentUserModel)
+async def get_login_user_info(
+ request: Request, current_user: CurrentUserModel = Depends(LoginService.get_current_user)
+):
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=current_user)
+
+
+@loginController.get('/getRouters')
+async def get_login_user_routers(
+ request: Request,
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ query_db: AsyncSession = Depends(get_db),
+):
+ logger.info('获取成功')
+ user_routers = await LoginService.get_current_user_routers(current_user.user.user_id, query_db)
+
+ return ResponseUtil.success(data=user_routers)
+
+
+@loginController.post('/register', response_model=CrudResponseModel)
+async def register_user(request: Request, user_register: UserRegister, query_db: AsyncSession = Depends(get_db)):
+ user_register_result = await LoginService.register_user_services(request, query_db, user_register)
+ logger.info(user_register_result.message)
+
+ return ResponseUtil.success(data=user_register_result, msg=user_register_result.message)
+
+
+# @loginController.post("/getSmsCode", response_model=SmsCode)
+# async def get_sms_code(request: Request, user: ResetUserModel, query_db: AsyncSession = Depends(get_db)):
+# try:
+# sms_result = await LoginService.get_sms_code_services(request, query_db, user)
+# if sms_result.is_success:
+# logger.info('获取成功')
+# return ResponseUtil.success(data=sms_result)
+# else:
+# logger.warning(sms_result.message)
+# return ResponseUtil.failure(msg=sms_result.message)
+# except Exception as e:
+# logger.exception(e)
+# return ResponseUtil.error(msg=str(e))
+#
+#
+# @loginController.post("/forgetPwd", response_model=CrudResponseModel)
+# async def forget_user_pwd(request: Request, forget_user: ResetUserModel, query_db: AsyncSession = Depends(get_db)):
+# try:
+# forget_user_result = await LoginService.forget_user_services(request, query_db, forget_user)
+# if forget_user_result.is_success:
+# logger.info(forget_user_result.message)
+# return ResponseUtil.success(data=forget_user_result, msg=forget_user_result.message)
+# else:
+# logger.warning(forget_user_result.message)
+# return ResponseUtil.failure(msg=forget_user_result.message)
+# except Exception as e:
+# logger.exception(e)
+# return ResponseUtil.error(msg=str(e))
+
+
+@loginController.post('/logout')
+async def logout(request: Request
+ # , token: Optional[str] = Depends(oauth2_scheme)
+ ):
+ token = request.headers.get('Authorization').split(" ")[-1]
+ payload = jwt.decode(
+ token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm], options={'verify_exp': False}
+ )
+ session_id: str = payload.get('session_id')
+ await LoginService.logout_services(request, session_id)
+ logger.info('退出成功')
+
+ return ResponseUtil.success(msg='退出成功')
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..0e3124e
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/menu_controller.py
@@ -0,0 +1,114 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from typing import List
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.menu_vo import DeleteMenuModel, MenuModel, MenuQueryModel
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.menu_service import MenuService
+from utils.log_util import logger
+from utils.response_util import ResponseUtil
+
+
+menuController = APIRouter(prefix='/system/menu', dependencies=[Depends(LoginService.get_current_user)])
+
+
+@menuController.get('/treeselect')
+async def get_system_menu_tree(
+ request: Request,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ menu_query_result = await MenuService.get_menu_tree_services(query_db, current_user)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=menu_query_result)
+
+
+@menuController.get('/roleMenuTreeselect/{role_id}')
+async def get_system_role_menu_tree(
+ request: Request,
+ role_id: int,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ role_menu_query_result = await MenuService.get_role_menu_tree_services(query_db, role_id, current_user)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=role_menu_query_result)
+
+
+@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: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ menu_query_result = await MenuService.get_menu_list_services(query_db, menu_query, current_user)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=menu_query_result)
+
+
+@menuController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:menu:add'))])
+@ValidateFields(validate_model='add_menu')
+@Log(title='菜单管理', business_type=BusinessType.INSERT)
+async def add_system_menu(
+ request: Request,
+ add_menu: MenuModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_menu.create_by = current_user.user.user_name
+ add_menu.create_time = datetime.now()
+ add_menu.update_by = current_user.user.user_name
+ add_menu.update_time = datetime.now()
+ add_menu_result = await MenuService.add_menu_services(query_db, add_menu)
+ logger.info(add_menu_result.message)
+
+ return ResponseUtil.success(msg=add_menu_result.message)
+
+
+@menuController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:menu:edit'))])
+@ValidateFields(validate_model='edit_menu')
+@Log(title='菜单管理', business_type=BusinessType.UPDATE)
+async def edit_system_menu(
+ request: Request,
+ edit_menu: MenuModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_menu.update_by = current_user.user.user_name
+ edit_menu.update_time = datetime.now()
+ edit_menu_result = await MenuService.edit_menu_services(query_db, edit_menu)
+ logger.info(edit_menu_result.message)
+
+ return ResponseUtil.success(msg=edit_menu_result.message)
+
+
+@menuController.delete('/{menu_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:menu:remove'))])
+@Log(title='菜单管理', business_type=BusinessType.DELETE)
+async def delete_system_menu(request: Request, menu_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_menu = DeleteMenuModel(menuIds=menu_ids)
+ delete_menu_result = await MenuService.delete_menu_services(query_db, delete_menu)
+ logger.info(delete_menu_result.message)
+
+ return ResponseUtil.success(msg=delete_menu_result.message)
+
+
+@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: AsyncSession = Depends(get_db)):
+ menu_detail_result = await MenuService.menu_detail_services(query_db, menu_id)
+ logger.info(f'获取menu_id为{menu_id}的信息成功')
+
+ return ResponseUtil.success(data=menu_detail_result)
diff --git a/ruoyi-fastapi-backend/module_admin/controller/message_controller.py b/ruoyi-fastapi-backend/module_admin/controller/message_controller.py
new file mode 100644
index 0000000..3da00e6
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/message_controller.py
@@ -0,0 +1,163 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.common_vo import CrudResponseModel
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.message_service import MessageService
+from module_admin.entity.vo.message_vo import DeleteMessageModel, MessageModel, MessagePageQueryModel, EditMessageModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+messageController = APIRouter(prefix='/system/message', dependencies=[Depends(LoginService.get_current_user)])
+
+
+# 查看告警类型分布
+@messageController.get(
+ '/alarm_type_distribution'
+)
+async def get_alarm_type_distribution(
+ request: Request,
+ query_db: AsyncSession = Depends(get_db),
+):
+ result = await MessageService.get_alarm_type_distribution_services(query_db)
+ return ResponseUtil.success(data=result)
+
+
+
+# 一键处理未处理的告警消息
+@messageController.get(
+ '/handle_all'
+)
+async def handle_all_message(
+ query_db: AsyncSession = Depends(get_db),
+):
+ result = await MessageService.handle_all_message_services(query_db)
+ return ResponseUtil.success(msg= result.message)
+
+
+# 查询告警数量信息
+@messageController.get(
+ '/count_message',
+)
+async def count_message(request: Request, query_db: AsyncSession = Depends(get_db)):
+ """"""
+ count_message_result = await MessageService.count_message(query_db)
+ return ResponseUtil.success(data = count_message_result)
+
+
+
+# # 获取所有消息列表
+# @messageController.get(
+# '/list', response_model=PageResponseModel
+# # , dependencies=[Depends(CheckUserInterfaceAuth('system:message:list'))]
+# )
+# async def get_system_message_list(
+# request: Request,
+# message_page_query: MessagePageQueryModel = Depends(MessagePageQueryModel.as_query),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取分页数据
+# message_page_query_result = await MessageService.get_message_list_services(query_db, message_page_query, is_page=True)
+# logger.info('获取成功')
+
+# return ResponseUtil.success(model_content=message_page_query_result)
+
+# 获取告警消息列表
+@messageController.get('/alert_message')
+async def get_alert_message(
+ request: Request,
+ pageNum: int = 1,
+ pageSize: int = 10,
+ query_db: AsyncSession = Depends(get_db),
+):
+ alert_message = await MessageService.get_alert_message_services(query_db, pageNum, pageSize)
+ return ResponseUtil.success(model_content=alert_message)
+
+
+# 该接口只做测试用, 并不对外开放
+# @messageController.post('/add_message'
+# # , dependencies=[Depends(CheckUserInterfaceAuth('system:message:add'))]
+# )
+# @ValidateFields(validate_model='add_message')
+# @Log(title='系统消息', business_type=BusinessType.INSERT)
+# async def add_system_message(
+# request: Request,
+# add_message: MessageModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# add_message.create_time = datetime.now()
+# add_message.create_by = current_user.user.user_name
+# add_message.update_time = datetime.now()
+# add_message.update_by = current_user.user.user_name
+# add_message.status = '0'
+# add_message_result = await MessageService.add_message_services(query_db, add_message)
+# logger.info(add_message_result.message)
+
+# return ResponseUtil.success(msg=add_message_result.message)
+
+
+# @messageController.put('/edit_message'
+# # , dependencies=[Depends(CheckUserInterfaceAuth('system:message:edit'))]
+# )
+# @ValidateFields(validate_model='edit_message')
+# @Log(title='系统消息', business_type=BusinessType.UPDATE)
+# async def edit_system_message(
+# request: Request,
+# edit_message: EditMessageModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# edit_message.update_by = current_user.user.user_name
+# edit_message.update_time = datetime.now()
+# edit_message_result = await MessageService.edit_message_services(query_db, edit_message)
+# logger.info(edit_message_result.message)
+
+# return ResponseUtil.success(msg=edit_message_result.message)
+
+
+# @messageController.delete('/{message_ids}'
+# , dependencies=[Depends(CheckUserInterfaceAuth('system:message:remove'))]
+# )
+# @Log(title='系统消息', business_type=BusinessType.DELETE)
+# async def delete_system_message(request: Request, message_ids: str, query_db: AsyncSession = Depends(get_db)):
+# delete_message = DeleteMessageModel(messageIds=message_ids)
+# delete_message_result = await MessageService.delete_message_services(query_db, delete_message)
+# logger.info(delete_message_result.message)
+
+# return ResponseUtil.success(msg=delete_message_result.message)
+
+
+# @messageController.get(
+# '/{message_id}', response_model=MessageModel
+# # , dependencies=[Depends(CheckUserInterfaceAuth('system:message:query'))]
+# )
+# async def query_detail_system_message(request: Request, message_id: int, query_db: AsyncSession = Depends(get_db)):
+# message_detail_result = await MessageService.message_detail_services(query_db, message_id)
+# logger.info(f'获取message_id为{message_id}的信息成功')
+
+# return ResponseUtil.success(data=message_detail_result)
+
+
+# @messageController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:message:export'))])
+# @Log(title='系统消息', business_type=BusinessType.EXPORT)
+# async def export_system_message_list(
+# request: Request,
+# message_page_query: MessagePageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# message_query_result = await MessageService.get_message_list_services(query_db, message_page_query, is_page=False)
+# message_export_result = await MessageService.export_message_list_services(message_query_result)
+# logger.info('导出成功')
+#
+# return ResponseUtil.streaming(data=bytes2file_response(message_export_result))
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..e1e4aa1
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/notice_controller.py
@@ -0,0 +1,89 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.notice_vo import DeleteNoticeModel, NoticeModel, NoticePageQueryModel
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.notice_service import NoticeService
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+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: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ notice_page_query_result = await NoticeService.get_notice_list_services(query_db, notice_page_query, is_page=True)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=notice_page_query_result)
+
+
+@noticeController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:notice:add'))])
+@ValidateFields(validate_model='add_notice')
+@Log(title='通知公告', business_type=BusinessType.INSERT)
+async def add_system_notice(
+ request: Request,
+ add_notice: NoticeModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_notice.create_by = current_user.user.user_name
+ add_notice.create_time = datetime.now()
+ add_notice.update_by = current_user.user.user_name
+ add_notice.update_time = datetime.now()
+ add_notice_result = await NoticeService.add_notice_services(query_db, add_notice)
+ logger.info(add_notice_result.message)
+
+ return ResponseUtil.success(msg=add_notice_result.message)
+
+
+@noticeController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:notice:edit'))])
+@ValidateFields(validate_model='edit_notice')
+@Log(title='通知公告', business_type=BusinessType.UPDATE)
+async def edit_system_notice(
+ request: Request,
+ edit_notice: NoticeModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_notice.update_by = current_user.user.user_name
+ edit_notice.update_time = datetime.now()
+ edit_notice_result = await NoticeService.edit_notice_services(query_db, edit_notice)
+ logger.info(edit_notice_result.message)
+
+ return ResponseUtil.success(msg=edit_notice_result.message)
+
+
+@noticeController.delete('/{notice_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:notice:remove'))])
+@Log(title='通知公告', business_type=BusinessType.DELETE)
+async def delete_system_notice(request: Request, notice_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_notice = DeleteNoticeModel(noticeIds=notice_ids)
+ delete_notice_result = await NoticeService.delete_notice_services(query_db, delete_notice)
+ logger.info(delete_notice_result.message)
+
+ return ResponseUtil.success(msg=delete_notice_result.message)
+
+
+@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: AsyncSession = Depends(get_db)):
+ notice_detail_result = await NoticeService.notice_detail_services(query_db, notice_id)
+ logger.info(f'获取notice_id为{notice_id}的信息成功')
+
+ return ResponseUtil.success(data=notice_detail_result)
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..bf65c8f
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/online_controller.py
@@ -0,0 +1,40 @@
+from fastapi import APIRouter, Depends, Request
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.online_vo import DeleteOnlineModel, OnlineQueryModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.online_service import OnlineService
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+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)
+):
+ # 获取全量数据
+ 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))
+ )
+
+
+@onlineController.delete('/{token_ids}', dependencies=[Depends(CheckUserInterfaceAuth('monitor:online:forceLogout'))])
+@Log(title='在线用户', business_type=BusinessType.FORCE)
+async def delete_monitor_online(request: Request, token_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_online = DeleteOnlineModel(tokenIds=token_ids)
+ delete_online_result = await OnlineService.delete_online_services(request, delete_online)
+ logger.info(delete_online_result.message)
+
+ return ResponseUtil.success(msg=delete_online_result.message)
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..e9c69c4
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/post_controler.py
@@ -0,0 +1,105 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.service.login_service import LoginService
+from module_admin.service.post_service import PostService
+from module_admin.entity.vo.post_vo import DeletePostModel, PostModel, PostPageQueryModel
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+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: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ post_page_query_result = await PostService.get_post_list_services(query_db, post_page_query, is_page=True)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=post_page_query_result)
+
+
+@postController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:post:add'))])
+@ValidateFields(validate_model='add_post')
+@Log(title='岗位管理', business_type=BusinessType.INSERT)
+async def add_system_post(
+ request: Request,
+ add_post: PostModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_post.create_by = current_user.user.user_name
+ add_post.create_time = datetime.now()
+ add_post.update_by = current_user.user.user_name
+ add_post.update_time = datetime.now()
+ add_post_result = await PostService.add_post_services(query_db, add_post)
+ logger.info(add_post_result.message)
+
+ return ResponseUtil.success(msg=add_post_result.message)
+
+
+@postController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:post:edit'))])
+@ValidateFields(validate_model='edit_post')
+@Log(title='岗位管理', business_type=BusinessType.UPDATE)
+async def edit_system_post(
+ request: Request,
+ edit_post: PostModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_post.update_by = current_user.user.user_name
+ edit_post.update_time = datetime.now()
+ edit_post_result = await PostService.edit_post_services(query_db, edit_post)
+ logger.info(edit_post_result.message)
+
+ return ResponseUtil.success(msg=edit_post_result.message)
+
+
+@postController.delete('/{post_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:post:remove'))])
+@Log(title='岗位管理', business_type=BusinessType.DELETE)
+async def delete_system_post(request: Request, post_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_post = DeletePostModel(postIds=post_ids)
+ delete_post_result = await PostService.delete_post_services(query_db, delete_post)
+ logger.info(delete_post_result.message)
+
+ return ResponseUtil.success(msg=delete_post_result.message)
+
+
+@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: AsyncSession = Depends(get_db)):
+ post_detail_result = await PostService.post_detail_services(query_db, post_id)
+ logger.info(f'获取post_id为{post_id}的信息成功')
+
+ return ResponseUtil.success(data=post_detail_result)
+
+
+@postController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:post:export'))])
+@Log(title='岗位管理', business_type=BusinessType.EXPORT)
+async def export_system_post_list(
+ request: Request,
+ post_page_query: PostPageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取全量数据
+ post_query_result = await PostService.get_post_list_services(query_db, post_page_query, is_page=False)
+ post_export_result = await PostService.export_post_list_services(post_query_result)
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(post_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/ragflow_controller.py b/ruoyi-fastapi-backend/module_admin/controller/ragflow_controller.py
new file mode 100644
index 0000000..2264548
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/ragflow_controller.py
@@ -0,0 +1,295 @@
+# from datetime import datetime
+import hashlib
+import json
+import time
+from typing import List
+from fastapi import APIRouter, Depends, Request, UploadFile, File
+from fastapi.responses import StreamingResponse
+# from pydantic_validation_decorator import ValidateFields
+# from sqlalchemy.ext.asyncio import AsyncSession
+# from config.enums import BusinessType
+# from config.get_db import get_db
+from module_admin.aspect.interface_auth import CheckRoleInterfaceAuth
+# from module_admin.entity.vo.notice_vo import DeleteNoticeModel, NoticeModel, NoticePageQueryModel
+# from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.ragflow_service import RAGFlowService
+from module_admin.service.weather_service import WeatherService, WeatherServiceError
+
+from utils.log_util import logger
+# from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+from module_admin.entity.vo.ragflow_vo import RagflowListQueryModel, ListDocumentsQueryModel, UpdateFileModel, DeleteFileModel, CreateDatasetModel, DocumentIdsModel, UpdateChatAssistantModel,\
+ CreateSessionWithChatModel, ConverseWithChatAssistantModel
+# from config.env import RAGFlowConfig
+
+
+
+ragflowController = APIRouter(prefix="/system/ragflow", dependencies=[Depends(LoginService.get_current_user)])
+
+
+
+# 查看数据集列表
+@ragflowController.post("/dataset_list"
+ # , response_model=PageResponseModel
+ # , dependencies=[Depends(CheckUserInterfaceAuth("system:ragflow:list"))]"
+ )
+async def get_system_ragflow_list(
+ request: Request,
+ rage_flow_dastset_query: RagflowListQueryModel ,
+ # query_db: AsyncSession = Depends(get_db),
+):
+
+ result = await RAGFlowService.get_ragflow_dataset_list_services(None, rage_flow_dastset_query)
+
+ return parse_result(result)
+
+# 创建数据集
+@ragflowController.post('/create_dataset')
+async def create_dataset(
+ request: Request,
+ create_dataset_params: CreateDatasetModel,
+):
+
+ result = await RAGFlowService.create_dataset_services(create_dataset_params)
+ return parse_result(result)
+
+# 更新数据集
+@ragflowController.post('/update_dataset/{dataset_id}')
+async def update_dataset(
+ request: Request,
+ dataset_id: str,
+ update_dataset_params: CreateDatasetModel,
+):
+ result = await RAGFlowService.update_dataset_services(dataset_id, update_dataset_params)
+ return parse_result(result)
+
+
+# 列出数据集中文档列表
+@ragflowController.get("/list_documents/{dataset_id}")
+async def list_documents_by_dataset_id(
+ request: Request,
+ dataset_id: str,
+ list_documents_query: ListDocumentsQueryModel = Depends(ListDocumentsQueryModel.as_query),
+
+ # query_db: AsyncSession = Depends(get_db),
+):
+ """
+ 列出数据集中文档列表
+ """
+ print(list_documents_query)
+ result = await RAGFlowService.list_documents_services(None, dataset_id, list_documents_query)
+
+ return parse_result(result)
+
+
+# 上传文件到数据集
+@ragflowController.post("/upload_file/{dataset_id}")
+async def upload_file_dataset(
+ dataset_id: str,
+ files: List[UploadFile] = File(...),
+ # query_db: AsyncSession = Depends(get_db),
+):
+ """
+ 上传文件到数据集
+ """
+ # print(file)
+ result = await RAGFlowService.upload_file_dataset_services(None, dataset_id ,files)
+
+ return parse_result(result)
+
+# 更新文档
+@ragflowController.post("/update_file/{dataset_id}/{document_id}")
+async def update_file_dataset(
+ dataset_id: str,
+ document_id: str,
+ update_params: UpdateFileModel,
+ # query_db: AsyncSession = Depends(get_db),
+):
+ """
+ 更新文件到数据集
+ """
+ # print(file)
+ result = await RAGFlowService.update_file_dataset_services(dataset_id ,document_id, update_params)
+
+ return parse_result(result)
+
+# 开始解析文档
+@ragflowController.post('/parse_documents/{dataset_id}')
+async def parse_documents(
+ dataset_id: str,
+ parse_params: DocumentIdsModel,
+ # query_db: AsyncSession = Depends(get_db),
+):
+ result = await RAGFlowService.parse_documents_services(dataset_id, parse_params)
+ return parse_result(result)
+
+# 停止解析文档
+@ragflowController.post('/stop_parse_documents/{dataset_id}')
+async def stop_parse_documents(
+ dataset_id: str,
+ parse_params: DocumentIdsModel,
+ # query_db: AsyncSession = Depends(get_db),
+):
+ result = await RAGFlowService.stop_parse_documents_services(dataset_id, parse_params)
+ return parse_result(result)
+
+# 删除文档
+@ragflowController.post('/delete_file/{dataset_id}')
+async def delete_file(
+ dataset_id: str,
+ delete_params: DeleteFileModel,
+ # query_db: AsyncSession = Depends(get_db),
+):
+ """
+ 删除文件
+ """
+ result = await RAGFlowService.delete_file_services(dataset_id, delete_params)
+
+ return parse_result(result)
+
+
+# 删除数据集
+@ragflowController.post('/delete_datasets')
+async def delete_datasets(
+ delete_params: DeleteFileModel,
+ # query_db: AsyncSession = Depends(get_db),
+):
+ """
+ 删除数据集
+ """
+ result = await RAGFlowService.delete_datasets_services(delete_params)
+ return parse_result(result)
+
+# 查看聊天助手列表
+@ragflowController.post('/get_chat_assistant_list')
+async def get_chat_assistant_list(
+ query_params: RagflowListQueryModel,
+):
+ """
+ 查看聊天助手列表
+ """
+
+ result = await RAGFlowService.get_chat_assistant_list_services(query_params)
+ return parse_result(result)
+
+ # pass
+
+# 更新聊天助手
+@ragflowController.post('/update_chat_assistant')
+async def update_chat_assistant(
+ update_params: UpdateChatAssistantModel,
+):
+ """
+ 更新聊天助手
+ """
+ result = await RAGFlowService.update_chat_assistant_services(update_params)
+ return parse_result(result)
+
+# 创建属于聊天助手的会话
+@ragflowController.post('/create_session_with_chat')
+async def create_session_with_chat(
+ create_params: CreateSessionWithChatModel,
+):
+ """
+ 创建属于聊天助手的会话
+ """
+
+ result = await RAGFlowService.create_session_with_chat_services(create_params)
+ return parse_result(result)
+
+# 与聊天助手进行对话
+@ragflowController.post('/converse_with_chat_assistant')
+async def converse_with_chat_assistant(
+ request: Request,
+ converse_params: ConverseWithChatAssistantModel,
+):
+ """
+ 与聊天助手进行对话
+ """
+
+ start_time = time.perf_counter()
+ redis = getattr(request.app.state, 'redis', None)
+ cache_key = None
+
+ if WeatherService.should_handle(converse_params.question):
+ try:
+ weather_payload = await WeatherService.get_weather_answer(converse_params.question, redis=redis)
+ except WeatherServiceError as exc:
+ return ResponseUtil.error(str(exc))
+
+ if converse_params.stream:
+ async def weather_stream():
+ yield format_sse(weather_payload)
+ yield format_sse({'status': 'completed'}, event='end')
+
+ return StreamingResponse(weather_stream(), media_type='text/event-stream')
+
+ return ResponseUtil.success(weather_payload)
+
+ if not converse_params.stream and redis:
+ cache_key = build_chat_cache_key(converse_params.chat_id, converse_params.question)
+ cached = await redis.get(cache_key)
+ if cached:
+ logger.info('ragflow对话命中缓存: chat=%s', converse_params.chat_id)
+ return ResponseUtil.success(json.loads(cached))
+
+ result = await RAGFlowService.converse_with_chat_assistant_services(converse_params)
+
+ if converse_params.stream:
+ async def stream_response():
+ try:
+ async for chunk in result:
+ payload = chunk.get('data') if isinstance(chunk, dict) else chunk
+ if not payload:
+ continue
+ body = payload if isinstance(payload, dict) else {'data': payload}
+ yield format_sse(body)
+ yield format_sse({'status': 'completed'}, event='end')
+ except Exception as exc:
+ logger.exception('ragflow流式对话异常: %s', exc)
+ yield format_sse({'message': str(exc)}, event='error')
+ finally:
+ logger.info('ragflow流式对话耗时 %.3fs', time.perf_counter() - start_time)
+
+ return StreamingResponse(stream_response(), media_type='text/event-stream')
+
+ response = parse_result(result)
+ if redis and cache_key and isinstance(result, dict) and result.get('code') == 0:
+ await redis.set(cache_key, json.dumps(result.get('data'), ensure_ascii=False), ex=60)
+ logger.info('ragflow对话耗时 %.3fs', time.perf_counter() - start_time)
+ return response
+
+
+
+ # return parse_result(result)
+
+# 获取用户权限
+@ragflowController.get('/get_user_permission', dependencies=[Depends(CheckRoleInterfaceAuth('pad'))])
+async def get_user_permission(current_user = Depends(LoginService.get_current_user)):
+ """
+ 获取用户权限
+ """
+
+ user_auth_list = current_user.permissions
+ print(user_auth_list)
+
+
+ return ResponseUtil.success(data=user_auth_list)
+
+def parse_result(result):
+ code = result.get('code', 0)
+ if code != 0:
+ return ResponseUtil.error(result.get('data', None))
+ return ResponseUtil.success(result.get('data', None))
+
+
+def build_chat_cache_key(chat_id: str, question: str) -> str:
+ digest = hashlib.sha256(question.encode('utf-8')).hexdigest()
+ return f'ragflow:chat:{chat_id}:{digest}'
+
+
+def format_sse(data: dict, event: str | None = None) -> str:
+ payload = json.dumps(data, ensure_ascii=False)
+ prefix = f'event: {event}\n' if event else ''
+ return f'{prefix}data: {payload}\n\n'
\ No newline at end of file
diff --git a/ruoyi-fastapi-backend/module_admin/controller/robot_action_controller.py b/ruoyi-fastapi-backend/module_admin/controller/robot_action_controller.py
new file mode 100644
index 0000000..72d7902
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/robot_action_controller.py
@@ -0,0 +1,106 @@
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth, CheckRoleInterfaceAuth
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.robot_action_service import Robot_actionService
+from module_admin.entity.vo.robot_action_vo import DeleteRobot_actionModel, Robot_actionModel, Robot_actionPageQueryModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+from datetime import datetime
+
+
+robot_actionController = APIRouter(prefix='/system/robot_action', dependencies=[Depends(LoginService.get_current_user)])
+
+
+@robot_actionController.get(
+ '/list', response_model=PageResponseModel
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:robot_action:list'))]
+)
+async def get_system_robot_action_list(
+ request: Request,
+ robot_action_page_query: Robot_actionPageQueryModel = Depends(Robot_actionPageQueryModel.as_query),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ robot_action_page_query_result = await Robot_actionService.get_robot_action_list_services(query_db, robot_action_page_query, is_page=False)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=robot_action_page_query_result)
+
+
+# @robot_actionController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:robot_action:add'))])
+# @ValidateFields(validate_model='add_robot_action')
+# @Log(title='机器人动作', business_type=BusinessType.INSERT)
+# async def add_system_robot_action(
+# request: Request,
+# add_robot_action: Robot_actionModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# add_robot_action.update_time = datetime.now()
+# add_robot_action_result = await Robot_actionService.add_robot_action_services(query_db, add_robot_action)
+# logger.info(add_robot_action_result.message)
+
+# return ResponseUtil.success(msg=add_robot_action_result.message)
+
+
+@robot_actionController.put(''
+ # , dependencies=[Depends(CheckUserInterfaceAuth('system:robot_action:edit'))]
+ )
+@ValidateFields(validate_model='edit_robot_action')
+@Log(title='机器人动作', business_type=BusinessType.UPDATE)
+async def edit_system_robot_action(
+ request: Request,
+ edit_robot_action: Robot_actionModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_robot_action.update_by = current_user.user.user_name
+ edit_robot_action.update_time = datetime.now()
+ edit_robot_action_result = await Robot_actionService.edit_robot_action_services(query_db, edit_robot_action)
+ logger.info(edit_robot_action_result.message)
+
+ return ResponseUtil.success(msg=edit_robot_action_result.message)
+
+
+# @robot_actionController.delete('/{ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:robot_action:remove'))])
+# @Log(title='机器人动作', business_type=BusinessType.DELETE)
+# async def delete_system_robot_action(request: Request, ids: str, query_db: AsyncSession = Depends(get_db)):
+# delete_robot_action = DeleteRobot_actionModel(ids=ids)
+# delete_robot_action_result = await Robot_actionService.delete_robot_action_services(query_db, delete_robot_action)
+# logger.info(delete_robot_action_result.message)
+
+# return ResponseUtil.success(msg=delete_robot_action_result.message)
+
+
+# @robot_actionController.get(
+# '/{id}', response_model=Robot_actionModel, dependencies=[Depends(CheckUserInterfaceAuth('system:robot_action:query'))]
+# )
+# async def query_detail_system_robot_action(request: Request, id: int, query_db: AsyncSession = Depends(get_db)):
+# robot_action_detail_result = await Robot_actionService.robot_action_detail_services(query_db, id)
+# logger.info(f'获取id为{id}的信息成功')
+
+# return ResponseUtil.success(data=robot_action_detail_result)
+
+
+# @robot_actionController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:robot_action:export'))])
+# @Log(title='机器人动作', business_type=BusinessType.EXPORT)
+# async def export_system_robot_action_list(
+# request: Request,
+# robot_action_page_query: Robot_actionPageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# robot_action_query_result = await Robot_actionService.get_robot_action_list_services(query_db, robot_action_page_query, is_page=False)
+# robot_action_export_result = await Robot_actionService.export_robot_action_list_services(robot_action_query_result)
+# logger.info('导出成功')
+
+# return ResponseUtil.streaming(data=bytes2file_response(robot_action_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/robot_role_controller.py b/ruoyi-fastapi-backend/module_admin/controller/robot_role_controller.py
new file mode 100644
index 0000000..2fd2d74
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/robot_role_controller.py
@@ -0,0 +1,110 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.role_service import RoleService
+from module_admin.entity.vo.role_vo import DeleteRoleModel, RoleModel, RolePageQueryModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+"""
+ 当前用不到这个接口
+"""
+
+
+# roleController = APIRouter(prefix='/system/role', dependencies=[Depends(LoginService.get_current_user)])
+#
+#
+# @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: AsyncSession = Depends(get_db),
+# ):
+# # 获取分页数据
+# role_page_query_result = await RoleService.get_role_list_services(query_db, role_page_query, is_page=True)
+# logger.info('获取成功')
+#
+# return ResponseUtil.success(model_content=role_page_query_result)
+
+
+# @roleController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:role:add'))])
+# @ValidateFields(validate_model='add_role')
+# @Log(title='系统角色', business_type=BusinessType.INSERT)
+# async def add_system_role(
+# request: Request,
+# add_role: RoleModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# add_role.create_time = datetime.now()
+# add_role.create_by = current_user.user.user_name
+# add_role.update_time = datetime.now()
+# add_role.update_by = current_user.user.user_name
+# add_role_result = await RoleService.add_role_services(query_db, add_role)
+# logger.info(add_role_result.message)
+#
+# return ResponseUtil.success(msg=add_role_result.message)
+
+
+# @roleController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))])
+# @ValidateFields(validate_model='edit_role')
+# @Log(title='系统角色', business_type=BusinessType.UPDATE)
+# async def edit_system_role(
+# request: Request,
+# edit_role: RoleModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# edit_role.update_by = current_user.user.user_name
+# edit_role.update_time = datetime.now()
+# edit_role_result = await RoleService.edit_role_services(query_db, edit_role)
+# logger.info(edit_role_result.message)
+#
+# return ResponseUtil.success(msg=edit_role_result.message)
+
+
+# @roleController.delete('/{robot_role_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:role:remove'))])
+# @Log(title='系统角色', business_type=BusinessType.DELETE)
+# async def delete_system_role(request: Request, robot_role_ids: str, query_db: AsyncSession = Depends(get_db)):
+# delete_role = DeleteRoleModel(robotRoleIds=robot_role_ids)
+# delete_role_result = await RoleService.delete_role_services(query_db, delete_role)
+# logger.info(delete_role_result.message)
+#
+# return ResponseUtil.success(msg=delete_role_result.message)
+
+
+# @roleController.get(
+# '/{robot_role_id}', response_model=RoleModel, dependencies=[Depends(CheckUserInterfaceAuth('system:role:query'))]
+# )
+# async def query_detail_system_role(request: Request, robot_role_id: int, query_db: AsyncSession = Depends(get_db)):
+# role_detail_result = await RoleService.role_detail_services(query_db, robot_role_id)
+# logger.info(f'获取robot_role_id为{robot_role_id}的信息成功')
+#
+# return ResponseUtil.success(data=role_detail_result)
+
+
+# @roleController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:role:export'))])
+# @Log(title='系统角色', business_type=BusinessType.EXPORT)
+# async def export_system_role_list(
+# request: Request,
+# role_page_query: RolePageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# role_query_result = await RoleService.get_role_list_services(query_db, role_page_query, is_page=False)
+# role_export_result = await RoleService.export_role_list_services(role_query_result)
+# logger.info('导出成功')
+#
+# return ResponseUtil.streaming(data=bytes2file_response(role_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/robot_role_pairing_controller.py b/ruoyi-fastapi-backend/module_admin/controller/robot_role_pairing_controller.py
new file mode 100644
index 0000000..18a0670
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/robot_role_pairing_controller.py
@@ -0,0 +1,155 @@
+from fastapi import APIRouter, Depends, Form, Request, Response
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.dao.robot_role_pairing_dao import PairingDao
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.robot_role_pairing_service import PairingService
+from module_admin.entity.vo.robot_role_pairing_vo import DeletePairingModel, PairingModel, PairingPageQueryModel, \
+ ChangeRobotRoleModel
+from utils.common_util import bytes2file_response, export_list2excel
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+from datetime import datetime
+
+
+pairingController = APIRouter(prefix='/system/robot_role'
+ # , dependencies=[Depends(LoginService.get_current_user)]
+ )
+
+
+# 获取机器人角色
+@pairingController.get(
+ '/{robot_id}',
+)
+async def get_system_role_pairing_list(
+ request: Request,
+ robot_id: int,
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取分页数据
+ pairing_list, style = await PairingService.get_role_pairing_list_services(query_db, robot_id)
+
+ return ResponseUtil.success(data={"role": pairing_list, "style":style})
+
+# 获取机器人角色新版
+@pairingController.get(
+ '/v1/{robot_id}',
+)
+async def get_system_role_pairing_list_v1(
+ request: Request,
+ robot_id: int,
+ query_db: AsyncSession = Depends(get_db),
+):
+
+ pairing_list, style = await PairingService.get_role_pairing_list_services_v1(query_db, robot_id)
+
+ return ResponseUtil.success(data={"role": pairing_list, "style":style})
+
+
+
+@pairingController.post(
+ '/change',
+)
+async def change_robot_role(
+ request: Request,
+ change_pairing: ChangeRobotRoleModel,
+ query_db: AsyncSession = Depends(get_db),
+):
+ await PairingService.change_robot_role_services(query_db,change_pairing)
+ return ResponseUtil.success()
+
+# @pairingController.get(
+# '/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:pairing:list'))]
+# )
+# async def get_system_pairing_list(
+# request: Request,
+# pairing_page_query: PairingPageQueryModel = Depends(PairingPageQueryModel.as_query),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取分页数据
+# pairing_page_query_result = await PairingService.get_pairing_list_services(query_db, pairing_page_query, is_page=True)
+# logger.info('获取成功')
+#
+# return ResponseUtil.success(model_content=pairing_page_query_result)
+#
+#
+# @pairingController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:pairing:add'))])
+# @ValidateFields(validate_model='add_pairing')
+# @Log(title='角色-机器人-映射', business_type=BusinessType.INSERT)
+# async def add_system_pairing(
+# request: Request,
+# add_pairing: PairingModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# add_pairing.update_time = datetime.now()
+# add_pairing.update_by = current_user.user.user_name
+# add_pairing_result = await PairingService.add_pairing_services(query_db, add_pairing)
+# logger.info(add_pairing_result.message)
+#
+# return ResponseUtil.success(msg=add_pairing_result.message)
+#
+#
+# @pairingController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:pairing:edit'))])
+# @ValidateFields(validate_model='edit_pairing')
+# @Log(title='角色-机器人-映射', business_type=BusinessType.UPDATE)
+# async def edit_system_pairing(
+# request: Request,
+# edit_pairing: PairingModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# edit_pairing.update_by = current_user.user.user_name
+# edit_pairing.update_time = datetime.now()
+# edit_pairing_result = await PairingService.edit_pairing_services(query_db, edit_pairing)
+# logger.info(edit_pairing_result.message)
+#
+# return ResponseUtil.success(msg=edit_pairing_result.message)
+#
+#
+# @pairingController.delete('/{pairing_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:pairing:remove'))])
+# @Log(title='角色-机器人-映射', business_type=BusinessType.DELETE)
+# async def delete_system_pairing(request: Request, pairing_ids: str, query_db: AsyncSession = Depends(get_db)):
+# delete_pairing = DeletePairingModel(pairingIds=pairing_ids)
+# delete_pairing_result = await PairingService.delete_pairing_services(query_db, delete_pairing)
+# logger.info(delete_pairing_result.message)
+#
+# return ResponseUtil.success(msg=delete_pairing_result.message)
+#
+#
+# @pairingController.get(
+# '/{pairing_id}', response_model=PairingModel, dependencies=[Depends(CheckUserInterfaceAuth('system:pairing:query'))]
+# )
+# async def query_detail_system_pairing(request: Request, pairing_id: int, query_db: AsyncSession = Depends(get_db)):
+# pairing_detail_result = await PairingService.pairing_detail_services(query_db, pairing_id)
+# logger.info(f'获取pairing_id为{pairing_id}的信息成功')
+#
+# return ResponseUtil.success(data=pairing_detail_result)
+#
+#
+# @pairingController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:pairing:export'))])
+# @Log(title='角色-机器人-映射', business_type=BusinessType.EXPORT)
+# async def export_system_pairing_list(
+# request: Request,
+# pairing_page_query: PairingPageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# pairing_query_result = await PairingService.get_pairing_list_services(query_db, pairing_page_query, is_page=False)
+# pairing_export_result = await PairingService.export_pairing_list_services(pairing_query_result)
+# logger.info('导出成功')
+
+# # return Response(
+# # content=pairing_export_result,
+# # media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+# # headers={
+# # "Content-Disposition": "attachment; filename=export.xlsx"
+# # }
+# # )
+# return ResponseUtil.streaming(data=bytes2file_response(pairing_export_result))
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..d4ab531
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/role_controller.py
@@ -0,0 +1,283 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.data_scope import GetDataScope
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.dept_vo import DeptModel
+from module_admin.entity.vo.role_vo import AddRoleModel, DeleteRoleModel, RoleModel, RolePageQueryModel
+from module_admin.entity.vo.user_vo import CrudUserRoleModel, CurrentUserModel, UserRolePageQueryModel
+from module_admin.service.dept_service import DeptService
+from module_admin.service.login_service import LoginService
+from module_admin.service.role_service import RoleService
+from module_admin.service.user_service import UserService
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+roleController = APIRouter(prefix='/system/role', dependencies=[Depends(LoginService.get_current_user)])
+
+
+@roleController.get('/deptTree/{role_id}', dependencies=[Depends(CheckUserInterfaceAuth('system:role:query'))])
+async def get_system_role_dept_tree(
+ request: Request,
+ role_id: int,
+ query_db: AsyncSession = Depends(get_db),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ dept_query_result = await DeptService.get_dept_tree_services(query_db, DeptModel(**{}), data_scope_sql)
+ role_dept_query_result = await RoleService.get_role_dept_tree_services(query_db, role_id)
+ role_dept_query_result.depts = dept_query_result
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=role_dept_query_result)
+
+
+@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: AsyncSession = Depends(get_db),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ role_page_query_result = await RoleService.get_role_list_services(
+ query_db, role_page_query, data_scope_sql, is_page=True
+ )
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=role_page_query_result)
+
+
+@roleController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:role:add'))])
+@ValidateFields(validate_model='add_role')
+@Log(title='角色管理', business_type=BusinessType.INSERT)
+async def add_system_role(
+ request: Request,
+ add_role: AddRoleModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ add_role.create_by = current_user.user.user_name
+ add_role.create_time = datetime.now()
+ add_role.update_by = current_user.user.user_name
+ add_role.update_time = datetime.now()
+ add_role_result = await RoleService.add_role_services(query_db, add_role)
+ logger.info(add_role_result.message)
+
+ return ResponseUtil.success(msg=add_role_result.message)
+
+
+@roleController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))])
+@ValidateFields(validate_model='edit_role')
+@Log(title='角色管理', business_type=BusinessType.UPDATE)
+async def edit_system_role(
+ request: Request,
+ edit_role: AddRoleModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ await RoleService.check_role_allowed_services(edit_role)
+ if not current_user.user.admin:
+ await RoleService.check_role_data_scope_services(query_db, str(edit_role.role_id), data_scope_sql)
+ edit_role.update_by = current_user.user.user_name
+ edit_role.update_time = datetime.now()
+ edit_role_result = await RoleService.edit_role_services(query_db, edit_role)
+ logger.info(edit_role_result.message)
+
+ return ResponseUtil.success(msg=edit_role_result.message)
+
+
+@roleController.put('/dataScope', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))])
+@Log(title='角色管理', business_type=BusinessType.GRANT)
+async def edit_system_role_datascope(
+ request: Request,
+ role_data_scope: AddRoleModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ await RoleService.check_role_allowed_services(role_data_scope)
+ if not current_user.user.admin:
+ await RoleService.check_role_data_scope_services(query_db, str(role_data_scope.role_id), data_scope_sql)
+ edit_role = AddRoleModel(
+ roleId=role_data_scope.role_id,
+ dataScope=role_data_scope.data_scope,
+ deptIds=role_data_scope.dept_ids,
+ deptCheckStrictly=role_data_scope.dept_check_strictly,
+ updateBy=current_user.user.user_name,
+ updateTime=datetime.now(),
+ )
+ role_data_scope_result = await RoleService.role_datascope_services(query_db, edit_role)
+ logger.info(role_data_scope_result.message)
+
+ return ResponseUtil.success(msg=role_data_scope_result.message)
+
+
+@roleController.delete('/{role_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:role:remove'))])
+@Log(title='角色管理', business_type=BusinessType.DELETE)
+async def delete_system_role(
+ request: Request,
+ role_ids: str,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ role_id_list = role_ids.split(',') if role_ids else []
+ if role_id_list:
+ for role_id in role_id_list:
+ await RoleService.check_role_allowed_services(RoleModel(roleId=int(role_id)))
+ if not current_user.user.admin:
+ await RoleService.check_role_data_scope_services(query_db, role_id, data_scope_sql)
+ delete_role = DeleteRoleModel(roleIds=role_ids, updateBy=current_user.user.user_name, updateTime=datetime.now())
+ delete_role_result = await RoleService.delete_role_services(query_db, delete_role)
+ logger.info(delete_role_result.message)
+
+ return ResponseUtil.success(msg=delete_role_result.message)
+
+
+@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: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ if not current_user.user.admin:
+ await RoleService.check_role_data_scope_services(query_db, str(role_id), data_scope_sql)
+ role_detail_result = await RoleService.role_detail_services(query_db, role_id)
+ logger.info(f'获取role_id为{role_id}的信息成功')
+
+ return ResponseUtil.success(data=role_detail_result.model_dump(by_alias=True))
+
+
+@roleController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:role:export'))])
+@Log(title='角色管理', business_type=BusinessType.EXPORT)
+async def export_system_role_list(
+ request: Request,
+ role_page_query: RolePageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ # 获取全量数据
+ role_query_result = await RoleService.get_role_list_services(
+ query_db, role_page_query, data_scope_sql, is_page=False
+ )
+ role_export_result = await RoleService.export_role_list_services(role_query_result)
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(role_export_result))
+
+
+@roleController.put('/changeStatus', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))])
+@Log(title='角色管理', business_type=BusinessType.UPDATE)
+async def reset_system_role_status(
+ request: Request,
+ change_role: AddRoleModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ await RoleService.check_role_allowed_services(change_role)
+ if not current_user.user.admin:
+ await RoleService.check_role_data_scope_services(query_db, str(change_role.role_id), data_scope_sql)
+ edit_role = AddRoleModel(
+ roleId=change_role.role_id,
+ status=change_role.status,
+ updateBy=current_user.user.user_name,
+ updateTime=datetime.now(),
+ type='status',
+ )
+ edit_role_result = await RoleService.edit_role_services(query_db, edit_role)
+ logger.info(edit_role_result.message)
+
+ return ResponseUtil.success(msg=edit_role_result.message)
+
+
+@roleController.get(
+ '/authUser/allocatedList',
+ response_model=PageResponseModel,
+ dependencies=[Depends(CheckUserInterfaceAuth('system:role:list'))],
+)
+async def get_system_allocated_user_list(
+ request: Request,
+ user_role: UserRolePageQueryModel = Depends(UserRolePageQueryModel.as_query),
+ query_db: AsyncSession = Depends(get_db),
+ data_scope_sql: str = Depends(GetDataScope('SysUser')),
+):
+ role_user_allocated_page_query_result = await RoleService.get_role_user_allocated_list_services(
+ query_db, user_role, data_scope_sql, is_page=True
+ )
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=role_user_allocated_page_query_result)
+
+
+@roleController.get(
+ '/authUser/unallocatedList',
+ response_model=PageResponseModel,
+ dependencies=[Depends(CheckUserInterfaceAuth('system:role:list'))],
+)
+async def get_system_unallocated_user_list(
+ request: Request,
+ user_role: UserRolePageQueryModel = Depends(UserRolePageQueryModel.as_query),
+ query_db: AsyncSession = Depends(get_db),
+ data_scope_sql: str = Depends(GetDataScope('SysUser')),
+):
+ role_user_unallocated_page_query_result = await RoleService.get_role_user_unallocated_list_services(
+ query_db, user_role, data_scope_sql, is_page=True
+ )
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=role_user_unallocated_page_query_result)
+
+
+@roleController.put('/authUser/selectAll', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))])
+@Log(title='角色管理', business_type=BusinessType.GRANT)
+async def add_system_role_user(
+ request: Request,
+ add_role_user: CrudUserRoleModel = Depends(CrudUserRoleModel.as_query),
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ if not current_user.user.admin:
+ await RoleService.check_role_data_scope_services(query_db, str(add_role_user.role_id), data_scope_sql)
+ add_role_user_result = await UserService.add_user_role_services(query_db, add_role_user)
+ logger.info(add_role_user_result.message)
+
+ return ResponseUtil.success(msg=add_role_user_result.message)
+
+
+@roleController.put('/authUser/cancel', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))])
+@Log(title='角色管理', business_type=BusinessType.GRANT)
+async def cancel_system_role_user(
+ request: Request, cancel_user_role: CrudUserRoleModel, query_db: AsyncSession = Depends(get_db)
+):
+ cancel_user_role_result = await UserService.delete_user_role_services(query_db, cancel_user_role)
+ logger.info(cancel_user_role_result.message)
+
+ return ResponseUtil.success(msg=cancel_user_role_result.message)
+
+
+@roleController.put('/authUser/cancelAll', dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))])
+@Log(title='角色管理', business_type=BusinessType.GRANT)
+async def batch_cancel_system_role_user(
+ request: Request,
+ batch_cancel_user_role: CrudUserRoleModel = Depends(CrudUserRoleModel.as_query),
+ query_db: AsyncSession = Depends(get_db),
+):
+ batch_cancel_user_role_result = await UserService.delete_user_role_services(query_db, batch_cancel_user_role)
+ logger.info(batch_cancel_user_role_result.message)
+
+ return ResponseUtil.success(msg=batch_cancel_user_role_result.message)
diff --git a/ruoyi-fastapi-backend/module_admin/controller/scheduler_controller.py b/ruoyi-fastapi-backend/module_admin/controller/scheduler_controller.py
new file mode 100644
index 0000000..229d923
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/scheduler_controller.py
@@ -0,0 +1,79 @@
+"""
+ 系统定时任务
+"""
+
+from config.get_scheduler import scheduler
+from apscheduler.triggers.cron import CronTrigger
+from apscheduler.triggers.interval import IntervalTrigger
+from module_admin.service.haikang_service import HaiKangService
+from module_admin.service.compreface_service import ComprefaceService
+from utils.log_util import logger
+
+
+# 定时获取访客图片并上传compreface
+async def download_visitor_image_and_upload_compreface():
+ """
+ 定时任务:获取海康访客图片并上传到compreface进行人脸识别
+ 同时删除已过期的访客人脸数据
+ 执行频率:每2分钟一次
+ """
+ try:
+ logger.info('开始执行定时任务:获取访客图片并上传compreface')
+
+ # 获取所有有效状态的访客图片(visitorStatus=1表示正常状态)
+ visitor_name_list = await HaiKangService.get_all_visitor_pictures(visitorStatus=1)
+ logger.info(f'获取到 {len(visitor_name_list)} 张有效访客图片')
+
+ # 如果有新的访客图片,将访客图片上传到compreface
+ if visitor_name_list:
+ result = await ComprefaceService.face_addition_batch_service(visitor_name_list)
+ logger.info(f'访客图片上传compreface结果: {result}')
+ else:
+ logger.info('没有新的访客图片需要处理')
+
+ # 删除过期的访客人脸数据
+ # 从visitor_name_list中提取所有访客的名字
+ active_visitor_names = [name for _, name in visitor_name_list] if visitor_name_list else []
+
+ logger.info(f'开始删除过期访客人脸,当前有效访客数量: {len(active_visitor_names)}')
+ delete_result = await ComprefaceService.delete_expired_visitor_faces_service(active_visitor_names)
+
+ if delete_result.get('error'):
+ logger.error(f'删除过期访客失败: {delete_result.get("error")}')
+ else:
+ logger.info(
+ f'删除过期访客完成 - 成功删除: {delete_result.get("deleted_count")} 个, '
+ f'失败: {delete_result.get("failed_count")} 个'
+ )
+ if delete_result.get('deleted_subjects'):
+ logger.info(f'已删除的访客: {", ".join(delete_result.get("deleted_subjects"))}')
+ if delete_result.get('failed_subjects'):
+ logger.warning(f'删除失败的访客: {delete_result.get("failed_subjects")}')
+
+ logger.info('定时任务执行完成:获取访客图片并上传compreface')
+ except Exception as e:
+ logger.error(f'定时任务执行失败:获取访客图片并上传compreface - {str(e)}', exc_info=True)
+
+
+# 初始化定时任务
+def init_scheduled_tasks():
+ """
+ 初始化所有定时任务
+ 在应用启动时调用此方法
+ """
+ # 添加访客图片上传任务,每2分钟执行一次
+ scheduler.add_job(
+ func=download_visitor_image_and_upload_compreface,
+ trigger=IntervalTrigger(minutes=2),
+ id='download_visitor_image_task',
+ name='定时获取访客图片并上传compreface',
+ replace_existing=True,
+ max_instances=1, # 同一时间只允许一个实例运行
+ )
+ logger.info('定时任务已添加:download_visitor_image_and_upload_compreface (每2分钟执行一次)')
+
+
+
+
+
+
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..f63fdf8
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/server_controller.py
@@ -0,0 +1,21 @@
+from fastapi import APIRouter, Depends, Request
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.server_vo import ServerMonitorModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.server_service import ServerService
+from utils.response_util import ResponseUtil
+from utils.log_util import logger
+
+
+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):
+ # 获取全量数据
+ server_info_query_result = await ServerService.get_server_monitor_info()
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=server_info_query_result)
diff --git a/ruoyi-fastapi-backend/module_admin/controller/sys_alert_controller.py b/ruoyi-fastapi-backend/module_admin/controller/sys_alert_controller.py
new file mode 100644
index 0000000..bacc616
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/sys_alert_controller.py
@@ -0,0 +1,106 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.sys_alert_service import Sys_alertService
+from module_admin.entity.vo.sys_alert_vo import DeleteSys_alertModel, Sys_alertModel, Sys_alertPageQueryModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+sys_alertController = APIRouter(prefix='/system/sys_alert', dependencies=[Depends(LoginService.get_current_user)])
+
+
+# @sys_alertController.get(
+# '/list', response_model=PageResponseModel
+# # , dependencies=[Depends(CheckUserInterfaceAuth('system:sys_alert:list'))]
+# )
+# async def get_system_sys_alert_list(
+# request: Request,
+# sys_alert_page_query: Sys_alertPageQueryModel = Depends(Sys_alertPageQueryModel.as_query),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取分页数据
+# sys_alert_page_query_result = await Sys_alertService.get_sys_alert_list_services(query_db, sys_alert_page_query, is_page=True)
+# logger.info('获取成功')
+
+# return ResponseUtil.success(model_content=sys_alert_page_query_result)
+
+
+# @sys_alertController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:sys_alert:add'))])
+# @ValidateFields(validate_model='add_sys_alert')
+# @Log(title='系统告警', business_type=BusinessType.INSERT)
+# async def add_system_sys_alert(
+# request: Request,
+# add_sys_alert: Sys_alertModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# add_sys_alert.create_time = datetime.now()
+# add_sys_alert.create_by = current_user.user.user_name
+# add_sys_alert.update_time = datetime.now()
+# add_sys_alert.update_by = current_user.user.user_name
+# add_sys_alert_result = await Sys_alertService.add_sys_alert_services(query_db, add_sys_alert)
+# logger.info(add_sys_alert_result.message)
+
+# return ResponseUtil.success(msg=add_sys_alert_result.message)
+
+
+# @sys_alertController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:sys_alert:edit'))])
+# @ValidateFields(validate_model='edit_sys_alert')
+# @Log(title='系统告警', business_type=BusinessType.UPDATE)
+# async def edit_system_sys_alert(
+# request: Request,
+# edit_sys_alert: Sys_alertModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# edit_sys_alert.update_by = current_user.user.user_name
+# edit_sys_alert.update_time = datetime.now()
+# edit_sys_alert_result = await Sys_alertService.edit_sys_alert_services(query_db, edit_sys_alert)
+# logger.info(edit_sys_alert_result.message)
+
+# return ResponseUtil.success(msg=edit_sys_alert_result.message)
+
+
+# @sys_alertController.delete('/{ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:sys_alert:remove'))])
+# @Log(title='系统告警', business_type=BusinessType.DELETE)
+# async def delete_system_sys_alert(request: Request, ids: str, query_db: AsyncSession = Depends(get_db)):
+# delete_sys_alert = DeleteSys_alertModel(ids=ids)
+# delete_sys_alert_result = await Sys_alertService.delete_sys_alert_services(query_db, delete_sys_alert)
+# logger.info(delete_sys_alert_result.message)
+
+# return ResponseUtil.success(msg=delete_sys_alert_result.message)
+
+
+# @sys_alertController.get(
+# '/{id}', response_model=Sys_alertModel, dependencies=[Depends(CheckUserInterfaceAuth('system:sys_alert:query'))]
+# )
+# async def query_detail_system_sys_alert(request: Request, id: int, query_db: AsyncSession = Depends(get_db)):
+# sys_alert_detail_result = await Sys_alertService.sys_alert_detail_services(query_db, id)
+# logger.info(f'获取id为{id}的信息成功')
+
+# return ResponseUtil.success(data=sys_alert_detail_result)
+
+
+# @sys_alertController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:sys_alert:export'))])
+# @Log(title='系统告警', business_type=BusinessType.EXPORT)
+# async def export_system_sys_alert_list(
+# request: Request,
+# sys_alert_page_query: Sys_alertPageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# sys_alert_query_result = await Sys_alertService.get_sys_alert_list_services(query_db, sys_alert_page_query, is_page=False)
+# sys_alert_export_result = await Sys_alertService.export_sys_alert_list_services(sys_alert_query_result)
+# logger.info('导出成功')
+
+# return ResponseUtil.streaming(data=bytes2file_response(sys_alert_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/sys_statistics_controller.py b/ruoyi-fastapi-backend/module_admin/controller/sys_statistics_controller.py
new file mode 100644
index 0000000..ef29d5a
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/sys_statistics_controller.py
@@ -0,0 +1,240 @@
+from datetime import datetime, date
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.identification_record_service import Identification_recordService
+from module_admin.service.sys_statistics_service import Sys_statisticsService
+from module_admin.entity.vo.sys_statistics_vo import DeleteSys_statisticsModel, Sys_statisticsModel, Sys_statisticsPageQueryModel
+from module_admin.entity.vo.identification_record_vo import Identification_recordModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+sys_statisticsController = APIRouter(prefix='/system/sys_statistics', dependencies=[Depends(LoginService.get_current_user)])
+
+# 添加识别记录
+@sys_statisticsController.post(
+ '/add_identification_record'
+)
+async def add_system_identification_record(
+ request: Request,
+ add_identification_record: Identification_recordModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ try:
+ # 设置创建时间和创建者
+ add_identification_record.create_time = datetime.now()
+ add_identification_record.create_by = current_user.user.user_name
+
+ # 调用服务层添加识别记录
+ result = await Identification_recordService.add_identification_record_services(query_db, add_identification_record)
+
+ if result.is_success:
+ logger.info(f'添加识别记录成功: {add_identification_record.person_name}')
+ return ResponseUtil.success(msg=result.message)
+ else:
+ logger.error(f'添加识别记录失败: {result.message}')
+ return ResponseUtil.error(msg=result.message)
+ except Exception as e:
+ logger.error(f'添加识别记录异常: {str(e)}')
+ return ResponseUtil.error(msg=f'添加识别记录失败: {str(e)}')
+
+
+# 获取统计数据
+@sys_statisticsController.get(
+ '/get_data'
+)
+async def get_statistics_data(
+ request: Request,
+ query_db: AsyncSession = Depends(get_db),
+):
+ data = await Sys_statisticsService.get_statistics_data_services(query_db)
+ return ResponseUtil.success(data=data)
+
+# 获取访问趋势
+@sys_statisticsController.get(
+ '/get_visitor_count/{day}'
+)
+async def get_visitor_count(
+ request: Request,
+ day: int,
+ query_db: AsyncSession = Depends(get_db),
+):
+ data = await Sys_statisticsService.get_visitor_count_services(query_db, day)
+ return ResponseUtil.success(data=data)
+
+# 获取门禁识别成功率
+@sys_statisticsController.get(
+ '/access_control_success_rate/{day}'
+)
+async def get_access_control_success_rate(
+ request: Request,
+ day: int,
+ query_db: AsyncSession = Depends(get_db),
+):
+ data = await Sys_statisticsService.get_access_control_success_rate_services(query_db, day)
+ return ResponseUtil.success(data=data)
+
+# 大模型调用次数加1
+@sys_statisticsController.get(
+ '/add_model_call_count'
+)
+async def add_model_call_count(
+ request: Request,
+ query_db: AsyncSession = Depends(get_db),
+ user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ data = await Sys_statisticsService.add_model_call_count_services(query_db, user.user.user_name)
+ if data.is_success:
+ logger.info('大模型调用次数加1成功')
+ return ResponseUtil.success(msg=data.message)
+ else:
+ logger.error('大模型调用次数加1失败')
+ return ResponseUtil.error(msg=data.message)
+
+# 门禁通行量次数加1
+@sys_statisticsController.get(
+ '/add_door_pass_count'
+)
+async def add_door_pass_count(
+ request: Request,
+ query_db: AsyncSession = Depends(get_db),
+ user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ data = await Sys_statisticsService.add_door_pass_count_services(query_db, user.user.user_name)
+ if data.is_success:
+ logger.info('门禁通行量次数加1成功')
+ return ResponseUtil.success(msg=data.message)
+ else:
+ logger.error('门禁通行量次数加1失败')
+ return ResponseUtil.error(msg=data.message)
+# 访客引导次数加1
+@sys_statisticsController.get(
+ '/add_visitor_guide_count'
+)
+async def add_visitor_guide_count(
+ request: Request,
+ query_db: AsyncSession = Depends(get_db),
+ user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ data = await Sys_statisticsService.add_visitor_guide_count_services(query_db, user.user.user_name)
+ if data.is_success:
+ logger.info('访客引导次数加1成功')
+ return ResponseUtil.success(msg=data.message)
+ else:
+ logger.error('访客引导次数加1失败')
+ return ResponseUtil.error(msg=data.message)
+
+# 展厅讲解次数加1
+@sys_statisticsController.get(
+ '/add_explain_count'
+)
+async def add_explain_count(
+ request: Request,
+ query_db: AsyncSession = Depends(get_db),
+ user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ data = await Sys_statisticsService.add_explain_count_services(query_db, user.user.user_name)
+ if data.is_success:
+ logger.info('展厅讲解次数加1成功')
+ return ResponseUtil.success(msg=data.message)
+ else:
+ logger.error('展厅讲解次数加1失败')
+ return ResponseUtil.error(msg=data.message)
+
+# @sys_statisticsController.get(
+# '/list', response_model=PageResponseModel
+# # , dependencies=[Depends(CheckUserInterfaceAuth('system:sys_statistics:list'))]
+# )
+# async def get_system_sys_statistics_list(
+# request: Request,
+# sys_statistics_page_query: Sys_statisticsPageQueryModel = Depends(Sys_statisticsPageQueryModel.as_query),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取分页数据
+# sys_statistics_page_query_result = await Sys_statisticsService.get_sys_statistics_list_services(query_db, sys_statistics_page_query, is_page=True)
+# logger.info('获取成功')
+#
+# return ResponseUtil.success(model_content=sys_statistics_page_query_result)
+
+
+# @sys_statisticsController.post(''
+# , dependencies=[Depends(CheckUserInterfaceAuth('system:sys_statistics:add'))]
+# )
+# @ValidateFields(validate_model='add_sys_statistics')
+# @Log(title='系统统计数据', business_type=BusinessType.INSERT)
+# async def add_system_sys_statistics(
+# request: Request,
+# add_sys_statistics: Sys_statisticsModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# add_sys_statistics.create_time = datetime.now()
+# add_sys_statistics.create_by = current_user.user.user_name
+# add_sys_statistics.update_time = datetime.now()
+# add_sys_statistics.update_by = current_user.user.user_name
+# add_sys_statistics_result = await Sys_statisticsService.add_sys_statistics_services(query_db, add_sys_statistics)
+# logger.info(add_sys_statistics_result.message)
+#
+# return ResponseUtil.success(msg=add_sys_statistics_result.message)
+#
+#
+# @sys_statisticsController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:sys_statistics:edit'))])
+# @ValidateFields(validate_model='edit_sys_statistics')
+# @Log(title='系统统计数据', business_type=BusinessType.UPDATE)
+# async def edit_system_sys_statistics(
+# request: Request,
+# edit_sys_statistics: Sys_statisticsModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# edit_sys_statistics.update_by = current_user.user.user_name
+# edit_sys_statistics.update_time = datetime.now()
+# edit_sys_statistics_result = await Sys_statisticsService.edit_sys_statistics_services(query_db, edit_sys_statistics)
+# logger.info(edit_sys_statistics_result.message)
+#
+# return ResponseUtil.success(msg=edit_sys_statistics_result.message)
+
+
+# @sys_statisticsController.delete('/{statistic_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:sys_statistics:remove'))])
+# @Log(title='系统统计数据', business_type=BusinessType.DELETE)
+# async def delete_system_sys_statistics(request: Request, statistic_ids: str, query_db: AsyncSession = Depends(get_db)):
+# delete_sys_statistics = DeleteSys_statisticsModel(statisticIds=statistic_ids)
+# delete_sys_statistics_result = await Sys_statisticsService.delete_sys_statistics_services(query_db, delete_sys_statistics)
+# logger.info(delete_sys_statistics_result.message)
+#
+# return ResponseUtil.success(msg=delete_sys_statistics_result.message)
+#
+#
+# @sys_statisticsController.get(
+# '/{statistic_id}', response_model=Sys_statisticsModel, dependencies=[Depends(CheckUserInterfaceAuth('system:sys_statistics:query'))]
+# )
+# async def query_detail_system_sys_statistics(request: Request, statistic_id: int, query_db: AsyncSession = Depends(get_db)):
+# sys_statistics_detail_result = await Sys_statisticsService.sys_statistics_detail_services(query_db, statistic_id)
+# logger.info(f'获取statistic_id为{statistic_id}的信息成功')
+#
+# return ResponseUtil.success(data=sys_statistics_detail_result)
+#
+#
+# @sys_statisticsController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:sys_statistics:export'))])
+# @Log(title='系统统计数据', business_type=BusinessType.EXPORT)
+# async def export_system_sys_statistics_list(
+# request: Request,
+# sys_statistics_page_query: Sys_statisticsPageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# sys_statistics_query_result = await Sys_statisticsService.get_sys_statistics_list_services(query_db, sys_statistics_page_query, is_page=False)
+# sys_statistics_export_result = await Sys_statisticsService.export_sys_statistics_list_services(sys_statistics_query_result)
+# logger.info('导出成功')
+#
+# return ResponseUtil.streaming(data=bytes2file_response(sys_statistics_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/test_controller.py b/ruoyi-fastapi-backend/module_admin/controller/test_controller.py
new file mode 100644
index 0000000..a5b73d0
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/test_controller.py
@@ -0,0 +1,58 @@
+from fastapi import APIRouter, Body, Depends, Form, Request
+from module_admin.service.login_service import LoginService
+from module_admin.entity.vo.test_vo import TestUserName, TestPostForm, TestUserNamePage
+from typing import List, Optional
+from module_admin.service.test_service import TestService
+from config.get_db import get_db
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.annotation.log_annotation import Log
+from config.enums import BusinessType, RedisInitKeyConfig
+from utils.response_util import ResponseUtil
+from utils.log_util import logger
+
+
+testController = APIRouter(prefix='/test')
+
+@testController.get('/hello', response_model= List[TestUserName]
+ # ,dependencies=[Depends(LoginService.get_current_user)]
+ )
+async def hello(request: Request,
+ name: Optional[str] = None,
+
+ query_db: AsyncSession = Depends(get_db), ):
+ print("名称", name)
+ user_name = await TestService.get_user_name(query_db,)
+ return user_name
+
+@testController.post('/hello_post_json', response_model=str
+ ,dependencies=[Depends(LoginService.get_current_user)]
+ )
+@Log(title='测试Log', business_type=BusinessType.OTHER, log_type='operation')
+async def hello_post_json(
+ request: Request,
+ login_info: TestUserNamePage,
+ query_db: AsyncSession = Depends(get_db)
+
+):
+ print(login_info.user_name)
+ print(login_info.page_num)
+ print(login_info.page_size)
+ # print(login_info.password)
+
+ user_name = await TestService.get_user_name(query_db, login_info)
+
+ logger.info("测试成功")
+
+ return ResponseUtil.success(model_content=user_name)
+
+@Log(title='测试post_form_data', business_type=BusinessType.OTHER, log_type='post')
+@testController.post('/hello_post_form_data', response_model=str)
+async def hello_post_form_data(
+ request: Request,
+ user_name: Optional[str] = Form(),
+ password: Optional[str] = Form()
+):
+ print("*"*100)
+ print(user_name)
+ print(password)
+ return "success"
\ No newline at end of file
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..ed7cf7a
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/user_controller.py
@@ -0,0 +1,399 @@
+import os
+from datetime import datetime
+from fastapi import APIRouter, Depends, File, Form, Query, Request, UploadFile
+from sqlalchemy.ext.asyncio import AsyncSession
+from typing import Literal, Optional, Union
+from pydantic_validation_decorator import ValidateFields
+from config.get_db import get_db
+from config.enums import BusinessType
+from config.env import UploadConfig
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.data_scope import GetDataScope
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.dept_vo import DeptModel
+from module_admin.entity.vo.user_vo import (
+ AddUserModel,
+ CrudUserRoleModel,
+ CurrentUserModel,
+ DeleteUserModel,
+ EditUserModel,
+ ResetPasswordModel,
+ ResetUserModel,
+ UserDetailModel,
+ UserInfoModel,
+ UserModel,
+ UserPageQueryModel,
+ UserProfileModel,
+ UserRoleQueryModel,
+ UserRoleResponseModel,
+)
+from module_admin.service.login_service import LoginService
+from module_admin.service.user_service import UserService
+from module_admin.service.role_service import RoleService
+from module_admin.service.dept_service import DeptService
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.pwd_util import PwdUtil
+from utils.response_util import ResponseUtil
+from utils.upload_util import UploadUtil
+
+
+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: AsyncSession = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysDept'))
+):
+ dept_query_result = await DeptService.get_dept_tree_services(query_db, DeptModel(**{}), data_scope_sql)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(data=dept_query_result)
+
+
+@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: AsyncSession = Depends(get_db),
+ data_scope_sql: str = Depends(GetDataScope('SysUser')),
+):
+ # 获取分页数据
+ user_page_query_result = await UserService.get_user_list_services(
+ query_db, user_page_query, data_scope_sql, is_page=True
+ )
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=user_page_query_result)
+
+
+@userController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:user:add'))])
+@ValidateFields(validate_model='add_user')
+@Log(title='用户管理', business_type=BusinessType.INSERT)
+async def add_system_user(
+ request: Request,
+ add_user: AddUserModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ dept_data_scope_sql: str = Depends(GetDataScope('SysDept')),
+ role_data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ if not current_user.user.admin:
+ await DeptService.check_dept_data_scope_services(query_db, add_user.dept_id, dept_data_scope_sql)
+ await RoleService.check_role_data_scope_services(
+ query_db, ','.join([str(item) for item in add_user.role_ids]), role_data_scope_sql
+ )
+ add_user.password = PwdUtil.get_password_hash(add_user.password)
+ add_user.create_by = current_user.user.user_name
+ add_user.create_time = datetime.now()
+ add_user.update_by = current_user.user.user_name
+ add_user.update_time = datetime.now()
+ add_user_result = await UserService.add_user_services(query_db, add_user)
+ logger.info(add_user_result.message)
+
+ return ResponseUtil.success(msg=add_user_result.message)
+
+
+@userController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:user:edit'))])
+@ValidateFields(validate_model='edit_user')
+@Log(title='用户管理', business_type=BusinessType.UPDATE)
+async def edit_system_user(
+ request: Request,
+ edit_user: EditUserModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ user_data_scope_sql: str = Depends(GetDataScope('SysUser')),
+ dept_data_scope_sql: str = Depends(GetDataScope('SysDept')),
+ role_data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ await UserService.check_user_allowed_services(edit_user)
+ if not current_user.user.admin:
+ await UserService.check_user_data_scope_services(query_db, edit_user.user_id, user_data_scope_sql)
+ await DeptService.check_dept_data_scope_services(query_db, edit_user.dept_id, dept_data_scope_sql)
+ await RoleService.check_role_data_scope_services(
+ query_db, ','.join([str(item) for item in edit_user.role_ids]), role_data_scope_sql
+ )
+ edit_user.update_by = current_user.user.user_name
+ edit_user.update_time = datetime.now()
+ edit_user_result = await UserService.edit_user_services(query_db, edit_user)
+ logger.info(edit_user_result.message)
+
+ return ResponseUtil.success(msg=edit_user_result.message)
+
+
+@userController.delete('/{user_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:user:remove'))])
+@Log(title='用户管理', business_type=BusinessType.DELETE)
+async def delete_system_user(
+ request: Request,
+ user_ids: str,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysUser')),
+):
+ user_id_list = user_ids.split(',') if user_ids else []
+ if user_id_list:
+ if current_user.user.user_id in list(map(int, user_id_list)):
+ logger.warning('当前登录用户不能删除')
+
+ return ResponseUtil.failure(msg='当前登录用户不能删除')
+ for user_id in user_id_list:
+ await UserService.check_user_allowed_services(UserModel(userId=int(user_id)))
+ if not current_user.user.admin:
+ await UserService.check_user_data_scope_services(query_db, int(user_id), data_scope_sql)
+ delete_user = DeleteUserModel(userIds=user_ids, updateBy=current_user.user.user_name, updateTime=datetime.now())
+ delete_user_result = await UserService.delete_user_services(query_db, delete_user)
+ logger.info(delete_user_result.message)
+
+ return ResponseUtil.success(msg=delete_user_result.message)
+
+
+@userController.put('/resetPwd', dependencies=[Depends(CheckUserInterfaceAuth('system:user:resetPwd'))])
+@Log(title='用户管理', business_type=BusinessType.UPDATE)
+async def reset_system_user_pwd(
+ request: Request,
+ reset_user: EditUserModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysUser')),
+):
+ await UserService.check_user_allowed_services(reset_user)
+ if not current_user.user.admin:
+ await UserService.check_user_data_scope_services(query_db, reset_user.user_id, data_scope_sql)
+ edit_user = EditUserModel(
+ userId=reset_user.user_id,
+ password=PwdUtil.get_password_hash(reset_user.password),
+ updateBy=current_user.user.user_name,
+ updateTime=datetime.now(),
+ type='pwd',
+ )
+ edit_user_result = await UserService.edit_user_services(query_db, edit_user)
+ logger.info(edit_user_result.message)
+
+ return ResponseUtil.success(msg=edit_user_result.message)
+
+
+@userController.put('/changeStatus', dependencies=[Depends(CheckUserInterfaceAuth('system:user:edit'))])
+@Log(title='用户管理', business_type=BusinessType.UPDATE)
+async def change_system_user_status(
+ request: Request,
+ change_user: EditUserModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysUser')),
+):
+ await UserService.check_user_allowed_services(change_user)
+ if not current_user.user.admin:
+ await UserService.check_user_data_scope_services(query_db, change_user.user_id, data_scope_sql)
+ edit_user = EditUserModel(
+ userId=change_user.user_id,
+ status=change_user.status,
+ updateBy=current_user.user.user_name,
+ updateTime=datetime.now(),
+ type='status',
+ )
+ edit_user_result = await UserService.edit_user_services(query_db, edit_user)
+ logger.info(edit_user_result.message)
+
+ return ResponseUtil.success(msg=edit_user_result.message)
+
+
+@userController.get('/profile', response_model=UserProfileModel)
+async def query_detail_system_user_profile(
+ request: Request,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ profile_user_result = await UserService.user_profile_services(query_db, current_user.user.user_id)
+ logger.info(f'获取user_id为{current_user.user.user_id}的信息成功')
+
+ return ResponseUtil.success(model_content=profile_user_result)
+
+
+@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, Literal['']]] = '',
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ data_scope_sql: str = Depends(GetDataScope('SysUser')),
+):
+ if user_id and not current_user.user.admin:
+ await UserService.check_user_data_scope_services(query_db, user_id, data_scope_sql)
+ detail_user_result = await UserService.user_detail_services(query_db, user_id)
+ logger.info(f'获取user_id为{user_id}的信息成功')
+
+ return ResponseUtil.success(model_content=detail_user_result)
+
+
+@userController.post('/profile/avatar')
+@Log(title='个人信息', business_type=BusinessType.UPDATE)
+async def change_system_user_profile_avatar(
+ request: Request,
+ avatarfile: bytes = File(),
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ if avatarfile:
+ relative_path = (
+ f'avatar/{datetime.now().strftime("%Y")}/{datetime.now().strftime("%m")}/{datetime.now().strftime("%d")}'
+ )
+ dir_path = os.path.join(UploadConfig.UPLOAD_PATH, relative_path)
+ try:
+ os.makedirs(dir_path)
+ except FileExistsError:
+ pass
+ avatar_name = f'avatar_{datetime.now().strftime("%Y%m%d%H%M%S")}{UploadConfig.UPLOAD_MACHINE}{UploadUtil.generate_random_number()}.png'
+ avatar_path = os.path.join(dir_path, avatar_name)
+ with open(avatar_path, 'wb') as f:
+ f.write(avatarfile)
+ edit_user = EditUserModel(
+ userId=current_user.user.user_id,
+ avatar=f'{UploadConfig.UPLOAD_PREFIX}/{relative_path}/{avatar_name}',
+ updateBy=current_user.user.user_name,
+ updateTime=datetime.now(),
+ type='avatar',
+ )
+ edit_user_result = await UserService.edit_user_services(query_db, edit_user)
+ logger.info(edit_user_result.message)
+
+ return ResponseUtil.success(dict_content={'imgUrl': edit_user.avatar}, msg=edit_user_result.message)
+ return ResponseUtil.failure(msg='上传图片异常,请联系管理员')
+
+
+@userController.put('/profile')
+@Log(title='个人信息', business_type=BusinessType.UPDATE)
+async def change_system_user_profile_info(
+ request: Request,
+ user_info: UserInfoModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ edit_user = EditUserModel(
+ **user_info.model_dump(exclude_unset=True, by_alias=True, exclude={'role_ids', 'post_ids'}),
+ userId=current_user.user.user_id,
+ userName=current_user.user.user_name,
+ updateBy=current_user.user.user_name,
+ updateTime=datetime.now(),
+ roleIds=current_user.user.role_ids.split(',') if current_user.user.role_ids else [],
+ postIds=current_user.user.post_ids.split(',') if current_user.user.post_ids else [],
+ role=current_user.user.role,
+ )
+ edit_user_result = await UserService.edit_user_services(query_db, edit_user)
+ logger.info(edit_user_result.message)
+
+ return ResponseUtil.success(msg=edit_user_result.message)
+
+
+@userController.put('/profile/updatePwd')
+@Log(title='个人信息', business_type=BusinessType.UPDATE)
+async def reset_system_user_password(
+ request: Request,
+ reset_password: ResetPasswordModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ reset_user = ResetUserModel(
+ userId=current_user.user.user_id,
+ oldPassword=reset_password.old_password,
+ password=reset_password.new_password,
+ updateBy=current_user.user.user_name,
+ updateTime=datetime.now(),
+ )
+ reset_user_result = await UserService.reset_user_services(query_db, reset_user)
+ logger.info(reset_user_result.message)
+
+ return ResponseUtil.success(msg=reset_user_result.message)
+
+
+@userController.post('/importData', dependencies=[Depends(CheckUserInterfaceAuth('system:user:import'))])
+@Log(title='用户管理', business_type=BusinessType.IMPORT)
+async def batch_import_system_user(
+ request: Request,
+ file: UploadFile = File(...),
+ update_support: bool = Query(alias='updateSupport'),
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ user_data_scope_sql: str = Depends(GetDataScope('SysUser')),
+ dept_data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ batch_import_result = await UserService.batch_import_user_services(
+ request, query_db, file, update_support, current_user, user_data_scope_sql, dept_data_scope_sql
+ )
+ logger.info(batch_import_result.message)
+
+ return ResponseUtil.success(msg=batch_import_result.message)
+
+
+@userController.post('/importTemplate', dependencies=[Depends(CheckUserInterfaceAuth('system:user:import'))])
+async def export_system_user_template(request: Request, query_db: AsyncSession = Depends(get_db)):
+ user_import_template_result = await UserService.get_user_import_template_services()
+ logger.info('获取成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(user_import_template_result))
+
+
+@userController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:user:export'))])
+@Log(title='用户管理', business_type=BusinessType.EXPORT)
+async def export_system_user_list(
+ request: Request,
+ user_page_query: UserPageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+ data_scope_sql: str = Depends(GetDataScope('SysUser')),
+):
+ # 获取全量数据
+ user_query_result = await UserService.get_user_list_services(
+ query_db, user_page_query, data_scope_sql, is_page=False
+ )
+ user_export_result = await UserService.export_user_list_services(user_query_result)
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(user_export_result))
+
+
+@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: AsyncSession = Depends(get_db)):
+ user_role_query = UserRoleQueryModel(userId=user_id)
+ user_role_allocated_query_result = await UserService.get_user_role_allocated_list_services(
+ query_db, user_role_query
+ )
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=user_role_allocated_query_result)
+
+
+@userController.put(
+ '/authRole',
+ response_model=UserRoleResponseModel,
+ dependencies=[Depends(CheckUserInterfaceAuth('system:user:edit'))],
+)
+@Log(title='用户管理', business_type=BusinessType.GRANT)
+async def update_system_role_user(
+ request: Request,
+ user_id: int = Query(alias='userId'),
+ role_ids: str = Query(alias='roleIds'),
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+ user_data_scope_sql: str = Depends(GetDataScope('SysUser')),
+ role_data_scope_sql: str = Depends(GetDataScope('SysDept')),
+):
+ if not current_user.user.admin:
+ await UserService.check_user_data_scope_services(query_db, user_id, user_data_scope_sql)
+ await RoleService.check_role_data_scope_services(query_db, role_ids, role_data_scope_sql)
+ add_user_role_result = await UserService.add_user_role_services(
+ query_db, CrudUserRoleModel(userId=user_id, roleIds=role_ids)
+ )
+ logger.info(add_user_role_result.message)
+
+ return ResponseUtil.success(msg=add_user_role_result.message)
diff --git a/ruoyi-fastapi-backend/module_admin/controller/visitor_type_controller.py b/ruoyi-fastapi-backend/module_admin/controller/visitor_type_controller.py
new file mode 100644
index 0000000..d73c855
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/visitor_type_controller.py
@@ -0,0 +1,105 @@
+# from datetime import datetime
+# from fastapi import APIRouter, Depends, Form, Request
+# from pydantic_validation_decorator import ValidateFields
+# from sqlalchemy.ext.asyncio import AsyncSession
+# from config.enums import BusinessType
+# from config.get_db import get_db
+# from module_admin.annotation.log_annotation import Log
+# from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+# from module_admin.entity.vo.user_vo import CurrentUserModel
+# from module_admin.service.login_service import LoginService
+# from module_admin.service.visitor_type_service import Visitor_typeService
+# from module_admin.entity.vo.visitor_type_vo import DeleteVisitor_typeModel, Visitor_typeModel, Visitor_typePageQueryModel
+# from utils.common_util import bytes2file_response
+# from utils.log_util import logger
+# from utils.page_util import PageResponseModel
+# from utils.response_util import ResponseUtil
+#
+#
+# visitor_typeController = APIRouter(prefix='/system/visitor_type', dependencies=[Depends(LoginService.get_current_user)])
+#
+#
+# @visitor_typeController.get(
+# '/list', response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:visitor_type:list'))]
+# )
+# async def get_system_visitor_type_list(
+# request: Request,
+# visitor_type_page_query: Visitor_typePageQueryModel = Depends(Visitor_typePageQueryModel.as_query),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取分页数据
+# visitor_type_page_query_result = await Visitor_typeService.get_visitor_type_list_services(query_db, visitor_type_page_query, is_page=True)
+# logger.info('获取成功')
+#
+# return ResponseUtil.success(model_content=visitor_type_page_query_result)
+#
+#
+# @visitor_typeController.post('', dependencies=[Depends(CheckUserInterfaceAuth('system:visitor_type:add'))])
+# @ValidateFields(validate_model='add_visitor_type')
+# @Log(title='访客类型', business_type=BusinessType.INSERT)
+# async def add_system_visitor_type(
+# request: Request,
+# add_visitor_type: Visitor_typeModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# add_visitor_type.create_time = datetime.now()
+# add_visitor_type.create_by = current_user.user.user_name
+# add_visitor_type.update_time = datetime.now()
+# add_visitor_type.update_by = current_user.user.user_name
+# add_visitor_type_result = await Visitor_typeService.add_visitor_type_services(query_db, add_visitor_type)
+# logger.info(add_visitor_type_result.message)
+#
+# return ResponseUtil.success(msg=add_visitor_type_result.message)
+#
+#
+# @visitor_typeController.put('', dependencies=[Depends(CheckUserInterfaceAuth('system:visitor_type:edit'))])
+# @ValidateFields(validate_model='edit_visitor_type')
+# @Log(title='访客类型', business_type=BusinessType.UPDATE)
+# async def edit_system_visitor_type(
+# request: Request,
+# edit_visitor_type: Visitor_typeModel,
+# query_db: AsyncSession = Depends(get_db),
+# current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+# ):
+# edit_visitor_type.update_by = current_user.user.user_name
+# edit_visitor_type.update_time = datetime.now()
+# edit_visitor_type_result = await Visitor_typeService.edit_visitor_type_services(query_db, edit_visitor_type)
+# logger.info(edit_visitor_type_result.message)
+#
+# return ResponseUtil.success(msg=edit_visitor_type_result.message)
+#
+#
+# @visitor_typeController.delete('/{type_ids}', dependencies=[Depends(CheckUserInterfaceAuth('system:visitor_type:remove'))])
+# @Log(title='访客类型', business_type=BusinessType.DELETE)
+# async def delete_system_visitor_type(request: Request, type_ids: str, query_db: AsyncSession = Depends(get_db)):
+# delete_visitor_type = DeleteVisitor_typeModel(typeIds=type_ids)
+# delete_visitor_type_result = await Visitor_typeService.delete_visitor_type_services(query_db, delete_visitor_type)
+# logger.info(delete_visitor_type_result.message)
+#
+# return ResponseUtil.success(msg=delete_visitor_type_result.message)
+#
+#
+# @visitor_typeController.get(
+# '/{type_id}', response_model=Visitor_typeModel, dependencies=[Depends(CheckUserInterfaceAuth('system:visitor_type:query'))]
+# )
+# async def query_detail_system_visitor_type(request: Request, type_id: int, query_db: AsyncSession = Depends(get_db)):
+# visitor_type_detail_result = await Visitor_typeService.visitor_type_detail_services(query_db, type_id)
+# logger.info(f'获取type_id为{type_id}的信息成功')
+#
+# return ResponseUtil.success(data=visitor_type_detail_result)
+#
+#
+# @visitor_typeController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('system:visitor_type:export'))])
+# @Log(title='访客类型', business_type=BusinessType.EXPORT)
+# async def export_system_visitor_type_list(
+# request: Request,
+# visitor_type_page_query: Visitor_typePageQueryModel = Form(),
+# query_db: AsyncSession = Depends(get_db),
+# ):
+# # 获取全量数据
+# visitor_type_query_result = await Visitor_typeService.get_visitor_type_list_services(query_db, visitor_type_page_query, is_page=False)
+# visitor_type_export_result = await Visitor_typeService.export_visitor_type_list_services(visitor_type_query_result)
+# logger.info('导出成功')
+#
+# return ResponseUtil.streaming(data=bytes2file_response(visitor_type_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/controller/words_controller.py b/ruoyi-fastapi-backend/module_admin/controller/words_controller.py
new file mode 100644
index 0000000..6d9633e
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/controller/words_controller.py
@@ -0,0 +1,173 @@
+from datetime import datetime
+from fastapi import APIRouter, Depends, Form, Request
+from pydantic_validation_decorator import ValidateFields
+from sqlalchemy.ext.asyncio import AsyncSession
+from config.enums import BusinessType
+from config.get_db import get_db
+from module_admin.annotation.log_annotation import Log
+from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.entity.vo.user_vo import CurrentUserModel
+from module_admin.service.login_service import LoginService
+from module_admin.service.visitor_type_service import Visitor_typeService
+from module_admin.service.words_service import WordsService
+from module_admin.entity.vo.words_vo import DeleteWordsModel, WordsModel, WordsPageQueryModel, WordsUpdateModel
+from utils.common_util import bytes2file_response
+from utils.log_util import logger
+from utils.page_util import PageResponseModel
+from utils.response_util import ResponseUtil
+
+
+
+wordsController = APIRouter(prefix='/guide/words', dependencies=[Depends(LoginService.get_current_user)])
+"""
+ 访客记录: visitor:record:list, dependencies=[Depends(CheckUserInterfaceAuth('guide:words:list'))]
+ 引导词管理: visitor:guide:list, dependencies=[Depends(CheckUserInterfaceAuth('visitor:guide:list'))]
+"""
+
+
+# 随机获取引导词
+@wordsController.get('/random_words/{type}')
+async def get_random_guide_word(request: Request, type: str, query_db: AsyncSession = Depends(get_db)):
+
+ guide_word = await WordsService.get_random_guide_word_services(query_db, type)
+ return ResponseUtil.success(data=guide_word)
+
+
+
+@wordsController.post(
+ '/list', response_model=PageResponseModel
+ # , dependencies=[Depends(CheckUserInterfaceAuth('guide:words:list'))]
+ , dependencies=[Depends(CheckUserInterfaceAuth('visitor:guide:list'))]
+)
+async def get_guide_words_list(
+ request: Request,
+ words_page_query: WordsPageQueryModel = Depends(WordsPageQueryModel.as_query),
+ query_db: AsyncSession = Depends(get_db),
+):
+ print(words_page_query.begin_time)
+ print(words_page_query.end_time)
+ # 获取分页数据
+ words_page_query_result = await WordsService.get_words_list_services(query_db, words_page_query, is_page=True)
+ logger.info('获取成功')
+
+ return ResponseUtil.success(model_content=words_page_query_result)
+
+
+#批量启用引导词
+@wordsController.post('/enable_guide/{guide_ids}', dependencies=[Depends(CheckUserInterfaceAuth('visitor:guide:list'))])
+async def enable_guide(
+ request: Request,
+ guide_ids: str,
+ query_db: AsyncSession = Depends(get_db),
+
+):
+ edit_del_result = await WordsService.edit_del_words_services(query_db=query_db, guide_ids=guide_ids, del_flg='0')
+ return ResponseUtil.success(msg=edit_del_result.message)
+
+#批量禁用引导词
+@wordsController.post('/disable_guide/{guide_ids}', dependencies=[Depends(CheckUserInterfaceAuth('visitor:guide:list'))])
+async def disable_guide(
+ request: Request,
+ guide_ids: str,
+ query_db: AsyncSession = Depends(get_db),
+
+):
+ edit_del_result = await WordsService.edit_del_words_services(query_db=query_db, guide_ids=guide_ids, del_flg='2')
+ return ResponseUtil.success(msg=edit_del_result.message)
+
+#获取访客类型列表
+@wordsController.get('/visitor_type_list',)
+async def get_system_visitor_type_list(
+ request: Request,
+ query_db: AsyncSession = Depends(get_db),
+):
+ visitor_type_list = await Visitor_typeService.get_system_visitor_type_list(query_db=query_db)
+ return ResponseUtil.success(data=visitor_type_list)
+
+
+
+@wordsController.post('/add_guide'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('guide:words:add'))]
+ , dependencies=[Depends(CheckUserInterfaceAuth('visitor:guide:list'))]
+ )
+@ValidateFields(validate_model='add_words')
+@Log(title='引导词', business_type=BusinessType.INSERT)
+async def add_guide_words(
+ request: Request,
+ add_words: WordsModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+ if add_words.type is None:
+ return ResponseUtil.failure(msg='引导词类型不能为空')
+ if add_words.text is None:
+ return ResponseUtil.failure(msg='引导词内容不能为空')
+ add_words.create_time = datetime.now()
+ add_words.update_time = datetime.now()
+ add_words.create_by = current_user.user.user_name
+ add_words.del_flag = "0"
+ add_words_result = await WordsService.add_words_services(query_db, add_words)
+ logger.info(add_words_result.message)
+
+ return ResponseUtil.success(msg=add_words_result.message)
+
+
+@wordsController.put('/edit_guide'
+ # ,dependencies=[Depends(CheckUserInterfaceAuth('guide:words:edit'))]
+ , dependencies=[Depends(CheckUserInterfaceAuth('visitor:guide:list'))]
+ )
+@ValidateFields(validate_model='edit_words')
+@Log(title='引导词', business_type=BusinessType.UPDATE)
+async def edit_guide_words(
+ request: Request,
+ edit_words: WordsUpdateModel,
+ query_db: AsyncSession = Depends(get_db),
+ current_user: CurrentUserModel = Depends(LoginService.get_current_user),
+):
+
+ edit_words.update_by = current_user.user.user_name
+ edit_words.update_time = datetime.now()
+ edit_words_result = await WordsService.edit_words_services(query_db, edit_words)
+ logger.info(edit_words_result.message)
+
+ return ResponseUtil.success(msg=edit_words_result.message)
+
+
+@wordsController.delete('/{guide_ids}'
+ # , dependencies=[Depends(CheckUserInterfaceAuth('guide:words:remove'))]
+ , dependencies=[Depends(CheckUserInterfaceAuth('visitor:guide:list'))]
+ )
+@Log(title='引导词', business_type=BusinessType.DELETE)
+async def delete_guide_words(request: Request, guide_ids: str, query_db: AsyncSession = Depends(get_db)):
+ delete_words = DeleteWordsModel(guideIds=guide_ids)
+ delete_words_result = await WordsService.delete_words_services(query_db, delete_words)
+ logger.info(delete_words_result.message)
+
+ return ResponseUtil.success(msg=delete_words_result.message)
+
+
+@wordsController.get(
+ '/{guide_id}', response_model=WordsModel
+ # , dependencies=[Depends(CheckUserInterfaceAuth('guide:words:query'))]
+ , dependencies=[Depends(CheckUserInterfaceAuth('visitor:guide:list'))]
+)
+async def query_detail_guide_words(request: Request, guide_id: int, query_db: AsyncSession = Depends(get_db)):
+ words_detail_result = await WordsService.words_detail_services(query_db, guide_id)
+ logger.info(f'获取guide_id为{guide_id}的信息成功')
+
+ return ResponseUtil.success(data=words_detail_result)
+
+
+@wordsController.post('/export', dependencies=[Depends(CheckUserInterfaceAuth('visitor:guide:list'))])
+@Log(title='引导词', business_type=BusinessType.EXPORT)
+async def export_guide_words_list(
+ request: Request,
+ words_page_query: WordsPageQueryModel = Form(),
+ query_db: AsyncSession = Depends(get_db),
+):
+ # 获取全量数据
+ words_query_result = await WordsService.get_words_list_services(query_db, words_page_query, is_page=False)
+ words_export_result = await WordsService.export_words_list_services(words_query_result)
+ logger.info('导出成功')
+
+ return ResponseUtil.streaming(data=bytes2file_response(words_export_result))
diff --git a/ruoyi-fastapi-backend/module_admin/dao/__init__.py b/ruoyi-fastapi-backend/module_admin/dao/__init__.py
new file mode 100644
index 0000000..e69de29
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..4f34a2f
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/config_dao.py
@@ -0,0 +1,116 @@
+from datetime import datetime, time
+from sqlalchemy import delete, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.config_do import SysConfig
+from module_admin.entity.vo.config_vo import ConfigModel, ConfigPageQueryModel
+from utils.page_util import PageUtil
+
+
+class ConfigDao:
+ """
+ 参数配置管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_config_detail_by_id(cls, db: AsyncSession, config_id: int):
+ """
+ 根据参数配置id获取参数配置详细信息
+
+ :param db: orm对象
+ :param config_id: 参数配置id
+ :return: 参数配置信息对象
+ """
+ config_info = (await db.execute(select(SysConfig).where(SysConfig.config_id == config_id))).scalars().first()
+
+ return config_info
+
+ @classmethod
+ async def get_config_detail_by_info(cls, db: AsyncSession, config: ConfigModel):
+ """
+ 根据参数配置参数获取参数配置信息
+
+ :param db: orm对象
+ :param config: 参数配置参数对象
+ :return: 参数配置信息对象
+ """
+ config_info = (
+ (
+ await db.execute(
+ select(SysConfig).where(
+ SysConfig.config_key == config.config_key if config.config_key else True,
+ SysConfig.config_value == config.config_value if config.config_value else True,
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return config_info
+
+ @classmethod
+ async def get_config_list(cls, db: AsyncSession, query_object: ConfigPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取参数配置列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 参数配置列表信息对象
+ """
+ query = (
+ select(SysConfig)
+ .where(
+ SysConfig.config_name.like(f'%{query_object.config_name}%') if query_object.config_name else True,
+ SysConfig.config_key.like(f'%{query_object.config_key}%') if query_object.config_key else True,
+ SysConfig.config_type == query_object.config_type if query_object.config_type else True,
+ SysConfig.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_time and query_object.end_time
+ else True,
+ )
+ .order_by(SysConfig.config_id)
+ .distinct()
+ )
+ config_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return config_list
+
+ @classmethod
+ async def add_config_dao(cls, db: AsyncSession, config: ConfigModel):
+ """
+ 新增参数配置数据库操作
+
+ :param db: orm对象
+ :param config: 参数配置对象
+ :return:
+ """
+ db_config = SysConfig(**config.model_dump())
+ db.add(db_config)
+ await db.flush()
+
+ return db_config
+
+ @classmethod
+ async def edit_config_dao(cls, db: AsyncSession, config: dict):
+ """
+ 编辑参数配置数据库操作
+
+ :param db: orm对象
+ :param config: 需要更新的参数配置字典
+ :return:
+ """
+ await db.execute(update(SysConfig), [config])
+
+ @classmethod
+ async def delete_config_dao(cls, db: AsyncSession, config: ConfigModel):
+ """
+ 删除参数配置数据库操作
+
+ :param db: orm对象
+ :param config: 参数配置对象
+ :return:
+ """
+ await db.execute(delete(SysConfig).where(SysConfig.config_id.in_([config.config_id])))
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..f450210
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/dept_dao.py
@@ -0,0 +1,307 @@
+from sqlalchemy import bindparam, func, or_, select, update # noqa: F401
+from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.util import immutabledict
+from typing import List
+from module_admin.entity.do.dept_do import SysDept
+from module_admin.entity.do.role_do import SysRoleDept # noqa: F401
+from module_admin.entity.do.user_do import SysUser
+from module_admin.entity.vo.dept_vo import DeptModel
+
+
+class DeptDao:
+ """
+ 部门管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_dept_by_id(cls, db: AsyncSession, dept_id: int):
+ """
+ 根据部门id获取在用部门信息
+
+ :param db: orm对象
+ :param dept_id: 部门id
+ :return: 在用部门信息对象
+ """
+ dept_info = (await db.execute(select(SysDept).where(SysDept.dept_id == dept_id))).scalars().first()
+
+ return dept_info
+
+ @classmethod
+ async def get_dept_detail_by_id(cls, db: AsyncSession, dept_id: int):
+ """
+ 根据部门id获取部门详细信息
+
+ :param db: orm对象
+ :param dept_id: 部门id
+ :return: 部门信息对象
+ """
+ dept_info = (
+ (await db.execute(select(SysDept).where(SysDept.dept_id == dept_id, SysDept.del_flag == '0')))
+ .scalars()
+ .first()
+ )
+
+ return dept_info
+
+ @classmethod
+ async def get_dept_detail_by_info(cls, db: AsyncSession, dept: DeptModel):
+ """
+ 根据部门参数获取部门信息
+
+ :param db: orm对象
+ :param dept: 部门参数对象
+ :return: 部门信息对象
+ """
+ dept_info = (
+ (
+ await db.execute(
+ select(SysDept).where(
+ SysDept.parent_id == dept.parent_id if dept.parent_id else True,
+ SysDept.dept_name == dept.dept_name if dept.dept_name else True,
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return dept_info
+
+ @classmethod
+ async def get_dept_info_for_edit_option(cls, db: AsyncSession, dept_info: DeptModel, data_scope_sql: str):
+ """
+ 获取部门编辑对应的在用部门列表信息
+
+ :param db: orm对象
+ :param dept_info: 部门对象
+ :param data_scope_sql: 数据权限对应的查询sql语句
+ :return: 部门列表信息
+ """
+ dept_result = (
+ (
+ await db.execute(
+ select(SysDept)
+ .where(
+ SysDept.dept_id != dept_info.dept_id,
+ ~SysDept.dept_id.in_(
+ select(SysDept.dept_id).where(func.find_in_set(dept_info.dept_id, SysDept.ancestors))
+ ),
+ SysDept.del_flag == '0',
+ SysDept.status == '0',
+ eval(data_scope_sql),
+ )
+ .order_by(SysDept.order_num)
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+
+ return dept_result
+
+ @classmethod
+ async def get_children_dept_dao(cls, db: AsyncSession, dept_id: int):
+ """
+ 根据部门id查询当前部门的子部门列表信息
+
+ :param db: orm对象
+ :param dept_id: 部门id
+ :return: 子部门信息列表
+ """
+ dept_result = (
+ (await db.execute(select(SysDept).where(func.find_in_set(dept_id, SysDept.ancestors)))).scalars().all()
+ )
+
+ return dept_result
+
+ @classmethod
+ async def get_dept_list_for_tree(cls, db: AsyncSession, dept_info: DeptModel, data_scope_sql: str):
+ """
+ 获取所有在用部门列表信息
+
+ :param db: orm对象
+ :param dept_info: 部门对象
+ :param data_scope_sql: 数据权限对应的查询sql语句
+ :return: 在用部门列表信息
+ """
+ dept_result = (
+ (
+ await db.execute(
+ select(SysDept)
+ .where(
+ SysDept.status == '0',
+ SysDept.del_flag == '0',
+ SysDept.dept_name.like(f'%{dept_info.dept_name}%') if dept_info.dept_name else True,
+ eval(data_scope_sql),
+ )
+ .order_by(SysDept.order_num)
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+
+ return dept_result
+
+ @classmethod
+ async def get_dept_list(cls, db: AsyncSession, page_object: DeptModel, data_scope_sql: str):
+ """
+ 根据查询参数获取部门列表信息
+
+ :param db: orm对象
+ :param page_object: 不分页查询参数对象
+ :param data_scope_sql: 数据权限对应的查询sql语句
+ :return: 部门列表信息对象
+ """
+ dept_result = (
+ (
+ await db.execute(
+ select(SysDept)
+ .where(
+ SysDept.del_flag == '0',
+ SysDept.dept_id == page_object.dept_id if page_object.dept_id is not None else True,
+ SysDept.status == page_object.status if page_object.status else True,
+ SysDept.dept_name.like(f'%{page_object.dept_name}%') if page_object.dept_name else True,
+ eval(data_scope_sql),
+ )
+ .order_by(SysDept.order_num)
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+
+ return dept_result
+
+ @classmethod
+ async def add_dept_dao(cls, db: AsyncSession, dept: DeptModel):
+ """
+ 新增部门数据库操作
+
+ :param db: orm对象
+ :param dept: 部门对象
+ :return: 新增校验结果
+ """
+ db_dept = SysDept(**dept.model_dump())
+ db.add(db_dept)
+ await db.flush()
+
+ return db_dept
+
+ @classmethod
+ async def edit_dept_dao(cls, db: AsyncSession, dept: dict):
+ """
+ 编辑部门数据库操作
+
+ :param db: orm对象
+ :param dept: 需要更新的部门字典
+ :return: 编辑校验结果
+ """
+ await db.execute(update(SysDept), [dept])
+
+ @classmethod
+ async def update_dept_children_dao(cls, db: AsyncSession, update_dept: List):
+ """
+ 更新子部门信息
+
+ :param db: orm对象
+ :param update_dept: 需要更新的部门列表
+ :return:
+ """
+ await db.execute(
+ update(SysDept)
+ .where(SysDept.dept_id == bindparam('dept_id'))
+ .values(
+ {
+ 'dept_id': bindparam('dept_id'),
+ 'ancestors': bindparam('ancestors'),
+ }
+ ),
+ update_dept,
+ execution_options=immutabledict({'synchronize_session': None}),
+ )
+
+ @classmethod
+ async def update_dept_status_normal_dao(cls, db: AsyncSession, dept_id_list: List):
+ """
+ 批量更新部门状态为正常
+
+ :param db: orm对象
+ :param dept_id_list: 部门id列表
+ :return:
+ """
+ await db.execute(update(SysDept).where(SysDept.dept_id.in_(dept_id_list)).values(status='0'))
+
+ @classmethod
+ async def delete_dept_dao(cls, db: AsyncSession, dept: DeptModel):
+ """
+ 删除部门数据库操作
+
+ :param db: orm对象
+ :param dept: 部门对象
+ :return:
+ """
+ await db.execute(
+ update(SysDept)
+ .where(SysDept.dept_id == dept.dept_id)
+ .values(del_flag='2', update_by=dept.update_by, update_time=dept.update_time)
+ )
+
+ @classmethod
+ async def count_normal_children_dept_dao(cls, db: AsyncSession, dept_id: int):
+ """
+ 根据部门id查询查询所有子部门(正常状态)的数量
+
+ :param db: orm对象
+ :param dept_id: 部门id
+ :return: 所有子部门(正常状态)的数量
+ """
+ normal_children_dept_count = (
+ await db.execute(
+ select(func.count('*'))
+ .select_from(SysDept)
+ .where(SysDept.status == '0', SysDept.del_flag == '0', func.find_in_set(dept_id, SysDept.ancestors))
+ )
+ ).scalar()
+
+ return normal_children_dept_count
+
+ @classmethod
+ async def count_children_dept_dao(cls, db: AsyncSession, dept_id: int):
+ """
+ 根据部门id查询查询所有子部门(所有状态)的数量
+
+ :param db: orm对象
+ :param dept_id: 部门id
+ :return: 所有子部门(所有状态)的数量
+ """
+ children_dept_count = (
+ await db.execute(
+ select(func.count('*'))
+ .select_from(SysDept)
+ .where(SysDept.del_flag == '0', SysDept.parent_id == dept_id)
+ .limit(1)
+ )
+ ).scalar()
+
+ return children_dept_count
+
+ @classmethod
+ async def count_dept_user_dao(cls, db: AsyncSession, dept_id: int):
+ """
+ 根据部门id查询查询部门下的用户数量
+
+ :param db: orm对象
+ :param dept_id: 部门id
+ :return: 部门下的用户数量
+ """
+ dept_user_count = (
+ await db.execute(
+ select(func.count('*')).select_from(SysUser).where(SysUser.dept_id == dept_id, SysUser.del_flag == '0')
+ )
+ ).scalar()
+
+ return dept_user_count
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..c5a8ed5
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/dict_dao.py
@@ -0,0 +1,281 @@
+from datetime import datetime, time
+from sqlalchemy import and_, delete, func, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.dict_do import SysDictType, SysDictData
+from module_admin.entity.vo.dict_vo import DictDataModel, DictDataPageQueryModel, DictTypeModel, DictTypePageQueryModel
+from utils.page_util import PageUtil
+from utils.time_format_util import list_format_datetime
+
+
+class DictTypeDao:
+ """
+ 字典类型管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_dict_type_detail_by_id(cls, db: AsyncSession, dict_id: int):
+ """
+ 根据字典类型id获取字典类型详细信息
+
+ :param db: orm对象
+ :param dict_id: 字典类型id
+ :return: 字典类型信息对象
+ """
+ dict_type_info = (await db.execute(select(SysDictType).where(SysDictType.dict_id == dict_id))).scalars().first()
+
+ return dict_type_info
+
+ @classmethod
+ async def get_dict_type_detail_by_info(cls, db: AsyncSession, dict_type: DictTypeModel):
+ """
+ 根据字典类型参数获取字典类型信息
+
+ :param db: orm对象
+ :param dict_type: 字典类型参数对象
+ :return: 字典类型信息对象
+ """
+ dict_type_info = (
+ (
+ await db.execute(
+ select(SysDictType).where(
+ SysDictType.dict_type == dict_type.dict_type if dict_type.dict_type else True,
+ SysDictType.dict_name == dict_type.dict_name if dict_type.dict_name else True,
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return dict_type_info
+
+ @classmethod
+ async def get_all_dict_type(cls, db: AsyncSession):
+ """
+ 获取所有的字典类型信息
+
+ :param db: orm对象
+ :return: 字典类型信息列表对象
+ """
+ dict_type_info = (await db.execute(select(SysDictType))).scalars().all()
+
+ return list_format_datetime(dict_type_info)
+
+ @classmethod
+ async def get_dict_type_list(cls, db: AsyncSession, query_object: DictTypePageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取字典类型列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 字典类型列表信息对象
+ """
+ query = (
+ select(SysDictType)
+ .where(
+ SysDictType.dict_name.like(f'%{query_object.dict_name}%') if query_object.dict_name else True,
+ SysDictType.dict_type.like(f'%{query_object.dict_type}%') if query_object.dict_type else True,
+ SysDictType.status == query_object.status if query_object.status else True,
+ SysDictType.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_time and query_object.end_time
+ else True,
+ )
+ .order_by(SysDictType.dict_id)
+ .distinct()
+ )
+ dict_type_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return dict_type_list
+
+ @classmethod
+ async def add_dict_type_dao(cls, db: AsyncSession, dict_type: DictTypeModel):
+ """
+ 新增字典类型数据库操作
+
+ :param db: orm对象
+ :param dict_type: 字典类型对象
+ :return:
+ """
+ db_dict_type = SysDictType(**dict_type.model_dump())
+ db.add(db_dict_type)
+ await db.flush()
+
+ return db_dict_type
+
+ @classmethod
+ async def edit_dict_type_dao(cls, db: AsyncSession, dict_type: dict):
+ """
+ 编辑字典类型数据库操作
+
+ :param db: orm对象
+ :param dict_type: 需要更新的字典类型字典
+ :return:
+ """
+ await db.execute(update(SysDictType), [dict_type])
+
+ @classmethod
+ async def delete_dict_type_dao(cls, db: AsyncSession, dict_type: DictTypeModel):
+ """
+ 删除字典类型数据库操作
+
+ :param db: orm对象
+ :param dict_type: 字典类型对象
+ :return:
+ """
+ await db.execute(delete(SysDictType).where(SysDictType.dict_id.in_([dict_type.dict_id])))
+
+
+class DictDataDao:
+ """
+ 字典数据管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_dict_data_detail_by_id(cls, db: AsyncSession, dict_code: int):
+ """
+ 根据字典数据id获取字典数据详细信息
+
+ :param db: orm对象
+ :param dict_code: 字典数据id
+ :return: 字典数据信息对象
+ """
+ dict_data_info = (
+ (await db.execute(select(SysDictData).where(SysDictData.dict_code == dict_code))).scalars().first()
+ )
+
+ return dict_data_info
+
+ @classmethod
+ async def get_dict_data_detail_by_info(cls, db: AsyncSession, dict_data: DictDataModel):
+ """
+ 根据字典数据参数获取字典数据信息
+
+ :param db: orm对象
+ :param dict_data: 字典数据参数对象
+ :return: 字典数据信息对象
+ """
+ dict_data_info = (
+ (
+ await db.execute(
+ select(SysDictData).where(
+ SysDictData.dict_type == dict_data.dict_type,
+ SysDictData.dict_label == dict_data.dict_label,
+ SysDictData.dict_value == dict_data.dict_value,
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return dict_data_info
+
+ @classmethod
+ async def get_dict_data_list(cls, db: AsyncSession, query_object: DictDataPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取字典数据列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 字典数据列表信息对象
+ """
+ query = (
+ select(SysDictData)
+ .where(
+ SysDictData.dict_type == query_object.dict_type if query_object.dict_type else True,
+ SysDictData.dict_label.like(f'%{query_object.dict_label}%') if query_object.dict_label else True,
+ SysDictData.status == query_object.status if query_object.status else True,
+ )
+ .order_by(SysDictData.dict_sort)
+ .distinct()
+ )
+ dict_data_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return dict_data_list
+
+ @classmethod
+ async def query_dict_data_list(cls, db: AsyncSession, dict_type: str):
+ """
+ 根据查询参数获取字典数据列表信息
+
+ :param db: orm对象
+ :param dict_type: 字典类型
+ :return: 字典数据列表信息对象
+ """
+ dict_data_list = (
+ (
+ await db.execute(
+ select(SysDictData)
+ .select_from(SysDictType)
+ .where(SysDictType.dict_type == dict_type if dict_type else True, SysDictType.status == '0')
+ .join(
+ SysDictData,
+ and_(SysDictType.dict_type == SysDictData.dict_type, SysDictData.status == '0'),
+ isouter=True,
+ )
+ .order_by(SysDictData.dict_sort)
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+
+ return dict_data_list
+
+ @classmethod
+ async def add_dict_data_dao(cls, db: AsyncSession, dict_data: DictDataModel):
+ """
+ 新增字典数据数据库操作
+
+ :param db: orm对象
+ :param dict_data: 字典数据对象
+ :return:
+ """
+ db_data_type = SysDictData(**dict_data.model_dump())
+ db.add(db_data_type)
+ await db.flush()
+
+ return db_data_type
+
+ @classmethod
+ async def edit_dict_data_dao(cls, db: AsyncSession, dict_data: dict):
+ """
+ 编辑字典数据数据库操作
+
+ :param db: orm对象
+ :param dict_data: 需要更新的字典数据字典
+ :return:
+ """
+ await db.execute(update(SysDictData), [dict_data])
+
+ @classmethod
+ async def delete_dict_data_dao(cls, db: AsyncSession, dict_data: DictDataModel):
+ """
+ 删除字典数据数据库操作
+
+ :param db: orm对象
+ :param dict_data: 字典数据对象
+ :return:
+ """
+ await db.execute(delete(SysDictData).where(SysDictData.dict_code.in_([dict_data.dict_code])))
+
+ @classmethod
+ async def count_dict_data_dao(cls, db: AsyncSession, dict_type: str):
+ """
+ 根据字典类型查询字典类型关联的字典数据数量
+
+ :param db: orm对象
+ :param dict_type: 字典类型
+ :return: 字典类型关联的字典数据数量
+ """
+ dict_data_count = (
+ await db.execute(select(func.count('*')).select_from(SysDictData).where(SysDictData.dict_type == dict_type))
+ ).scalar()
+
+ return dict_data_count
diff --git a/ruoyi-fastapi-backend/module_admin/dao/door_dao.py b/ruoyi-fastapi-backend/module_admin/dao/door_dao.py
new file mode 100644
index 0000000..0e5ae6d
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/door_dao.py
@@ -0,0 +1,154 @@
+from sqlalchemy import delete, select, update, or_
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.door_do import Door
+from module_admin.entity.vo.door_vo import DoorModel, DoorPageQueryModel
+from utils.page_util import PageUtil
+
+
+class DoorDao:
+ """
+ 门禁设备模块数据库操作层
+ """
+
+ @classmethod
+ async def get_door_door_status(cls, db: AsyncSession):
+ """获取门禁状态
+ """
+ stmt = (
+ select(Door.indexCode, Door.name).select_from(Door).where(Door.permission=="1")
+ )
+ result = await db.execute(stmt)
+
+ return result.mappings().all()
+
+ @classmethod
+ async def get_door_index_code_list(cls, db: AsyncSession, permission: str = None):
+ """
+ 获取门禁设备索引编码列表
+ """
+ query = select(Door.indexCode).where(Door.permission == permission if permission else True)
+ result = await db.execute(query)
+ return [row[0] for row in result.all()]
+
+ @classmethod
+ async def get_door_detail_by_id(cls, db: AsyncSession, id: int):
+ """
+ 根据主键自增获取门禁设备详细信息
+
+ :param db: orm对象
+ :param id: 主键自增
+ :return: 门禁设备信息对象
+ """
+ door_info = (
+ (
+ await db.execute(
+ select(Door)
+ .where(
+ Door.id == id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return door_info
+
+ @classmethod
+ async def get_door_detail_by_info(cls, db: AsyncSession, door: DoorModel):
+ """
+ 根据门禁设备参数获取门禁设备信息
+
+ :param db: orm对象
+ :param door: 门禁设备参数对象
+ :return: 门禁设备信息对象
+ """
+ door_info = (
+ (
+ await db.execute(
+ select(Door).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return door_info
+
+ @classmethod
+ async def get_door_list(cls, db: AsyncSession, query_object: DoorPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取门禁设备列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 门禁设备列表信息对象
+ """
+ query = (
+ select(Door)
+ .where(
+ # Door.index_code == query_object.index_code if query_object.index_code else True,
+ or_(
+ (
+ Door.name.like(f"%{query_object.key_word}%")
+ if query_object.key_word
+ else True
+ ),
+ (
+ Door.install_location.like(f"%{query_object.key_word}%")
+ if query_object.key_word
+ else True
+ ),
+ ),
+ # Door.status == query_object.status if query_object.status else True,
+ (
+ Door.permission == query_object.permission
+ if query_object.permission
+ else True
+ ),
+ )
+ .order_by(Door.id)
+ .distinct()
+ )
+ door_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return door_list
+
+ @classmethod
+ async def add_door_dao(cls, db: AsyncSession, door: DoorModel):
+ """
+ 新增门禁设备数据库操作
+
+ :param db: orm对象
+ :param door: 门禁设备对象
+ :return:
+ """
+ db_door = Door(**door.model_dump(exclude={}))
+ db.add(db_door)
+ await db.flush()
+
+ return db_door
+
+ @classmethod
+ async def edit_door_dao(cls, db: AsyncSession, door: dict):
+ """
+ 编辑门禁设备数据库操作
+
+ :param db: orm对象
+ :param door: 需要更新的门禁设备字典
+ :return:
+ """
+ await db.execute(update(Door), [door])
+
+ @classmethod
+ async def delete_door_dao(cls, db: AsyncSession, door: DoorModel):
+ """
+ 删除门禁设备数据库操作
+
+ :param db: orm对象
+ :param door: 门禁设备对象
+ :return:
+ """
+ await db.execute(delete(Door).where(Door.id.in_([door.id])))
diff --git a/ruoyi-fastapi-backend/module_admin/dao/explanation_content_dao.py b/ruoyi-fastapi-backend/module_admin/dao/explanation_content_dao.py
new file mode 100644
index 0000000..085aa66
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/explanation_content_dao.py
@@ -0,0 +1,135 @@
+from datetime import datetime, time
+from sqlalchemy import delete, select, update, desc
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.explanation_content_do import ExplanationContent
+from module_admin.entity.vo.explanation_content_vo import Explanation_contentModel, Explanation_contentPageQueryModel
+from utils.page_util import PageUtil
+
+
+class Explanation_contentDao:
+ """
+ 讲解内容模块数据库操作层
+ """
+
+ @classmethod
+ async def get_explanation_content_detail_by_id(cls, db: AsyncSession, explanation_content_id: int):
+ """
+ 根据主键ID获取讲解内容详细信息
+
+ :param db: orm对象
+ :param explanation_content_id: 主键ID
+ :return: 讲解内容信息对象
+ """
+ explanation_content_info = (
+ (
+ await db.execute(
+ select(ExplanationContent)
+ .where(
+ ExplanationContent.explanation_content_id == explanation_content_id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return explanation_content_info
+
+ @classmethod
+ async def get_explanation_content_detail_by_info(cls, db: AsyncSession, explanation_content: Explanation_contentModel):
+ """
+ 根据讲解内容参数获取讲解内容信息
+
+ :param db: orm对象
+ :param explanation_content: 讲解内容参数对象
+ :return: 讲解内容信息对象
+ """
+ explanation_content_info = (
+ (
+ await db.execute(
+ select(ExplanationContent).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return explanation_content_info
+
+ @classmethod
+ async def get_explanation_content_list(cls, db: AsyncSession, query_object: Explanation_contentPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取讲解内容列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 讲解内容列表信息对象
+ """
+ query = (
+ select(ExplanationContent)
+ .where(
+ # ExplanationContent.title == query_object.title if query_object.title else True,
+ # ExplanationContent.content == query_object.content if query_object.content else True,
+ ExplanationContent.type == query_object.type if query_object.type else True,
+ # ExplanationContent.spend_time == query_object.spend_time if query_object.spend_time else True,
+ ExplanationContent.language == query_object.language if query_object.language else True,
+ ExplanationContent.status == query_object.status if query_object.status else True,
+ ExplanationContent.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_create_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_create_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_create_time and query_object.end_create_time
+ else True,
+ ExplanationContent.update_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_update_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_update_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_update_time and query_object.end_update_time
+ else True,
+ )
+ .order_by(desc(ExplanationContent.update_time))
+ .distinct()
+ )
+ explanation_content_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return explanation_content_list
+
+ @classmethod
+ async def add_explanation_content_dao(cls, db: AsyncSession, explanation_content: Explanation_contentModel):
+ """
+ 新增讲解内容数据库操作
+
+ :param db: orm对象
+ :param explanation_content: 讲解内容对象
+ :return:
+ """
+ db_explanation_content = ExplanationContent(**explanation_content.model_dump(exclude={}))
+ db.add(db_explanation_content)
+ await db.flush()
+
+ return db_explanation_content
+
+ @classmethod
+ async def edit_explanation_content_dao(cls, db: AsyncSession, explanation_content: dict):
+ """
+ 编辑讲解内容数据库操作
+
+ :param db: orm对象
+ :param explanation_content: 需要更新的讲解内容字典
+ :return:
+ """
+ await db.execute(update(ExplanationContent), [explanation_content])
+
+ @classmethod
+ async def delete_explanation_content_dao(cls, db: AsyncSession, explanation_content: Explanation_contentModel):
+ """
+ 删除讲解内容数据库操作
+
+ :param db: orm对象
+ :param explanation_content: 讲解内容对象
+ :return:
+ """
+ await db.execute(delete(ExplanationContent).where(ExplanationContent.explanation_content_id.in_([explanation_content.explanation_content_id])))
+
diff --git a/ruoyi-fastapi-backend/module_admin/dao/explanation_content_type_dao.py b/ruoyi-fastapi-backend/module_admin/dao/explanation_content_type_dao.py
new file mode 100644
index 0000000..cac3fa0
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/explanation_content_type_dao.py
@@ -0,0 +1,120 @@
+from sqlalchemy import delete, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.explanation_content_type_do import ExplanationContentType
+from module_admin.entity.vo.explanation_content_type_vo import Explanation_content_typeModel, Explanation_content_typePageQueryModel
+from utils.page_util import PageUtil
+
+
+class Explanation_content_typeDao:
+ """
+ 讲解内容类型模块数据库操作层
+ """
+
+ @classmethod
+ async def get_explanation_content_type_detail_by_id(cls, db: AsyncSession, content_type_id: int):
+ """
+ 根据主键ID获取讲解内容类型详细信息
+
+ :param db: orm对象
+ :param content_type_id: 主键ID
+ :return: 讲解内容类型信息对象
+ """
+ explanation_content_type_info = (
+ (
+ await db.execute(
+ select(ExplanationContentType)
+ .where(
+ ExplanationContentType.content_type_id == content_type_id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return explanation_content_type_info
+
+ @classmethod
+ async def get_explanation_content_type_detail_by_info(cls, db: AsyncSession, explanation_content_type: Explanation_content_typeModel):
+ """
+ 根据讲解内容类型参数获取讲解内容类型信息
+
+ :param db: orm对象
+ :param explanation_content_type: 讲解内容类型参数对象
+ :return: 讲解内容类型信息对象
+ """
+ explanation_content_type_info = (
+ (
+ await db.execute(
+ select(ExplanationContentType).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return explanation_content_type_info
+
+ @classmethod
+ async def get_explanation_content_type_list(cls, db: AsyncSession, query_object: Explanation_content_typePageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取讲解内容类型列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 讲解内容类型列表信息对象
+ """
+ query = (
+ select(ExplanationContentType)
+ .where(
+ ExplanationContentType.name.like(f'%{query_object.name}%') if query_object.name else True,
+ ExplanationContentType.type_value == query_object.type_value if query_object.type_value else True,
+ ExplanationContentType.create_time.like(f'%{query_object.create_time}%') if query_object.create_time else True,
+ ExplanationContentType.update_time.like(f'%{query_object.update_time}%') if query_object.update_time else True,
+ )
+ .order_by(ExplanationContentType.content_type_id)
+ .distinct()
+ )
+ # explanation_content_type_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+ explanation_content_type_list = (await db.execute(query)).scalars().all()
+ return explanation_content_type_list
+
+ @classmethod
+ async def add_explanation_content_type_dao(cls, db: AsyncSession, explanation_content_type: Explanation_content_typeModel):
+ """
+ 新增讲解内容类型数据库操作
+
+ :param db: orm对象
+ :param explanation_content_type: 讲解内容类型对象
+ :return:
+ """
+ db_explanation_content_type = ExplanationContentType(**explanation_content_type.model_dump(exclude={}))
+ db.add(db_explanation_content_type)
+ await db.flush()
+
+ return db_explanation_content_type
+
+ @classmethod
+ async def edit_explanation_content_type_dao(cls, db: AsyncSession, explanation_content_type: dict):
+ """
+ 编辑讲解内容类型数据库操作
+
+ :param db: orm对象
+ :param explanation_content_type: 需要更新的讲解内容类型字典
+ :return:
+ """
+ await db.execute(update(ExplanationContentType), [explanation_content_type])
+
+ @classmethod
+ async def delete_explanation_content_type_dao(cls, db: AsyncSession, explanation_content_type: Explanation_content_typeModel):
+ """
+ 删除讲解内容类型数据库操作
+
+ :param db: orm对象
+ :param explanation_content_type: 讲解内容类型对象
+ :return:
+ """
+ await db.execute(delete(ExplanationContentType).where(ExplanationContentType.content_type_id.in_([explanation_content_type.content_type_id])))
+
diff --git a/ruoyi-fastapi-backend/module_admin/dao/explanation_style_dao.py b/ruoyi-fastapi-backend/module_admin/dao/explanation_style_dao.py
new file mode 100644
index 0000000..cb922e6
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/explanation_style_dao.py
@@ -0,0 +1,131 @@
+from datetime import datetime, time
+from sqlalchemy import delete, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.explanation_style_do import ExplanationStyle
+from module_admin.entity.vo.explanation_style_vo import Explanation_styleModel, Explanation_stylePageQueryModel
+from utils.page_util import PageUtil
+
+
+class Explanation_styleDao:
+ """
+ 讲解风格模块数据库操作层
+ """
+
+ @classmethod
+ async def get_explanation_style_detail_by_id(cls, db: AsyncSession, explanation_style_id: int):
+ """
+ 根据主键ID获取讲解风格详细信息
+
+ :param db: orm对象
+ :param explanation_style_id: 主键ID
+ :return: 讲解风格信息对象
+ """
+ explanation_style_info = (
+ (
+ await db.execute(
+ select(ExplanationStyle)
+ .where(
+ ExplanationStyle.explanation_style_id == explanation_style_id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return explanation_style_info
+
+ @classmethod
+ async def get_explanation_style_detail_by_info(cls, db: AsyncSession, explanation_style: Explanation_styleModel):
+ """
+ 根据讲解风格参数获取讲解风格信息
+
+ :param db: orm对象
+ :param explanation_style: 讲解风格参数对象
+ :return: 讲解风格信息对象
+ """
+ explanation_style_info = (
+ (
+ await db.execute(
+ select(ExplanationStyle).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return explanation_style_info
+
+ @classmethod
+ async def get_explanation_style_list(cls, db: AsyncSession, query_object: Explanation_stylePageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取讲解风格列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 讲解风格列表信息对象
+ """
+ query = (
+ select(ExplanationStyle)
+ .where(
+ ExplanationStyle.name.like(f'%{query_object.name}%') if query_object.name else True,
+ ExplanationStyle.detail == query_object.detail if query_object.detail else True,
+ ExplanationStyle.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_create_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_create_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_create_time and query_object.end_create_time
+ else True,
+ ExplanationStyle.update_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_update_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_update_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_update_time and query_object.end_update_time
+ else True,
+ )
+ .order_by(ExplanationStyle.explanation_style_id)
+ .distinct()
+ )
+ explanation_style_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return explanation_style_list
+
+ @classmethod
+ async def add_explanation_style_dao(cls, db: AsyncSession, explanation_style: Explanation_styleModel):
+ """
+ 新增讲解风格数据库操作
+
+ :param db: orm对象
+ :param explanation_style: 讲解风格对象
+ :return:
+ """
+ db_explanation_style = ExplanationStyle(**explanation_style.model_dump(exclude={}))
+ db.add(db_explanation_style)
+ await db.flush()
+
+ return db_explanation_style
+
+ @classmethod
+ async def edit_explanation_style_dao(cls, db: AsyncSession, explanation_style: dict):
+ """
+ 编辑讲解风格数据库操作
+
+ :param db: orm对象
+ :param explanation_style: 需要更新的讲解风格字典
+ :return:
+ """
+ await db.execute(update(ExplanationStyle), [explanation_style])
+
+ @classmethod
+ async def delete_explanation_style_dao(cls, db: AsyncSession, explanation_style: Explanation_styleModel):
+ """
+ 删除讲解风格数据库操作
+
+ :param db: orm对象
+ :param explanation_style: 讲解风格对象
+ :return:
+ """
+ await db.execute(delete(ExplanationStyle).where(ExplanationStyle.explanation_style_id.in_([explanation_style.explanation_style_id])))
+
diff --git a/ruoyi-fastapi-backend/module_admin/dao/explanation_style_robot_pair_dao.py b/ruoyi-fastapi-backend/module_admin/dao/explanation_style_robot_pair_dao.py
new file mode 100644
index 0000000..be2c316
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/explanation_style_robot_pair_dao.py
@@ -0,0 +1,194 @@
+from datetime import datetime, time
+from sqlalchemy import delete, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+
+from module_admin.entity.do.explanation_style_do import ExplanationStyle
+from module_admin.entity.do.explanation_style_robot_pair_do import ExplanationStyleRobotPairing
+from module_admin.entity.do.explanation_style_do import ExplanationStyle
+from module_admin.entity.vo.explanation_style_robot_pair_vo import Explanation_style_robot_pairModel, \
+ Explanation_style_robot_pairPageQueryModel, SwitchExplanationStyleModel
+from utils.page_util import PageUtil
+
+
+class Explanation_style_robot_pairDao:
+ """
+ 讲解风格--机器人配对模块数据库操作层
+ """
+
+ @classmethod
+ async def get_robot_role_style(cls, db: AsyncSession, robot_id: int, role_id: int):
+ """根据机器人id和角色id获取机器人角色样式
+
+ Args:
+ db (AsyncSession): 数据库会话
+ robot_id (int): 机器人id
+ role_id (int): 角色id
+
+ Returns:
+ _type_: _description_
+ """
+
+ stmt = (
+ select(ExplanationStyleRobotPairing.pairing_id, ExplanationStyleRobotPairing.explanation_style_id, ExplanationStyle.name, ExplanationStyle.detail, ExplanationStyleRobotPairing.status, ExplanationStyleRobotPairing.robot_role_id)
+ .select_from(ExplanationStyleRobotPairing)
+ .join(ExplanationStyle, ExplanationStyle.explanation_style_id == ExplanationStyleRobotPairing.explanation_style_id)
+ .where(ExplanationStyleRobotPairing.robot_id == robot_id, ExplanationStyleRobotPairing.robot_role_id == role_id)
+ )
+
+ result = (await db.execute(stmt)).mappings().all()
+ return result
+
+
+
+ @classmethod
+ async def switch_explanation_style(cls, db: AsyncSession, robot_id: int = None, pairing_id: int = None, robot_role_id: str = None ,status:str = '0', update_by: str = None):
+
+ condition = list()
+
+ if robot_id:
+ condition.append(ExplanationStyleRobotPairing.robot_id == robot_id)
+ if pairing_id:
+ condition.append(ExplanationStyleRobotPairing.pairing_id == pairing_id)
+ if robot_role_id:
+ condition.append(ExplanationStyleRobotPairing.robot_role_id == robot_role_id)
+
+ query = (
+ update(ExplanationStyleRobotPairing)
+ .values(status=status)
+ .where(*condition)
+ )
+ return await db.execute(query)
+
+ @classmethod
+ async def get_style_robot_pair_list(cls, db: AsyncSession, robot_id: int):
+ query = (
+ select(ExplanationStyleRobotPairing.pairing_id, ExplanationStyle.name, ExplanationStyle.detail, ExplanationStyleRobotPairing.status)
+ .select_from(ExplanationStyleRobotPairing)
+ .join(ExplanationStyle, ExplanationStyle.explanation_style_id == ExplanationStyleRobotPairing.explanation_style_id)
+ .where(ExplanationStyleRobotPairing.robot_id == robot_id)
+ )
+
+ result = (await db.execute(query)).mappings().all()
+ return result
+
+
+ @classmethod
+ async def get_explanation_style_robot_pair_detail_by_id(cls, db: AsyncSession, pairing_id: int):
+ """
+ 根据主键ID获取讲解风格--机器人配对详细信息
+
+ :param db: orm对象
+ :param pairing_id: 主键ID
+ :return: 讲解风格--机器人配对信息对象
+ """
+ explanation_style_robot_pair_info = (
+ (
+ await db.execute(
+ select(ExplanationStyleRobotPairing)
+ .where(
+ ExplanationStyleRobotPairing.pairing_id == pairing_id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return explanation_style_robot_pair_info
+
+ @classmethod
+ async def get_explanation_style_robot_pair_detail_by_info(cls, db: AsyncSession, explanation_style_robot_pair: Explanation_style_robot_pairModel):
+ """
+ 根据讲解风格--机器人配对参数获取讲解风格--机器人配对信息
+
+ :param db: orm对象
+ :param explanation_style_robot_pair: 讲解风格--机器人配对参数对象
+ :return: 讲解风格--机器人配对信息对象
+ """
+ explanation_style_robot_pair_info = (
+ (
+ await db.execute(
+ select(ExplanationStyleRobotPairing).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return explanation_style_robot_pair_info
+
+ @classmethod
+ async def get_explanation_style_robot_pair_list(cls, db: AsyncSession, query_object: Explanation_style_robot_pairPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取讲解风格--机器人配对列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 讲解风格--机器人配对列表信息对象
+ """
+ query = (
+ select(ExplanationStyleRobotPairing)
+ .where(
+ ExplanationStyleRobotPairing.robot_id == query_object.robot_id if query_object.robot_id else True,
+ ExplanationStyleRobotPairing.explanation_style_id == query_object.explanation_style_id if query_object.explanation_style_id else True,
+ ExplanationStyleRobotPairing.prompt == query_object.prompt if query_object.prompt else True,
+ ExplanationStyleRobotPairing.status == query_object.status if query_object.status else True,
+ ExplanationStyleRobotPairing.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_create_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_create_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_create_time and query_object.end_create_time
+ else True,
+ ExplanationStyleRobotPairing.update_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_update_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_update_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_update_time and query_object.end_update_time
+ else True,
+ )
+ .order_by(ExplanationStyleRobotPairing.pairing_id)
+ .distinct()
+ )
+ explanation_style_robot_pair_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return explanation_style_robot_pair_list
+
+ @classmethod
+ async def add_explanation_style_robot_pair_dao(cls, db: AsyncSession, explanation_style_robot_pair: Explanation_style_robot_pairModel):
+ """
+ 新增讲解风格--机器人配对数据库操作
+
+ :param db: orm对象
+ :param explanation_style_robot_pair: 讲解风格--机器人配对对象
+ :return:
+ """
+ db_explanation_style_robot_pair = ExplanationStyleRobotPairing(**explanation_style_robot_pair.model_dump(exclude={}))
+ db.add(db_explanation_style_robot_pair)
+ await db.flush()
+
+ return db_explanation_style_robot_pair
+
+ @classmethod
+ async def edit_explanation_style_robot_pair_dao(cls, db: AsyncSession, explanation_style_robot_pair: dict):
+ """
+ 编辑讲解风格--机器人配对数据库操作
+
+ :param db: orm对象
+ :param explanation_style_robot_pair: 需要更新的讲解风格--机器人配对字典
+ :return:
+ """
+ await db.execute(update(ExplanationStyleRobotPairing), [explanation_style_robot_pair])
+
+ @classmethod
+ async def delete_explanation_style_robot_pair_dao(cls, db: AsyncSession, explanation_style_robot_pair: Explanation_style_robot_pairModel):
+ """
+ 删除讲解风格--机器人配对数据库操作
+
+ :param db: orm对象
+ :param explanation_style_robot_pair: 讲解风格--机器人配对对象
+ :return:
+ """
+ await db.execute(delete(ExplanationStyleRobotPairing).where(ExplanationStyleRobotPairing.pairing_id.in_([explanation_style_robot_pair.pairing_id])))
+
diff --git a/ruoyi-fastapi-backend/module_admin/dao/identification_record_dao.py b/ruoyi-fastapi-backend/module_admin/dao/identification_record_dao.py
new file mode 100644
index 0000000..d2ae3b4
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/identification_record_dao.py
@@ -0,0 +1,129 @@
+from datetime import datetime, time
+from sqlalchemy import delete, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.identification_record_do import IdentificationRecord
+from module_admin.entity.vo.identification_record_vo import Identification_recordModel, Identification_recordPageQueryModel
+from utils.page_util import PageUtil
+
+
+class Identification_recordDao:
+ """
+ 识别记录模块数据库操作层
+ """
+
+ @classmethod
+ async def get_identification_record_detail_by_id(cls, db: AsyncSession, id: int):
+ """
+ 根据主键 自增获取识别记录详细信息
+
+ :param db: orm对象
+ :param id: 主键 自增
+ :return: 识别记录信息对象
+ """
+ identification_record_info = (
+ (
+ await db.execute(
+ select(IdentificationRecord)
+ .where(
+ IdentificationRecord.id == id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return identification_record_info
+
+ @classmethod
+ async def get_identification_record_detail_by_info(cls, db: AsyncSession, identification_record: Identification_recordModel):
+ """
+ 根据识别记录参数获取识别记录信息
+
+ :param db: orm对象
+ :param identification_record: 识别记录参数对象
+ :return: 识别记录信息对象
+ """
+ identification_record_info = (
+ (
+ await db.execute(
+ select(IdentificationRecord).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return identification_record_info
+
+ @classmethod
+ async def get_identification_record_list(cls, db: AsyncSession, query_object: Identification_recordPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取识别记录列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 识别记录列表信息对象
+ """
+ query = (
+ select(IdentificationRecord)
+ .where(
+ IdentificationRecord.person_name.like(f'%{query_object.person_name}%') if query_object.person_name else True,
+ IdentificationRecord.door_name.like(f'%{query_object.door_name}%') if query_object.door_name else True,
+ IdentificationRecord.status == query_object.status if query_object.status else True,
+ IdentificationRecord.source == query_object.source if query_object.source else True,
+ IdentificationRecord.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_create_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_create_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_create_time and query_object.end_create_time
+ else True,
+ )
+ .order_by(IdentificationRecord.create_time.desc())
+ .distinct()
+ )
+ identification_record_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return identification_record_list
+
+ @classmethod
+ async def add_identification_record_dao(cls, db: AsyncSession, identification_record: Identification_recordModel):
+ """
+ 新增识别记录数据库操作
+
+ :param db: orm对象
+ :param identification_record: 识别记录对象
+ :return:
+ """
+ # 排除 id 字段(自增主键),但保留其他所有字段
+ data = identification_record.model_dump(exclude={'id'}, exclude_none=True)
+ db_identification_record = IdentificationRecord(**data)
+ db.add(db_identification_record)
+ await db.flush()
+
+ return db_identification_record
+
+ @classmethod
+ async def edit_identification_record_dao(cls, db: AsyncSession, identification_record: dict):
+ """
+ 编辑识别记录数据库操作
+
+ :param db: orm对象
+ :param identification_record: 需要更新的识别记录字典
+ :return:
+ """
+ await db.execute(update(IdentificationRecord), [identification_record])
+
+ @classmethod
+ async def delete_identification_record_dao(cls, db: AsyncSession, identification_record: Identification_recordModel):
+ """
+ 删除识别记录数据库操作
+
+ :param db: orm对象
+ :param identification_record: 识别记录对象
+ :return:
+ """
+ await db.execute(delete(IdentificationRecord).where(IdentificationRecord.id.in_([identification_record.id])))
+
diff --git a/ruoyi-fastapi-backend/module_admin/dao/identification_statistics_dao.py b/ruoyi-fastapi-backend/module_admin/dao/identification_statistics_dao.py
new file mode 100644
index 0000000..ff38f41
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/identification_statistics_dao.py
@@ -0,0 +1,200 @@
+from datetime import datetime, time
+from sqlalchemy import delete, select, update, func
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.identification_statistics_do import IdentificationStatistics
+from module_admin.entity.vo.identification_statistics_vo import Identification_statisticsModel, Identification_statisticsPageQueryModel
+from utils.page_util import PageUtil
+
+
+class Identification_statisticsDao:
+ """
+ 识别统计模块数据库操作层
+ """
+
+ @classmethod
+ async def get_today_identification_statistics(cls, query_db: AsyncSession):
+ stmt = (
+ select(IdentificationStatistics.employ, IdentificationStatistics.stranger, IdentificationStatistics.visitor, IdentificationStatistics.create_time)
+ .where(IdentificationStatistics.create_time == datetime.now().date())
+ )
+
+ result = await query_db.execute(stmt)
+ return result.mappings().all()
+
+ @classmethod
+ async def get_total_identification_statistics(cls, query_db: AsyncSession):
+ """
+ 获取总的识别统计数据 - 返回各个字段的总和
+
+ :param query_db: orm对象
+ :return: 总和统计数据
+ """
+ stmt = select(
+ func.sum(IdentificationStatistics.employ).label('employ'),
+ func.sum(IdentificationStatistics.visitor).label('visitor'),
+ func.sum(IdentificationStatistics.stranger).label('stranger')
+ )
+
+ result = await query_db.execute(stmt)
+ return result.mappings().first()
+
+ @classmethod
+ async def get_identification_statistics_detail_by_id(cls, db: AsyncSession, id: int):
+ """
+ 根据主键 自增获取识别统计详细信息
+
+ :param db: orm对象
+ :param id: 主键 自增
+ :return: 识别统计信息对象
+ """
+ identification_statistics_info = (
+ (
+ await db.execute(
+ select(IdentificationStatistics)
+ .where(
+ IdentificationStatistics.id == id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return identification_statistics_info
+
+ @classmethod
+ async def get_identification_statistics_detail_by_info(cls, db: AsyncSession, identification_statistics: Identification_statisticsModel):
+ """
+ 根据识别统计参数获取识别统计信息
+
+ :param db: orm对象
+ :param identification_statistics: 识别统计参数对象
+ :return: 识别统计信息对象
+ """
+ identification_statistics_info = (
+ (
+ await db.execute(
+ select(IdentificationStatistics).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return identification_statistics_info
+
+ @classmethod
+ async def get_identification_statistics_list(cls, db: AsyncSession, query_object: Identification_statisticsPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取识别统计列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 识别统计列表信息对象
+ """
+ query = (
+ select(IdentificationStatistics)
+ .where(
+ IdentificationStatistics.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_create_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_create_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_create_time and query_object.end_create_time
+ else True,
+ )
+ .order_by(IdentificationStatistics.id)
+ .distinct()
+ )
+ identification_statistics_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return identification_statistics_list
+
+ @classmethod
+ async def add_identification_statistics_dao(cls, db: AsyncSession, identification_statistics: Identification_statisticsModel):
+ """
+ 新增识别统计数据库操作
+
+ :param db: orm对象
+ :param identification_statistics: 识别统计对象
+ :return:
+ """
+ db_identification_statistics = IdentificationStatistics(**identification_statistics.model_dump(exclude={}))
+ db.add(db_identification_statistics)
+ await db.flush()
+
+ return db_identification_statistics
+
+ @classmethod
+ async def edit_identification_statistics_dao(cls, db: AsyncSession, identification_statistics: dict):
+ """
+ 编辑识别统计数据库操作
+
+ :param db: orm对象
+ :param identification_statistics: 需要更新的识别统计字典
+ :return:
+ """
+ await db.execute(update(IdentificationStatistics), [identification_statistics])
+
+ @classmethod
+ async def delete_identification_statistics_dao(cls, db: AsyncSession, identification_statistics: Identification_statisticsModel):
+ """
+ 删除识别统计数据库操作
+
+ :param db: orm对象
+ :param identification_statistics: 识别统计对象
+ :return:
+ """
+ await db.execute(delete(IdentificationStatistics).where(IdentificationStatistics.id.in_([identification_statistics.id])))
+
+ @classmethod
+ async def update_identification_statistics_dao(cls, db: AsyncSession, person_status: str):
+ """
+ 更新今日识别统计数据库操作 - 根据person_status增加相应字段的计数
+
+ :param db: orm对象
+ :param person_status: 人员状态 (0 陌生人, 1 员工, 2 访客)
+ :return:
+ """
+ today = datetime.now().date()
+
+ # 查询今日是否已有统计记录
+ stmt = select(IdentificationStatistics).where(
+ func.date(IdentificationStatistics.create_time) == today
+ )
+ result = await db.execute(stmt)
+ today_statistics = result.scalars().first()
+
+ # 如果没有今日记录,创建新记录
+ if not today_statistics:
+ new_statistics = IdentificationStatistics(
+ employ=1 if person_status == "1" else 0,
+ visitor=1 if person_status == "2" else 0,
+ stranger=1 if person_status == "0" else 0,
+ create_time=datetime.now(),
+ create_by="system"
+ )
+ db.add(new_statistics)
+ await db.flush()
+ else:
+ # 根据person_status更新相应字段
+ if person_status == "0": # 陌生人
+ await db.execute(
+ update(IdentificationStatistics)
+ .where(IdentificationStatistics.id == today_statistics.id)
+ .values(stranger=IdentificationStatistics.stranger + 1)
+ )
+ elif person_status == "1": # 员工
+ await db.execute(
+ update(IdentificationStatistics)
+ .where(IdentificationStatistics.id == today_statistics.id)
+ .values(employ=IdentificationStatistics.employ + 1)
+ )
+ elif person_status == "2": # 访客
+ await db.execute(
+ update(IdentificationStatistics)
+ .where(IdentificationStatistics.id == today_statistics.id)
+ .values(visitor=IdentificationStatistics.visitor + 1)
+ )
+
diff --git a/ruoyi-fastapi-backend/module_admin/dao/info_dao.py b/ruoyi-fastapi-backend/module_admin/dao/info_dao.py
new file mode 100644
index 0000000..0724b95
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/info_dao.py
@@ -0,0 +1,173 @@
+from sqlalchemy import delete, select, update, desc
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.info_do import RobotInfo
+from module_admin.entity.vo.info_vo import InfoModel, InfoPageQueryModel
+from utils.page_util import PageUtil
+from datetime import datetime, time
+
+
+
+class InfoDao:
+ """
+ 机器人信息模块数据库操作层
+ """
+
+
+ @classmethod
+ async def get_video_uri(cls, robot_id: int, query_db: AsyncSession):
+ """根据机器人id获取视频流地址
+
+ Args:
+ robot_id (int): 机器人地址
+ query_db (AsyncSession): _description_
+ """
+ stmt = (
+ select(RobotInfo.video_uri)
+ .select_from(RobotInfo)
+ .where(RobotInfo.robot_id == robot_id)
+ )
+
+ result = await query_db.execute(stmt)
+
+ return result.scalars().first()
+
+ @classmethod
+ async def refresh_robot_by_ids(cls, db: AsyncSession, robot_ids: str):
+ id_list = map(int, robot_ids.strip(" ").split(','))
+ refresh_result = (
+ (
+ await db.execute(
+ select(RobotInfo)
+ .where(RobotInfo.robot_id.in_(id_list))
+
+ )
+
+ )
+ .scalars()
+ .all()
+ )
+ return refresh_result
+
+ @classmethod
+ async def get_info_detail_by_id(cls, db: AsyncSession, robot_id: int):
+ """
+ 根据机器人ID获取机器人信息详细信息
+
+ :param db: orm对象
+ :param robot_id: 机器人ID
+ :return: 机器人信息信息对象
+ """
+ info_info = (
+ (
+ await db.execute(
+ select(RobotInfo)
+ .where(
+ RobotInfo.robot_id == robot_id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return info_info
+
+ @classmethod
+ async def get_info_detail_by_info(cls, db: AsyncSession, info: InfoModel):
+ """
+ 根据机器人信息参数获取机器人信息信息
+
+ :param db: orm对象
+ :param info: 机器人信息参数对象
+ :return: 机器人信息信息对象
+ """
+ info_info = (
+ (
+ await db.execute(
+ select(RobotInfo).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return info_info
+
+ @classmethod
+ async def get_info_list(cls, db: AsyncSession, query_object: InfoPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取机器人信息列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 机器人信息列表信息对象
+ """
+ query = (
+ select(RobotInfo)
+ .where(
+ RobotInfo.name.like(f'%{query_object.name}%') if query_object.name else True,
+ RobotInfo.model == query_object.model if query_object.model else True,
+ RobotInfo.serial == query_object.serial if query_object.serial else True,
+ RobotInfo.online == query_object.online if query_object.online else True,
+ RobotInfo.power == query_object.power if query_object.power else True,
+ RobotInfo.run_time == query_object.run_time if query_object.run_time else True,
+ RobotInfo.position == query_object.position if query_object.position else True,
+ RobotInfo.image == query_object.image if query_object.image else True,
+ RobotInfo.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_create_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_create_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_create_time and query_object.end_create_time else True,
+ RobotInfo.update_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_update_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_update_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_update_time and query_object.end_update_time else True
+
+ )
+ .order_by(desc(RobotInfo.create_time))
+ .distinct()
+ )
+ info_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return info_list
+
+ @classmethod
+ async def add_info_dao(cls, db: AsyncSession, info: InfoModel):
+ """
+ 新增机器人信息数据库操作
+
+ :param db: orm对象
+ :param info: 机器人信息对象
+ :return:
+ """
+ db_info = RobotInfo(**info.model_dump(exclude={}))
+ db.add(db_info)
+ await db.flush()
+
+ return db_info
+
+ @classmethod
+ async def edit_info_dao(cls, db: AsyncSession, info: dict):
+ """
+ 编辑机器人信息数据库操作
+
+ :param db: orm对象
+ :param info: 需要更新的机器人信息字典
+ :return:
+ """
+ await db.execute(update(RobotInfo), [info])
+
+ @classmethod
+ async def delete_info_dao(cls, db: AsyncSession, info: InfoModel):
+ """
+ 删除机器人信息数据库操作
+
+ :param db: orm对象
+ :param info: 机器人信息对象
+ :return:
+ """
+ await db.execute(delete(RobotInfo).where(RobotInfo.robot_id.in_([info.robot_id])))
+
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..805d460
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/job_dao.py
@@ -0,0 +1,126 @@
+from sqlalchemy import delete, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.job_do import SysJob
+from module_admin.entity.vo.job_vo import JobModel, JobPageQueryModel
+from utils.page_util import PageUtil
+
+
+class JobDao:
+ """
+ 定时任务管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_job_detail_by_id(cls, db: AsyncSession, job_id: int):
+ """
+ 根据定时任务id获取定时任务详细信息
+
+ :param db: orm对象
+ :param job_id: 定时任务id
+ :return: 定时任务信息对象
+ """
+ job_info = (await db.execute(select(SysJob).where(SysJob.job_id == job_id))).scalars().first()
+
+ return job_info
+
+ @classmethod
+ async def get_job_detail_by_info(cls, db: AsyncSession, job: JobModel):
+ """
+ 根据定时任务参数获取定时任务信息
+
+ :param db: orm对象
+ :param job: 定时任务参数对象
+ :return: 定时任务信息对象
+ """
+ job_info = (
+ (
+ await db.execute(
+ select(SysJob).where(
+ SysJob.job_name == job.job_name,
+ SysJob.job_group == job.job_group,
+ SysJob.job_executor == job.job_executor,
+ SysJob.invoke_target == job.invoke_target,
+ SysJob.job_args == job.job_args,
+ SysJob.job_kwargs == job.job_kwargs,
+ SysJob.cron_expression == job.cron_expression,
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return job_info
+
+ @classmethod
+ async def get_job_list(cls, db: AsyncSession, query_object: JobPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取定时任务列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 定时任务列表信息对象
+ """
+ query = (
+ select(SysJob)
+ .where(
+ SysJob.job_name.like(f'%{query_object.job_name}%') if query_object.job_name else True,
+ SysJob.job_group == query_object.job_group if query_object.job_group else True,
+ SysJob.status == query_object.status if query_object.status else True,
+ )
+ .order_by(SysJob.job_id)
+ .distinct()
+ )
+ job_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return job_list
+
+ @classmethod
+ async def get_job_list_for_scheduler(cls, db: AsyncSession):
+ """
+ 获取定时任务列表信息
+
+ :param db: orm对象
+ :return: 定时任务列表信息对象
+ """
+ job_list = (await db.execute(select(SysJob).where(SysJob.status == '0').distinct())).scalars().all()
+
+ return job_list
+
+ @classmethod
+ async def add_job_dao(cls, db: AsyncSession, job: JobModel):
+ """
+ 新增定时任务数据库操作
+
+ :param db: orm对象
+ :param job: 定时任务对象
+ :return:
+ """
+ db_job = SysJob(**job.model_dump())
+ db.add(db_job)
+ await db.flush()
+
+ return db_job
+
+ @classmethod
+ async def edit_job_dao(cls, db: AsyncSession, job: dict):
+ """
+ 编辑定时任务数据库操作
+
+ :param db: orm对象
+ :param job: 需要更新的定时任务字典
+ :return:
+ """
+ await db.execute(update(SysJob), [job])
+
+ @classmethod
+ async def delete_job_dao(cls, db: AsyncSession, job: JobModel):
+ """
+ 删除定时任务数据库操作
+
+ :param db: orm对象
+ :param job: 定时任务对象
+ :return:
+ """
+ await db.execute(delete(SysJob).where(SysJob.job_id.in_([job.job_id])))
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..586a3d6
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/job_log_dao.py
@@ -0,0 +1,79 @@
+from datetime import datetime, time
+from sqlalchemy import delete, desc, select
+from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.orm import Session
+from module_admin.entity.do.job_do import SysJobLog
+from module_admin.entity.vo.job_vo import JobLogModel, JobLogPageQueryModel
+from utils.page_util import PageUtil
+
+
+class JobLogDao:
+ """
+ 定时任务日志管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_job_log_list(cls, db: AsyncSession, query_object: JobLogPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取定时任务日志列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 定时任务日志列表信息对象
+ """
+ query = (
+ select(SysJobLog)
+ .where(
+ SysJobLog.job_name.like(f'%{query_object.job_name}%') if query_object.job_name else True,
+ SysJobLog.job_group == query_object.job_group if query_object.job_group else True,
+ SysJobLog.status == query_object.status if query_object.status else True,
+ SysJobLog.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_time and query_object.end_time
+ else True,
+ )
+ .order_by(desc(SysJobLog.create_time))
+ .distinct()
+ )
+ job_log_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return job_log_list
+
+ @classmethod
+ def add_job_log_dao(cls, db: Session, job_log: JobLogModel):
+ """
+ 新增定时任务日志数据库操作
+
+ :param db: orm对象
+ :param job_log: 定时任务日志对象
+ :return:
+ """
+ db_job_log = SysJobLog(**job_log.model_dump())
+ db.add(db_job_log)
+ db.flush()
+
+ return db_job_log
+
+ @classmethod
+ async def delete_job_log_dao(cls, db: AsyncSession, job_log: JobLogModel):
+ """
+ 删除定时任务日志数据库操作
+
+ :param db: orm对象
+ :param job_log: 定时任务日志对象
+ :return:
+ """
+ await db.execute(delete(SysJobLog).where(SysJobLog.job_log_id.in_([job_log.job_log_id])))
+
+ @classmethod
+ async def clear_job_log_dao(cls, db: AsyncSession):
+ """
+ 清除定时任务日志数据库操作
+
+ :param db: orm对象
+ :return:
+ """
+ await db.execute(delete(SysJobLog))
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..684f3d7
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/log_dao.py
@@ -0,0 +1,171 @@
+from datetime import datetime, time
+from sqlalchemy import asc, delete, desc, select
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.log_do import SysLogininfor, SysOperLog
+from module_admin.entity.vo.log_vo import LogininforModel, LoginLogPageQueryModel, OperLogModel, OperLogPageQueryModel
+from utils.common_util import SnakeCaseUtil
+from utils.page_util import PageUtil
+from utils.time_format_util import TimeFormatUtil
+
+
+class OperationLogDao:
+ """
+ 操作日志管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_operation_log_list(cls, db: AsyncSession, query_object: OperLogPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取操作日志列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 操作日志列表信息对象
+ """
+ if query_object.is_asc == 'ascending':
+ order_by_column = asc(getattr(SysOperLog, SnakeCaseUtil.camel_to_snake(query_object.order_by_column), None))
+ elif query_object.is_asc == 'descending':
+ order_by_column = desc(
+ getattr(SysOperLog, SnakeCaseUtil.camel_to_snake(query_object.order_by_column), None)
+ )
+ else:
+ order_by_column = desc(SysOperLog.oper_time)
+ query = (
+ select(SysOperLog)
+ .where(
+ SysOperLog.title.like(f'%{query_object.title}%') if query_object.title else True,
+ SysOperLog.oper_name.like(f'%{query_object.oper_name}%') if query_object.oper_name else True,
+ SysOperLog.business_type == query_object.business_type if query_object.business_type else True,
+ SysOperLog.status == query_object.status if query_object.status else True,
+ SysOperLog.oper_time.between(
+ datetime.combine(TimeFormatUtil.parse_date(query_object.begin_time), time(00, 00, 00)),
+ datetime.combine(TimeFormatUtil.parse_date(query_object.end_time), time(23, 59, 59)),
+ )
+ if query_object.begin_time and query_object.end_time
+ else True,
+ )
+ .distinct()
+ .order_by(order_by_column)
+ )
+ operation_log_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return operation_log_list
+
+ @classmethod
+ async def add_operation_log_dao(cls, db: AsyncSession, operation_log: OperLogModel):
+ """
+ 新增操作日志数据库操作
+
+ :param db: orm对象
+ :param operation_log: 操作日志对象
+ :return: 新增校验结果
+ """
+ db_operation_log = SysOperLog(**operation_log.model_dump())
+ db.add(db_operation_log)
+ await db.flush()
+
+ return db_operation_log
+
+ @classmethod
+ async def delete_operation_log_dao(cls, db: AsyncSession, operation_log: OperLogModel):
+ """
+ 删除操作日志数据库操作
+
+ :param db: orm对象
+ :param operation_log: 操作日志对象
+ :return:
+ """
+ await db.execute(delete(SysOperLog).where(SysOperLog.oper_id.in_([operation_log.oper_id])))
+
+ @classmethod
+ async def clear_operation_log_dao(cls, db: AsyncSession):
+ """
+ 清除操作日志数据库操作
+
+ :param db: orm对象
+ :return:
+ """
+ await db.execute(delete(SysOperLog))
+
+
+class LoginLogDao:
+ """
+ 登录日志管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_login_log_list(cls, db: AsyncSession, query_object: LoginLogPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取登录日志列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 登录日志列表信息对象
+ """
+ if query_object.is_asc == 'ascending':
+ order_by_column = asc(
+ getattr(SysLogininfor, SnakeCaseUtil.camel_to_snake(query_object.order_by_column), None)
+ )
+ elif query_object.is_asc == 'descending':
+ order_by_column = desc(
+ getattr(SysLogininfor, SnakeCaseUtil.camel_to_snake(query_object.order_by_column), None)
+ )
+ else:
+ order_by_column = desc(SysLogininfor.login_time)
+ query = (
+ select(SysLogininfor)
+ .where(
+ SysLogininfor.ipaddr.like(f'%{query_object.ipaddr}%') if query_object.ipaddr else True,
+ SysLogininfor.user_name.like(f'%{query_object.user_name}%') if query_object.user_name else True,
+ SysLogininfor.status == query_object.status if query_object.status else True,
+ SysLogininfor.login_time.between(
+ datetime.combine(TimeFormatUtil.parse_date(query_object.begin_time), time(00, 00, 00)),
+ datetime.combine(TimeFormatUtil.parse_date(query_object.end_time), time(23, 59, 59)),
+ )
+ if query_object.begin_time and query_object.end_time
+ else True,
+ )
+ .distinct()
+ .order_by(order_by_column)
+ )
+ login_log_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return login_log_list
+
+ @classmethod
+ async def add_login_log_dao(cls, db: AsyncSession, login_log: LogininforModel):
+ """
+ 新增登录日志数据库操作
+
+ :param db: orm对象
+ :param login_log: 登录日志对象
+ :return: 新增校验结果
+ """
+ db_login_log = SysLogininfor(**login_log.model_dump())
+ db.add(db_login_log)
+ await db.flush()
+
+ return db_login_log
+
+ @classmethod
+ async def delete_login_log_dao(cls, db: AsyncSession, login_log: LogininforModel):
+ """
+ 删除登录日志数据库操作
+
+ :param db: orm对象
+ :param login_log: 登录日志对象
+ :return:
+ """
+ await db.execute(delete(SysLogininfor).where(SysLogininfor.info_id.in_([login_log.info_id])))
+
+ @classmethod
+ async def clear_login_log_dao(cls, db: AsyncSession):
+ """
+ 清除登录日志数据库操作
+
+ :param db: orm对象
+ :return:
+ """
+ await db.execute(delete(SysLogininfor))
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..9764a4a
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/login_dao.py
@@ -0,0 +1,28 @@
+from sqlalchemy import and_, select
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.dept_do import SysDept
+from module_admin.entity.do.user_do import SysUser
+
+
+async def login_by_account(db: AsyncSession, user_name: str):
+ """
+ 根据用户名查询用户信息
+
+ :param db: orm对象
+ :param user_name: 用户名
+ :return: 用户对象
+ """
+ user = (
+ await db.execute(
+ select(SysUser, SysDept)
+ .where(SysUser.user_name == user_name, SysUser.del_flag == '0')
+ .join(
+ SysDept,
+ and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == '0', SysDept.del_flag == '0'),
+ isouter=True,
+ )
+ .distinct()
+ )
+ ).first()
+
+ return user
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..976fcdf
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/menu_dao.py
@@ -0,0 +1,227 @@
+from sqlalchemy import and_, delete, func, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.menu_do import SysMenu
+from module_admin.entity.do.role_do import SysRole, SysRoleMenu
+from module_admin.entity.do.user_do import SysUser, SysUserRole
+from module_admin.entity.vo.menu_vo import MenuModel, MenuQueryModel
+
+
+class MenuDao:
+ """
+ 菜单管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_menu_detail_by_id(cls, db: AsyncSession, menu_id: int):
+ """
+ 根据菜单id获取菜单详细信息
+
+ :param db: orm对象
+ :param menu_id: 菜单id
+ :return: 菜单信息对象
+ """
+ menu_info = (await db.execute(select(SysMenu).where(SysMenu.menu_id == menu_id))).scalars().first()
+
+ return menu_info
+
+ @classmethod
+ async def get_menu_detail_by_info(cls, db: AsyncSession, menu: MenuModel):
+ """
+ 根据菜单参数获取菜单信息
+
+ :param db: orm对象
+ :param menu: 菜单参数对象
+ :return: 菜单信息对象
+ """
+ menu_info = (
+ (
+ await db.execute(
+ select(SysMenu).where(
+ SysMenu.parent_id == menu.parent_id if menu.parent_id else True,
+ SysMenu.menu_name == menu.menu_name if menu.menu_name else True,
+ SysMenu.menu_type == menu.menu_type if menu.menu_type else True,
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return menu_info
+
+ @classmethod
+ async def get_menu_list_for_tree(cls, db: AsyncSession, user_id: int, role: list):
+ """
+ 根据角色信息获取所有在用菜单列表信息
+
+ :param db: orm对象
+ :param user_id: 用户id
+ :param role: 用户角色列表信息
+ :return: 菜单列表信息
+ """
+ role_id_list = [item.role_id for item in role]
+ if 1 in role_id_list:
+ menu_query_all = (
+ (await db.execute(select(SysMenu).where(SysMenu.status == '0'
+ # , SysMenu.visible == '0'
+ ).order_by(SysMenu.order_num).distinct()))
+ .scalars()
+ .all()
+ )
+ else:
+ menu_query_all = (
+ (
+ await db.execute(
+ select(SysMenu)
+ .select_from(SysUser)
+ .where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
+ .join(SysUserRole, SysUser.user_id == SysUserRole.user_id, isouter=True)
+ .join(
+ SysRole,
+ and_(
+ SysUserRole.role_id == SysRole.role_id, SysRole.status == '0', SysRole.del_flag == '0'
+ ),
+ isouter=True,
+ )
+ .join(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id, isouter=True)
+ .join(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == '0'
+ # , SysMenu.visible == '0'
+ ))
+ .order_by(SysMenu.order_num)
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+
+ return menu_query_all
+
+ @classmethod
+ async def get_menu_list(cls, db: AsyncSession, page_object: MenuQueryModel, user_id: int, role: list):
+ """
+ 根据查询参数获取菜单列表信息
+
+ :param db: orm对象
+ :param page_object: 不分页查询参数对象
+ :param user_id: 用户id
+ :param role: 用户角色列表
+ :return: 菜单列表信息对象
+ """
+ role_id_list = [item.role_id for item in role]
+ if 1 in role_id_list:
+ menu_query_all = (
+ (
+ await db.execute(
+ select(SysMenu)
+ .where(
+ SysMenu.status == page_object.status if page_object.status else True,
+ SysMenu.menu_name.like(f'%{page_object.menu_name}%') if page_object.menu_name else True,
+ )
+ .order_by(SysMenu.order_num)
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+ else:
+ menu_query_all = (
+ (
+ await db.execute(
+ select(SysMenu)
+ .select_from(SysUser)
+ .where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
+ .join(SysUserRole, SysUser.user_id == SysUserRole.user_id, isouter=True)
+ .join(
+ SysRole,
+ and_(
+ SysUserRole.role_id == SysRole.role_id, SysRole.status == '0', SysRole.del_flag == '0'
+ ),
+ isouter=True,
+ )
+ .join(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id, isouter=True)
+ .join(
+ SysMenu,
+ and_(
+ SysRoleMenu.menu_id == SysMenu.menu_id,
+ SysMenu.status == page_object.status if page_object.status else True,
+ SysMenu.menu_name.like(f'%{page_object.menu_name}%') if page_object.menu_name else True,
+ ),
+ )
+ .order_by(SysMenu.order_num)
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+
+ return menu_query_all
+
+ @classmethod
+ async def add_menu_dao(cls, db: AsyncSession, menu: MenuModel):
+ """
+ 新增菜单数据库操作
+
+ :param db: orm对象
+ :param menu: 菜单对象
+ :return:
+ """
+ db_menu = SysMenu(**menu.model_dump())
+ db.add(db_menu)
+ await db.flush()
+
+ return db_menu
+
+ @classmethod
+ async def edit_menu_dao(cls, db: AsyncSession, menu: dict):
+ """
+ 编辑菜单数据库操作
+
+ :param db: orm对象
+ :param menu: 需要更新的菜单字典
+ :return:
+ """
+ await db.execute(update(SysMenu), [menu])
+
+ @classmethod
+ async def delete_menu_dao(cls, db: AsyncSession, menu: MenuModel):
+ """
+ 删除菜单数据库操作
+
+ :param db: orm对象
+ :param menu: 菜单对象
+ :return:
+ """
+ await db.execute(delete(SysMenu).where(SysMenu.menu_id.in_([menu.menu_id])))
+
+ @classmethod
+ async def has_child_by_menu_id_dao(cls, db: AsyncSession, menu_id: int):
+ """
+ 根据菜单id查询菜单关联子菜单的数量
+
+ :param db: orm对象
+ :param menu_id: 菜单id
+ :return: 菜单关联子菜单的数量
+ """
+ menu_count = (
+ await db.execute(select(func.count('*')).select_from(SysMenu).where(SysMenu.parent_id == menu_id))
+ ).scalar()
+
+ return menu_count
+
+ @classmethod
+ async def check_menu_exist_role_dao(cls, db: AsyncSession, menu_id: int):
+ """
+ 根据菜单id查询菜单关联角色数量
+
+ :param db: orm对象
+ :param menu_id: 菜单id
+ :return: 菜单关联角色数量
+ """
+ role_count = (
+ await db.execute(select(func.count('*')).select_from(SysRoleMenu).where(SysRoleMenu.menu_id == menu_id))
+ ).scalar()
+
+ return role_count
diff --git a/ruoyi-fastapi-backend/module_admin/dao/message_dao.py b/ruoyi-fastapi-backend/module_admin/dao/message_dao.py
new file mode 100644
index 0000000..d2b90c0
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/message_dao.py
@@ -0,0 +1,175 @@
+from sqlalchemy import delete, select, update, func
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.message_do import Message
+from module_admin.entity.vo.common_vo import CrudResponseModel
+from module_admin.entity.vo.message_vo import MessageModel, MessagePageQueryModel
+from utils.page_util import PageUtil
+from datetime import datetime, time
+
+
+class MessageDao:
+ """
+ 系统消息模块数据库操作层
+ """
+
+ @classmethod
+ async def get_alarm_type_distribution(cls, db: AsyncSession):
+ stmt = (
+ select(Message.specific_type, func.count(Message.specific_type).label("cnt"))
+ .where(Message.type == '0')
+ .group_by(Message.specific_type)
+ )
+ return (await db.execute(stmt)).mappings().all()
+
+ @classmethod
+ async def handle_all_message(cls, db: AsyncSession):
+ query = (
+ update(Message)
+ .where(Message.status == '0', Message.type == '0')
+ .values(status='1')
+ )
+
+
+ await db.execute(query)
+
+
+
+ @classmethod
+ async def get_message_detail_by_id(cls, db: AsyncSession, message_id: int):
+ """
+ 根据消息主键获取系统消息详细信息
+
+ :param db: orm对象
+ :param message_id: 消息主键
+ :return: 系统消息信息对象
+ """
+ message_info = (
+ (
+ await db.execute(
+ select(Message)
+ .where(
+ Message.message_id == message_id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return message_info
+
+ @classmethod
+ async def count_message(cls, db: AsyncSession):
+ query_unhandled_message_count = (
+ select(func.count(Message.message_id))
+ .where(Message.status=='0', Message.type=='0')
+ )
+
+ unhandled_message_count = (
+ await db.execute(query_unhandled_message_count)
+ ).scalar()
+
+ query_handled_message_count = (
+ select(func.count(Message.message_id))
+ .where(Message.status=='1', Message.type=='0')
+ )
+
+ handled_message_count = (
+ await db.execute(query_handled_message_count)
+ ).scalar()
+
+ return unhandled_message_count+ handled_message_count, unhandled_message_count, handled_message_count
+
+ @classmethod
+ async def get_message_detail_by_info(cls, db: AsyncSession, message: MessageModel):
+ """
+ 根据系统消息参数获取系统消息信息
+
+ :param db: orm对象
+ :param message: 系统消息参数对象
+ :return: 系统消息信息对象
+ """
+ message_info = (
+ (
+ await db.execute(
+ select(Message).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return message_info
+
+ @classmethod
+ async def get_message_list(cls, db: AsyncSession, query_object: MessagePageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取系统消息列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 系统消息列表信息对象
+ """
+ query = (
+ select(Message)
+ .where(
+ Message.type == query_object.type if query_object.type else True,
+ Message.status == query_object.status if query_object.status else True,
+ # Message.detail == query_object.detail if query_object.detail else True,
+ Message.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_create_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_create_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_create_time and query_object.end_create_time else True,
+ Message.update_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_update_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_update_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_update_time and query_object.end_update_time else True
+ )
+ .order_by(Message.message_id)
+ .distinct()
+ )
+ message_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return message_list
+
+ @classmethod
+ async def add_message_dao(cls, db: AsyncSession, message: MessageModel):
+ """
+ 新增系统消息数据库操作
+
+ :param db: orm对象
+ :param message: 系统消息对象
+ :return:
+ """
+ db_message = Message(**message.model_dump(exclude={}))
+ db.add(db_message)
+ await db.flush()
+
+ return db_message
+
+ @classmethod
+ async def edit_message_dao(cls, db: AsyncSession, message: dict):
+ """
+ 编辑系统消息数据库操作
+
+ :param db: orm对象
+ :param message: 需要更新的系统消息字典
+ :return:
+ """
+ await db.execute(update(Message), [message])
+
+ @classmethod
+ async def delete_message_dao(cls, db: AsyncSession, message: MessageModel):
+ """
+ 删除系统消息数据库操作
+
+ :param db: orm对象
+ :param message: 系统消息对象
+ :return:
+ """
+ await db.execute(delete(Message).where(Message.message_id.in_([message.message_id])))
+
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..9f48a14
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/notice_dao.py
@@ -0,0 +1,117 @@
+from datetime import datetime, time
+from sqlalchemy import delete, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.notice_do import SysNotice
+from module_admin.entity.vo.notice_vo import NoticeModel, NoticePageQueryModel
+from utils.page_util import PageUtil
+
+
+class NoticeDao:
+ """
+ 通知公告管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_notice_detail_by_id(cls, db: AsyncSession, notice_id: int):
+ """
+ 根据通知公告id获取通知公告详细信息
+
+ :param db: orm对象
+ :param notice_id: 通知公告id
+ :return: 通知公告信息对象
+ """
+ notice_info = (await db.execute(select(SysNotice).where(SysNotice.notice_id == notice_id))).scalars().first()
+
+ return notice_info
+
+ @classmethod
+ async def get_notice_detail_by_info(cls, db: AsyncSession, notice: NoticeModel):
+ """
+ 根据通知公告参数获取通知公告信息
+
+ :param db: orm对象
+ :param notice: 通知公告参数对象
+ :return: 通知公告信息对象
+ """
+ notice_info = (
+ (
+ await db.execute(
+ select(SysNotice).where(
+ SysNotice.notice_title == notice.notice_title,
+ SysNotice.notice_type == notice.notice_type,
+ SysNotice.notice_content == notice.notice_content,
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return notice_info
+
+ @classmethod
+ async def get_notice_list(cls, db: AsyncSession, query_object: NoticePageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取通知公告列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 通知公告列表信息对象
+ """
+ query = (
+ select(SysNotice)
+ .where(
+ SysNotice.notice_title.like(f'%{query_object.notice_title}%') if query_object.notice_title else True,
+ SysNotice.create_by.like(f'%{query_object.create_by}%') if query_object.create_by else True,
+ SysNotice.notice_type == query_object.notice_type if query_object.notice_type else True,
+ SysNotice.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_time and query_object.end_time
+ else True,
+ )
+ .order_by(SysNotice.notice_id)
+ .distinct()
+ )
+ notice_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return notice_list
+
+ @classmethod
+ async def add_notice_dao(cls, db: AsyncSession, notice: NoticeModel):
+ """
+ 新增通知公告数据库操作
+
+ :param db: orm对象
+ :param notice: 通知公告对象
+ :return:
+ """
+ db_notice = SysNotice(**notice.model_dump())
+ db.add(db_notice)
+ await db.flush()
+
+ return db_notice
+
+ @classmethod
+ async def edit_notice_dao(cls, db: AsyncSession, notice: dict):
+ """
+ 编辑通知公告数据库操作
+
+ :param db: orm对象
+ :param notice: 需要更新的通知公告字典
+ :return:
+ """
+ await db.execute(update(SysNotice), [notice])
+
+ @classmethod
+ async def delete_notice_dao(cls, db: AsyncSession, notice: NoticeModel):
+ """
+ 删除通知公告数据库操作
+
+ :param db: orm对象
+ :param notice: 通知公告对象
+ :return:
+ """
+ await db.execute(delete(SysNotice).where(SysNotice.notice_id.in_([notice.notice_id])))
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..7d90088
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/post_dao.py
@@ -0,0 +1,143 @@
+from sqlalchemy import delete, func, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.post_do import SysPost
+from module_admin.entity.do.user_do import SysUserPost
+from module_admin.entity.vo.post_vo import PostModel, PostPageQueryModel
+from utils.page_util import PageUtil
+
+
+class PostDao:
+ """
+ 岗位管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_post_by_id(cls, db: AsyncSession, post_id: int):
+ """
+ 根据岗位id获取在用岗位详细信息
+
+ :param db: orm对象
+ :param post_id: 岗位id
+ :return: 在用岗位信息对象
+ """
+ post_info = (
+ (await db.execute(select(SysPost).where(SysPost.post_id == post_id, SysPost.status == '0')))
+ .scalars()
+ .first()
+ )
+
+ return post_info
+
+ @classmethod
+ async def get_post_detail_by_id(cls, db: AsyncSession, post_id: int):
+ """
+ 根据岗位id获取岗位详细信息
+
+ :param db: orm对象
+ :param post_id: 岗位id
+ :return: 岗位信息对象
+ """
+ post_info = (await db.execute(select(SysPost).where(SysPost.post_id == post_id))).scalars().first()
+
+ return post_info
+
+ @classmethod
+ async def get_post_detail_by_info(cls, db: AsyncSession, post: PostModel):
+ """
+ 根据岗位参数获取岗位信息
+
+ :param db: orm对象
+ :param post: 岗位参数对象
+ :return: 岗位信息对象
+ """
+ post_info = (
+ (
+ await db.execute(
+ select(SysPost).where(
+ SysPost.post_name == post.post_name if post.post_name else True,
+ SysPost.post_code == post.post_code if post.post_code else True,
+ SysPost.post_sort == post.post_sort if post.post_sort else True,
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return post_info
+
+ @classmethod
+ async def get_post_list(cls, db: AsyncSession, query_object: PostPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取岗位列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 岗位列表信息对象
+ """
+ query = (
+ select(SysPost)
+ .where(
+ SysPost.post_code.like(f'%{query_object.post_code}%') if query_object.post_code else True,
+ SysPost.post_name.like(f'%{query_object.post_name}%') if query_object.post_name else True,
+ SysPost.status == query_object.status if query_object.status else True,
+ )
+ .order_by(SysPost.post_sort)
+ .distinct()
+ )
+ post_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return post_list
+
+ @classmethod
+ async def add_post_dao(cls, db: AsyncSession, post: PostModel):
+ """
+ 新增岗位数据库操作
+
+ :param db: orm对象
+ :param post: 岗位对象
+ :return:
+ """
+ db_post = SysPost(**post.model_dump())
+ db.add(db_post)
+ await db.flush()
+
+ return db_post
+
+ @classmethod
+ async def edit_post_dao(cls, db: AsyncSession, post: dict):
+ """
+ 编辑岗位数据库操作
+
+ :param db: orm对象
+ :param post: 需要更新的岗位字典
+ :return:
+ """
+ await db.execute(update(SysPost), [post])
+
+ @classmethod
+ async def delete_post_dao(cls, db: AsyncSession, post: PostModel):
+ """
+ 删除岗位数据库操作
+
+ :param db: orm对象
+ :param post: 岗位对象
+ :return:
+ """
+ await db.execute(delete(SysPost).where(SysPost.post_id.in_([post.post_id])))
+
+ @classmethod
+ async def count_user_post_dao(cls, db: AsyncSession, post_id: int):
+ """
+ 根据岗位id查询岗位关联的用户数量
+
+ :param db: orm对象
+ :param post_id: 岗位id
+ :return: 岗位关联的用户数量
+ """
+ user_post_count = (
+ await db.execute(select(func.count('*')).select_from(SysUserPost).where(SysUserPost.post_id == post_id))
+ ).scalar()
+
+ return user_post_count
diff --git a/ruoyi-fastapi-backend/module_admin/dao/robot_action_dao.py b/ruoyi-fastapi-backend/module_admin/dao/robot_action_dao.py
new file mode 100644
index 0000000..4e8d230
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/robot_action_dao.py
@@ -0,0 +1,119 @@
+from sqlalchemy import delete, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.robot_action_do import RobotAction
+from module_admin.entity.vo.robot_action_vo import Robot_actionModel, Robot_actionPageQueryModel
+from utils.page_util import PageUtil
+
+
+class Robot_actionDao:
+ """
+ 机器人动作模块数据库操作层
+ """
+
+ @classmethod
+ async def get_robot_action_detail_by_id(cls, db: AsyncSession, id: int):
+ """
+ 根据主键 自增获取机器人动作详细信息
+
+ :param db: orm对象
+ :param id: 主键 自增
+ :return: 机器人动作信息对象
+ """
+ robot_action_info = (
+ (
+ await db.execute(
+ select(RobotAction)
+ .where(
+ RobotAction.id == id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return robot_action_info
+
+ @classmethod
+ async def get_robot_action_detail_by_info(cls, db: AsyncSession, robot_action: Robot_actionModel):
+ """
+ 根据机器人动作参数获取机器人动作信息
+
+ :param db: orm对象
+ :param robot_action: 机器人动作参数对象
+ :return: 机器人动作信息对象
+ """
+ robot_action_info = (
+ (
+ await db.execute(
+ select(RobotAction).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return robot_action_info
+
+ @classmethod
+ async def get_robot_action_list(cls, db: AsyncSession, query_object: Robot_actionPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取机器人动作列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 机器人动作列表信息对象
+ """
+ query = (
+ select(RobotAction)
+ .where(
+ RobotAction.id == query_object.id if query_object.id else True,
+ RobotAction.name.like(f'%{query_object.name}%') if query_object.name else True,
+ RobotAction.status == query_object.status if query_object.status else True,
+ )
+ .order_by(RobotAction.id)
+ .distinct()
+ )
+ robot_action_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return robot_action_list
+
+ @classmethod
+ async def add_robot_action_dao(cls, db: AsyncSession, robot_action: Robot_actionModel):
+ """
+ 新增机器人动作数据库操作
+
+ :param db: orm对象
+ :param robot_action: 机器人动作对象
+ :return:
+ """
+ db_robot_action = RobotAction(**robot_action.model_dump(exclude={}))
+ db.add(db_robot_action)
+ await db.flush()
+
+ return db_robot_action
+
+ @classmethod
+ async def edit_robot_action_dao(cls, db: AsyncSession, robot_action: dict):
+ """
+ 编辑机器人动作数据库操作
+
+ :param db: orm对象
+ :param robot_action: 需要更新的机器人动作字典
+ :return:
+ """
+ await db.execute(update(RobotAction), [robot_action])
+
+ @classmethod
+ async def delete_robot_action_dao(cls, db: AsyncSession, robot_action: Robot_actionModel):
+ """
+ 删除机器人动作数据库操作
+
+ :param db: orm对象
+ :param robot_action: 机器人动作对象
+ :return:
+ """
+ await db.execute(delete(RobotAction).where(RobotAction.id.in_([robot_action.id])))
+
diff --git a/ruoyi-fastapi-backend/module_admin/dao/robot_role_dao.py b/ruoyi-fastapi-backend/module_admin/dao/robot_role_dao.py
new file mode 100644
index 0000000..a9de2ab
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/robot_role_dao.py
@@ -0,0 +1,131 @@
+from datetime import datetime, time
+from sqlalchemy import delete, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.role_do import RobotRole
+from module_admin.entity.vo.role_vo import RoleModel, RolePageQueryModel
+from utils.page_util import PageUtil
+
+
+class RoleDao:
+ """
+ 系统角色模块数据库操作层
+ """
+
+ @classmethod
+ async def get_role_detail_by_id(cls, db: AsyncSession, robot_role_id: int):
+ """
+ 根据机器人角色ID获取系统角色详细信息
+
+ :param db: orm对象
+ :param robot_role_id: 机器人角色ID
+ :return: 系统角色信息对象
+ """
+ role_info = (
+ (
+ await db.execute(
+ select(RobotRole)
+ .where(
+ RobotRole.robot_role_id == robot_role_id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return role_info
+
+ @classmethod
+ async def get_role_detail_by_info(cls, db: AsyncSession, role: RoleModel):
+ """
+ 根据系统角色参数获取系统角色信息
+
+ :param db: orm对象
+ :param role: 系统角色参数对象
+ :return: 系统角色信息对象
+ """
+ role_info = (
+ (
+ await db.execute(
+ select(RobotRole).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return role_info
+
+ @classmethod
+ async def get_role_list(cls, db: AsyncSession, query_object: RolePageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取系统角色列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 系统角色列表信息对象
+ """
+ query = (
+ select(RobotRole)
+ .where(
+ RobotRole.rolename.like(f'%{query_object.rolename}%') if query_object.rolename else True,
+ RobotRole.detail == query_object.detail if query_object.detail else True,
+ RobotRole.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_create_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_create_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_create_time and query_object.end_create_time
+ else True,
+ RobotRole.update_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_update_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_update_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_update_time and query_object.end_update_time
+ else True,
+ )
+ .order_by(RobotRole.robot_role_id)
+ .distinct()
+ )
+ role_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return role_list
+
+ @classmethod
+ async def add_role_dao(cls, db: AsyncSession, role: RoleModel):
+ """
+ 新增系统角色数据库操作
+
+ :param db: orm对象
+ :param role: 系统角色对象
+ :return:
+ """
+ db_role = RobotRole(**role.model_dump(exclude={}))
+ db.add(db_role)
+ await db.flush()
+
+ return db_role
+
+ @classmethod
+ async def edit_role_dao(cls, db: AsyncSession, role: dict):
+ """
+ 编辑系统角色数据库操作
+
+ :param db: orm对象
+ :param role: 需要更新的系统角色字典
+ :return:
+ """
+ await db.execute(update(RobotRole), [role])
+
+ @classmethod
+ async def delete_role_dao(cls, db: AsyncSession, role: RoleModel):
+ """
+ 删除系统角色数据库操作
+
+ :param db: orm对象
+ :param role: 系统角色对象
+ :return:
+ """
+ await db.execute(delete(RobotRole).where(RobotRole.robot_role_id.in_([role.robot_role_id])))
+
diff --git a/ruoyi-fastapi-backend/module_admin/dao/robot_role_pairing_dao.py b/ruoyi-fastapi-backend/module_admin/dao/robot_role_pairing_dao.py
new file mode 100644
index 0000000..a00e348
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/robot_role_pairing_dao.py
@@ -0,0 +1,153 @@
+from sqlalchemy import delete, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+
+from module_admin.entity.do.robot_role_do import RobotRole
+from module_admin.entity.do.robot_role_pairing_do import RobotRolePairing
+
+from module_admin.entity.vo.robot_role_pairing_vo import PairingModel, PairingPageQueryModel
+from utils.page_util import PageUtil
+
+
+class PairingDao:
+ """
+ 角色-机器人-映射模块数据库操作层
+ """
+
+ @classmethod
+ async def edit_pairing_status(cls, db: AsyncSession,robot_id: int = None, pairing_id: int = None, status: str = '0'):
+
+ condition = list()
+ if robot_id:
+ condition.append(RobotRolePairing.robot_id == robot_id)
+ if pairing_id:
+ condition.append(RobotRolePairing.pairing_id == pairing_id)
+ query = (
+ update(RobotRolePairing)
+ .values(status=status)
+ .where(
+ * condition
+ )
+ )
+ await db.execute(query)
+
+ @classmethod
+ async def get_role_pairing_list(cls, db: AsyncSession, robot_id: int):
+
+ query = (
+ select(RobotRolePairing.pairing_id, RobotRole.robot_role_id ,RobotRole.rolename, RobotRole.detail, RobotRolePairing.status)
+ .select_from(RobotRolePairing)
+ .join(RobotRole, RobotRole.robot_role_id == RobotRolePairing.robot_role_id)
+ .where(RobotRolePairing.robot_id == robot_id)
+ )
+ role_pairing_list = (
+ await db.execute(query)
+ ).mappings().all()
+ return role_pairing_list
+ @classmethod
+ async def get_pairing_detail_by_id(cls, db: AsyncSession, pairing_id: int):
+ """
+ 根据主键ID获取角色-机器人-映射详细信息
+
+ :param db: orm对象
+ :param pairing_id: 主键ID
+ :return: 角色-机器人-映射信息对象
+ """
+ pairing_info = (
+ (
+ await db.execute(
+ select(RobotRolePairing)
+ .where(
+ RobotRolePairing.pairing_id == pairing_id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return pairing_info
+
+ @classmethod
+ async def get_pairing_detail_by_info(cls, db: AsyncSession, pairing: PairingModel):
+ """
+ 根据角色-机器人-映射参数获取角色-机器人-映射信息
+
+ :param db: orm对象
+ :param pairing: 角色-机器人-映射参数对象
+ :return: 角色-机器人-映射信息对象
+ """
+ pairing_info = (
+ (
+ await db.execute(
+ select(RobotRolePairing).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return pairing_info
+
+ @classmethod
+ async def get_pairing_list(cls, db: AsyncSession, query_object: PairingPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取角色-机器人-映射列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 角色-机器人-映射列表信息对象
+ """
+ query = (
+ select(RobotRolePairing)
+ .where(
+ RobotRolePairing.pairing_id == query_object.pairing_id if query_object.pairing_id else True,
+ RobotRolePairing.robot_role_id == query_object.robot_role_id if query_object.robot_role_id else True,
+ RobotRolePairing.robot_id == query_object.robot_id if query_object.robot_id else True,
+ RobotRolePairing.status == query_object.status if query_object.status else True,
+ )
+ .order_by(RobotRolePairing.pairing_id)
+ .distinct()
+ )
+ pairing_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return pairing_list
+
+ @classmethod
+ async def add_pairing_dao(cls, db: AsyncSession, pairing: PairingModel):
+ """
+ 新增角色-机器人-映射数据库操作
+
+ :param db: orm对象
+ :param pairing: 角色-机器人-映射对象
+ :return:
+ """
+ db_pairing = RobotRolePairing(**pairing.model_dump(exclude={}))
+ db.add(db_pairing)
+ await db.flush()
+
+ return db_pairing
+
+ @classmethod
+ async def edit_pairing_dao(cls, db: AsyncSession, pairing: dict):
+ """
+ 编辑角色-机器人-映射数据库操作
+
+ :param db: orm对象
+ :param pairing: 需要更新的角色-机器人-映射字典
+ :return:
+ """
+ await db.execute(update(RobotRolePairing), [pairing])
+
+ @classmethod
+ async def delete_pairing_dao(cls, db: AsyncSession, pairing: PairingModel):
+ """
+ 删除角色-机器人-映射数据库操作
+
+ :param db: orm对象
+ :param pairing: 角色-机器人-映射对象
+ :return:
+ """
+ await db.execute(delete(RobotRolePairing).where(RobotRolePairing.pairing_id.in_([pairing.pairing_id])))
+
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..534c7f3
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/role_dao.py
@@ -0,0 +1,338 @@
+from datetime import datetime, time
+from sqlalchemy import and_, delete, desc, func, or_, select, update # noqa: F401
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.dept_do import SysDept
+from module_admin.entity.do.menu_do import SysMenu
+from module_admin.entity.do.role_do import SysRole, SysRoleMenu, SysRoleDept
+from module_admin.entity.do.user_do import SysUser, SysUserRole
+from module_admin.entity.vo.role_vo import RoleDeptModel, RoleMenuModel, RoleModel, RolePageQueryModel
+from utils.page_util import PageUtil
+
+
+class RoleDao:
+ """
+ 角色管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_role_by_name(cls, db: AsyncSession, role_name: str):
+ """
+ 根据角色名获取在用角色信息
+
+ :param db: orm对象
+ :param role_name: 角色名
+ :return: 当前角色名的角色信息对象
+ """
+ query_role_info = (
+ (
+ await db.execute(
+ select(SysRole)
+ .where(SysRole.status == '0', SysRole.del_flag == '0', SysRole.role_name == role_name)
+ .order_by(desc(SysRole.create_time))
+ .distinct()
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return query_role_info
+
+ @classmethod
+ async def get_role_by_info(cls, db: AsyncSession, role: RoleModel):
+ """
+ 根据角色参数获取角色信息
+
+ :param db: orm对象
+ :param role: 角色参数
+ :return: 当前角色参数的角色信息对象
+ """
+ query_role_info = (
+ (
+ await db.execute(
+ select(SysRole)
+ .where(
+ SysRole.del_flag == '0',
+ SysRole.role_name == role.role_name if role.role_name else True,
+ SysRole.role_key == role.role_key if role.role_key else True,
+ )
+ .order_by(desc(SysRole.create_time))
+ .distinct()
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return query_role_info
+
+ @classmethod
+ async def get_role_by_id(cls, db: AsyncSession, role_id: int):
+ """
+ 根据角色id获取在用角色信息
+
+ :param db: orm对象
+ :param role_id: 角色id
+ :return: 当前角色id的角色信息对象
+ """
+ role_info = (
+ (
+ await db.execute(
+ select(SysRole).where(SysRole.role_id == role_id, SysRole.status == '0', SysRole.del_flag == '0')
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return role_info
+
+ @classmethod
+ async def get_role_detail_by_id(cls, db: AsyncSession, role_id: int):
+ """
+ 根据role_id获取角色详细信息
+
+ :param db: orm对象
+ :param role_id: 角色id
+ :return: 当前role_id的角色信息对象
+ """
+ query_role_info = (
+ (await db.execute(select(SysRole).where(SysRole.del_flag == '0', SysRole.role_id == role_id).distinct()))
+ .scalars()
+ .first()
+ )
+
+ return query_role_info
+
+ @classmethod
+ async def get_role_select_option_dao(cls, db: AsyncSession):
+ """
+ 获取编辑页面对应的在用角色列表信息
+
+ :param db: orm对象
+ :return: 角色列表信息
+ """
+ role_info = (
+ (
+ await db.execute(
+ select(SysRole).where(SysRole.role_id != 1, SysRole.status == '0', SysRole.del_flag == '0')
+ )
+ )
+ .scalars()
+ .all()
+ )
+
+ return role_info
+
+ @classmethod
+ async def get_role_list(
+ cls, db: AsyncSession, query_object: RolePageQueryModel, data_scope_sql: str, is_page: bool = False
+ ):
+ """
+ 根据查询参数获取角色列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param data_scope_sql: 数据权限对应的查询sql语句
+ :param is_page: 是否开启分页
+ :return: 角色列表信息对象
+ """
+ query = (
+ select(SysRole)
+ .join(SysUserRole, SysUserRole.role_id == SysRole.role_id, isouter=True)
+ .join(SysUser, SysUser.user_id == SysUserRole.user_id, isouter=True)
+ .join(SysDept, SysDept.dept_id == SysUser.dept_id, isouter=True)
+ .where(
+ SysRole.del_flag == '0',
+ SysRole.role_id == query_object.role_id if query_object.role_id is not None else True,
+ SysRole.role_name.like(f'%{query_object.role_name}%') if query_object.role_name else True,
+ SysRole.role_key.like(f'%{query_object.role_key}%') if query_object.role_key else True,
+ SysRole.status == query_object.status if query_object.status else True,
+ SysRole.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_time and query_object.end_time
+ else True,
+ eval(data_scope_sql),
+ )
+ .order_by(SysRole.role_sort)
+ .distinct()
+ )
+ role_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return role_list
+
+ @classmethod
+ async def add_role_dao(cls, db: AsyncSession, role: RoleModel):
+ """
+ 新增角色数据库操作
+
+ :param db: orm对象
+ :param role: 角色对象
+ :return:
+ """
+ db_role = SysRole(**role.model_dump(exclude={'admin'}))
+ db.add(db_role)
+ await db.flush()
+
+ return db_role
+
+ @classmethod
+ async def edit_role_dao(cls, db: AsyncSession, role: dict):
+ """
+ 编辑角色数据库操作
+
+ :param db: orm对象
+ :param role: 需要更新的角色字典
+ :return:
+ """
+ await db.execute(update(SysRole), [role])
+
+ @classmethod
+ async def delete_role_dao(cls, db: AsyncSession, role: RoleModel):
+ """
+ 删除角色数据库操作
+
+ :param db: orm对象
+ :param role: 角色对象
+ :return:
+ """
+ await db.execute(
+ update(SysRole)
+ .where(SysRole.role_id == role.role_id)
+ .values(del_flag='2', update_by=role.update_by, update_time=role.update_time)
+ )
+
+ @classmethod
+ async def get_role_menu_dao(cls, db: AsyncSession, role: RoleModel):
+ """
+ 根据角色id获取角色菜单关联列表信息
+
+ :param db: orm对象
+ :param role: 角色对象
+ :return: 角色菜单关联列表信息
+ """
+ role_menu_query_all = (
+ (
+ await db.execute(
+ select(SysMenu)
+ .join(SysRoleMenu, SysRoleMenu.menu_id == SysMenu.menu_id)
+ .where(
+ SysRoleMenu.role_id == role.role_id,
+ ~SysMenu.menu_id.in_(
+ select(SysMenu.parent_id)
+ .select_from(SysMenu)
+ .join(
+ SysRoleMenu,
+ and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysRoleMenu.role_id == role.role_id),
+ )
+ )
+ if role.menu_check_strictly
+ else True,
+ )
+ .order_by(SysMenu.parent_id, SysMenu.order_num)
+ )
+ )
+ .scalars()
+ .all()
+ )
+
+ return role_menu_query_all
+
+ @classmethod
+ async def add_role_menu_dao(cls, db: AsyncSession, role_menu: RoleMenuModel):
+ """
+ 新增角色菜单关联信息数据库操作
+
+ :param db: orm对象
+ :param role_menu: 用户角色菜单关联对象
+ :return:
+ """
+ db_role_menu = SysRoleMenu(**role_menu.model_dump())
+ db.add(db_role_menu)
+
+ @classmethod
+ async def delete_role_menu_dao(cls, db: AsyncSession, role_menu: RoleMenuModel):
+ """
+ 删除角色菜单关联信息数据库操作
+
+ :param db: orm对象
+ :param role_menu: 角色菜单关联对象
+ :return:
+ """
+ await db.execute(delete(SysRoleMenu).where(SysRoleMenu.role_id.in_([role_menu.role_id])))
+
+ @classmethod
+ async def get_role_dept_dao(cls, db: AsyncSession, role: RoleModel):
+ """
+ 根据角色id获取角色部门关联列表信息
+
+ :param db: orm对象
+ :param role: 角色对象
+ :return: 角色部门关联列表信息
+ """
+ role_dept_query_all = (
+ (
+ await db.execute(
+ select(SysDept)
+ .join(SysRoleDept, SysRoleDept.dept_id == SysDept.dept_id)
+ .where(
+ SysRoleDept.role_id == role.role_id,
+ ~SysDept.dept_id.in_(
+ select(SysDept.parent_id)
+ .select_from(SysDept)
+ .join(
+ SysRoleDept,
+ and_(SysRoleDept.dept_id == SysDept.dept_id, SysRoleDept.role_id == role.role_id),
+ )
+ )
+ if role.dept_check_strictly
+ else True,
+ )
+ .order_by(SysDept.parent_id, SysDept.order_num)
+ )
+ )
+ .scalars()
+ .all()
+ )
+
+ return role_dept_query_all
+
+ @classmethod
+ async def add_role_dept_dao(cls, db: AsyncSession, role_dept: RoleDeptModel):
+ """
+ 新增角色部门关联信息数据库操作
+
+ :param db: orm对象
+ :param role_dept: 用户角色部门关联对象
+ :return:
+ """
+ db_role_dept = SysRoleDept(**role_dept.dict())
+ db.add(db_role_dept)
+
+ @classmethod
+ async def delete_role_dept_dao(cls, db: AsyncSession, role_dept: RoleDeptModel):
+ """
+ 删除角色部门关联信息数据库操作
+
+ :param db: orm对象
+ :param role_dept: 角色部门关联对象
+ :return:
+ """
+ await db.execute(delete(SysRoleDept).where(SysRoleDept.role_id.in_([role_dept.role_id])))
+
+ @classmethod
+ async def count_user_role_dao(cls, db: AsyncSession, role_id: int):
+ """
+ 根据角色id查询角色关联用户数量
+
+ :param db: orm对象
+ :param role_id: 角色id
+ :return: 角色关联用户数量
+ """
+ user_count = (
+ await db.execute(select(func.count('*')).select_from(SysUserRole).where(SysUserRole.role_id == role_id))
+ ).scalar()
+
+ return user_count
diff --git a/ruoyi-fastapi-backend/module_admin/dao/sys_alert_dao.py b/ruoyi-fastapi-backend/module_admin/dao/sys_alert_dao.py
new file mode 100644
index 0000000..c6ecb0e
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/sys_alert_dao.py
@@ -0,0 +1,132 @@
+from datetime import datetime, time
+from sqlalchemy import delete, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.sys_alert_do import SysAlert
+from module_admin.entity.vo.sys_alert_vo import Sys_alertModel, Sys_alertPageQueryModel
+from utils.page_util import PageUtil
+
+
+class Sys_alertDao:
+ """
+ 系统告警模块数据库操作层
+ """
+
+ @classmethod
+ async def get_sys_alert_detail_by_id(cls, db: AsyncSession, id: int):
+ """
+ 根据主键 自增获取系统告警详细信息
+
+ :param db: orm对象
+ :param id: 主键 自增
+ :return: 系统告警信息对象
+ """
+ sys_alert_info = (
+ (
+ await db.execute(
+ select(SysAlert)
+ .where(
+ SysAlert.id == id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return sys_alert_info
+
+ @classmethod
+ async def get_sys_alert_detail_by_info(cls, db: AsyncSession, sys_alert: Sys_alertModel):
+ """
+ 根据系统告警参数获取系统告警信息
+
+ :param db: orm对象
+ :param sys_alert: 系统告警参数对象
+ :return: 系统告警信息对象
+ """
+ sys_alert_info = (
+ (
+ await db.execute(
+ select(SysAlert).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return sys_alert_info
+
+ @classmethod
+ async def get_sys_alert_list(cls, db: AsyncSession, query_object: Sys_alertPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取系统告警列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 系统告警列表信息对象
+ """
+ query = (
+ select(SysAlert)
+ .where(
+ SysAlert.detail.like(f'%{query_object.detail}%') if query_object.detail else True,
+ SysAlert.type == query_object.type if query_object.type else True,
+ SysAlert.status == query_object.status if query_object.status else True,
+ SysAlert.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_create_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_create_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_create_time and query_object.end_create_time
+ else True,
+ SysAlert.update_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_update_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_update_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_update_time and query_object.end_update_time
+ else True,
+ )
+ .order_by(SysAlert.id)
+ .distinct()
+ )
+ sys_alert_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return sys_alert_list
+
+ @classmethod
+ async def add_sys_alert_dao(cls, db: AsyncSession, sys_alert: Sys_alertModel):
+ """
+ 新增系统告警数据库操作
+
+ :param db: orm对象
+ :param sys_alert: 系统告警对象
+ :return:
+ """
+ db_sys_alert = SysAlert(**sys_alert.model_dump(exclude={}))
+ db.add(db_sys_alert)
+ await db.flush()
+
+ return db_sys_alert
+
+ @classmethod
+ async def edit_sys_alert_dao(cls, db: AsyncSession, sys_alert: dict):
+ """
+ 编辑系统告警数据库操作
+
+ :param db: orm对象
+ :param sys_alert: 需要更新的系统告警字典
+ :return:
+ """
+ await db.execute(update(SysAlert), [sys_alert])
+
+ @classmethod
+ async def delete_sys_alert_dao(cls, db: AsyncSession, sys_alert: Sys_alertModel):
+ """
+ 删除系统告警数据库操作
+
+ :param db: orm对象
+ :param sys_alert: 系统告警对象
+ :return:
+ """
+ await db.execute(delete(SysAlert).where(SysAlert.id.in_([sys_alert.id])))
+
diff --git a/ruoyi-fastapi-backend/module_admin/dao/sys_statistics_dao.py b/ruoyi-fastapi-backend/module_admin/dao/sys_statistics_dao.py
new file mode 100644
index 0000000..4f63829
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/sys_statistics_dao.py
@@ -0,0 +1,266 @@
+from sqlalchemy import delete, select, update, and_, desc, insert, func
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.sys_statistics_do import SysStatistics
+from module_admin.entity.vo.sys_statistics_vo import Sys_statisticsModel, Sys_statisticsPageQueryModel
+from utils.page_util import PageUtil
+from datetime import datetime, date
+
+
+class Sys_statisticsDao:
+ """
+ 系统统计数据模块数据库操作层
+ """
+
+ @classmethod
+ async def get_total_statistics_data(cls, db: AsyncSession):
+ """
+ 添加系统统计数据
+ :param query_db:
+ :param page_object:
+ :return:
+ """
+ stmt = (
+ select(
+ func.sum(SysStatistics.llm_call).label('llm_call'),
+ func.sum(SysStatistics.access_control_count).label('access_control_count'),
+ func.sum(SysStatistics.visitor_guide).label('visitor_guide'),
+ func.sum(SysStatistics.exhibition_explanation).label('exhibition_explanation'),
+ )
+ )
+
+ result = await db.execute(stmt)
+ return result.mappings().first()
+
+ @classmethod
+ async def add_count(cls, db: AsyncSession, field: str, user_name:str , count: int = 1):
+ stmt = (
+ select(SysStatistics)
+ .where(
+ SysStatistics.stat_date == datetime.now().date()
+ )
+ )
+ result = (await db.execute(stmt)).scalars().first()
+ if result:
+ update_stmt = (
+ update(SysStatistics)
+ .where(
+ SysStatistics.statistic_id == result.statistic_id
+ )
+ .values(
+ {
+ field: getattr(result, field) + count,
+ "update_time": datetime.now(),
+ "update_by": user_name,
+ }
+ )
+ )
+ await db.execute(update_stmt)
+ else:
+ insert_stmt = (
+ insert(SysStatistics)
+ .values(
+ {
+ "stat_date": datetime.now().date(),
+ field: 1,
+ "create_time": datetime.now(),
+ "update_time": datetime.now(),
+ "create_by": user_name,
+ }
+ )
+ )
+ await db.execute(insert_stmt)
+
+ @classmethod
+ async def add_model_call_count(cls, db: AsyncSession, user_name: str):
+ stmt = (
+ select(SysStatistics)
+ .where(
+ SysStatistics.stat_date == datetime.now().date()
+ )
+ )
+ result = (await db.execute(stmt)).scalars().first()
+ if result:
+ update_stmt = (
+ update(SysStatistics)
+ .where(
+ SysStatistics.statistic_id == result.statistic_id
+ )
+ .values(
+ {
+ "llm_call": result.llm_call + 1,
+ "update_time": datetime.now(),
+ "update_by": user_name,
+ }
+ )
+ )
+ await db.execute(update_stmt)
+ else:
+ insert_stmt = (
+ insert(SysStatistics)
+ .values(
+ {
+ "stat_date": datetime.now().date(),
+ "llm_call": 1,
+ "create_time": datetime.now(),
+ "update_time": datetime.now(),
+ "create_by": user_name,
+ }
+ )
+ )
+ await db.execute(insert_stmt)
+
+
+
+
+
+ @classmethod
+ async def get_access_control_success_rate(cls, db: AsyncSession, start_time: datetime, end_time: datetime):
+ stmt = (
+ select(SysStatistics.access_control_success_rate, SysStatistics.stat_date)
+ .where(
+ and_(
+ SysStatistics.stat_date >= start_time,
+ SysStatistics.stat_date <= end_time
+ )
+ )
+ .order_by(desc(SysStatistics.stat_date))
+ )
+
+ return (await db.execute(stmt)).mappings().all()
+
+ @classmethod
+ async def get_visitor_count(cls, db: AsyncSession, start_time: date, end_time: date):
+ stmt = (
+ select(SysStatistics.visitor_guide, SysStatistics.stat_date)
+ .where(
+ and_(
+ SysStatistics.stat_date >= start_time,
+ SysStatistics.stat_date <= end_time,
+ )
+ )
+ .order_by(desc(SysStatistics.stat_date))
+ )
+
+ return (await db.execute(stmt)).mappings().all()
+
+ @classmethod
+ async def get_statistics_data(cls, db: AsyncSession, dd: date):
+ # 使用 func.date() 确保日期格式正确比较
+ stmt = (
+ select(
+ SysStatistics.llm_call,
+ SysStatistics.access_control_count,
+ SysStatistics.visitor_guide,
+ SysStatistics.exhibition_explanation
+ )
+ .where(func.date(SysStatistics.stat_date) == dd)
+ )
+ result = (await db.execute(stmt)).mappings().first()
+ return result
+
+
+ @classmethod
+ async def get_sys_statistics_detail_by_id(cls, db: AsyncSession, statistic_id: int):
+ """
+ 根据统计项目ID获取系统统计数据详细信息
+
+ :param db: orm对象
+ :param statistic_id: 统计项目ID
+ :return: 系统统计数据信息对象
+ """
+ sys_statistics_info = (
+ (
+ await db.execute(
+ select(SysStatistics)
+ .where(
+ SysStatistics.statistic_id == statistic_id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return sys_statistics_info
+
+ @classmethod
+ async def get_sys_statistics_detail_by_info(cls, db: AsyncSession, sys_statistics: Sys_statisticsModel):
+ """
+ 根据系统统计数据参数获取系统统计数据信息
+
+ :param db: orm对象
+ :param sys_statistics: 系统统计数据参数对象
+ :return: 系统统计数据信息对象
+ """
+ sys_statistics_info = (
+ (
+ await db.execute(
+ select(SysStatistics).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return sys_statistics_info
+
+ @classmethod
+ async def get_sys_statistics_list(cls, db: AsyncSession, query_object: Sys_statisticsPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取系统统计数据列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 系统统计数据列表信息对象
+ """
+ query = (
+ select(SysStatistics)
+ .where(
+ SysStatistics.stat_date == query_object.stat_date if query_object.stat_date else True,
+ )
+ .order_by(SysStatistics.statistic_id)
+ .distinct()
+ )
+ sys_statistics_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return sys_statistics_list
+
+ @classmethod
+ async def add_sys_statistics_dao(cls, db: AsyncSession, sys_statistics: Sys_statisticsModel):
+ """
+ 新增系统统计数据数据库操作
+
+ :param db: orm对象
+ :param sys_statistics: 系统统计数据对象
+ :return:
+ """
+ db_sys_statistics = SysStatistics(**sys_statistics.model_dump(exclude={}))
+ db.add(db_sys_statistics)
+ await db.flush()
+
+ return db_sys_statistics
+
+ @classmethod
+ async def edit_sys_statistics_dao(cls, db: AsyncSession, sys_statistics: dict):
+ """
+ 编辑系统统计数据数据库操作
+
+ :param db: orm对象
+ :param sys_statistics: 需要更新的系统统计数据字典
+ :return:
+ """
+ await db.execute(update(SysStatistics), [sys_statistics])
+
+ @classmethod
+ async def delete_sys_statistics_dao(cls, db: AsyncSession, sys_statistics: Sys_statisticsModel):
+ """
+ 删除系统统计数据数据库操作
+
+ :param db: orm对象
+ :param sys_statistics: 系统统计数据对象
+ :return:
+ """
+ await db.execute(delete(SysStatistics).where(SysStatistics.statistic_id.in_([sys_statistics.statistic_id])))
+
diff --git a/ruoyi-fastapi-backend/module_admin/dao/test_dao.py b/ruoyi-fastapi-backend/module_admin/dao/test_dao.py
new file mode 100644
index 0000000..f1651fc
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/test_dao.py
@@ -0,0 +1,16 @@
+from sqlalchemy import select
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.user_do import SysUser
+from utils.page_util import PageUtil
+from module_admin.entity.vo.test_vo import TestUserNamePage
+
+
+class TestDao:
+ @classmethod
+ async def get_user_name(cls, db: AsyncSession, query_paramter: TestUserNamePage):
+ query = (
+ select(SysUser.user_name)
+ )
+ # user_name = await db.execute(select(SysUser.user_name))
+ user_name = await PageUtil.paginate(db, query, query_paramter.page_num, query_paramter.page_size, True)
+ return user_name
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..cc53566
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/user_dao.py
@@ -0,0 +1,568 @@
+from datetime import datetime, time
+from sqlalchemy import and_, delete, desc, func, or_, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.dept_do import SysDept
+from module_admin.entity.do.menu_do import SysMenu
+from module_admin.entity.do.post_do import SysPost
+from module_admin.entity.do.role_do import SysRole, SysRoleDept, SysRoleMenu # noqa: F401
+from module_admin.entity.do.user_do import SysUser, SysUserPost, SysUserRole
+from module_admin.entity.vo.user_vo import (
+ UserModel,
+ UserPageQueryModel,
+ UserPostModel,
+ UserRoleModel,
+ UserRolePageQueryModel,
+ UserRoleQueryModel,
+)
+from utils.page_util import PageUtil
+
+
+class UserDao:
+ """
+ 用户管理模块数据库操作层
+ """
+
+ @classmethod
+ async def get_user_by_name(cls, db: AsyncSession, user_name: str):
+ """
+ 根据用户名获取用户信息
+
+ :param db: orm对象
+ :param user_name: 用户名
+ :return: 当前用户名的用户信息对象
+ """
+ query_user_info = (
+ (
+ await db.execute(
+ select(SysUser)
+ .where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_name == user_name)
+ .order_by(desc(SysUser.create_time))
+ .distinct()
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return query_user_info
+
+ @classmethod
+ async def get_user_by_info(cls, db: AsyncSession, user: UserModel):
+ """
+ 根据用户参数获取用户信息
+
+ :param db: orm对象
+ :param user: 用户参数
+ :return: 当前用户参数的用户信息对象
+ """
+ query_user_info = (
+ (
+ await db.execute(
+ select(SysUser)
+ .where(
+ SysUser.del_flag == '0',
+ SysUser.user_name == user.user_name if user.user_name else True,
+ SysUser.phonenumber == user.phonenumber if user.phonenumber else True,
+ SysUser.email == user.email if user.email else True,
+ )
+ .order_by(desc(SysUser.create_time))
+ .distinct()
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return query_user_info
+
+ @classmethod
+ async def get_user_by_id(cls, db: AsyncSession, user_id: int):
+ """
+ 根据user_id获取用户信息
+
+ :param db: orm对象
+ :param user_id: 用户id
+ :return: 当前user_id的用户信息对象
+ """
+ query_user_basic_info = (
+ (
+ await db.execute(
+ select(SysUser)
+ .where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
+ .distinct()
+ )
+ )
+ .scalars()
+ .first()
+ )
+ query_user_dept_info = (
+ (
+ await db.execute(
+ select(SysDept)
+ .select_from(SysUser)
+ .where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
+ .join(
+ SysDept,
+ and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == '0', SysDept.del_flag == '0'),
+ )
+ .distinct()
+ )
+ )
+ .scalars()
+ .first()
+ )
+ query_user_role_info = (
+ (
+ await db.execute(
+ select(SysRole)
+ .select_from(SysUser)
+ .where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
+ .join(SysUserRole, SysUser.user_id == SysUserRole.user_id, isouter=True)
+ .join(
+ SysRole,
+ and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == '0', SysRole.del_flag == '0'),
+ )
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+ query_user_post_info = (
+ (
+ await db.execute(
+ select(SysPost)
+ .select_from(SysUser)
+ .where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
+ .join(SysUserPost, SysUser.user_id == SysUserPost.user_id, isouter=True)
+ .join(SysPost, and_(SysUserPost.post_id == SysPost.post_id, SysPost.status == '0'))
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+ role_id_list = [item.role_id for item in query_user_role_info]
+ if 1 in role_id_list:
+ query_user_menu_info = (
+ (await db.execute(select(SysMenu).where(SysMenu.status == '0').distinct())).scalars().all()
+ )
+ else:
+ query_user_menu_info = (
+ (
+ await db.execute(
+ select(SysMenu)
+ .select_from(SysUser)
+ .where(SysUser.status == '0', SysUser.del_flag == '0', SysUser.user_id == user_id)
+ .join(SysUserRole, SysUser.user_id == SysUserRole.user_id, isouter=True)
+ .join(
+ SysRole,
+ and_(
+ SysUserRole.role_id == SysRole.role_id, SysRole.status == '0', SysRole.del_flag == '0'
+ ),
+ isouter=True,
+ )
+ .join(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id, isouter=True)
+ .join(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == '0'))
+ .order_by(SysMenu.order_num)
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+
+ results = 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
+ async def get_user_detail_by_id(cls, db: AsyncSession, user_id: int):
+ """
+ 根据user_id获取用户详细信息
+
+ :param db: orm对象
+ :param user_id: 用户id
+ :return: 当前user_id的用户信息对象
+ """
+ query_user_basic_info = (
+ (await db.execute(select(SysUser).where(SysUser.del_flag == '0', SysUser.user_id == user_id).distinct()))
+ .scalars()
+ .first()
+ )
+ query_user_dept_info = (
+ (
+ await db.execute(
+ select(SysDept)
+ .select_from(SysUser)
+ .where(SysUser.del_flag == '0', SysUser.user_id == user_id)
+ .join(
+ SysDept,
+ and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == '0', SysDept.del_flag == '0'),
+ )
+ .distinct()
+ )
+ )
+ .scalars()
+ .first()
+ )
+ query_user_role_info = (
+ (
+ await db.execute(
+ select(SysRole)
+ .select_from(SysUser)
+ .where(SysUser.del_flag == '0', SysUser.user_id == user_id)
+ .join(SysUserRole, SysUser.user_id == SysUserRole.user_id, isouter=True)
+ .join(
+ SysRole,
+ and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == '0', SysRole.del_flag == '0'),
+ )
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+ query_user_post_info = (
+ (
+ await db.execute(
+ select(SysPost)
+ .select_from(SysUser)
+ .where(SysUser.del_flag == '0', SysUser.user_id == user_id)
+ .join(SysUserPost, SysUser.user_id == SysUserPost.user_id, isouter=True)
+ .join(SysPost, and_(SysUserPost.post_id == SysPost.post_id, SysPost.status == '0'))
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+ query_user_menu_info = (
+ (
+ await db.execute(
+ select(SysMenu)
+ .select_from(SysUser)
+ .where(SysUser.del_flag == '0', SysUser.user_id == user_id)
+ .join(SysUserRole, SysUser.user_id == SysUserRole.user_id, isouter=True)
+ .join(
+ SysRole,
+ and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == '0', SysRole.del_flag == '0'),
+ isouter=True,
+ )
+ .join(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id, isouter=True)
+ .join(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == '0'))
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+ results = 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
+ async def get_user_list(
+ cls, db: AsyncSession, query_object: UserPageQueryModel, data_scope_sql: str, is_page: bool = False
+ ):
+ """
+ 根据查询参数获取用户列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param data_scope_sql: 数据权限对应的查询sql语句
+ :param is_page: 是否开启分页
+ :return: 用户列表信息对象
+ """
+ query = (
+ select(SysUser, SysDept)
+ .where(
+ SysUser.del_flag == '0',
+ or_(
+ SysUser.dept_id == query_object.dept_id,
+ SysUser.dept_id.in_(
+ select(SysDept.dept_id).where(func.find_in_set(query_object.dept_id, SysDept.ancestors))
+ ),
+ )
+ if query_object.dept_id
+ else True,
+ SysUser.user_id == query_object.user_id if query_object.user_id is not None else True,
+ SysUser.user_name.like(f'%{query_object.user_name}%') if query_object.user_name else True,
+ SysUser.nick_name.like(f'%{query_object.nick_name}%') if query_object.nick_name else True,
+ SysUser.email.like(f'%{query_object.email}%') if query_object.email else True,
+ SysUser.phonenumber.like(f'%{query_object.phonenumber}%') if query_object.phonenumber else True,
+ SysUser.status == query_object.status if query_object.status else True,
+ SysUser.sex == query_object.sex if query_object.sex else True,
+ SysUser.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_time and query_object.end_time
+ else True,
+ eval(data_scope_sql),
+ )
+ .join(
+ SysDept,
+ and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == '0', SysDept.del_flag == '0'),
+ isouter=True,
+ )
+ .order_by(SysUser.user_id)
+ .distinct()
+ )
+ user_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return user_list
+
+ @classmethod
+ async def add_user_dao(cls, db: AsyncSession, user: UserModel):
+ """
+ 新增用户数据库操作
+
+ :param db: orm对象
+ :param user: 用户对象
+ :return: 新增校验结果
+ """
+ db_user = SysUser(**user.model_dump(exclude={'admin'}))
+ db.add(db_user)
+ await db.flush()
+
+ return db_user
+
+ @classmethod
+ async def edit_user_dao(cls, db: AsyncSession, user: dict):
+ """
+ 编辑用户数据库操作
+
+ :param db: orm对象
+ :param user: 需要更新的用户字典
+ :return: 编辑校验结果
+ """
+ await db.execute(update(SysUser), [user])
+
+ @classmethod
+ async def delete_user_dao(cls, db: AsyncSession, user: UserModel):
+ """
+ 删除用户数据库操作
+
+ :param db: orm对象
+ :param user: 用户对象
+ :return:
+ """
+ await db.execute(
+ update(SysUser)
+ .where(SysUser.user_id == user.user_id)
+ .values(del_flag='2', update_by=user.update_by, update_time=user.update_time)
+ )
+
+ @classmethod
+ async def get_user_role_allocated_list_by_user_id(cls, db: AsyncSession, query_object: UserRoleQueryModel):
+ """
+ 根据用户id获取用户已分配的角色列表信息数据库操作
+
+ :param db: orm对象
+ :param query_object: 用户角色查询对象
+ :return: 用户已分配的角色列表信息
+ """
+ allocated_role_list = (
+ (
+ await db.execute(
+ select(SysRole)
+ .where(
+ SysRole.del_flag == '0',
+ SysRole.role_id != 1,
+ SysRole.role_name == query_object.role_name if query_object.role_name else True,
+ SysRole.role_key == query_object.role_key if query_object.role_key else True,
+ SysRole.role_id.in_(
+ select(SysUserRole.role_id).where(SysUserRole.user_id == query_object.user_id)
+ ),
+ )
+ .distinct()
+ )
+ )
+ .scalars()
+ .all()
+ )
+
+ return allocated_role_list
+
+ @classmethod
+ async def get_user_role_allocated_list_by_role_id(
+ cls, db: AsyncSession, query_object: UserRolePageQueryModel, data_scope_sql: str, is_page: bool = False
+ ):
+ """
+ 根据角色id获取已分配的用户列表信息
+
+ :param db: orm对象
+ :param query_object: 用户角色查询对象
+ :param data_scope_sql: 数据权限对应的查询sql语句
+ :param is_page: 是否开启分页
+ :return: 角色已分配的用户列表信息
+ """
+ query = (
+ select(SysUser)
+ .join(SysDept, SysDept.dept_id == SysUser.dept_id, isouter=True)
+ .join(SysUserRole, SysUserRole.user_id == SysUser.user_id, isouter=True)
+ .join(SysRole, SysRole.role_id == SysUserRole.role_id, isouter=True)
+ .where(
+ SysUser.del_flag == '0',
+ SysUser.user_name == query_object.user_name if query_object.user_name else True,
+ SysUser.phonenumber == query_object.phonenumber if query_object.phonenumber else True,
+ SysRole.role_id == query_object.role_id,
+ eval(data_scope_sql),
+ )
+ .distinct()
+ )
+ allocated_user_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return allocated_user_list
+
+ @classmethod
+ async def get_user_role_unallocated_list_by_role_id(
+ cls, db: AsyncSession, query_object: UserRolePageQueryModel, data_scope_sql: str, is_page: bool = False
+ ):
+ """
+ 根据角色id获取未分配的用户列表信息
+
+ :param db: orm对象
+ :param query_object: 用户角色查询对象
+ :param data_scope_sql: 数据权限对应的查询sql语句
+ :param is_page: 是否开启分页
+ :return: 角色未分配的用户列表信息
+ """
+ query = (
+ select(SysUser)
+ .join(SysDept, SysDept.dept_id == SysUser.dept_id, isouter=True)
+ .join(SysUserRole, SysUserRole.user_id == SysUser.user_id, isouter=True)
+ .join(SysRole, SysRole.role_id == SysUserRole.role_id, isouter=True)
+ .where(
+ SysUser.del_flag == '0',
+ SysUser.user_name == query_object.user_name if query_object.user_name else True,
+ SysUser.phonenumber == query_object.phonenumber if query_object.phonenumber else True,
+ or_(SysRole.role_id != query_object.role_id, SysRole.role_id.is_(None)),
+ ~SysUser.user_id.in_(
+ select(SysUser.user_id)
+ .select_from(SysUser)
+ .join(
+ SysUserRole,
+ and_(SysUserRole.user_id == SysUser.user_id, SysUserRole.role_id == query_object.role_id),
+ )
+ ),
+ eval(data_scope_sql),
+ )
+ .distinct()
+ )
+ unallocated_user_list = await PageUtil.paginate(
+ db, query, query_object.page_num, query_object.page_size, is_page
+ )
+
+ return unallocated_user_list
+
+ @classmethod
+ async def add_user_role_dao(cls, db: AsyncSession, user_role: UserRoleModel):
+ """
+ 新增用户角色关联信息数据库操作
+
+ :param db: orm对象
+ :param user_role: 用户角色关联对象
+ :return:
+ """
+ db_user_role = SysUserRole(**user_role.model_dump())
+ db.add(db_user_role)
+
+ @classmethod
+ async def delete_user_role_dao(cls, db: AsyncSession, user_role: UserRoleModel):
+ """
+ 删除用户角色关联信息数据库操作
+
+ :param db: orm对象
+ :param user_role: 用户角色关联对象
+ :return:
+ """
+ await db.execute(delete(SysUserRole).where(SysUserRole.user_id.in_([user_role.user_id])))
+
+ @classmethod
+ async def delete_user_role_by_user_and_role_dao(cls, db: AsyncSession, user_role: UserRoleModel):
+ """
+ 根据用户id及角色id删除用户角色关联信息数据库操作
+
+ :param db: orm对象
+ :param user_role: 用户角色关联对象
+ :return:
+ """
+ await db.execute(
+ delete(SysUserRole).where(
+ SysUserRole.user_id == user_role.user_id if user_role.user_id else True,
+ SysUserRole.role_id == user_role.role_id if user_role.role_id else True,
+ )
+ )
+
+ @classmethod
+ async def get_user_role_detail(cls, db: AsyncSession, user_role: UserRoleModel):
+ """
+ 根据用户角色关联获取用户角色关联详细信息
+
+ :param db: orm对象
+ :param user_role: 用户角色关联对象
+ :return: 用户角色关联信息
+ """
+ user_role_info = (
+ (
+ await db.execute(
+ select(SysUserRole)
+ .where(SysUserRole.user_id == user_role.user_id, SysUserRole.role_id == user_role.role_id)
+ .distinct()
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return user_role_info
+
+ @classmethod
+ async def add_user_post_dao(cls, db: AsyncSession, user_post: UserPostModel):
+ """
+ 新增用户岗位关联信息数据库操作
+
+ :param db: orm对象
+ :param user_post: 用户岗位关联对象
+ :return:
+ """
+ db_user_post = SysUserPost(**user_post.model_dump())
+ db.add(db_user_post)
+
+ @classmethod
+ async def delete_user_post_dao(cls, db: AsyncSession, user_post: UserPostModel):
+ """
+ 删除用户岗位关联信息数据库操作
+
+ :param db: orm对象
+ :param user_post: 用户岗位关联对象
+ :return:
+ """
+ await db.execute(delete(SysUserPost).where(SysUserPost.user_id.in_([user_post.user_id])))
+
+ @classmethod
+ async def get_user_dept_info(cls, db: AsyncSession, dept_id: int):
+ dept_basic_info = (
+ (
+ await db.execute(
+ select(SysDept).where(SysDept.dept_id == dept_id, SysDept.status == '0', SysDept.del_flag == '0')
+ )
+ )
+ .scalars()
+ .first()
+ )
+ return dept_basic_info
diff --git a/ruoyi-fastapi-backend/module_admin/dao/visitor_type_dao.py b/ruoyi-fastapi-backend/module_admin/dao/visitor_type_dao.py
new file mode 100644
index 0000000..cdf2bd3
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/visitor_type_dao.py
@@ -0,0 +1,125 @@
+from sqlalchemy import delete, select, update
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.visitor_type_do import VisitorType
+from module_admin.entity.vo.visitor_type_vo import Visitor_typeModel, Visitor_typePageQueryModel
+from utils.page_util import PageUtil
+
+
+class Visitor_typeDao:
+ """
+ 访客类型模块数据库操作层
+ """
+
+ @classmethod
+ async def get_system_visitor_type_list(cls, db: AsyncSession):
+ stmt = (
+ select(VisitorType.type_id, VisitorType.name, VisitorType.value)
+ )
+ return (await db.execute(stmt)).mappings().all()
+
+ @classmethod
+ async def get_visitor_type_detail_by_id(cls, db: AsyncSession, type_id: int):
+ """
+ 根据访客类型ID获取访客类型详细信息
+
+ :param db: orm对象
+ :param type_id: 访客类型ID
+ :return: 访客类型信息对象
+ """
+ visitor_type_info = (
+ (
+ await db.execute(
+ select(VisitorType)
+ .where(
+ VisitorType.type_id == type_id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return visitor_type_info
+
+ @classmethod
+ async def get_visitor_type_detail_by_info(cls, db: AsyncSession, visitor_type: Visitor_typeModel):
+ """
+ 根据访客类型参数获取访客类型信息
+
+ :param db: orm对象
+ :param visitor_type: 访客类型参数对象
+ :return: 访客类型信息对象
+ """
+ visitor_type_info = (
+ (
+ await db.execute(
+ select(VisitorType).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return visitor_type_info
+
+ @classmethod
+ async def get_visitor_type_list(cls, db: AsyncSession, query_object: Visitor_typePageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取访客类型列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 访客类型列表信息对象
+ """
+ query = (
+ select(VisitorType)
+ .where(
+ VisitorType.name.like(f'%{query_object.name}%') if query_object.name else True,
+ VisitorType.value == query_object.value if query_object.value else True,
+ )
+ .order_by(VisitorType.type_id)
+ .distinct()
+ )
+ visitor_type_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return visitor_type_list
+
+ @classmethod
+ async def add_visitor_type_dao(cls, db: AsyncSession, visitor_type: Visitor_typeModel):
+ """
+ 新增访客类型数据库操作
+
+ :param db: orm对象
+ :param visitor_type: 访客类型对象
+ :return:
+ """
+ db_visitor_type = VisitorType(**visitor_type.model_dump(exclude={}))
+ db.add(db_visitor_type)
+ await db.flush()
+
+ return db_visitor_type
+
+ @classmethod
+ async def edit_visitor_type_dao(cls, db: AsyncSession, visitor_type: dict):
+ """
+ 编辑访客类型数据库操作
+
+ :param db: orm对象
+ :param visitor_type: 需要更新的访客类型字典
+ :return:
+ """
+ await db.execute(update(VisitorType), [visitor_type])
+
+ @classmethod
+ async def delete_visitor_type_dao(cls, db: AsyncSession, visitor_type: Visitor_typeModel):
+ """
+ 删除访客类型数据库操作
+
+ :param db: orm对象
+ :param visitor_type: 访客类型对象
+ :return:
+ """
+ await db.execute(delete(VisitorType).where(VisitorType.type_id.in_([visitor_type.type_id])))
+
diff --git a/ruoyi-fastapi-backend/module_admin/dao/words_dao.py b/ruoyi-fastapi-backend/module_admin/dao/words_dao.py
new file mode 100644
index 0000000..08a6de1
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/dao/words_dao.py
@@ -0,0 +1,161 @@
+from datetime import datetime, time
+from sqlalchemy import delete, select, update, desc
+from sqlalchemy.ext.asyncio import AsyncSession
+from module_admin.entity.do.words_do import GuideWords
+from module_admin.entity.vo.words_vo import WordsModel, WordsPageQueryModel
+from utils.page_util import PageUtil
+
+
+class WordsDao:
+ """
+ 引导词模块数据库操作层
+ """
+
+ @classmethod
+ async def get_words_by_type(cls, db: AsyncSession, type: str):
+ """
+ 获取随机引导词
+
+ :param db: orm对象
+ :param type: 引导词类型
+ :return: 引导词对象
+ """
+ guide_word = (
+ (
+ await db.execute(
+ select(GuideWords.text)
+ .where(
+ GuideWords.type == type,
+ GuideWords.del_flag == '0'
+ )
+ )
+ )
+ )
+ return guide_word.scalars().all()
+
+
+ @classmethod
+ async def edit_del_words(cls, db: AsyncSession, guide_ids: str, del_flag:str):
+
+ guide_id_list = guide_ids.strip(' ').split(',')
+
+ stmt = (
+ update(GuideWords)
+ .values(del_flag = del_flag)
+ .where(GuideWords.guide_id.in_(guide_id_list))
+ )
+
+ return await db.execute(stmt)
+
+ @classmethod
+ async def get_words_detail_by_id(cls, db: AsyncSession, guide_id: int):
+ """
+ 根据引导词id获取引导词详细信息
+
+ :param db: orm对象
+ :param guide_id: 引导词id
+ :return: 引导词信息对象
+ """
+ words_info = (
+ (
+ await db.execute(
+ select(GuideWords)
+ .where(
+ GuideWords.guide_id == guide_id
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return words_info
+
+ @classmethod
+ async def get_words_detail_by_info(cls, db: AsyncSession, words: WordsModel):
+ """
+ 根据引导词参数获取引导词信息
+
+ :param db: orm对象
+ :param words: 引导词参数对象
+ :return: 引导词信息对象
+ """
+ words_info = (
+ (
+ await db.execute(
+ select(GuideWords).where(
+ )
+ )
+ )
+ .scalars()
+ .first()
+ )
+
+ return words_info
+
+ @classmethod
+ async def get_words_list(cls, db: AsyncSession, query_object: WordsPageQueryModel, is_page: bool = False):
+ """
+ 根据查询参数获取引导词列表信息
+
+ :param db: orm对象
+ :param query_object: 查询参数对象
+ :param is_page: 是否开启分页
+ :return: 引导词列表信息对象
+ """
+ query = (
+ select(GuideWords)
+ .where(
+ GuideWords.type == query_object.type if query_object.type else True,
+ GuideWords.text.like(f'%{query_object.text}%') if query_object.text else True,
+ GuideWords.create_time.between(
+ datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
+ datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)),
+ )
+ if query_object.begin_time and query_object.end_time
+ else True,
+ )
+ .order_by(desc(GuideWords.create_time))
+ .distinct()
+ )
+ words_list = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page)
+
+ return words_list
+
+ @classmethod
+ async def add_words_dao(cls, db: AsyncSession, words: WordsModel):
+ """
+ 新增引导词数据库操作
+
+ :param db: orm对象
+ :param words: 引导词对象
+ :return:
+ """
+ db_words = GuideWords(**words.model_dump(exclude={}))
+ db.add(db_words)
+ await db.flush()
+
+ return db_words
+
+ @classmethod
+ async def edit_words_dao(cls, db: AsyncSession, words: dict):
+ """
+ 编辑引导词数据库操作
+
+ :param db: orm对象
+ :param words: 需要更新的引导词字典
+ :return:
+ """
+ await db.execute(update(GuideWords), [words])
+
+ @classmethod
+ async def delete_words_dao(cls, db: AsyncSession, words: WordsModel):
+ """
+ 删除引导词数据库操作
+
+ :param db: orm对象
+ :param words: 引导词对象
+ :return:
+ """
+ await db.execute(delete(GuideWords).where(GuideWords.guide_id.in_([words.guide_id])))
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/__init__.py b/ruoyi-fastapi-backend/module_admin/entity/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/__init__.py b/ruoyi-fastapi-backend/module_admin/entity/do/__init__.py
new file mode 100644
index 0000000..e69de29
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..012d2be
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/config_do.py
@@ -0,0 +1,22 @@
+from datetime import datetime
+from sqlalchemy import Column, DateTime, Integer, String
+from config.database import Base
+
+
+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=None, comment='备注')
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..44e2f02
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/dept_do.py
@@ -0,0 +1,26 @@
+from datetime import datetime
+from sqlalchemy import Column, DateTime, Integer, String
+from config.database import Base
+
+
+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..7a155ea
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/dict_do.py
@@ -0,0 +1,46 @@
+from datetime import datetime
+from sqlalchemy import Column, DateTime, Integer, String, UniqueConstraint
+from config.database import Base
+
+
+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=None, 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=None, comment='样式属性(其他样式扩展)')
+ list_class = Column(String(100), nullable=True, default=None, 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=None, comment='备注')
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/door_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/door_do.py
new file mode 100644
index 0000000..fe4d4fd
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/door_do.py
@@ -0,0 +1,24 @@
+from sqlalchemy import Column, BigInteger, String, DateTime
+from config.database import Base
+
+
+class Door(Base):
+ """
+ 门禁设备表
+ """
+
+ __tablename__ = 'door'
+
+ id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='主键自增')
+ indexCode = Column(String(64), nullable=True, comment='设备索引编码')
+ name = Column(String(32), nullable=True, comment='门禁名称')
+ install_location = Column(String(32), nullable=True, comment='位置名称')
+ status = Column(String(2), nullable=True, comment='设备状态, 0 离线, 1 正常在线')
+ permission = Column(String(2), nullable=True, comment='权限, 0 禁用, 1 正常调用')
+ create_time = Column(DateTime, nullable=True, comment='创建时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+ update_time = Column(DateTime, nullable=True, comment='更新时间')
+ update_by = Column(String(64), nullable=True, comment='更新者')
+
+
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/explanation_content_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/explanation_content_do.py
new file mode 100644
index 0000000..6eb29dc
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/explanation_content_do.py
@@ -0,0 +1,25 @@
+from sqlalchemy import Column, Text, DateTime, String, BigInteger, Integer
+from config.database import Base
+
+
+class ExplanationContent(Base):
+ """
+ 讲解内容表
+ """
+
+ __tablename__ = 'explanation_content'
+
+ explanation_content_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='主键ID')
+ title = Column(String(64), nullable=False, comment='标题')
+ content = Column(Text, nullable=True, comment='讲解内容')
+ type = Column(String(2), nullable=True, comment='类型(0科技类)')
+ spend_time = Column(Integer, nullable=True, comment='预计时长(分钟)')
+ language = Column(String(2), nullable=True, comment='语言(0中文,1英文)')
+ status = Column(String(2), nullable=True, comment='状态(0未开始,1已就绪,2播放中,3已完成)')
+ create_time = Column(DateTime, nullable=True, comment='创建时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+ update_time = Column(DateTime, nullable=True, comment='修改时间')
+ update_by = Column(String(64), nullable=True, comment='修改者')
+
+
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/explanation_content_type_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/explanation_content_type_do.py
new file mode 100644
index 0000000..49fe731
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/explanation_content_type_do.py
@@ -0,0 +1,21 @@
+from sqlalchemy import BigInteger, DateTime, String, Column
+from config.database import Base
+
+
+class ExplanationContentType(Base):
+ """
+ 讲解内容类型表
+ """
+
+ __tablename__ = 'explanation_content_type'
+
+ content_type_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='主键ID')
+ name = Column(String(32), nullable=True, comment='类型名称')
+ type_value = Column(String(2), nullable=True, comment='类型值(与讲解内容表中的type对应)')
+ create_time = Column(DateTime, nullable=True, comment='创建时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+ update_time = Column(DateTime, nullable=True, comment='修改时间')
+ update_by = Column(String(64), nullable=True, comment='修改者')
+
+
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/explanation_style_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/explanation_style_do.py
new file mode 100644
index 0000000..df7a137
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/explanation_style_do.py
@@ -0,0 +1,21 @@
+from sqlalchemy import BigInteger, DateTime, String, Column
+from config.database import Base
+
+
+class ExplanationStyle(Base):
+ """
+ 讲解风格表
+ """
+
+ __tablename__ = 'explanation_style'
+
+ explanation_style_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='主键ID')
+ name = Column(String(32), nullable=True, comment='风格名称')
+ detail = Column(String(64), nullable=True, comment='风格简介')
+ create_time = Column(DateTime, nullable=True, comment='创建时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+ update_time = Column(DateTime, nullable=True, comment='修改时间')
+ update_by = Column(String(64), nullable=True, comment='修改者')
+
+
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/explanation_style_robot_pair_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/explanation_style_robot_pair_do.py
new file mode 100644
index 0000000..3ee4b24
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/explanation_style_robot_pair_do.py
@@ -0,0 +1,24 @@
+from sqlalchemy import BigInteger, DateTime, String, Column
+from config.database import Base
+
+
+class ExplanationStyleRobotPairing(Base):
+ """
+ 讲解风格--机器人配对表
+ """
+
+ __tablename__ = 'explanation_style_robot_pairing'
+
+ pairing_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='主键ID')
+ robot_id = Column(BigInteger, nullable=False, comment='机器人ID(关联robot_info表)')
+ robot_role_id = Column(BigInteger, nullable=False, comment='机器人角色ID(关联robot_role表)')
+ explanation_style_id = Column(BigInteger, nullable=False, comment='讲解风格ID(关联explanation_style表)')
+ prompt = Column(String(128), nullable=True, comment='提示词')
+ status = Column(String(2), nullable=True, comment='状态(0停用,1使用)')
+ create_time = Column(DateTime, nullable=True, comment='创建时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+ update_time = Column(DateTime, nullable=True, comment='修改时间')
+ update_by = Column(String(64), nullable=True, comment='修改者')
+
+
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/identification_record_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/identification_record_do.py
new file mode 100644
index 0000000..7145067
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/identification_record_do.py
@@ -0,0 +1,24 @@
+from sqlalchemy import DateTime, String, BigInteger, Column
+from config.database import Base
+
+
+class IdentificationRecord(Base):
+ """
+ 识别记录表
+ """
+
+ __tablename__ = 'identification_record'
+
+ id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='主键 自增')
+ person_name = Column(String(20), nullable=True, comment='识别人姓名')
+ door_name = Column(String(20), nullable=True, comment='门禁点')
+ status = Column(String(2), nullable=True, comment='识别结果, 0 拒绝, 1 通过')
+ source = Column(String(20), nullable=True, comment='识别来源')
+ pic_uri = Column(String(256), nullable=True, comment='图像url')
+ create_time = Column(DateTime, nullable=True, comment='创建时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+ conf = Column(BigInteger, default=0, comment='识别置信度')
+ person_status = Column(String(2), nullable=True, comment=' 0 陌生人, 1 员工, 2 访客')
+
+
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/identification_statistics_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/identification_statistics_do.py
new file mode 100644
index 0000000..d43c5a9
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/identification_statistics_do.py
@@ -0,0 +1,20 @@
+from sqlalchemy import String, Column, BigInteger, DateTime
+from config.database import Base
+
+
+class IdentificationStatistics(Base):
+ """
+ 识别统计表
+ """
+
+ __tablename__ = 'identification_statistics'
+
+ id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='主键 自增')
+ employ = Column(BigInteger, nullable=True, comment='员工识别成功数')
+ visitor = Column(BigInteger, nullable=True, comment='访客识别成功数')
+ stranger = Column(BigInteger, nullable=True, comment='位置人员识别失败数')
+ create_time = Column(DateTime, nullable=True, comment='创建时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+
+
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/info_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/info_do.py
new file mode 100644
index 0000000..2eeed5f
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/info_do.py
@@ -0,0 +1,33 @@
+from sqlalchemy import DateTime, String, BigInteger, CHAR, Column
+from config.database import Base
+
+
+class RobotInfo(Base):
+ """
+ 机器人信息表
+ """
+
+ __tablename__ = 'robot_info'
+
+ robot_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='机器人ID')
+ name = Column(String(32), nullable=False, comment='机器人名称')
+ model = Column(String(32), nullable=True, comment='机器人型号')
+ serial = Column(String(32), nullable=True, comment='序列号')
+ online = Column(String(2), nullable=True, comment='机器人在线状态(0在线, 1离线)')
+ power = Column(String(3), nullable=True, comment='电量')
+ run_time = Column(BigInteger, nullable=True, comment='总运行时长(单位:分钟)')
+ position = Column(String(32), nullable=True, comment='位置')
+ image = Column(String(512), nullable=True, comment='机器人外观图地址')
+ create_time = Column(DateTime, nullable=True, comment='创建时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+ update_time = Column(DateTime, nullable=True, comment='修改时间')
+ update_by = Column(String(64), nullable=True, comment='修改者')
+ del_flag = Column(CHAR(1), nullable=False, comment='删除标志(0存在, 2删除)')
+ remark = Column(String(512), nullable=True, comment='备注')
+ last_charge = Column(DateTime, nullable=True, comment='上一次充电时间')
+ ip = Column(String(16), nullable=True, comment='机器人ip')
+ status = Column(String(2), nullable=False, comment='使用情况(0待机,1正在运行)')
+ video_uri = Column(String(512), nullable=True, comment='视频uri')
+
+
+
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..b2cbfb8
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/job_do.py
@@ -0,0 +1,54 @@
+from datetime import datetime
+from sqlalchemy import Column, DateTime, Integer, String
+from config.database import Base
+
+
+class SysJob(Base):
+ """
+ 定时任务调度表
+ """
+
+ __tablename__ = 'sys_job'
+
+ job_id = Column(Integer, primary_key=True, autoincrement=True, comment='任务ID')
+ job_name = Column(String(64), nullable=True, default='', comment='任务名称')
+ job_group = Column(String(64), nullable=True, default='default', comment='任务组名')
+ job_executor = Column(String(64), nullable=True, default='default', comment='任务执行器')
+ invoke_target = Column(String(500), nullable=False, comment='调用目标字符串')
+ job_args = Column(String(255), nullable=True, default='', comment='位置参数')
+ job_kwargs = Column(String(255), nullable=True, default='', comment='关键字参数')
+ cron_expression = Column(String(255), nullable=True, default='', comment='cron执行表达式')
+ misfire_policy = Column(
+ String(20),
+ nullable=True,
+ default='3',
+ comment='计划执行错误策略(1立即执行 2执行一次 3放弃执行)',
+ )
+ concurrent = Column(String(1), nullable=True, default='1', comment='是否并发执行(0允许 1禁止)')
+ 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='备注信息')
+
+
+class SysJobLog(Base):
+ """
+ 定时任务调度日志表
+ """
+
+ __tablename__ = 'sys_job_log'
+
+ job_log_id = Column(Integer, primary_key=True, autoincrement=True, comment='任务日志ID')
+ job_name = Column(String(64), nullable=False, comment='任务名称')
+ job_group = Column(String(64), nullable=False, comment='任务组名')
+ job_executor = Column(String(64), nullable=False, comment='任务执行器')
+ invoke_target = Column(String(500), nullable=False, comment='调用目标字符串')
+ job_args = Column(String(255), nullable=True, default='', comment='位置参数')
+ job_kwargs = Column(String(255), nullable=True, default='', comment='关键字参数')
+ job_trigger = Column(String(255), nullable=True, default='', comment='任务触发器')
+ job_message = Column(String(500), nullable=True, default='', comment='日志信息')
+ status = Column(String(1), nullable=True, default='0', comment='执行状态(0正常 1失败)')
+ exception_info = Column(String(2000), 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..f9e14ab
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/log_do.py
@@ -0,0 +1,54 @@
+from datetime import datetime
+from sqlalchemy import BigInteger, Column, DateTime, Index, Integer, String
+from config.database import Base
+
+
+class SysLogininfor(Base):
+ """
+ 系统访问记录
+ """
+
+ __tablename__ = 'sys_logininfor'
+
+ info_id = Column(Integer, primary_key=True, autoincrement=True, comment='访问ID')
+ user_name = Column(String(50), nullable=True, default='', comment='用户账号')
+ ipaddr = Column(String(128), nullable=True, default='', comment='登录IP地址')
+ login_location = Column(String(255), nullable=True, default='', comment='登录地点')
+ browser = Column(String(50), nullable=True, default='', comment='浏览器类型')
+ os = Column(String(50), nullable=True, default='', comment='操作系统')
+ status = Column(String(1), nullable=True, default='0', comment='登录状态(0成功 1失败)')
+ msg = Column(String(255), 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), nullable=True, default='', comment='模块标题')
+ business_type = Column(Integer, default=0, comment='业务类型(0其它 1新增 2修改 3删除)')
+ method = Column(String(100), nullable=True, default='', comment='方法名称')
+ request_method = Column(String(10), nullable=True, default='', comment='请求方式')
+ operator_type = Column(Integer, default=0, comment='操作类别(0其它 1后台用户 2手机端用户)')
+ oper_name = Column(String(50), nullable=True, default='', comment='操作人员')
+ dept_name = Column(String(50), nullable=True, default='', comment='部门名称')
+ oper_url = Column(String(255), nullable=True, default='', comment='请求URL')
+ oper_ip = Column(String(128), nullable=True, default='', comment='主机地址')
+ oper_location = Column(String(255), nullable=True, default='', comment='操作地点')
+ oper_param = Column(String(2000), nullable=True, default='', comment='请求参数')
+ json_result = Column(String(2000), nullable=True, default='', comment='返回参数')
+ status = Column(Integer, default=0, comment='操作状态(0正常 1异常)')
+ error_msg = Column(String(2000), 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..d3ccffa
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/menu_do.py
@@ -0,0 +1,32 @@
+from datetime import datetime
+from sqlalchemy import Column, DateTime, Integer, String
+from config.database import Base
+
+
+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='路由参数')
+ route_name = Column(String(50), nullable=True, default='', 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/message_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/message_do.py
new file mode 100644
index 0000000..a423f60
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/message_do.py
@@ -0,0 +1,24 @@
+from sqlalchemy import DateTime, String, BigInteger, Column
+from config.database import Base
+
+
+class Message(Base):
+ """
+ 系统消息表
+ """
+
+ __tablename__ = 'message'
+
+ message_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='消息主键')
+ type = Column(String(2), nullable=False, comment='类型(0告警消息)')
+ status = Column(String(2), nullable=False, comment='状态(0未处理,1已处理)')
+ detail = Column(String(512), nullable=True, comment='消息描述')
+ create_time = Column(DateTime, nullable=False, comment='创建时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+ update_time = Column(DateTime, nullable=False, comment='更新时间')
+ update_by = Column(String(64), nullable=False, comment='更新者')
+ remark = Column(String(512), nullable=True, comment='备注')
+ specific_type = Column(String(2), nullable=True, comment='具体消息类型(0 设备故障, 1 系统异常, 2安全告警, 3 网络异常)')
+
+
+
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..9d1eb98
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/notice_do.py
@@ -0,0 +1,22 @@
+from datetime import datetime
+from sqlalchemy import Column, DateTime, Integer, LargeBinary, String
+from config.database import Base
+
+
+class SysNotice(Base):
+ """
+ 通知公告表
+ """
+
+ __tablename__ = 'sys_notice'
+
+ notice_id = Column(Integer, primary_key=True, autoincrement=True, comment='公告ID')
+ notice_title = Column(String(50), nullable=False, comment='公告标题')
+ notice_type = Column(String(1), nullable=False, comment='公告类型(1通知 2公告)')
+ notice_content = Column(LargeBinary, comment='公告内容')
+ status = Column(String(1), default='0', comment='公告状态(0正常 1关闭)')
+ create_by = Column(String(64), default='', comment='创建者')
+ create_time = Column(DateTime, comment='创建时间', default=datetime.now())
+ update_by = Column(String(64), default='', comment='更新者')
+ update_time = Column(DateTime, comment='更新时间', default=datetime.now())
+ remark = Column(String(255), 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..f231f72
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/post_do.py
@@ -0,0 +1,22 @@
+from datetime import datetime
+from sqlalchemy import Column, DateTime, Integer, String
+from config.database import Base
+
+
+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=None, comment='备注')
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/robot_action_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/robot_action_do.py
new file mode 100644
index 0000000..e4acfdd
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/robot_action_do.py
@@ -0,0 +1,21 @@
+from sqlalchemy import Integer, String, BigInteger, Column, DateTime
+from config.database import Base
+
+
+class RobotAction(Base):
+ """
+ 机器人动作表
+ """
+
+ __tablename__ = 'robot_action'
+
+ id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='主键 自增')
+ name = Column(String(50), nullable=True, comment='动作名')
+ description = Column(String(50), nullable=True, comment='描述')
+ interval_min = Column(Integer, nullable=True, comment='最小时间间隔 单位s')
+ interval_max = Column(Integer, nullable=True, comment='最大时间间隔 单位s')
+ status = Column(String(2), nullable=True, comment='动作状态 0 未启用, 1已启用')
+ update_time = Column(DateTime, nullable=True, comment='修改时间')
+
+
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/robot_role_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/robot_role_do.py
new file mode 100644
index 0000000..eac1af0
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/robot_role_do.py
@@ -0,0 +1,22 @@
+from sqlalchemy import BigInteger, String, Column, DateTime
+from config.database import Base
+
+
+class RobotRole(Base):
+ """
+ 系统角色表
+ """
+
+ __tablename__ = 'robot_role'
+
+ robot_role_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='机器人角色ID')
+ rolename = Column(String(32), nullable=False, comment='角色名称')
+ detail = Column(String(512), nullable=True, comment='角色介绍')
+ create_time = Column(DateTime, nullable=False, comment='创建时间')
+ create_by = Column(String(64), nullable=False, comment='创建者')
+ update_time = Column(DateTime, nullable=False, comment='修改时间')
+ update_by = Column(String(64), nullable=False, comment='修改者')
+ remark = Column(String(512), nullable=True, comment='备注')
+
+
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/robot_role_pairing_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/robot_role_pairing_do.py
new file mode 100644
index 0000000..ee56a05
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/robot_role_pairing_do.py
@@ -0,0 +1,32 @@
+from sqlalchemy import DateTime, Column, BigInteger, String
+from config.database import Base
+
+
+class RobotRolePairing(Base):
+ """
+ 角色-机器人-映射表
+ """
+
+ __tablename__ = 'robot_role_pairing'
+
+ pairing_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='主键ID')
+ robot_role_id = Column(BigInteger, nullable=False, comment='机器人角色ID')
+ robot_id = Column(BigInteger, nullable=False, comment='机器人ID')
+ status = Column(String(2), nullable=False, comment='使用情况(0停用,1使用)')
+ update_time = Column(DateTime, nullable=False, comment='更新时间')
+ update_by = Column(String(64), nullable=False, comment='修改者')
+
+
+#
+# class RolePairing(Base):
+# """
+# 角色-机器人-映射表对应pydantic模型, 查询角色-机器人结果的返回模型
+# """
+#
+#
+# pairing_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='主键ID')
+# rolename = Column(String(32), nullable=False, comment='角色名称')
+# detail = Column(String(512), nullable=True, comment='角色介绍')
+# status = Column(String(2), nullable=False, comment='使用情况(0停用,1使用)')
+
+
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..58d4de1
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/role_do.py
@@ -0,0 +1,52 @@
+from datetime import datetime
+from sqlalchemy import Column, DateTime, Integer, String
+from config.database import Base
+
+
+class SysRole(Base):
+ """
+ 角色信息表
+ """
+
+ __tablename__ = 'sys_role'
+
+ role_id = Column(Integer, primary_key=True, autoincrement=True, comment='角色ID')
+ role_name = Column(String(30), nullable=False, comment='角色名称')
+ role_key = Column(String(100), nullable=False, comment='角色权限字符串')
+ role_sort = Column(Integer, nullable=False, comment='显示顺序')
+ data_scope = Column(
+ String(1),
+ 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), nullable=False, default='0', comment='角色状态(0正常 1停用)')
+ del_flag = Column(String(1), default='0', comment='删除标志(0代表存在 2代表删除)')
+ create_by = Column(String(64), default='', comment='创建者')
+ create_time = Column(DateTime, default=datetime.now(), comment='创建时间')
+ update_by = Column(String(64), default='', comment='更新者')
+ update_time = Column(DateTime, default=datetime.now(), comment='更新时间')
+ remark = Column(String(500), default=None, 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/sys_alert_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/sys_alert_do.py
new file mode 100644
index 0000000..425942a
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/sys_alert_do.py
@@ -0,0 +1,22 @@
+from sqlalchemy import String, Column, BigInteger, DateTime
+from config.database import Base
+
+
+class SysAlert(Base):
+ """
+ 系统告警表
+ """
+
+ __tablename__ = 'sys_alert'
+
+ id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='主键 自增')
+ detail = Column(String(256), nullable=True, comment='告警描述')
+ type = Column(String(2), nullable=True, comment='告警类型')
+ status = Column(String(2), nullable=True, comment='告警状态, 0 未处理, 1 已处理')
+ create_time = Column(DateTime, nullable=True, comment='创建时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+ update_time = Column(DateTime, nullable=True, comment='更新时间')
+ update_by = Column(String(64), nullable=True, comment='更新者')
+
+
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/sys_statistics_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/sys_statistics_do.py
new file mode 100644
index 0000000..38d0918
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/sys_statistics_do.py
@@ -0,0 +1,25 @@
+from sqlalchemy import Date, DateTime, String, BigInteger, Column, Integer
+from config.database import Base
+
+
+class SysStatistics(Base):
+ """
+ 系统统计数据表
+ """
+
+ __tablename__ = 'sys_statistics'
+
+ statistic_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='统计项目ID')
+ stat_date = Column(Date, nullable=False, comment='统计日期')
+ llm_call = Column(BigInteger, nullable=True, comment='大模型调用量')
+ access_control_count = Column(BigInteger, nullable=True, comment='门禁通行量')
+ visitor_guide = Column(BigInteger, nullable=True, comment='访客引导次数')
+ exhibition_explanation = Column(BigInteger, nullable=True, comment='展厅讲解次数')
+ access_control_success_rate = Column(Integer, nullable=True, comment='门禁识别成功率(%)')
+ create_time = Column(DateTime, nullable=True, comment='创建时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+ update_time = Column(DateTime, nullable=False, comment='修改时间')
+ update_by = Column(String(64), nullable=True, comment='修改者')
+
+
+
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..2dd0ba0
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/user_do.py
@@ -0,0 +1,53 @@
+from datetime import datetime
+from sqlalchemy import Column, DateTime, Integer, String
+from config.database import Base
+
+
+class SysUser(Base):
+ """
+ 用户信息表
+ """
+
+ __tablename__ = 'sys_user'
+
+ user_id = Column(Integer, primary_key=True, autoincrement=True, comment='用户ID')
+ dept_id = Column(Integer, default=None, comment='部门ID')
+ user_name = Column(String(30), nullable=False, comment='用户账号')
+ nick_name = Column(String(30), nullable=False, comment='用户昵称')
+ user_type = Column(String(2), default='00', comment='用户类型(00系统用户)')
+ email = Column(String(50), default='', comment='用户邮箱')
+ phonenumber = Column(String(11), default='', comment='手机号码')
+ sex = Column(String(1), default='0', comment='用户性别(0男 1女 2未知)')
+ avatar = Column(String(100), default='', comment='头像地址')
+ password = Column(String(100), default='', comment='密码')
+ status = Column(String(1), default='0', comment='帐号状态(0正常 1停用)')
+ del_flag = Column(String(1), default='0', comment='删除标志(0代表存在 2代表删除)')
+ login_ip = Column(String(128), default='', comment='最后登录IP')
+ login_date = Column(DateTime, comment='最后登录时间')
+ create_by = Column(String(64), default='', comment='创建者')
+ create_time = Column(DateTime, comment='创建时间', default=datetime.now())
+ update_by = Column(String(64), default='', comment='更新者')
+ update_time = Column(DateTime, comment='更新时间', default=datetime.now())
+ remark = Column(String(500), default=None, 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/do/visitor_type_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/visitor_type_do.py
new file mode 100644
index 0000000..8f698af
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/visitor_type_do.py
@@ -0,0 +1,21 @@
+from sqlalchemy import BigInteger, DateTime, String, Column
+from config.database import Base
+
+
+class VisitorType(Base):
+ """
+ 访客类型表
+ """
+
+ __tablename__ = 'visitor_type'
+
+ type_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='访客类型ID')
+ name = Column(String(64), nullable=True, comment='类型名称')
+ value = Column(String(2), nullable=False, comment='类型值')
+ create_time = Column(DateTime, nullable=True, comment='创建时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+ update_time = Column(DateTime, nullable=True, comment='修改时间')
+ update_by = Column(String(64), nullable=True, comment='修改者')
+
+
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/do/words_do.py b/ruoyi-fastapi-backend/module_admin/entity/do/words_do.py
new file mode 100644
index 0000000..d253fee
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/do/words_do.py
@@ -0,0 +1,26 @@
+from sqlalchemy import CHAR, Column, DateTime, String, BigInteger
+from config.database import Base
+
+
+class GuideWords(Base):
+ """
+ 引导词表
+ """
+
+ __tablename__ = 'guide_words'
+
+ guide_id = Column(BigInteger, primary_key=True, autoincrement=True, nullable=False, comment='引导词id')
+ type = Column(String(2), nullable=True, comment='引导词类型')
+ text = Column(String(1024), nullable=True, comment='文本内容')
+ create_time = Column(DateTime, nullable=True, comment='创建时间')
+ update_time = Column(DateTime, nullable=True, comment='修改时间')
+ create_by = Column(String(64), nullable=True, comment='创建者')
+ remark = Column(String(500), nullable=True, comment='备注')
+ del_flag = Column(CHAR(1), nullable=False, comment='删除标志(0代表存在 2代表删除)')
+ update_by = Column(String(64), nullable=True, comment='修改者')
+ title = Column(String(64), nullable=True, comment='引导词名称')
+ label = Column(String(2), nullable=True, comment='引导词标签')
+ trigger_type = Column(String(2), nullable=True, comment='触发方式')
+
+
+
diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/__init__.py b/ruoyi-fastapi-backend/module_admin/entity/vo/__init__.py
new file mode 100644
index 0000000..e69de29
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..79b49fb
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/cache_vo.py
@@ -0,0 +1,28 @@
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from typing import Any, List, Optional
+
+
+class CacheMonitorModel(BaseModel):
+ """
+ 缓存监控信息对应pydantic模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ command_stats: Optional[List] = Field(default=[], description='命令统计')
+ db_size: Optional[int] = Field(default=None, description='Key数量')
+ info: Optional[dict] = Field(default={}, description='Redis信息')
+
+
+class CacheInfoModel(BaseModel):
+ """
+ 缓存监控对象对应pydantic模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ cache_key: Optional[str] = Field(default=None, description='缓存键名')
+ cache_name: Optional[str] = Field(default=None, description='缓存名称')
+ cache_value: Optional[Any] = Field(default=None, description='缓存内容')
+ remark: Optional[str] = Field(default=None, description='备注')
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..258be5d
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/common_vo.py
@@ -0,0 +1,26 @@
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from typing import Any, Optional
+
+
+class CrudResponseModel(BaseModel):
+ """
+ 操作响应模型
+ """
+
+ is_success: bool = Field(description='操作是否成功')
+ message: str = Field(description='响应信息')
+ result: Optional[Any] = Field(default=None, description='响应结果')
+
+
+class UploadResponseModel(BaseModel):
+ """
+ 上传响应模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ file_name: Optional[str] = Field(default=None, description='新文件映射路径')
+ new_file_name: Optional[str] = Field(default=None, description='新文件名称')
+ original_filename: Optional[str] = Field(default=None, description='原文件名称')
+ url: Optional[str] = Field(default=None, description='新文件url')
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..917d7d6
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/config_vo.py
@@ -0,0 +1,74 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from pydantic_validation_decorator import NotBlank, Size
+from typing import Literal, Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+class ConfigModel(BaseModel):
+ """
+ 参数配置表对应pydantic模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ config_id: Optional[int] = Field(default=None, description='参数主键')
+ config_name: Optional[str] = Field(default=None, description='参数名称')
+ config_key: Optional[str] = Field(default=None, description='参数键名')
+ config_value: Optional[str] = Field(default=None, description='参数键值')
+ config_type: Optional[Literal['Y', 'N']] = Field(default=None, description='系统内置(Y是 N否)')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ update_by: Optional[str] = Field(default=None, description='更新者')
+ update_time: Optional[datetime] = Field(default=None, description='更新时间')
+ remark: Optional[str] = Field(default=None, description='备注')
+
+ @NotBlank(field_name='config_key', message='参数名称不能为空')
+ @Size(field_name='config_key', min_length=0, max_length=100, message='参数名称长度不能超过100个字符')
+ def get_config_key(self):
+ return self.config_key
+
+ @NotBlank(field_name='config_name', message='参数键名不能为空')
+ @Size(field_name='config_name', min_length=0, max_length=100, message='参数键名长度不能超过100个字符')
+ def get_config_name(self):
+ return self.config_name
+
+ @NotBlank(field_name='config_value', message='参数键值不能为空')
+ @Size(field_name='config_value', min_length=0, max_length=500, message='参数键值长度不能超过500个字符')
+ def get_config_value(self):
+ return self.config_value
+
+ def validate_fields(self):
+ self.get_config_key()
+ self.get_config_name()
+ self.get_config_value()
+
+
+class ConfigQueryModel(ConfigModel):
+ """
+ 参数配置管理不分页查询模型
+ """
+
+ begin_time: Optional[str] = Field(default=None, description='开始时间')
+ end_time: Optional[str] = Field(default=None, description='结束时间')
+
+
+@as_query
+class ConfigPageQueryModel(ConfigQueryModel):
+ """
+ 参数配置管理分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteConfigModel(BaseModel):
+ """
+ 删除参数配置模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ config_ids: str = Field(description='需要删除的参数主键')
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..dcad117
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/dept_vo.py
@@ -0,0 +1,75 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from pydantic_validation_decorator import Network, NotBlank, Size
+from typing import Literal, Optional
+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] = Field(default=None, description='部门id')
+ parent_id: Optional[int] = Field(default=None, description='父部门id')
+ ancestors: Optional[str] = Field(default=None, description='祖级列表')
+ dept_name: Optional[str] = Field(default=None, description='部门名称')
+ order_num: Optional[int] = Field(default=None, description='显示顺序')
+ leader: Optional[str] = Field(default=None, description='负责人')
+ phone: Optional[str] = Field(default=None, description='联系电话')
+ email: Optional[str] = Field(default=None, description='邮箱')
+ status: Optional[Literal['0', '1']] = Field(default=None, description='部门状态(0正常 1停用)')
+ del_flag: Optional[Literal['0', '2']] = Field(default=None, description='删除标志(0代表存在 2代表删除)')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ update_by: Optional[str] = Field(default=None, description='更新者')
+ update_time: Optional[datetime] = Field(default=None, description='更新时间')
+
+ @NotBlank(field_name='dept_name', message='部门名称不能为空')
+ @Size(field_name='dept_name', min_length=0, max_length=30, message='部门名称长度不能超过30个字符')
+ def get_dept_name(self):
+ return self.dept_name
+
+ @NotBlank(field_name='order_num', message='显示顺序不能为空')
+ def get_order_num(self):
+ return self.order_num
+
+ @Size(field_name='phone', min_length=0, max_length=11, message='联系电话长度不能超过11个字符')
+ def get_phone(self):
+ return self.phone
+
+ @Network(field_name='email', field_type='EmailStr', message='邮箱格式不正确')
+ @Size(field_name='email', min_length=0, max_length=50, message='邮箱长度不能超过50个字符')
+ def get_email(self):
+ return self.email
+
+ def validate_fields(self):
+ self.get_dept_name()
+ self.get_order_num()
+ self.get_phone()
+ self.get_email()
+
+
+@as_query
+class DeptQueryModel(DeptModel):
+ """
+ 部门管理不分页查询模型
+ """
+
+ begin_time: Optional[str] = Field(default=None, description='开始时间')
+ end_time: Optional[str] = Field(default=None, description='结束时间')
+
+
+class DeleteDeptModel(BaseModel):
+ """
+ 删除部门模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ dept_ids: str = Field(default=None, description='需要删除的部门id')
+ update_by: Optional[str] = Field(default=None, description='更新者')
+ update_time: Optional[str] = Field(default=None, description='更新时间')
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..3251f08
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/dict_vo.py
@@ -0,0 +1,149 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from pydantic_validation_decorator import NotBlank, Pattern, Size
+from typing import Literal, Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+class DictTypeModel(BaseModel):
+ """
+ 字典类型表对应pydantic模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ dict_id: Optional[int] = Field(default=None, description='字典主键')
+ dict_name: Optional[str] = Field(default=None, description='字典名称')
+ dict_type: Optional[str] = Field(default=None, description='字典类型')
+ status: Optional[Literal['0', '1']] = Field(default=None, description='状态(0正常 1停用)')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ update_by: Optional[str] = Field(default=None, description='更新者')
+ update_time: Optional[datetime] = Field(default=None, description='更新时间')
+ remark: Optional[str] = Field(default=None, description='备注')
+
+ @NotBlank(field_name='dict_name', message='字典名称不能为空')
+ @Size(field_name='dict_name', min_length=0, max_length=100, message='字典类型名称长度不能超过100个字符')
+ def get_dict_name(self):
+ return self.dict_name
+
+ @NotBlank(field_name='dict_type', message='字典类型不能为空')
+ @Size(field_name='dict_type', min_length=0, max_length=100, message='字典类型类型长度不能超过100个字符')
+ @Pattern(
+ field_name='dict_type',
+ regexp='^[a-z][a-z0-9_]*$',
+ message='字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)',
+ )
+ def get_dict_type(self):
+ return self.dict_type
+
+ def validate_fields(self):
+ self.get_dict_name()
+ self.get_dict_type()
+
+
+class DictDataModel(BaseModel):
+ """
+ 字典数据表对应pydantic模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ dict_code: Optional[int] = Field(default=None, description='字典编码')
+ dict_sort: Optional[int] = Field(default=None, description='字典排序')
+ dict_label: Optional[str] = Field(default=None, description='字典标签')
+ dict_value: Optional[str] = Field(default=None, description='字典键值')
+ dict_type: Optional[str] = Field(default=None, description='字典类型')
+ css_class: Optional[str] = Field(default=None, description='样式属性(其他样式扩展)')
+ list_class: Optional[str] = Field(default=None, description='表格回显样式')
+ is_default: Optional[Literal['Y', 'N']] = Field(default=None, description='是否默认(Y是 N否)')
+ status: Optional[Literal['0', '1']] = Field(default=None, description='状态(0正常 1停用)')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ update_by: Optional[str] = Field(default=None, description='更新者')
+ update_time: Optional[datetime] = Field(default=None, description='更新时间')
+ remark: Optional[str] = Field(default=None, description='备注')
+
+ @NotBlank(field_name='dict_label', message='字典标签不能为空')
+ @Size(field_name='dict_label', min_length=0, max_length=100, message='字典标签长度不能超过100个字符')
+ def get_dict_label(self):
+ return self.dict_label
+
+ @NotBlank(field_name='dict_value', message='字典键值不能为空')
+ @Size(field_name='dict_value', min_length=0, max_length=100, message='字典键值长度不能超过100个字符')
+ def get_dict_value(self):
+ return self.dict_value
+
+ @NotBlank(field_name='dict_type', message='字典类型不能为空')
+ @Size(field_name='dict_type', min_length=0, max_length=100, message='字典类型长度不能超过100个字符')
+ def get_dict_type(self):
+ return self.dict_type
+
+ @Size(field_name='css_class', min_length=0, max_length=100, message='样式属性长度不能超过100个字符')
+ def get_css_class(self):
+ return self.css_class
+
+ def validate_fields(self):
+ self.get_dict_label()
+ self.get_dict_value()
+ self.get_dict_type()
+ self.get_css_class()
+
+
+class DictTypeQueryModel(DictTypeModel):
+ """
+ 字典类型管理不分页查询模型
+ """
+
+ begin_time: Optional[str] = Field(default=None, description='开始时间')
+ end_time: Optional[str] = Field(default=None, description='结束时间')
+
+
+@as_query
+class DictTypePageQueryModel(DictTypeQueryModel):
+ """
+ 字典类型管理分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteDictTypeModel(BaseModel):
+ """
+ 删除字典类型模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ dict_ids: str = Field(description='需要删除的字典主键')
+
+
+class DictDataQueryModel(DictDataModel):
+ """
+ 字典数据管理不分页查询模型
+ """
+
+ begin_time: Optional[str] = Field(default=None, description='开始时间')
+ end_time: Optional[str] = Field(default=None, description='结束时间')
+
+
+@as_query
+class DictDataPageQueryModel(DictDataQueryModel):
+ """
+ 字典数据管理分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteDictDataModel(BaseModel):
+ """
+ 删除字典数据模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ dict_codes: str = Field(description='需要删除的字典编码')
diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/door_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/door_vo.py
new file mode 100644
index 0000000..7c3bd09
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/door_vo.py
@@ -0,0 +1,57 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from typing import Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+
+
+class DoorModel(BaseModel):
+ """
+ 门禁设备表对应pydantic模型
+ """
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ id: Optional[int] = Field(default=None, description='主键自增')
+ indexCode: Optional[str] = Field(default=None, description='设备索引编码')
+ name: Optional[str] = Field(default=None, description='门禁名称')
+ install_location: Optional[str] = Field(default=None, description='位置名称')
+ status: Optional[str] = Field(default=None, description='设备状态, 0 离线, 1 正常在线')
+ permission: Optional[str] = Field(default=None, description='权限, 0 禁用, 1 正常调用')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+ update_time: Optional[datetime] = Field(default=None, description='更新时间')
+ update_by: Optional[str] = Field(default=None, description='更新者')
+
+
+
+
+
+
+class DoorQueryModel(DoorModel):
+ """
+ 门禁设备不分页查询模型
+ """
+ key_word : Optional[str] = Field(default=None, description='模糊查询字段')
+
+
+
+@as_query
+class DoorPageQueryModel(DoorQueryModel):
+ """
+ 门禁设备分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteDoorModel(BaseModel):
+ """
+ 删除门禁设备模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ ids: str = Field(description='需要删除的主键自增')
diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/explanation_content_type_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/explanation_content_type_vo.py
new file mode 100644
index 0000000..fbcfb6b
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/explanation_content_type_vo.py
@@ -0,0 +1,53 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from typing import Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+
+
+class Explanation_content_typeModel(BaseModel):
+ """
+ 讲解内容类型表对应pydantic模型
+ """
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ content_type_id: Optional[int] = Field(default=None, description='主键ID')
+ name: Optional[str] = Field(default=None, description='类型名称')
+ type_value: Optional[str] = Field(default=None, description='类型值(与讲解内容表中的type对应)')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+ update_time: Optional[datetime] = Field(default=None, description='修改时间')
+ update_by: Optional[str] = Field(default=None, description='修改者')
+
+
+
+
+
+
+class Explanation_content_typeQueryModel(Explanation_content_typeModel):
+ """
+ 讲解内容类型不分页查询模型
+ """
+ pass
+
+
+@as_query
+class Explanation_content_typePageQueryModel(Explanation_content_typeQueryModel):
+ """
+ 讲解内容类型分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteExplanation_content_typeModel(BaseModel):
+ """
+ 删除讲解内容类型模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ content_type_ids: str = Field(description='需要删除的主键ID')
diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/explanation_content_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/explanation_content_vo.py
new file mode 100644
index 0000000..d552d35
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/explanation_content_vo.py
@@ -0,0 +1,67 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from pydantic_validation_decorator import NotBlank
+from typing import Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+
+
+class Explanation_contentModel(BaseModel):
+ """
+ 讲解内容表对应pydantic模型
+ """
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ explanation_content_id: Optional[int] = Field(default=None, description='主键ID')
+ title: Optional[str] = Field(default=None, description='标题')
+ content: Optional[str] = Field(default=None, description='讲解内容')
+ type: Optional[str] = Field(default=None, description='类型(0科技类)')
+ spend_time: Optional[int] = Field(default=None, description='预计时长(分钟)')
+ language: Optional[str] = Field(default=None, description='语言(0中文,1英文)')
+ status: Optional[str] = Field(default=None, description='状态(0未开始,1已就绪,2播放中,3已完成)')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+ update_time: Optional[datetime] = Field(default=None, description='修改时间')
+ update_by: Optional[str] = Field(default=None, description='修改者')
+
+ @NotBlank(field_name='title', message='标题不能为空')
+ def get_title(self):
+ return self.title
+
+
+ def validate_fields(self):
+ self.get_title()
+
+
+
+
+class Explanation_contentQueryModel(Explanation_contentModel):
+ """
+ 讲解内容不分页查询模型
+ """
+ begin_create_time: Optional[str] = Field(default=None, description='开始创建时间')
+ end_create_time: Optional[str] = Field(default=None, description='结束创建时间')
+ begin_update_time: Optional[str] = Field(default=None, description='开始修改时间')
+ end_update_time: Optional[str] = Field(default=None, description='结束修改时间')
+
+
+@as_query
+class Explanation_contentPageQueryModel(Explanation_contentQueryModel):
+ """
+ 讲解内容分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteExplanation_contentModel(BaseModel):
+ """
+ 删除讲解内容模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ explanation_content_ids: str = Field(description='需要删除的主键ID')
diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/explanation_style_robot_pair_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/explanation_style_robot_pair_vo.py
new file mode 100644
index 0000000..2c74bd2
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/explanation_style_robot_pair_vo.py
@@ -0,0 +1,83 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from pydantic_validation_decorator import NotBlank
+from typing import Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+class SwitchExplanationStyleModel(BaseModel):
+
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ pairing_id: int = Field(default=None, description='主键ID')
+ robot_id: int = Field(default=None, description='机器人ID(关联robot_info表)')
+ robot_role_id: int = Field(default=None, description='机器人角色ID(关联robot_role表)')
+
+ update_by: Optional[str] = Field(default=None, description='修改者')
+
+
+
+
+
+class Explanation_style_robot_pairModel(BaseModel):
+ """
+ 讲解风格--机器人配对表对应pydantic模型
+ """
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ pairing_id: Optional[int] = Field(default=None, description='主键ID')
+ robot_id: Optional[int] = Field(default=None, description='机器人ID(关联robot_info表)')
+ robot_role_id : Optional[int] = Field(default=None, description='机器人角色ID(关联robot_role表)')
+ explanation_style_id: Optional[int] = Field(default=None, description='讲解风格ID(关联explanation_style表)')
+ prompt: Optional[str] = Field(default=None, description='提示词')
+ status: Optional[str] = Field(default=None, description='状态(0停用,1使用)')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+ update_time: Optional[datetime] = Field(default=None, description='修改时间')
+ update_by: Optional[str] = Field(default=None, description='修改者')
+
+ @NotBlank(field_name='robot_id', message='机器人ID不能为空')
+ def get_robot_id(self):
+ return self.robot_id
+
+ @NotBlank(field_name='explanation_style_id', message='讲解风格ID不能为空')
+ def get_explanation_style_id(self):
+ return self.explanation_style_id
+
+
+ def validate_fields(self):
+ self.get_robot_id()
+ self.get_explanation_style_id()
+
+
+
+
+class Explanation_style_robot_pairQueryModel(Explanation_style_robot_pairModel):
+ """
+ 讲解风格--机器人配对不分页查询模型
+ """
+ begin_create_time: Optional[str] = Field(default=None, description='开始创建时间')
+ end_create_time: Optional[str] = Field(default=None, description='结束创建时间')
+ begin_update_time: Optional[str] = Field(default=None, description='开始修改时间')
+ end_update_time: Optional[str] = Field(default=None, description='结束修改时间')
+
+
+@as_query
+class Explanation_style_robot_pairPageQueryModel(Explanation_style_robot_pairQueryModel):
+ """
+ 讲解风格--机器人配对分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteExplanation_style_robot_pairModel(BaseModel):
+ """
+ 删除讲解风格--机器人配对模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ pairing_ids: str = Field(description='需要删除的主键ID')
diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/explanation_style_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/explanation_style_vo.py
new file mode 100644
index 0000000..6920c07
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/explanation_style_vo.py
@@ -0,0 +1,56 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from typing import Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+
+
+class Explanation_styleModel(BaseModel):
+ """
+ 讲解风格表对应pydantic模型
+ """
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ explanation_style_id: Optional[int] = Field(default=None, description='主键ID')
+ name: Optional[str] = Field(default=None, description='风格名称')
+ detail: Optional[str] = Field(default=None, description='风格简介')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+ update_time: Optional[datetime] = Field(default=None, description='修改时间')
+ update_by: Optional[str] = Field(default=None, description='修改者')
+
+
+
+
+
+
+class Explanation_styleQueryModel(Explanation_styleModel):
+ """
+ 讲解风格不分页查询模型
+ """
+ begin_create_time: Optional[str] = Field(default=None, description='开始创建时间')
+ end_create_time: Optional[str] = Field(default=None, description='结束创建时间')
+ begin_update_time: Optional[str] = Field(default=None, description='开始修改时间')
+ end_update_time: Optional[str] = Field(default=None, description='结束修改时间')
+
+
+@as_query
+class Explanation_stylePageQueryModel(Explanation_styleQueryModel):
+ """
+ 讲解风格分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteExplanation_styleModel(BaseModel):
+ """
+ 删除讲解风格模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ explanation_style_ids: str = Field(description='需要删除的主键ID')
diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/haikang_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/haikang_vo.py
new file mode 100644
index 0000000..d8c5158
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/haikang_vo.py
@@ -0,0 +1,24 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from typing import Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+class VisitorReservationQueryModel(BaseModel):
+
+
+ receptionistId: Optional[str] = Field(default=None, description='接待ID')
+ visitorName: Optional[str] = Field(default=None, description='访客姓名,支持模糊搜索')
+ phoneNo: Optional[str] = Field(default=None, description='访客手机号')
+ visitStartTimeBegin: Optional[str] = Field(default=None, description='开始时间, 满足ISO8601格式')
+ visitStartTimeEnd: Optional[str] = Field(default=None, description='结束时间, 满足ISO8601格式')
+ visitEndTimeBegin: Optional[str] = Field(default=None, description='开始时间, 满足ISO8601格式')
+ visitEndTimeEnd: Optional[str] = Field(default=None, description='结束时间, 满足ISO8601格式')
+
+ pageNo: int = Field(default=1, description='页码')
+ pageSize: int = Field(default=20, description='每页数量')
+
+ visitorStatus: Optional[int] = Field(default=None, description='访客状态')
+ orderId: Optional[str] = Field(default=None, description='订单ID')
+
\ No newline at end of file
diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/identification_record_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/identification_record_vo.py
new file mode 100644
index 0000000..684bb6f
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/identification_record_vo.py
@@ -0,0 +1,57 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from typing import Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+
+
+class Identification_recordModel(BaseModel):
+ """
+ 识别记录表对应pydantic模型
+ """
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ id: Optional[int] = Field(default=None, description='主键 自增')
+ person_name: Optional[str] = Field(default=None, description='识别人姓名')
+ door_name: Optional[str] = Field(default=None, description='门禁点')
+ status: Optional[str] = Field(default=None, description='识别结果, 0 拒绝, 1 通过')
+ source: Optional[str] = Field(default=None, description='识别来源')
+ pic_uri: Optional[str] = Field(default=None, description='图像url')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+ conf: Optional[int] = Field(default=None, description='识别置信度')
+ person_status: Optional[str] = Field(default=None, description=' 0 陌生人, 1 员工, 2 访客')
+
+
+
+
+
+
+class Identification_recordQueryModel(Identification_recordModel):
+ """
+ 识别记录不分页查询模型
+ """
+ begin_create_time: Optional[str] = Field(default=None, description='开始创建时间')
+ end_create_time: Optional[str] = Field(default=None, description='结束创建时间')
+
+
+@as_query
+class Identification_recordPageQueryModel(Identification_recordQueryModel):
+ """
+ 识别记录分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteIdentification_recordModel(BaseModel):
+ """
+ 删除识别记录模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ ids: str = Field(description='需要删除的主键 自增')
diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/identification_statistics_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/identification_statistics_vo.py
new file mode 100644
index 0000000..c9d6992
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/identification_statistics_vo.py
@@ -0,0 +1,53 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from typing import Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+
+
+class Identification_statisticsModel(BaseModel):
+ """
+ 识别统计表对应pydantic模型
+ """
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ id: Optional[int] = Field(default=None, description='主键 自增')
+ employ: Optional[int] = Field(default=None, description='员工识别成功数')
+ visitor: Optional[int] = Field(default=None, description='访客识别成功数')
+ stranger: Optional[int] = Field(default=None, description='位置人员识别失败数')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+
+
+
+
+
+
+class Identification_statisticsQueryModel(Identification_statisticsModel):
+ """
+ 识别统计不分页查询模型
+ """
+ begin_create_time: Optional[str] = Field(default=None, description='开始创建时间')
+ end_create_time: Optional[str] = Field(default=None, description='结束创建时间')
+
+
+@as_query
+class Identification_statisticsPageQueryModel(Identification_statisticsQueryModel):
+ """
+ 识别统计分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteIdentification_statisticsModel(BaseModel):
+ """
+ 删除识别统计模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ ids: str = Field(description='需要删除的主键 自增')
diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/info_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/info_vo.py
new file mode 100644
index 0000000..a1b5406
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/info_vo.py
@@ -0,0 +1,100 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from pydantic_validation_decorator import NotBlank
+from typing import Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+
+
+class InfoModel(BaseModel):
+ """
+ 机器人信息表对应pydantic模型
+ """
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ robot_id: Optional[int] = Field(default=None, description='机器人ID')
+ name: Optional[str] = Field(default=None, description='机器人名称')
+ model: Optional[str] = Field(default=None, description='机器人型号')
+ serial: Optional[str] = Field(default=None, description='序列号')
+ online: Optional[str] = Field(default=None, description='机器人在线状态(0在线, 1离线)')
+ power: Optional[str] = Field(default=None, description='电量')
+ run_time: Optional[int] = Field(default=None, description='总运行时长(单位:分钟)')
+ position: Optional[str] = Field(default=None, description='位置')
+ image: Optional[str] = Field(default=None, description='机器人外观图地址')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+ update_time: Optional[datetime] = Field(default=None, description='修改时间')
+ update_by: Optional[str] = Field(default=None, description='修改者')
+ del_flag: Optional[str] = Field(default=None, description='删除标志(0存在, 2删除)')
+ remark: Optional[str] = Field(default=None, description='备注')
+ last_charge: Optional[datetime] = Field(default=None, description='上一次充电时间')
+ ip: Optional[str] = Field(default=None, description='机器人ip')
+ status: Optional[str] = Field(default=None, description='使用情况(0待机,1正在运行)')
+ video_uri: Optional[str] = Field(default=None, description='视频uri')
+
+
+ @NotBlank(field_name='name', message='机器人名称不能为空')
+ def get_name(self):
+ return self.name
+
+ # @NotBlank(field_name='del_flag', message='删除标志(0存在, 2删除)不能为空')
+ # def get_del_flag(self):
+ # return self.del_flag
+
+
+ def validate_fields(self):
+ self.get_name()
+ # self.get_del_flag()
+
+
+
+
+class InfoQueryModel(InfoModel):
+ """
+ 机器人信息不分页查询模型
+ """
+ begin_create_time: Optional[str] = Field(default=None, description='开始创建时间')
+ end_create_time: Optional[str] = Field(default=None, description='结束创建时间')
+ begin_update_time: Optional[str] = Field(default=None, description='开始修改时间')
+ end_update_time: Optional[str] = Field(default=None, description='结束修改时间')
+
+
+@as_query
+class InfoPageQueryModel(InfoQueryModel):
+ """
+ 机器人信息分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class InfoUpdateModel(BaseModel):
+ """
+ 更新机器人信息模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ robot_id: Optional[int] = Field(default=None, description='机器人ID')
+
+ name: Optional[str] = Field(default=None, description='机器人名称')
+ model: Optional[str] = Field(default=None, description='机器人型号')
+ serial: Optional[str] = Field(default=None, description='序列号')
+ position: Optional[str] = Field(default=None, description='位置')
+ image: Optional[str] = Field(default=None, description='机器人外观图地址')
+ remark: Optional[str] = Field(default=None, description='备注')
+ update_time: Optional[datetime] = Field(default=None, description='修改时间')
+ update_by: Optional[str] = Field(default=None, description='修改者')
+
+
+class DeleteInfoModel(BaseModel):
+ """
+ 删除机器人信息模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ robot_ids: str = Field(description='需要删除的机器人ID')
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..960a837
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/job_vo.py
@@ -0,0 +1,134 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from pydantic_validation_decorator import NotBlank, Size
+from typing import Literal, Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+class JobModel(BaseModel):
+ """
+ 定时任务调度表对应pydantic模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ job_id: Optional[int] = Field(default=None, description='任务ID')
+ job_name: Optional[str] = Field(default=None, description='任务名称')
+ job_group: Optional[str] = Field(default=None, description='任务组名')
+ job_executor: Optional[str] = Field(default=None, description='任务执行器')
+ invoke_target: Optional[str] = Field(default=None, description='调用目标字符串')
+ job_args: Optional[str] = Field(default=None, description='位置参数')
+ job_kwargs: Optional[str] = Field(default=None, description='关键字参数')
+ cron_expression: Optional[str] = Field(default=None, description='cron执行表达式')
+ misfire_policy: Optional[Literal['1', '2', '3']] = Field(
+ default=None, description='计划执行错误策略(1立即执行 2执行一次 3放弃执行)'
+ )
+ concurrent: Optional[Literal['0', '1']] = Field(default=None, description='是否并发执行(0允许 1禁止)')
+ status: Optional[Literal['0', '1']] = Field(default=None, description='状态(0正常 1暂停)')
+ create_by: Optional[str] = Field(default=None, description='创建者')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+ update_by: Optional[str] = Field(default=None, description='更新者')
+ update_time: Optional[datetime] = Field(default=None, description='更新时间')
+ remark: Optional[str] = Field(default=None, description='备注信息')
+
+ @NotBlank(field_name='invoke_target', message='调用目标字符串不能为空')
+ @Size(field_name='invoke_target', min_length=0, max_length=500, message='调用目标字符串长度不能超过500个字符')
+ def get_invoke_target(self):
+ return self.invoke_target
+
+ @NotBlank(field_name='cron_expression', message='Cron执行表达式不能为空')
+ @Size(field_name='cron_expression', min_length=0, max_length=255, message='Cron执行表达式不能超过255个字符')
+ def get_cron_expression(self):
+ return self.cron_expression
+
+ def validate_fields(self):
+ self.get_invoke_target()
+ self.get_cron_expression()
+
+
+class JobLogModel(BaseModel):
+ """
+ 定时任务调度日志表对应pydantic模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ job_log_id: Optional[int] = Field(default=None, description='任务日志ID')
+ job_name: Optional[str] = Field(default=None, description='任务名称')
+ job_group: Optional[str] = Field(default=None, description='任务组名')
+ job_executor: Optional[str] = Field(default=None, description='任务执行器')
+ invoke_target: Optional[str] = Field(default=None, description='调用目标字符串')
+ job_args: Optional[str] = Field(default=None, description='位置参数')
+ job_kwargs: Optional[str] = Field(default=None, description='关键字参数')
+ job_trigger: Optional[str] = Field(default=None, description='任务触发器')
+ job_message: Optional[str] = Field(default=None, description='日志信息')
+ status: Optional[Literal['0', '1']] = Field(default=None, description='执行状态(0正常 1失败)')
+ exception_info: Optional[str] = Field(default=None, description='异常信息')
+ create_time: Optional[datetime] = Field(default=None, description='创建时间')
+
+
+class JobQueryModel(JobModel):
+ """
+ 定时任务管理不分页查询模型
+ """
+
+ begin_time: Optional[str] = Field(default=None, description='开始时间')
+ end_time: Optional[str] = Field(default=None, description='结束时间')
+
+
+@as_query
+class JobPageQueryModel(JobQueryModel):
+ """
+ 定时任务管理分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class EditJobModel(JobModel):
+ """
+ 编辑定时任务模型
+ """
+
+ type: Optional[str] = Field(default=None, description='操作类型')
+
+
+class DeleteJobModel(BaseModel):
+ """
+ 删除定时任务模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ job_ids: str = Field(description='需要删除的定时任务ID')
+
+
+class JobLogQueryModel(JobLogModel):
+ """
+ 定时任务日志不分页查询模型
+ """
+
+ begin_time: Optional[str] = Field(default=None, description='开始时间')
+ end_time: Optional[str] = Field(default=None, description='结束时间')
+
+
+@as_query
+class JobLogPageQueryModel(JobLogQueryModel):
+ """
+ 定时任务日志管理分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteJobLogModel(BaseModel):
+ """
+ 删除定时任务日志模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ job_log_ids: str = Field(description='需要删除的定时任务日志ID')
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..739ad6c
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/log_vo.py
@@ -0,0 +1,131 @@
+from datetime import datetime
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_camel
+from typing import Literal, Optional
+from module_admin.annotation.pydantic_annotation import as_query
+
+
+class OperLogModel(BaseModel):
+ """
+ 操作日志表对应pydantic模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ oper_id: Optional[int] = Field(default=None, description='日志主键')
+ title: Optional[str] = Field(default=None, description='模块标题')
+ business_type: Optional[Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']] = (
+ Field(
+ default=None, description='业务类型(0其它 1新增 2修改 3删除 4授权 5导出 6导入 7强退 8生成代码 9清空数据)'
+ )
+ )
+ method: Optional[str] = Field(default=None, description='方法名称')
+ request_method: Optional[str] = Field(default=None, description='请求方式')
+ operator_type: Optional[Literal[0, 1, 2]] = Field(
+ default=None, description='操作类别(0其它 1后台用户 2手机端用户)'
+ )
+ oper_name: Optional[str] = Field(default=None, description='操作人员')
+ dept_name: Optional[str] = Field(default=None, description='部门名称')
+ oper_url: Optional[str] = Field(default=None, description='请求URL')
+ oper_ip: Optional[str] = Field(default=None, description='主机地址')
+ oper_location: Optional[str] = Field(default=None, description='操作地点')
+ oper_param: Optional[str] = Field(default=None, description='请求参数')
+ json_result: Optional[str] = Field(default=None, description='返回参数')
+ status: Optional[Literal[0, 1, '0', '1']] = Field(default=None, description='操作状态(0正常 1异常)')
+ error_msg: Optional[str] = Field(default=None, description='错误消息')
+ oper_time: Optional[datetime] = Field(default=None, description='操作时间')
+ cost_time: Optional[int] = Field(default=None, description='消耗时间')
+
+
+class LogininforModel(BaseModel):
+ """
+ 登录日志表对应pydantic模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
+
+ info_id: Optional[int] = Field(default=None, description='访问ID')
+ user_name: Optional[str] = Field(default=None, description='用户账号')
+ ipaddr: Optional[str] = Field(default=None, description='登录IP地址')
+ login_location: Optional[str] = Field(default=None, description='登录地点')
+ browser: Optional[str] = Field(default=None, description='浏览器类型')
+ os: Optional[str] = Field(default=None, description='操作系统')
+ status: Optional[Literal['0', '1']] = Field(default=None, description='登录状态(0成功 1失败)')
+ msg: Optional[str] = Field(default=None, description='提示消息')
+ login_time: Optional[datetime] = Field(default=None, description='访问时间')
+
+
+class OperLogQueryModel(OperLogModel):
+ """
+ 操作日志管理不分页查询模型
+ """
+
+ order_by_column: Optional[str] = Field(default=None, description='排序的字段名称')
+ is_asc: Optional[Literal['ascending', 'descending']] = Field(
+ default=None, description='排序方式(ascending升序 descending降序)'
+ )
+ begin_time: Optional[str] = Field(default=None, description='开始时间')
+ end_time: Optional[str] = Field(default=None, description='结束时间')
+
+
+@as_query
+class OperLogPageQueryModel(OperLogQueryModel):
+ """
+ 操作日志管理分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteOperLogModel(BaseModel):
+ """
+ 删除操作日志模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ oper_ids: str = Field(description='需要删除的日志主键')
+
+
+class LoginLogQueryModel(LogininforModel):
+ """
+ 登录日志管理不分页查询模型
+ """
+
+ order_by_column: Optional[str] = Field(default=None, description='排序的字段名称')
+ is_asc: Optional[Literal['ascending', 'descending']] = Field(
+ default=None, description='排序方式(ascending升序 descending降序)'
+ )
+ begin_time: Optional[str] = Field(default=None, description='开始时间')
+ end_time: Optional[str] = Field(default=None, description='结束时间')
+
+
+@as_query
+class LoginLogPageQueryModel(LoginLogQueryModel):
+ """
+ 登录日志管理分页查询模型
+ """
+
+ page_num: int = Field(default=1, description='当前页码')
+ page_size: int = Field(default=10, description='每页记录数')
+
+
+class DeleteLoginLogModel(BaseModel):
+ """
+ 删除登录日志模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ info_ids: str = Field(description='需要删除的访问ID')
+
+
+class UnlockUser(BaseModel):
+ """
+ 解锁用户模型
+ """
+
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ user_name: str = Field(description='用户名称')
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..fbd6e07
--- /dev/null
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/login_vo.py
@@ -0,0 +1,87 @@
+import re
+from pydantic import BaseModel, ConfigDict, Field, model_validator
+from pydantic.alias_generators import to_camel
+from typing import List, Optional, Union
+from exceptions.exception import ModelValidatorException
+from module_admin.entity.vo.menu_vo import MenuModel
+
+
+class UserLogin(BaseModel):
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ user_name: str = Field(description='用户名称')
+ password: str = Field(description='用户密码')
+ code: Optional[str] = Field(default=None, description='验证码')
+ uuid: Optional[str] = Field(default=None, description='会话编号')
+ login_info: Optional[dict] = Field(default=None, description='登录信息,前端无需传递')
+ captcha_enabled: Optional[bool] = Field(default=None, description='是否启用验证码,前端无需传递')
+
+
+class UserRegister(BaseModel):
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ username: str = Field(description='用户名称')
+ password: str = Field(description='用户密码')
+ confirm_password: str = Field(description='用户二次确认密码')
+ code: Optional[str] = Field(default=None, description='验证码')
+ uuid: Optional[str] = Field(default=None, description='会话编号')
+
+ @model_validator(mode='after')
+ def check_password(self) -> 'UserRegister':
+ pattern = r"""^[^<>"'|\\]+$"""
+ if self.password is None or re.match(pattern, self.password):
+ return self
+ else:
+ raise ModelValidatorException(message='密码不能包含非法字符:< > " \' \\ |')
+
+
+class Token(BaseModel):
+ access_token: str = Field(description='token信息')
+ token_type: str = Field(description='token类型')
+
+
+class CaptchaCode(BaseModel):
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ captcha_enabled: bool = Field(description='是否启用验证码')
+ register_enabled: bool = Field(description='是否启用注册')
+ img: str = Field(description='验证码图片')
+ uuid: str = Field(description='会话编号')
+
+
+class SmsCode(BaseModel):
+ is_success: Optional[bool] = Field(default=None, description='操作是否成功')
+ sms_code: str = Field(description='短信验证码')
+ session_id: str = Field(description='会话编号')
+ message: Optional[str] = Field(default=None, description='响应信息')
+
+
+class MenuTreeModel(MenuModel):
+ children: Optional[Union[List['MenuTreeModel'], None]] = Field(default=None, description='子菜单')
+
+
+class MetaModel(BaseModel):
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ title: Optional[str] = Field(default=None, description='设置路由在侧边栏和面包屑中展示的名字')
+ icon: Optional[str] = Field(default=None, description='设置路由的图标')
+ no_cache: Optional[bool] = Field(default=None, description='设置为true,则不会被