update
This commit is contained in:
parent
64793795da
commit
0ea6f481b4
58
ruoyi-fastapi-backend/.gitignore
vendored
Normal file
58
ruoyi-fastapi-backend/.gitignore
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
.pytest_cache/
|
||||
.mypy_cache/
|
||||
.ruff_cache/
|
||||
|
||||
# Virtual Environment
|
||||
venv/
|
||||
.venv/
|
||||
ENV/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# IDEs
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Project specific (local artifacts)
|
||||
|
||||
server.py
|
||||
visitor/visitorIds.txt
|
||||
.env.dev
|
||||
.env.prod
|
||||
|
||||
|
||||
employee/
|
||||
employee/face_images/
|
||||
test/
|
||||
visitor/
|
||||
myenv/
|
||||
96
ruoyi-fastapi-backend/001#U6d4b#U8bd5#U6d77#U5eb7api.py
Normal file
96
ruoyi-fastapi-backend/001#U6d4b#U8bd5#U6d77#U5eb7api.py
Normal file
@ -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"]))
|
||||
|
||||
536
ruoyi-fastapi-backend/002#U7b80#U5355http#U670d#U52a1.py
Normal file
536
ruoyi-fastapi-backend/002#U7b80#U5355http#U670d#U52a1.py
Normal file
@ -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 """
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Python HTTP 服务端</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 40px; }
|
||||
.api-list { background: #f5f5f5; padding: 20px; border-radius: 5px; }
|
||||
.api-item { margin: 10px 0; padding: 10px; background: white; border-radius: 3px; }
|
||||
.method { font-weight: bold; color: #007cba; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🚀 Python HTTP 服务端</h1>
|
||||
<p>服务运行成功!以下是可用的API接口:</p>
|
||||
|
||||
<div class="api-list">
|
||||
<div class="api-item">
|
||||
<span class="method">GET</span> /api/status - 获取服务状态
|
||||
</div>
|
||||
<div class="api-item">
|
||||
<span class="method">GET</span> /api/time - 获取当前时间
|
||||
</div>
|
||||
<div class="api-item">
|
||||
<span class="method">GET</span> /api/user - 获取用户列表
|
||||
</div>
|
||||
<div class="api-item">
|
||||
<span class="method">GET</span> /api/user?id=1 - 获取指定用户
|
||||
</div>
|
||||
<div class="api-item">
|
||||
<span class="method">POST</span> /api/login - 用户登录 (username: admin, password: 123456)
|
||||
</div>
|
||||
<div class="api-item">
|
||||
<span class="method">POST</span> /api/echo - 回显接收到的数据
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>测试示例:</h3>
|
||||
<pre>
|
||||
# 获取状态
|
||||
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
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
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)
|
||||
11
ruoyi-fastapi-backend/003#U6d4b#U8bd5#U6d77#U5eb7url.py
Normal file
11
ruoyi-fastapi-backend/003#U6d4b#U8bd5#U6d77#U5eb7url.py
Normal file
@ -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)
|
||||
@ -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-----------------------------------------
|
||||
@ -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)
|
||||
@ -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()
|
||||
@ -0,0 +1,5 @@
|
||||
import os
|
||||
|
||||
txt_path = "./test/image_base64.txt"
|
||||
print(os.path.dirname(txt_path))
|
||||
# os.makedirs(, exist_ok=True)
|
||||
49
ruoyi-fastapi-backend/008获取员工图片.py
Normal file
49
ruoyi-fastapi-backend/008获取员工图片.py
Normal file
@ -0,0 +1,49 @@
|
||||
import asyncio
|
||||
|
||||
|
||||
from utils.haikang_util import HaikangUtil
|
||||
|
||||
|
||||
|
||||
async def get_all_employee_pictures(pageNo=2, pageSize=1000):
|
||||
|
||||
result = await HaikangUtil.get_person_list(pageNo=pageNo, pageSize=pageSize)
|
||||
|
||||
print(result)
|
||||
|
||||
employee_list = list()
|
||||
if result[0]:
|
||||
total = result[1]['total']
|
||||
employee_list += result[1]['list']
|
||||
while pageNo * pageSize < total:
|
||||
pageNo += 1
|
||||
result = await HaikangUtil.get_person_list(pageNo=pageNo + 1, pageSize=pageSize, )
|
||||
if result[0]:
|
||||
employee_list += result[1]['list']
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
with open("./employee/personIds.txt", "r", encoding="utf-8") as f:
|
||||
person_ids = [line.rstrip('\n') for line in f]
|
||||
# print(visitor_ids)
|
||||
person_ids_add = list()
|
||||
for employee in employee_list:
|
||||
# print(employee)
|
||||
if employee["personId"] not in person_ids:
|
||||
person_ids_add.append(employee["personId"])
|
||||
# 获取图片
|
||||
try:
|
||||
await HaikangUtil.get_person_picture(employee.get("personPhoto")[0]["serverIndexCode"], employee.get("personPhoto")[0]["picUri"], f"./employee/face_images/{employee['personName']}")
|
||||
await asyncio.sleep(0.5)
|
||||
except Exception as e:
|
||||
print(f"error: {e}, personId: {employee['personId']}")
|
||||
|
||||
|
||||
with open("./employee/personIds.txt", "a", encoding="utf-8") as f:
|
||||
for person_id in person_ids_add:
|
||||
f.write(person_id + "\n")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(get_all_employee_pictures())
|
||||
24
ruoyi-fastapi-backend/009上传员工图片到人脸库.py
Normal file
24
ruoyi-fastapi-backend/009上传员工图片到人脸库.py
Normal file
@ -0,0 +1,24 @@
|
||||
from utils.compreface_util import ComprefaceUtil
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
image_path = "/root/robot_project/kangda-robot-backend-master/ruoyi-fastapi-backend/employee/face_images"
|
||||
|
||||
person_name = os.listdir(image_path)
|
||||
|
||||
for person in person_name:
|
||||
image_list = os.listdir(os.path.join(image_path, person))
|
||||
for image in image_list:
|
||||
t = os.path.join(image_path, person, image)
|
||||
try:
|
||||
asyncio.run(ComprefaceUtil.face_addition(t, person))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(f"error: {person}")
|
||||
|
||||
# break
|
||||
143
ruoyi-fastapi-backend/010上传员工图片到人脸库_追加版.py
Normal file
143
ruoyi-fastapi-backend/010上传员工图片到人脸库_追加版.py
Normal file
@ -0,0 +1,143 @@
|
||||
import os
|
||||
import requests
|
||||
from pathlib import Path
|
||||
|
||||
# CompreFace配置
|
||||
# COMPREFACE_BASE_URL = "http://localhost"
|
||||
# COMPERFACE_BASE_PORT = "9900"
|
||||
# COMPREFACE_API_KEY = "9115667c-4968-400f-9006-7b2646fba8df"
|
||||
# COMPREFACE_API_KEY_DETECTION = "08f31eab-de28-4c00-aea5-eb69f2619783"
|
||||
COMPREFACE_URL = "http://localhost:9900" # 修改为你的CompreFace地址
|
||||
API_KEY = "9115667c-4968-400f-9006-7b2646fba8df" # 修改为你的API Key
|
||||
RECOGNITION_API = f"{COMPREFACE_URL}/api/v1/recognition"
|
||||
|
||||
# 图片目录
|
||||
FACE_IMAGES_DIR = "employee/face_images"
|
||||
|
||||
|
||||
def get_all_subjects():
|
||||
"""获取人脸库中所有已存在的subject(人名)"""
|
||||
headers = {"x-api-key": API_KEY}
|
||||
try:
|
||||
response = requests.get(f"{RECOGNITION_API}/subjects", headers=headers)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
return data.get("subjects", [])
|
||||
except Exception as e:
|
||||
print(f"获取subjects列表失败: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def get_subject_faces(subject):
|
||||
"""获取指定subject的所有人脸图片"""
|
||||
headers = {"x-api-key": API_KEY}
|
||||
try:
|
||||
response = requests.get(
|
||||
f"{RECOGNITION_API}/faces",
|
||||
headers=headers,
|
||||
params={"subject": subject}
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
return data.get("faces", [])
|
||||
except Exception as e:
|
||||
print(f"获取{subject}的人脸列表失败: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def upload_face(subject, image_path):
|
||||
"""上传人脸图片到CompreFace"""
|
||||
headers = {"x-api-key": API_KEY}
|
||||
|
||||
try:
|
||||
with open(image_path, "rb") as image_file:
|
||||
files = {"file": image_file}
|
||||
params = {"subject": subject}
|
||||
|
||||
response = requests.post(
|
||||
f"{RECOGNITION_API}/faces",
|
||||
headers=headers,
|
||||
params=params,
|
||||
files=files
|
||||
)
|
||||
response.raise_for_status()
|
||||
return True, response.json()
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
||||
|
||||
def main():
|
||||
# 检查目录是否存在
|
||||
if not os.path.exists(FACE_IMAGES_DIR):
|
||||
print(f"错误: 目录 {FACE_IMAGES_DIR} 不存在")
|
||||
return
|
||||
|
||||
# 获取已存在的subjects
|
||||
print("正在获取人脸库中已存在的人员...")
|
||||
existing_subjects = get_all_subjects()
|
||||
print(f"人脸库中已有 {len(existing_subjects)} 个人员")
|
||||
|
||||
# 创建已存在人脸的映射(subject -> face count)
|
||||
existing_faces = {}
|
||||
for subject in existing_subjects:
|
||||
faces = get_subject_faces(subject)
|
||||
existing_faces[subject] = len(faces)
|
||||
print(f" - {subject}: {len(faces)} 张图片")
|
||||
|
||||
print("\n开始处理本地图片...")
|
||||
|
||||
# 统计信息
|
||||
total_images = 0
|
||||
uploaded_count = 0
|
||||
skipped_count = 0
|
||||
failed_count = 0
|
||||
|
||||
# 遍历目录
|
||||
for person_dir in Path(FACE_IMAGES_DIR).iterdir():
|
||||
if not person_dir.is_dir():
|
||||
continue
|
||||
|
||||
subject = person_dir.name
|
||||
print(f"\n处理人员: {subject}")
|
||||
|
||||
# 获取该人员的所有图片
|
||||
image_files = list(person_dir.glob("*.jpg")) + \
|
||||
list(person_dir.glob("*.jpeg")) + \
|
||||
list(person_dir.glob("*.png"))
|
||||
|
||||
total_images += len(image_files)
|
||||
|
||||
# 检查是否已在库中
|
||||
if subject in existing_faces:
|
||||
# 如果库中已有足够的图片,跳过
|
||||
if existing_faces[subject] >= len(image_files):
|
||||
print(f" ✓ 已存在 {existing_faces[subject]} 张图片,跳过上传")
|
||||
skipped_count += len(image_files)
|
||||
continue
|
||||
else:
|
||||
print(f" 库中已有 {existing_faces[subject]} 张,本地有 {len(image_files)} 张")
|
||||
|
||||
# 上传图片
|
||||
for image_path in image_files:
|
||||
print(f" 上传: {image_path.name}...", end=" ")
|
||||
success, result = upload_face(subject, str(image_path))
|
||||
|
||||
if success:
|
||||
print("✓ 成功")
|
||||
uploaded_count += 1
|
||||
else:
|
||||
print(f"✗ 失败: {result}")
|
||||
failed_count += 1
|
||||
|
||||
# 输出统计信息
|
||||
print("\n" + "="*50)
|
||||
print("上传完成!")
|
||||
print(f"总图片数: {total_images}")
|
||||
print(f"成功上传: {uploaded_count}")
|
||||
print(f"跳过: {skipped_count}")
|
||||
print(f"失败: {failed_count}")
|
||||
print("="*50)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Binary file not shown.
@ -1,483 +0,0 @@
|
||||
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',
|
||||
}
|
||||
)
|
||||
@ -1,30 +0,0 @@
|
||||
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
|
||||
@ -1,78 +0,0 @@
|
||||
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': '短信验证码'}
|
||||
@ -1,463 +0,0 @@
|
||||
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, Optional
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
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: Optional[str] = None
|
||||
app_redoc_url: Optional[str] = None
|
||||
app_openapi_url: Optional[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(BaseSettings):
|
||||
"""
|
||||
RAGFlow 配置
|
||||
|
||||
注意:必须在 `.env.*` 加载完成后再实例化读取,
|
||||
所以使用 BaseSettings 而不是类常量。
|
||||
"""
|
||||
RAGFLOW_BASE_URL: str = "http://10.0.0.202:82"
|
||||
RAGFLOW_API_KEY: str = "ragflow-hlMjRmNzE2ODNiNTExZjA4ZTNlMDI0Mm"
|
||||
|
||||
class SearchSettings:
|
||||
"""搜索服务配置"""
|
||||
|
||||
SEARCH_API_BASE = os.getenv("BAIDU_SEARCH_API_BASE", "https://qianfan.baidubce.com/v2/ai_search/chat/completions")
|
||||
SEARCH_API_KEY = os.getenv("SEARCH_API_KEY", "")
|
||||
SEARCH_ENGINE = os.getenv("SEARCH_ENGINE", "baidu")
|
||||
SEARCH_LANG = os.getenv("SEARCH_LANG", "zh-cn")
|
||||
SEARCH_COUNTRY = os.getenv("SEARCH_COUNTRY", "cn")
|
||||
SEARCH_NUM_RESULTS = int(os.getenv("SEARCH_NUM_RESULTS", "5"))
|
||||
SEARCH_CACHE_TTL = int(os.getenv("SEARCH_CACHE_TTL", "1800"))
|
||||
|
||||
# Baidu AppBuilder Configuration
|
||||
BAIDU_APPBUILDER_API_KEY = os.getenv("BAIDU_APPBUILDER_API_KEY", "bce-v3/ALTAK-rXC12MxT2UeVrJP2S5kMJ/e42eb37b3c345e9bfb3e2eb0dcac8004888592f3")
|
||||
|
||||
# ZhipuAI Configuration
|
||||
ZHIPUAI_API_KEY = os.getenv("ZHIPUAI_API_KEY", "c86394f9af6f4d4ebecf3f6105d3483e.tn3vZmeVB8IzO6i3")
|
||||
ZHIPUAI_API_BASE = os.getenv("ZHIPUAI_API_BASE", "https://open.bigmodel.cn/api/paas/v4/chat/completions")
|
||||
|
||||
|
||||
class DeepSeekSettings:
|
||||
"""DeepSeek大语言模型配置"""
|
||||
DEEPSEEK_API_BASE = os.getenv("DEEPSEEK_API_BASE", "https://api.deepseek.com")
|
||||
DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "sk-56b608b26a6949e4b09b5bf5f11c8f5b")
|
||||
DEEPSEEK_MODEL = os.getenv("DEEPSEEK_MODEL", "deepseek-chat")
|
||||
|
||||
|
||||
class KBSettings(BaseSettings):
|
||||
"""内部知识库 Provider 配置(ragflow / es_bm25)
|
||||
|
||||
注意:必须在 `.env.*` 加载完成后再实例化读取,
|
||||
所以使用 BaseSettings 而不是在 import 时读取环境变量。
|
||||
"""
|
||||
|
||||
KB_PROVIDER: Literal['ragflow', 'es_bm25'] = 'ragflow' # type: ignore[assignment]
|
||||
|
||||
# ES/OpenSearch
|
||||
KB_ES_URL: str = ''
|
||||
KB_ES_INDEX: str = 'kb_chunks_v1'
|
||||
KB_ES_USERNAME: str = ''
|
||||
KB_ES_PASSWORD: str = ''
|
||||
KB_ES_VERIFY_SSL: bool = True
|
||||
|
||||
# retrieval
|
||||
KB_TOP_K: int = 8
|
||||
KB_MIN_SCORE: float = 0.0
|
||||
KB_MIN_COVERAGE: float = 0.2
|
||||
KB_CACHE_TTL: int = 1800 # seconds
|
||||
|
||||
# fallback
|
||||
KB_FALLBACK: Literal['ragflow', 'none'] = 'ragflow' # type: ignore[assignment]
|
||||
|
||||
def model_post_init(self, __context) -> None: # type: ignore[override]
|
||||
# 规范化:dotenv/环境变量可能带空格/大小写
|
||||
provider = (str(self.KB_PROVIDER) if self.KB_PROVIDER is not None else 'ragflow').strip().lower()
|
||||
self.KB_PROVIDER = provider if provider in ('ragflow', 'es_bm25') else 'ragflow' # type: ignore[assignment]
|
||||
|
||||
es_url = (self.KB_ES_URL or '').strip().rstrip('/')
|
||||
self.KB_ES_URL = es_url
|
||||
|
||||
fallback = (str(self.KB_FALLBACK) if self.KB_FALLBACK is not None else 'ragflow').strip().lower()
|
||||
self.KB_FALLBACK = fallback if fallback in ('ragflow', 'none') else 'ragflow' # type: ignore[assignment]
|
||||
|
||||
|
||||
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_search_config(self):
|
||||
"""获取搜索配置"""
|
||||
return SearchSettings()
|
||||
|
||||
@lru_cache()
|
||||
def get_deepseek_config(self):
|
||||
"""获取DeepSeek配置"""
|
||||
return DeepSeekSettings()
|
||||
|
||||
@lru_cache()
|
||||
def get_kb_config(self):
|
||||
"""获取内部知识库配置"""
|
||||
return KBSettings()
|
||||
|
||||
@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, _unknown = parser.parse_known_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}'
|
||||
# 加载配置:优先从项目根目录读取,避免工作目录不同导致找不到 .env.*
|
||||
project_root = Path(__file__).resolve().parent.parent
|
||||
env_path = project_root / env_file
|
||||
def manual_load_env(path: Path, *, override: bool = True) -> None:
|
||||
"""
|
||||
python-dotenv 对某些格式(如 KEY = "value")兼容性不一,
|
||||
这里做一个非常宽松的兜底解析,确保关键配置一定能读到。
|
||||
"""
|
||||
try:
|
||||
with path.open("r", encoding="utf-8-sig") as f:
|
||||
for raw_line in f:
|
||||
line = raw_line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
if "=" not in line:
|
||||
continue
|
||||
key, val = line.split("=", 1)
|
||||
key = key.strip()
|
||||
val = val.strip()
|
||||
if not key:
|
||||
continue
|
||||
# 去掉行尾注释(不处理引号内 # 的复杂情况,当前 .env 无此需求)
|
||||
if "#" in val:
|
||||
val = val.split("#", 1)[0].strip()
|
||||
# 去掉首尾引号
|
||||
if len(val) >= 2 and val[0] == val[-1] and val[0] in ("'", '"'):
|
||||
val = val[1:-1]
|
||||
if override or key not in os.environ:
|
||||
os.environ[key] = val
|
||||
except Exception:
|
||||
# 兜底解析失败时不阻断启动
|
||||
return
|
||||
|
||||
if env_path.exists():
|
||||
load_dotenv(str(env_path), override=True)
|
||||
# 再做一次宽松解析兜底
|
||||
manual_load_env(env_path, override=True)
|
||||
else:
|
||||
# 兜底:保持旧行为(从当前工作目录读取)
|
||||
load_dotenv(env_file, override=True)
|
||||
manual_load_env(Path(env_file), override=True)
|
||||
|
||||
|
||||
# 实例化获取配置类
|
||||
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()
|
||||
# 搜索配置
|
||||
SearchConfig = get_config.get_search_config()
|
||||
# DeepSeek配置
|
||||
DeepSeekConfig = get_config.get_deepseek_config()
|
||||
|
||||
# KB Provider配置
|
||||
KBConfig = get_config.get_kb_config()
|
||||
@ -1,24 +0,0 @@
|
||||
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('数据库连接成功')
|
||||
@ -1,77 +0,0 @@
|
||||
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)
|
||||
@ -1,279 +0,0 @@
|
||||
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()
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,345 +0,0 @@
|
||||
# 康达机器人系统架构文档
|
||||
|
||||
## 1. 系统概述
|
||||
|
||||
康达机器人系统是一个集机器人控制、人脸识别、门禁管理、文档检索和智能对话于一体的综合性管理平台。系统采用前后端分离架构,集成了多种外部服务和AI能力,为用户提供全面的机器人管理和控制解决方案。
|
||||
|
||||
## 2. 系统架构图
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph 客户端层
|
||||
Web控制台["Web控制台\n(管理界面)"]
|
||||
Pad控制端["Pad控制端\n(现场操作)"]
|
||||
end
|
||||
|
||||
subgraph 后端服务层
|
||||
FastAPI["FastAPI后端服务\n(核心业务逻辑)"]
|
||||
Redis["Redis缓存\n(会话、数据缓存)"]
|
||||
Database[("数据库\n(MySQL/PostgreSQL)")]
|
||||
end
|
||||
|
||||
subgraph 外部服务层
|
||||
RAGFlow["RAGFlow\n(文档检索与智能对话)"]
|
||||
SearchAPI["搜索API\n(SerpAPI)"]
|
||||
Compreface["Compreface\n(人脸识别)"]
|
||||
HaiKang["海康平台\n(门禁控制)"]
|
||||
end
|
||||
|
||||
subgraph 设备层
|
||||
Robot["机器人\n(执行动作、采集数据)"]
|
||||
Door["门禁设备\n(开关控制)"]
|
||||
Camera["摄像头\n(图像采集)"]
|
||||
end
|
||||
|
||||
%% 连接关系
|
||||
Web控制台 -->|HTTP请求| FastAPI
|
||||
Pad控制端 -->|HTTP请求| FastAPI
|
||||
FastAPI -->|读写数据| Database
|
||||
FastAPI -->|缓存操作| Redis
|
||||
FastAPI -->|调用接口| RAGFlow
|
||||
FastAPI -->|搜索增强| SearchAPI
|
||||
FastAPI -->|人脸识别| Compreface
|
||||
FastAPI -->|门禁控制| HaiKang
|
||||
FastAPI -->|控制指令| Robot
|
||||
HaiKang -->|控制| Door
|
||||
Camera -->|图像数据| Compreface
|
||||
Robot -->|状态反馈| FastAPI
|
||||
Door -->|状态反馈| HaiKang
|
||||
HaiKang -->|状态更新| FastAPI
|
||||
|
||||
%% 大模型服务
|
||||
RAGFlow -->|调用| LLM["大语言模型\n(云上服务)"]
|
||||
SearchAPI -->|调用| SearchEngine["搜索引擎\n(第三方)"]
|
||||
```
|
||||
|
||||
## 3. 系统组件说明
|
||||
|
||||
### 3.1 客户端层
|
||||
|
||||
#### Web控制台
|
||||
- **功能**:提供系统管理、用户管理、角色管理、机器人配置、讲解内容管理等功能
|
||||
- **技术**:Vue.js + Element Plus
|
||||
- **访问方式**:浏览器访问
|
||||
|
||||
#### Pad控制端
|
||||
- **功能**:现场控制机器人、查看实时状态、处理识别记录
|
||||
- **技术**:Web应用(适配Pad设备)
|
||||
- **访问方式**:Pad浏览器或专用应用
|
||||
|
||||
### 3.2 后端服务层
|
||||
|
||||
#### FastAPI后端服务
|
||||
- **核心功能**:
|
||||
- 提供RESTful API接口
|
||||
- 处理业务逻辑
|
||||
- 集成各种外部服务
|
||||
- 权限管理与认证
|
||||
- 系统监控与日志
|
||||
- **主要模块**:
|
||||
- 用户管理模块
|
||||
- 角色管理模块
|
||||
- 机器人信息管理模块
|
||||
- 机器人动作管理模块
|
||||
- 门禁设备管理模块
|
||||
- 识别记录管理模块
|
||||
- RAGFlow文档管理模块
|
||||
- 人脸识别管理模块
|
||||
- 系统统计模块
|
||||
|
||||
#### Redis缓存
|
||||
- **功能**:
|
||||
- 会话管理
|
||||
- 数据缓存
|
||||
- 分布式锁
|
||||
- 消息队列(可选)
|
||||
|
||||
#### 数据库
|
||||
- **功能**:存储系统配置、用户信息、机器人数据、识别记录等持久化数据
|
||||
- **支持类型**:MySQL、PostgreSQL
|
||||
|
||||
### 3.3 外部服务层
|
||||
|
||||
#### RAGFlow
|
||||
- **功能**:
|
||||
- 文档检索与管理
|
||||
- 智能对话(基于大语言模型)
|
||||
- 知识库管理
|
||||
- **集成方式**:API调用
|
||||
|
||||
#### 搜索API
|
||||
- **功能**:
|
||||
- 增强智能对话的搜索能力
|
||||
- 提供实时信息查询
|
||||
- **技术**:SerpAPI
|
||||
|
||||
#### Compreface
|
||||
- **功能**:
|
||||
- 人脸识别
|
||||
- 人脸比对
|
||||
- 人脸检测
|
||||
- **集成方式**:API调用
|
||||
|
||||
#### 海康平台
|
||||
- **功能**:
|
||||
- 门禁设备管理
|
||||
- 门禁状态查询
|
||||
- 门禁控制(开关门)
|
||||
- 门禁事件记录
|
||||
- **集成方式**:API调用
|
||||
|
||||
### 3.4 设备层
|
||||
|
||||
#### 机器人
|
||||
- **功能**:
|
||||
- 执行预设动作
|
||||
- 采集环境数据
|
||||
- 接收并执行控制指令
|
||||
- 反馈执行状态
|
||||
|
||||
#### 门禁设备
|
||||
- **功能**:
|
||||
- 开关门控制
|
||||
- 状态反馈
|
||||
- 事件记录
|
||||
|
||||
#### 摄像头
|
||||
- **功能**:
|
||||
- 图像采集
|
||||
- 视频监控
|
||||
- 人脸数据采集
|
||||
|
||||
## 4. 核心工作流程
|
||||
|
||||
### 4.1 用户认证流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 用户 as 用户
|
||||
participant Web as Web控制台/Pad控制端
|
||||
participant FastAPI as FastAPI后端
|
||||
participant Redis as Redis缓存
|
||||
participant DB as 数据库
|
||||
|
||||
用户->>Web: 输入用户名密码
|
||||
Web->>FastAPI: POST /system/user/login
|
||||
FastAPI->>DB: 查询用户信息
|
||||
DB-->>FastAPI: 返回用户数据
|
||||
FastAPI->>FastAPI: 验证密码
|
||||
FastAPI->>Redis: 生成并存储token
|
||||
Redis-->>FastAPI: 返回token
|
||||
FastAPI-->>Web: 返回token和用户信息
|
||||
Web->>Web: 存储token
|
||||
```
|
||||
|
||||
### 4.2 门禁控制流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Pad as Pad控制端
|
||||
participant FastAPI as FastAPI后端
|
||||
participant HaiKang as 海康平台API
|
||||
participant Door as 门禁设备
|
||||
|
||||
Pad->>FastAPI: POST /system/door/control_door
|
||||
FastAPI->>HaiKang: 请求门禁控制
|
||||
HaiKang->>Door: 发送开关指令
|
||||
Door-->>HaiKang: 执行结果
|
||||
HaiKang-->>FastAPI: 返回控制结果
|
||||
FastAPI-->>Pad: 返回操作状态
|
||||
```
|
||||
|
||||
### 4.3 人脸识别流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Camera as 摄像头
|
||||
participant Compreface as Compreface
|
||||
participant FastAPI as FastAPI后端
|
||||
participant DB as 数据库
|
||||
participant Robot as 机器人
|
||||
|
||||
Camera->>Compreface: 上传人脸图像
|
||||
Compreface->>Compreface: 检测人脸
|
||||
Compreface->>Compreface: 提取特征
|
||||
Compreface->>FastAPI: 返回识别结果
|
||||
FastAPI->>DB: 保存识别记录
|
||||
FastAPI->>Robot: 发送响应指令
|
||||
Robot-->>FastAPI: 执行结果
|
||||
```
|
||||
|
||||
### 4.4 智能对话流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as 用户
|
||||
participant Web as Web/Pad客户端
|
||||
participant FastAPI as FastAPI后端
|
||||
participant Redis as Redis缓存
|
||||
participant RAGFlow as RAGFlow
|
||||
participant Search as 搜索API
|
||||
participant LLM as 大语言模型
|
||||
|
||||
User->>Web: 输入问题
|
||||
Web->>FastAPI: POST /system/ragflow/converse_with_chat_assistant
|
||||
FastAPI->>Redis: 检查缓存
|
||||
alt 缓存命中
|
||||
Redis-->>FastAPI: 返回缓存结果
|
||||
else 缓存未命中
|
||||
FastAPI->>RAGFlow: 发送对话请求
|
||||
RAGFlow->>Search: 检查是否需要搜索增强
|
||||
alt 需要搜索增强
|
||||
Search->>SearchEngine: 执行搜索
|
||||
SearchEngine-->>Search: 返回搜索结果
|
||||
Search-->>RAGFlow: 返回搜索内容
|
||||
end
|
||||
RAGFlow->>LLM: 调用大语言模型
|
||||
LLM-->>RAGFlow: 返回生成内容
|
||||
RAGFlow-->>FastAPI: 返回对话结果
|
||||
FastAPI->>Redis: 存储结果到缓存
|
||||
end
|
||||
FastAPI-->>Web: 返回对话结果
|
||||
Web->>User: 显示回答
|
||||
```
|
||||
|
||||
## 5. 系统集成与依赖
|
||||
|
||||
### 5.1 内部依赖
|
||||
|
||||
| 依赖名称 | 用途 | 版本 |
|
||||
|---------|------|------|
|
||||
| FastAPI | 后端框架 | 最新 |
|
||||
| SQLAlchemy | ORM框架 | 最新 |
|
||||
| Redis | 缓存服务 | 最新 |
|
||||
| Pydantic | 数据验证 | 最新 |
|
||||
| python-jose | JWT认证 | 最新 |
|
||||
|
||||
### 5.2 外部服务依赖
|
||||
|
||||
| 服务名称 | 用途 | 集成方式 |
|
||||
|---------|------|---------|
|
||||
| RAGFlow | 文档检索与智能对话 | API调用 |
|
||||
| Compreface | 人脸识别 | API调用 |
|
||||
| 海康平台 | 门禁控制 | API调用 |
|
||||
| SerpAPI | 搜索增强 | API调用 |
|
||||
|
||||
## 6. 系统特性与优势
|
||||
|
||||
1. **模块化设计**:系统采用模块化架构,各功能模块独立,便于维护和扩展
|
||||
2. **前后端分离**:前端使用Vue.js,后端使用FastAPI,提高开发效率和系统性能
|
||||
3. **多端支持**:支持Web控制台和Pad控制端,满足不同场景的使用需求
|
||||
4. **AI能力集成**:集成了人脸识别、智能对话和搜索增强等AI能力,提升系统智能化水平
|
||||
5. **丰富的外部接口**:与门禁系统、机器人硬件等多种外部设备和服务进行集成
|
||||
6. **完善的权限管理**:支持基于角色的权限控制,确保系统安全
|
||||
7. **实时监控与统计**:提供系统统计和识别记录管理,便于数据分析和决策
|
||||
|
||||
## 7. 系统部署架构
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph 服务器集群
|
||||
LoadBalancer["负载均衡器\n(Nginx)"]
|
||||
Backend1["FastAPI节点1"]
|
||||
Backend2["FastAPI节点2"]
|
||||
Backend3["FastAPI节点3"]
|
||||
end
|
||||
|
||||
subgraph 数据层
|
||||
DB[("主数据库\n(MySQL/PostgreSQL)")]
|
||||
DB2[("从数据库\n(MySQL/PostgreSQL)")]
|
||||
RedisCluster["Redis集群\n(主从架构)"]
|
||||
end
|
||||
|
||||
subgraph 外部服务
|
||||
RAGFlow["RAGFlow服务"]
|
||||
Compreface["Compreface服务"]
|
||||
HaiKang["海康平台"]
|
||||
end
|
||||
|
||||
用户["用户"] -->|HTTP请求| LoadBalancer
|
||||
LoadBalancer -->|分发请求| Backend1
|
||||
LoadBalancer -->|分发请求| Backend2
|
||||
LoadBalancer -->|分发请求| Backend3
|
||||
|
||||
Backend1 -->|读写| DB
|
||||
Backend2 -->|读写| DB
|
||||
Backend3 -->|读写| DB
|
||||
DB -->|同步| DB2
|
||||
|
||||
Backend1 -->|缓存操作| RedisCluster
|
||||
Backend2 -->|缓存操作| RedisCluster
|
||||
Backend3 -->|缓存操作| RedisCluster
|
||||
|
||||
Backend1 -->|API调用| RAGFlow
|
||||
Backend2 -->|API调用| RAGFlow
|
||||
Backend3 -->|API调用| RAGFlow
|
||||
|
||||
Backend1 -->|API调用| Compreface
|
||||
Backend2 -->|API调用| Compreface
|
||||
Backend3 -->|API调用| Compreface
|
||||
|
||||
Backend1 -->|API调用| HaiKang
|
||||
Backend2 -->|API调用| HaiKang
|
||||
Backend3 -->|API调用| HaiKang
|
||||
```
|
||||
|
||||
## 8. 系统扩展性设计
|
||||
|
||||
1. **微服务架构支持**:系统可扩展为微服务架构,将不同功能模块拆分为独立服务
|
||||
2. **插件化设计**:支持通过插件方式扩展系统功能
|
||||
3. **API网关集成**:可集成API网关,统一管理API访问
|
||||
4. **容器化部署**:支持Docker容器化部署,便于水平扩展
|
||||
5. **云原生支持**:支持在Kubernetes等容器编排平台上部署
|
||||
|
||||
## 9. 系统安全设计
|
||||
|
||||
1. **身份认证与授权**:采用JWT令牌机制,支持基于角色的访问控制
|
||||
2. **数据加密**:敏感数据加密存储,传输过程使用HTTPS
|
||||
3. **接口安全**:所有API接口均需认证,部分接口需额外权限验证
|
||||
4. **日志审计**:记录所有用户操作和系统事件,便于审计和追踪
|
||||
5. **防SQL注入**:使用ORM框架和参数化查询,防止SQL注入攻击
|
||||
6. **防XSS攻击**:对输入输出进行过滤,防止跨站脚本攻击
|
||||
|
||||
## 10. 总结
|
||||
|
||||
康达机器人系统采用现代化的技术架构,集成了多种AI能力和外部服务,为用户提供全面的机器人管理和控制解决方案。系统具有良好的扩展性、安全性和可维护性,能够满足不同规模和场景的使用需求。
|
||||
@ -1,587 +0,0 @@
|
||||
一、公司概况类
|
||||
1.康达新材料(集团)股份有限公司成立于哪一年?
|
||||
•答案:1988年
|
||||
2.康达新材料(集团)股份有限公司的上市时间是什么时候?
|
||||
•答案:2012年4月
|
||||
3.康达新材料(集团)股份有限公司的股票代码是什么?
|
||||
•答案:002669.SZ
|
||||
4.康达新材的实际控制人是谁?
|
||||
•答案:唐山市人民政府国有资产监督管理委员会
|
||||
5.康达新材的股权背景链条是怎样的?
|
||||
•答案:唐山市人民政府国有资产监督管理委员会→唐山控股发展集团股份有限公司→唐山工业控股集团有限公司→康达新材料(集团)股份有限公司
|
||||
6.康达新材的三大业务板块是什么?
|
||||
•答案:胶粘剂与特种树脂新材料、电子信息材料和电子科技
|
||||
7.康达新材的业务范围覆盖哪些新兴产业?
|
||||
•答案:装备制造、新能源、轨道交通、航空航天、电子信息及低碳环保等新兴产业
|
||||
8.截至2024年底,康达新材的总资产是多少?
|
||||
•答案:69.40亿元
|
||||
9.康达新材2024年的营业收入是多少?
|
||||
•答案:31亿元
|
||||
10.截至2024年底,康达新材的员工总人数有多少?
|
||||
•答案:1637人
|
||||
11.康达新材的管理总部位于哪里?
|
||||
•答案:浦东新区张江科学城
|
||||
12.康达新材在国内哪些地区设有子公司、生产基地或分支机构?
|
||||
•答案:上海、北京、天津、深圳、大连、成都、香港、福建、唐山等地
|
||||
13.康达新材的海外布局有多少处?
|
||||
•答案:3处
|
||||
14.康达新材有多少个生产基地?
|
||||
•答案:7个
|
||||
15.康达新材的胶粘剂产品牌号数有多少?
|
||||
•答案:500+
|
||||
16.康达新材在多个细分领域的市场地位如何?
|
||||
•答案:多项细分领域市场地位第一
|
||||
17.康达新材的使命是什么?
|
||||
•答案:服务国家战略,引领行业发展
|
||||
18.康达新材的愿景是什么?
|
||||
•答案:成为具有全球化视野的“新材料+电子科技”集团化企业
|
||||
19.康达新材的核心价值观是什么?
|
||||
•答案:融合、协同、创新、超越
|
||||
20.康达新材的企业作风是什么?
|
||||
•答案:不断学习,立即行动,没有任何借口
|
||||
21.康达新材的发展战略核心是什么?
|
||||
•答案:坚守“稳中求进”工作总基调,以科技创新为根本动力,以转型升级、抢占新赛道为基本路径,以高质量发展为主题,立足主业,打造“新材料+电子科技”国家级“专精特新”小巨人企业集群
|
||||
22.康达新材“1+2+3+1+N”战略布局中的“1个产业研究院”具体定位是什么?
|
||||
•答案:产业发展智库,科技成果研发、转化合作平台,技术信息共享中心
|
||||
23.康达新材“1+2+3+1+N”战略布局中的“2个战略新兴产业方向”是什么?
|
||||
•答案:新材料+电子科技
|
||||
24.康达新材“1+2+3+1+N”战略布局中的“3大业务板块”具体指什么?
|
||||
•答案:胶粘剂与特种树脂新材料+电子信息材料+电子科技
|
||||
25.康达新材“1+2+3+1+N”战略布局中的“1+N个产业园(基地)”已在哪些地区建立?
|
||||
•答案:上海、北京、成都、天津、大连、深圳、唐山、福建邵武、香港、泰国等地
|
||||
26.康达新材在新材料领域的进口替代目标涉及哪些领域?
|
||||
•答案:半导体材料、显示材料、生物医用材料、新能源材料、高性能纤维、高性能膜材料、先进高分子材料及其他领域
|
||||
27.康达新材在半导体材料领域的进口依赖材料有哪些?
|
||||
•答案:大尺寸硅材料、大尺寸碳化硅单晶、SOI、高饱和度光刻胶、高性能靶材、电子特种气体、湿电子化学品、氮化镓单晶/氮化镓单晶衬底、化学机械抛光(CMP)材料、封装基板、高密度陶瓷材料等
|
||||
28.康达新材在显示材料领域的进口依赖材料有哪些?
|
||||
•答案:OLED发光材料、超薄玻璃、高世代线玻璃基板、精细金属掩模板(FMM)、光学膜、柔性PI膜、偏光片、高性能水汽阻隔膜、异方性导电胶膜(ACF)、特种光学聚氨酯膜(PET)、OCA光学胶、微球、抗指纹涂层(AFC)涂料等
|
||||
29.康达新材在生物医用材料领域的进口依赖材料有哪些?
|
||||
•答案:医用级钛粉与镍钛合金粉、苯乙烯类热塑性弹性体、医用级聚乳酸、碲锌镉晶体、人工晶状体等
|
||||
30.康达新材在新能源材料领域的进口依赖材料有哪些?
|
||||
•答案:硅碳负极材料、电解铜箔、电解液添加剂、铝塑膜、质子交换膜、氢燃料电池催化剂、气体扩散层材料等
|
||||
31.康达新材在高性能纤维领域的进口依赖材料有哪些?
|
||||
•答案:高性能碳纤维及其复合材料、高性能对位芳纶纤维及其复合材料、超高分子量聚乙烯纤维等
|
||||
32.康达新材在高性能膜材料领域的进口依赖材料有哪些?
|
||||
•答案:海水淡化反渗透膜、陶瓷膜、离子交换膜、中空纤维膜、高导热石墨膜等
|
||||
33.康达新材在先进高分子材料领域的进口依赖材料有哪些?
|
||||
•答案:聚苯硫醚(PPS)、聚砜(PSF)、聚醚醚酮(PEEK)、聚偏氯乙烯(PVDF)、聚甲醛(POM)、有机硅等
|
||||
34.康达新材在“其他”领域的进口依赖材料有哪些?
|
||||
•答案:高频覆铜板基材、液晶高分子聚合物(LCP)等
|
||||
35.康达新材及下属子公司中获得国家级“专精特新”小巨人企业认定的有哪些?
|
||||
•答案:大连齐化新材料有限公司(有效期:2022年7月1日至2025年6月30日)、成都必控科技有限责任公司、上海晶材新材料科技有限公司、康达新材料(集团)股份有限公司
|
||||
36.北京力源兴达科技有限公司获得了什么级别的“专精特新”相关认定?
|
||||
•答案:北京市专精特新“小巨人”及中小企业
|
||||
37.成都赛英科技有限公司获得了什么级别的“专精特新”相关认定?有效期是多久?
|
||||
•答案:四川省专精特新中小企业,有效期为2022年11月1日-2025年10月31日
|
||||
38.上海理日新材料科技有限公司获得了什么级别的“专精特新”相关认定?
|
||||
•答案:上海市专精特新中小企业
|
||||
39.大连齐化获得了哪些资质认证?
|
||||
•答案:2020年12月被科技部火炬中心认定为国家级“高新技术企业”,被辽宁省科技厅认定为“雏鹰企业”;2023年5月,质检中心被中国石油和化学工业联合会认定为“A级”石油和化工企业质量检验机构;通过ISO9001、ISO14001、ISO45001三体系认证
|
||||
40.河北惟新科技有限公司获得了什么资质?认定编号和有效期是多少?
|
||||
•答案:河北省科技型中小企业,认定编号:K23016712,有效期3年
|
||||
二、历史发展类
|
||||
1.康达新材在1988年发生了什么关键事件?
|
||||
•答案:成立上海康达化工实验厂
|
||||
2.康达新材在2000年发生了什么关键事件?
|
||||
•答案:成立上海康达化工技术研究所
|
||||
3.康达新材在2002年完成了哪两项重要事件?
|
||||
•答案:引入战略投资者,完成股份制改革,整体变更设立为上海康达化工新材料股份有限公司;获得中国合格评定国家认可委员会(CNAS)认可的实验室资质
|
||||
4.康达新材在2008年发生了什么关键事件?
|
||||
•答案:成立上海康达化工有限责任公司;被认定为国家高新技术企业
|
||||
5.康达新材在2012年发生了什么关键事件?
|
||||
•答案:成功在深圳交易所上市
|
||||
6.康达新材在2016年发生了什么关键事件?
|
||||
•答案:非公开发行股票融资8.5亿元,助推康达新材高质量发展
|
||||
7.2018年康达新材收购了成都赛英科技有限公司,此次收购的目的是什么?
|
||||
•答案:完善电子科技领域产业链布局
|
||||
8.2018年康达新材收购了上海晶材新材料科技有限公司,此次收购的目的是什么?
|
||||
•答案:布局先进陶瓷新材料领域,打造“专精特新”小巨人企业集群
|
||||
9.2018年康达新材收购了大连齐化新材料有限公司,此次收购的目的是什么?
|
||||
•答案:向产业链上游战略延伸,打造环氧树脂产业闭环“生态圈”
|
||||
10.2019年康达新材收购了成都必控科技有限责任公司,此次收购的意义是什么?
|
||||
•答案:成功迈入电子科技领域
|
||||
11.康达新材的研发中心被认定为国家企业技术中心是在哪一年?
|
||||
•答案:2019年
|
||||
12.康达新材的控股股东变更为唐山工业控股集团有限公司是在哪一年?
|
||||
•答案:2019年
|
||||
13.康达新材更名为“康达新材料(集团)股份有限公司”是在什么时间?
|
||||
•答案:2021年
|
||||
14.康达新材在2021年进行集团化战略布局的范围是什么?
|
||||
•答案:全国区域
|
||||
15.康达新材被认定为国家级“专精特新”小巨人企业是在哪一年?
|
||||
•答案:2021年
|
||||
16.2022年康达新材收购了西安彩晶光电科技股份有限公司,此次收购进入了哪些领域?
|
||||
•答案:电子信息材料、新能源材料、医药领域,打造新材料“第二增长极”
|
||||
17.康达新材收购河北惟新科技有限公司是在哪一年?
|
||||
•答案:2023年
|
||||
18.康达新材收购河北惟新科技有限公司解决了什么“卡脖子”问题?
|
||||
•答案:大尺寸ITO靶材“卡脖子”问题
|
||||
三、生产基地与研发中心类
|
||||
1.奉贤研发及生产基地(上海)的位置在哪里?
|
||||
•答案:上海奉贤杭州湾经济技术开发区
|
||||
2.奉贤研发及生产基地(上海)的占地面积是多少?
|
||||
•答案:205亩
|
||||
3.奉贤研发及生产基地(上海)的建设历程是怎样的?
|
||||
•答案:包含一期、二期、三期项目,2009年一期建成投产,2015年二期建成投产,2019年三期建成投产
|
||||
4.奉贤研发及生产基地(上海)的定位是什么?
|
||||
•答案:遵循高端化、数字化、一体化、绿色生态的发展理念,打造胶粘剂、复合材料等新材料产业链;奉贤基地二期全面启用后,形成集科研开发、生产、服务、贸易为一体的科研产业综合实体
|
||||
5.奉贤研发及生产基地(上海)的胶粘剂产品产能是多少?
|
||||
•答案:环氧结构胶、环氧基体树脂、丙烯酸胶、聚氨酯胶、丁基胶等胶粘剂产品产能80000吨
|
||||
6.奉贤研发及生产基地(上海)的复合材料产能是多少?
|
||||
•答案:环氧基、聚氨酯基复合材料10000吨
|
||||
7.唐山丰南生产基地(河北)的位置在哪里?
|
||||
•答案:河北省唐山市丰南经济开发区化工产业园区
|
||||
8.唐山丰南生产基地(河北)的占地面积是多少?
|
||||
•答案:114亩
|
||||
9.唐山丰南生产基地(河北)的主要产品有哪些?
|
||||
•答案:水性聚氨酯丙烯酸酯分散体、无溶剂复膜胶、改性型溶剂聚氨酯复膜胶、改性橡胶树脂溶剂胶、高性能丙烯酸酯结构胶、硅胶、环氧树脂胶
|
||||
10.唐山丰南生产基地(河北)辐射哪些市场?
|
||||
•答案:北方市场
|
||||
11.唐山丰南生产基地(河北)的产品应用于哪些领域?
|
||||
•答案:风电叶片制造、包装材料、轨道交通、汽车、电子电器、机械设备、建筑等领域(含在建基地)
|
||||
12.福建邵武生产基地的位置在哪里?
|
||||
•答案:福建省邵武市金塘省级化工园区
|
||||
13.福建邵武生产基地的占地面积是多少?
|
||||
•答案:95亩
|
||||
14.福建邵武生产基地的主要产品有哪些?
|
||||
•答案:功能型复膜胶、改性弹性树脂胶黏剂、高性能结构胶、功能性聚酯及反应型热熔胶、特种树脂中间体
|
||||
15.福建邵武生产基地辐射哪些市场?
|
||||
•答案:南方市场
|
||||
16.福建邵武生产基地的产品应用于哪些领域?
|
||||
•答案:包装材料、电子电器、机械设备、建筑装饰及工业维修等领域
|
||||
17.福建邵武生产基地的战略意义是什么?
|
||||
•答案:为核心产品提供重要原材料(含在建基地)
|
||||
18.大连齐化产业园区的位置在哪里?
|
||||
•答案:辽宁省大连经济技术开发区
|
||||
19.大连齐化产业园区的主要产品系列有哪些?
|
||||
•答案:双酚A型环氧树脂、耐热型环氧树脂和特种环氧树脂三大系列
|
||||
20.大连齐化产业园区的具体产品包含哪些?
|
||||
•答案:双酚A型液体环氧树脂、双酚A型固体环氧树脂、溴化环氧树脂、邻甲酚醛环氧树脂、苯酚酚醛环氧树脂、双酚F型环氧树脂、苯氧基树脂、环氧固化剂、环氧稀释剂等多个品种
|
||||
21.大连齐化产业园区液态环氧树脂的装置数量、产能和实际产能分别是多少?
|
||||
•答案:装置数量1套,产能3.4万吨,实际产能4.3万吨
|
||||
22.大连齐化产业园区固态环氧树脂的装置数量、产能和实际产能分别是多少?
|
||||
•答案:装置数量1套,产能0.5万吨,实际产能1.5万吨
|
||||
23.大连齐化产业园区邻甲酚醛环氧树脂的装置数量和产能分别是多少?
|
||||
•答案:装置数量1套,产能0.5万吨
|
||||
24.大连齐化产业园区特种树脂中试装置的数量和产能分别是多少?
|
||||
•答案:装置数量1套,产能1000吨
|
||||
25.大连齐化产业园区的战略定位是什么?
|
||||
•答案:重点布局下游产业链,打造环氧树脂产业闭环“生态圈”,以大连齐化的低溴环氧树脂技术为依托,开展覆铜板领域的战略布局
|
||||
26.成都康达电子科技产业园的位置在哪里?
|
||||
•答案:四川省成都市高新区未来科技城
|
||||
27.成都康达电子科技产业园的占地面积是多少?
|
||||
•答案:64亩
|
||||
28.成都康达电子科技产业园的定位是什么?
|
||||
•答案:打造电子科技领域系统级产品研发、生产科技创新园区,使之成为四川乃至西南地区具有比较优势的电子信息特色产业园,推动地方战略性新兴产业的发展,助力公司电子科技核心业务做强做精
|
||||
29.康达新材的胶粘剂研发中心拥有哪些资质平台?
|
||||
•答案:国家级的企业技术中心、上海胶粘剂工程技术研究中心、上海市企业技术中心、企业博士后科研工作总站、国家认定博士后科研工作站(人力资源和社会保障部、全国博士后管委会制发)、国家认可委认可的CNAS国家实验室、德国劳氏船级社(GL)中国认可检测中心
|
||||
30.康达新材胶粘剂研发中心的技术型研发占比多少?主要内容是什么?
|
||||
•答案:占比25%,主要内容是引进项目的消化、吸收
|
||||
31.康达新材胶粘剂研发中心的产品型研发占比多少?主要内容是什么?
|
||||
•答案:占比50%,主要内容是现有产品的再创新;中试生产及工艺优化
|
||||
32.康达新材胶粘剂研发中心的关键技术研发占比多少?主要内容是什么?
|
||||
•答案:占比20%,主要内容是关键原材料及高端技术研发,为产品持续的市场竞争力提供支持
|
||||
33.康达新材胶粘剂研发中心的技术服务型研发占比多少?主要内容是什么?
|
||||
•答案:占比5%,主要内容是与目前的板块服务相对应
|
||||
四、财务数据类
|
||||
1.康达新材2021年的营业收入是多少?
|
||||
•答案:27.93亿元
|
||||
2.康达新材2021年归属于上市公司股东的净利润是多少?
|
||||
•答案:-2.46亿元
|
||||
3.康达新材2023年的营业收入是多少?
|
||||
•答案:8.77亿元
|
||||
4.康达新材2023年归属于上市公司股东的净利润是多少?
|
||||
•答案:0.06亿元
|
||||
5.康达新材2024年的营业收入是多少?
|
||||
•答案:31.01亿元
|
||||
6.康达新材2024年归属于上市公司股东的净利润是多少?
|
||||
•答案:0.30亿元
|
||||
7.2022年康达新材的研发人员数量是多少?
|
||||
•答案:376人
|
||||
8.2023年康达新材的研发人员数量是多少?
|
||||
•答案:378人
|
||||
9.2024年康达新材的研发人员数量是多少?
|
||||
•答案:486人
|
||||
10.2022-2024年康达新材的研发人员数量呈现什么趋势?
|
||||
•答案:呈线性增长趋势
|
||||
11.2022年康达新材的研发费用是多少?
|
||||
•答案:1.26亿元
|
||||
12.2023年康达新材的研发费用是多少?
|
||||
•答案:1.78亿元
|
||||
13.2024年康达新材的研发费用是多少?
|
||||
•答案:2.04亿元
|
||||
14.2022-2024年康达新材的研发费用呈现什么趋势?
|
||||
•答案:呈线性增长趋势
|
||||
15.2024年康达新材的研发人员数量占比是多少?
|
||||
•答案:22.97%
|
||||
16.2024年康达新材的非研发人员占比是多少?
|
||||
•答案:77.03%
|
||||
17.2024年康达新材的研发支出占营业收入总额的比例是多少?
|
||||
•答案:6.56%
|
||||
18.2024年康达新材的非研发支出占比是多少?
|
||||
•答案:93.44%
|
||||
19.康达新材2021年的总资产是多少?
|
||||
•答案:37.28亿元
|
||||
20.康达新材2022年的总资产是多少?
|
||||
•答案:53.33亿元
|
||||
21.康达新材2023年的总资产是多少?
|
||||
•答案:69.40亿元
|
||||
22.康达新材2024年的总资产是多少?
|
||||
•答案:71.23亿元
|
||||
23.康达新材2021年的净资产是多少?
|
||||
•答案:22.91亿元
|
||||
24.康达新材2022年的净资产是多少?
|
||||
•答案:29.06亿元
|
||||
25.康达新材2023年的净资产是多少?
|
||||
•答案:31.43亿元
|
||||
26.康达新材2024年的净资产是多少?
|
||||
•答案:33.09亿元
|
||||
五、业务介绍类
|
||||
(一)胶粘剂与特种树脂新材料
|
||||
1.康达新材的胶粘剂与特种树脂新材料业务国内布局包含哪些企业?
|
||||
•答案:河北惟新科技、北京康达晟珊、北京力源兴达、唐山南区康达化工新材有限公司、康达国际供应链(天津)有限公司、大连齐化新材、上海康达新材料科技有限公司、上海理日新材、上海晶材科技、福建南平天宇、成都必控科技、福建康达鑫宇新材料有限公司、成都赛英科技、深圳康达电子材料研发有限公司、上海康达新材(香港)有限公司
|
||||
2.康达新材的胶粘剂与特种树脂新材料业务海外研发办事处设在哪些国家?
|
||||
•答案:德国、日本
|
||||
3.康达新材的胶粘剂与特种树脂新材料业务海外营销办事处设在哪个国家?
|
||||
•答案:印尼
|
||||
4.康达新材的胶粘剂与特种树脂新材料业务海外生产基地设在何处?
|
||||
•答案:泰国曼谷
|
||||
5.康达新材的胶粘剂与特种树脂新材料业务海外覆盖哪些区域?
|
||||
•答案:德国、丹麦、西班牙、日本、东南亚、巴西等
|
||||
6.胶粘剂与特种树脂新材料业务的产品体系包含多少大系列?
|
||||
•答案:8大产品系列
|
||||
7.胶粘剂与特种树脂新材料业务的产品体系包含多少种规格型号?
|
||||
•答案:500多种
|
||||
8.胶粘剂与特种树脂新材料业务的核心应用领域有哪些?
|
||||
•答案:风力发电、光伏太阳能、轨道交通、航空航天、海洋船舶工程、软包装复合、橡塑制品、建筑工程、电子电器、汽摩配件、电机电梯、矿业设备、工业维修、新能源汽车、工程维护、高端制造等
|
||||
9.康达新材在风电领域的核心产品有哪些?
|
||||
•答案:风电胶粘剂系列产品(结构胶、灌注树脂、胶条、喷胶)
|
||||
10.2024年康达新材风电领域的销售量是多少?
|
||||
•答案:近9万吨
|
||||
11.2024年康达新材风电领域的销售额(不含税)是多少?
|
||||
•答案:约16亿元
|
||||
12.康达新材风电领域的客户覆盖范围是什么?
|
||||
•答案:国内所有叶片厂商及其基地,同时产品向海外市场推广
|
||||
13.康达新材风电领域的核心客户有哪些?
|
||||
•答案:三一重能、中国中材、中国国电、艾郎科技、中科宇能、远景能源、金风科技、时代新材、上玻院、双瑞风电、上海电气、天顺风能、LM艾尔姆、明阳电气、MENS Gamesa等
|
||||
14.康达新材在风电领域的市场地位如何?
|
||||
•答案:市场占有率第一,自主产品保持品牌技术领先、行业领导地位
|
||||
15.康达新材在复膜胶领域的市场份额是多少?
|
||||
•答案:25%
|
||||
16.复膜胶领域中,法国波士胶的市场份额是多少?
|
||||
•答案:25%
|
||||
17.复膜胶领域中,德国汉高的市场份额是多少?
|
||||
•答案:20%
|
||||
18.复膜胶领域中,其他30余家企业的市场份额总和是多少?
|
||||
•答案:30%
|
||||
19.康达新材在复膜胶领域的市场地位如何?
|
||||
•答案:市场占有率第一
|
||||
20.2024年康达新材复膜胶领域的国内客户数量是多少?
|
||||
•答案:700家
|
||||
21.2024年康达新材复膜胶领域的销售额是多少?
|
||||
•答案:约3.2亿元
|
||||
22.康达新材复膜胶领域的销售额年增长率是多少?
|
||||
•答案:10-15%
|
||||
23.康达新材复膜胶领域的软包装复合胶包含哪些类型?
|
||||
•答案:溶剂型聚氨酯复膜胶、无溶剂聚氨酯复膜胶
|
||||
24.康达新材复膜胶领域的硬包装胶包含哪些类型?
|
||||
•答案:外包装热熔胶、标签快递单压敏胶等
|
||||
25.康达新材复膜胶领域的能量固化印刷油墨包含哪些类型?
|
||||
•答案:UV胶印油墨、UV柔版光油等
|
||||
26.康达新材复膜胶领域的可降解材料包含哪些类型?
|
||||
•答案:酪蛋白标签胶、环保餐具涂层
|
||||
27.康达新材复膜胶领域的发展规划有哪些?
|
||||
•答案:传统替代、新品攻关(UV油墨、可降解材料)、专业化服务、节能环保、对接“一带一路”
|
||||
28.康达新材在轨道交通领域的核心产品有哪些?
|
||||
•答案:双组份丙烯酸酯结构胶、单组份结构胶、UV胶、PUR、双组份环氧胶、单组份环氧胶、瞬干胶等系列化产品(可根据客户需求个性化调整)
|
||||
29.康达新材轨道交通领域重点布局哪些行业?
|
||||
•答案:消费电子产品组装行业、微型扬声器行业、安防行业、动力电池行业
|
||||
30.康达新材轨道交通领域在消费电子行业的核心客户有哪些?
|
||||
•答案:联想、华硕、罗技、韶音
|
||||
31.康达新材轨道交通领域的产品在消费电子行业应用于哪些产品组装?
|
||||
•答案:智能手机、笔记本/平板电脑、穿戴设备、无人机、投影仪组装
|
||||
32.康达新材轨道交通领域在微型扬声器行业的核心客户有哪些?
|
||||
•答案:瑞声、歌尔、豪声、上声
|
||||
33.康达新材轨道交通领域在安防行业的核心客户有哪些?
|
||||
•答案:海康威视、大华
|
||||
34.康达新材轨道交通领域针对高铁/地铁的产品有哪些?
|
||||
•答案:地板胶、阻尼浆、阻尼贴片
|
||||
35.康达新材轨道交通领域针对高铁/地铁的产品取得了哪些进展?
|
||||
•答案:阻尼贴片材料通过中车系统检测验证,地板胶阻尼浆在中车系统重点客户试装成功
|
||||
36.康达新材通用工业领域的销售渠道以什么为主?
|
||||
•答案:经销商销售渠道
|
||||
37.康达新材通用工业领域重点覆盖哪些市场?
|
||||
•答案:通用工业(工业装配维修、扬声器、电梯、电机、结构胶等各类工业应用)、流通市场(民用及家装DIY、电商)
|
||||
38.康达新材的汽车胶粘剂用于哪些场景?
|
||||
•答案:汽车厢体、车门的结构粘接及汽车内饰件粘接
|
||||
39.康达新材的汽车胶粘剂能为客户提供什么优化方案?
|
||||
•答案:汽车轻量化和减少VOCs排放的优化方案
|
||||
40.康达新材家电用胶粘剂的既定目标客户有哪些?
|
||||
•答案:格力、美的等国内知名家电企业
|
||||
41.康达新材家电用胶粘剂目前已涉及美的哪些产品应用?
|
||||
•答案:美的厨热、冰箱和家用空调等产品
|
||||
42.康达新材家电用胶粘剂未来的拓展计划是什么?
|
||||
•答案:拓展至中央空调、洗衣机等其他生活电器
|
||||
(二)核心子公司业务详情(胶粘剂与特种树脂新材料板块)
|
||||
1.大连齐化新材料有限公司的成立时间是什么时候?
|
||||
•答案:起步于2001年
|
||||
2.大连齐化新材料有限公司的企业定位是什么?
|
||||
•答案:以生产销售高品质环氧树脂为主,集新材料研制、开发、生产、销售、服务、转让为一体的综合性高新技术化工企业
|
||||
3.大连齐化新材料有限公司的产品体系包含哪些系列?
|
||||
•答案:双酚A型环氧树脂、耐热型环氧树脂和特种环氧树脂三大系列
|
||||
4.大连齐化新材料有限公司的具体产品有哪些?
|
||||
•答案:双酚A型液体环氧树脂、双酚A型固体环氧树脂、溴化环氧树脂、邻甲酚醛环氧树脂、苯酚酚醛环氧树脂、双酚F型环氧树脂、苯氧基树脂、环氧固化剂、环氧稀释剂等
|
||||
5.大连齐化新材料有限公司的产品质量指标有什么优势?
|
||||
•答案:各项指标均优于中国国家标准GB/T 13657-2011《双酚A型环氧树脂》,通过RoSH和REACH认证,双酚A型环氧树脂被辽宁省工信厅认定为“专精特新”产品并获欧洲化学品管理局(ECHA)“REACH注册证书”
|
||||
6.大连齐化新材料有限公司拥有多少项实用新型专利?
|
||||
•答案:21项
|
||||
7.大连齐化新材料有限公司拥有多少项软件著作权?
|
||||
•答案:20项
|
||||
8.大连齐化新材料有限公司正在申请多少项发明专利?
|
||||
•答案:2项
|
||||
9.大连齐化新材料有限公司通过了哪些体系认证?
|
||||
•答案:ISO9001、ISO14001、ISO45001三体系认证
|
||||
10.大连齐化新材料有限公司获得国家级“高新技术企业”认定的时间是什么时候?
|
||||
•答案:2020年12月
|
||||
11.大连齐化新材料有限公司获得辽宁省科技厅“雏鹰企业”认定的时间是什么时候?
|
||||
•答案:2020年12月
|
||||
12.大连齐化新材料有限公司获得国家级“专精特新”小巨人企业认定的时间是什么时候?
|
||||
•答案:2021年7月
|
||||
13.大连齐化新材料有限公司的销售模式是什么?
|
||||
•答案:直销模式
|
||||
14.南平天宇实业被康达新材收购的时间是什么时候?
|
||||
•答案:2021年4月
|
||||
15.南平天宇实业的占地面积是多少?
|
||||
•答案:33000㎡
|
||||
16.南平天宇实业的年产产值是多少?
|
||||
•答案:近6000万元
|
||||
17.南平天宇实业的生产工艺技术水平如何?
|
||||
•答案:国际领先水平
|
||||
18.南平天宇实业的产品定位是什么?
|
||||
•答案:亚洲国家“水性商标胶粘剂”生产量最大、生产品种最多、工艺技术最领先的绿色环保水基性商标胶粘剂生产企业
|
||||
19.南平天宇实业的“远洋牌”水性系列商标胶有什么特点?
|
||||
•答案:质量超越国际领先水平,性能稳定
|
||||
20.南平天宇实业的“远洋牌”封箱专用热熔胶有什么特点?
|
||||
•答案:可塑性粘合剂,无毒、无味,属绿色环保型产品,物理状态随温度改变(化学特性不变)
|
||||
21.南平天宇实业的“远洋牌”水性纸制品胶粘剂的原材料是什么?
|
||||
•答案:优质高分子材料PVA、VAC、改性淀粉共聚合成
|
||||
22.南平天宇实业的“远洋牌”水性纸制品胶粘剂有什么特点?
|
||||
•答案:无毒性、无公害、无腐蚀性及氧化性,符合欧美最新标准,为食品级纸制品行业不可替代的绿色环保产品
|
||||
23.南平天宇实业的核心客户有哪些?
|
||||
•答案:百威啤酒、珠江啤酒、青岛啤酒、雪花啤酒、达利食品集团等
|
||||
24.上海理日新材料的技术地位如何?
|
||||
•答案:成熟的研发创新能力,技术和产品保持国内领先、国际先进水平,国内市场品牌影响和占有率仅次于德国汉高等顶级跨国公司,产品已通过多家著名跨国公司认证并供货
|
||||
25.上海理日新材料的应用领域有哪些?
|
||||
•答案:汽车部件、电子电缆、衣饰、制鞋、建材、装饰、包装以及低压注塑等行业
|
||||
26.上海理日新材料服务多少家客户?
|
||||
•答案:数百家
|
||||
27.上海理日新材料的环保型高性能热熔胶包含哪些类型?
|
||||
•答案:聚酯、聚酰胺热熔胶
|
||||
28.上海理日新材料的环保型高性能热熔胶有什么特点?
|
||||
•答案:不含化学溶剂、无污染,粘接强度高,耐油耐化学性好,粘接材料广泛
|
||||
29.上海理日新材料的环保型高性能热熔胶应用于哪些企业?
|
||||
•答案:汽车滤清器生产企业、制鞋服饰企业、电子电缆生产企业
|
||||
30.上海理日新材料的环保型低压注塑封装材料的目标市场是什么?
|
||||
•答案:电缆电子行业中的电池板生产企业、线束插头/微型开关生产企业、连接器/传感器生产企业和PCB线路板生产企业
|
||||
31.四氢呋喃可作为上海理日新材料产品的哪些应用领域?
|
||||
•答案:合成树脂的工业溶剂、合成革的表面处理剂、合成农药等领域
|
||||
(三)高性能复合材料方向
|
||||
1.康达新材在高性能复合材料方向的战略定位是什么?
|
||||
•答案:聚焦玻璃纤维、碳纤维的环氧树脂、聚氨酯类复合材料领域,计划通过2-3年深化拓展;基于自身工艺和技术积累,通过投资业务契合度企业、技术创新、加大研发投入等方式,切入先进复合材料领域,推动向高分子新材料领域战略转型
|
||||
2.康达新材落实先进复合材料转型战略的关键举措是什么?
|
||||
•答案:对聚发新材进行增资
|
||||
3.拉挤树脂(Urepul)的基础材料是什么?
|
||||
•答案:玻纤&碳纤
|
||||
4.拉挤树脂(Urepul)的应用领域有哪些?
|
||||
•答案:桥架、绝缘梯、桥梁、高压管道、景观工程、高铁、轨道、光伏
|
||||
5.SMC/预浸料(Urepreg)的基础材料是什么?
|
||||
•答案:玻纤&碳纤
|
||||
6.SMC/预浸料(Urepreg)的应用领域有哪些?
|
||||
•答案:汽车、轨道交通、电子产品
|
||||
7.RTM树脂(Urethan)的基础材料是什么?
|
||||
•答案:玻纤&碳纤
|
||||
8.RTM树脂(Urethan)的应用领域有哪些?
|
||||
•答案:风电叶片、汽车内外饰、高铁
|
||||
9.缠绕树脂(Urecom)的基础材料是什么?
|
||||
•答案:玻纤&碳纤
|
||||
10.缠绕树脂(Urecom)的应用领域有哪些?
|
||||
•答案:电线杆、传动轴
|
||||
11.截至2025年8月,康达新材高性能复合材料已实现量产应用的产品有哪些?
|
||||
•答案:电缆桥架、高铁设备舱及裙板、新能源车电池盒骨架
|
||||
12.截至2025年8月,康达新材高性能复合材料技术开发完成的产品有哪些?
|
||||
•答案:汽车型材、托盘、高铁步道板、绝缘臂腕
|
||||
13.截至2025年8月,康达新材高性能复合材料处于开发中的产品有哪些?
|
||||
•答案:太阳能边框及支架、铁路鱼尾板
|
||||
14.康达新材的高性能复合材料在性能方面有什么优势?
|
||||
•答案:出色的机械性能,与玻纤完美结合,更高的粘接强度,优异的抗疲劳性能
|
||||
15.康达新材的高性能复合材料在生产效率方面有什么优势?
|
||||
•答案:更快的灌注速度,更快的固化速度
|
||||
16.康达新材的高性能复合材料在综合成本方面有什么优势?
|
||||
•答案:模具投入少,具有竞争力的树脂价格
|
||||
(四)电子信息材料
|
||||
1.惟新科技被康达新材收购的时间是什么时候?
|
||||
•答案:2023年
|
||||
2.惟新科技的企业定位是什么?
|
||||
•答案:国家高新技术企业,致力于研发和生产高端ITO(氧化铟锡)靶材及ITO粉体、ITO气氛烧结炉等ITO周边产品,解决大尺寸ITO靶材“卡脖子”问题
|
||||
3.惟新科技的核心技术有哪些?
|
||||
•答案:与清华大学化工系合作,研发微反应器制备高活性纳米ITO粉体技术;自主研发适合超大尺寸靶材制备的湿法注浆成型技术,拥有自主知识产权的超大空间高温烧结炉
|
||||
4.惟新科技成功制备的大尺寸ITO靶材规格是多少?
|
||||
•答案:1550x250mm
|
||||
5.惟新科技制备的大尺寸ITO靶材性能指标如何?
|
||||
•答案:与国际同类靶材相当,达到国际先进水平,填补国内空白
|
||||
6.惟新科技的战略意义有哪些?
|
||||
•答案:打破技术壁垒,减少高端靶材进口依赖,助力中美贸易战应对;推动我国铟资源高效利用;填补我国ITO靶材在中、高端平板显示器制造领域应用空白;促进ITO靶材平板生产工艺研发与人才培养
|
||||
7.惟新科技的ITO靶材试验线设备升级改造完成时间是什么时候?
|
||||
•答案:2022年
|
||||
8.惟新科技目前的产能提升目标是什么?
|
||||
•答案:推进30T/年产能提升工作
|
||||
9.惟新科技的溅射靶材下游覆盖哪些领域?
|
||||
•答案:平板显示器、光伏电池、记录媒体、半导体等领域
|
||||
10.平板显示器领域在惟新科技溅射靶材下游应用中占比多少?
|
||||
•答案:33.8%
|
||||
11.记录媒体领域在惟新科技溅射靶材下游应用中占比多少?
|
||||
•答案:28.6%
|
||||
12.半导体领域在惟新科技溅射靶材下游应用中占比多少?
|
||||
•答案:18.5%
|
||||
13.晶材科技成立于哪一年?
|
||||
•答案:2016年
|
||||
14.晶材科技被康达新材收购的时间是什么时候?
|
||||
•答案:2018年
|
||||
15.晶材科技的企业定位是什么?
|
||||
•答案:专注于高端电子陶瓷材料国产化,集研发与规模化生产于一体,提供先进材料解决方案,代理德国瓦克化学有机硅产品
|
||||
16.晶材科技的技术路线是什么?
|
||||
•答案:微晶玻璃体系、微晶玻璃/陶瓷复合体系
|
||||
17.晶材科技在行业中的地位如何?
|
||||
•答案:目前市场唯一批量供货的国产生料带供应商,解决“卡脖子”难题
|
||||
18.晶材科技的自研产品有哪些?
|
||||
•答案:陶瓷生料带、贵金属浆料、瓷粉、导电胶
|
||||
19.晶材科技的自研产品应用于什么技术领域?
|
||||
•答案:低温共烧陶瓷技术(LTCC)的电子元器件、电路模块制造和封装
|
||||
20.晶材科技的代理产品是什么?
|
||||
•答案:有机硅水胶(车载显示器光学全贴合重要原材料)
|
||||
21.晶材科技的产品在军用领域有什么应用?
|
||||
•答案:有源相控阵雷达
|
||||
22.晶材科技的产品在民用领域有什么应用?
|
||||
•答案:高端半导体传感器、光通信器件、汽车电子电路模块、5G路由器、5G机房、数据中心等
|
||||
23.晶材科技的生料带现有产能是多少?
|
||||
•答案:240万片/年
|
||||
24.晶材科技的贵金属浆料现有产能是多少?
|
||||
•答案:30吨/年
|
||||
(五)电子科技——赛英科技
|
||||
1.赛英科技被康达新材收购的时间是什么时候?
|
||||
•答案:2018年
|
||||
2.赛英科技的地理位置在哪里?
|
||||
•答案:成都龙潭新经济产业功能区
|
||||
3.赛英科技的科研生产基地规模是多少?
|
||||
•答案:8000余平方米
|
||||
4.赛英科技的核心业务是什么?
|
||||
•答案:科研、生产整机雷达、微波组件、专用仪器仪表、海洋通讯产品
|
||||
5.赛英科技在安全管控领域的产品有哪些?
|
||||
•答案:近程警戒雷达(地面、海面)、周界安全警戒雷达(系列产品)
|
||||
6.赛英科技在交通监测领域的产品有哪些?
|
||||
•答案:FOD跑道异物监测雷达(塔架、移动)
|
||||
7.赛英科技在工业监测领域的产品有哪些?
|
||||
•答案:高炉料面监测雷达、微位移监测雷达(应用于大型钢厂、铁路大坝等重要区域)
|
||||
8.赛英科技获得了哪些企业荣誉?
|
||||
•答案:成都市企业技术中心、成都市成华区2020年度“工业纳税先进单位”、四川省“专精特新”中小企业、成都市科技装备业商会“副会长单位”等
|
||||
六、康达新材日常聊天类
|
||||
1.你能给康达新材创作一首诗吗?
|
||||
•答案:匠心三十载耕耘,新材科技破卡颈胶黏万物兴百业,引领制造向前行
|
||||
2.如果用三个关键词概括康达新材的核心气质,你觉得会是什么?为什么?
|
||||
•答案:硬核(深耕“卡脖子”技术)、创新(研发投入持续增长)、多元(覆盖多业务板块)
|
||||
3.假设你要给朋友介绍康达新材,会用一段轻松有趣的话怎么说?
|
||||
•答案:这家1988年成立的国企超牛!能做粘风电叶片的“超级胶水”,还搞定高端ITO靶材,业务横跨新材料和电子科技,妥妥的科技实力派~
|
||||
4.要是给康达新材设计一句接地气的宣传语,不局限于官方口号,你会怎么写?
|
||||
•答案:康达新材,粘得牢、造得硬,让中国制造更靠谱!
|
||||
5.想象康达新材要举办一场员工家庭日活动,你觉得可以设计哪些有趣的互动环节?
|
||||
•答案:①亲子胶粘剂组装模型;②VR参观生产基地答题;③家庭新材料元素手作赛
|
||||
6.如果你是康达新材的一名新员工,入职第一天最想了解公司的哪三个方面?
|
||||
•答案:①岗位与核心业务的关联;②研发创新支持政策;③培训与晋升路径
|
||||
7.用拟人化的手法描述康达新材,你会把它比作哪种形象?理由是什么?
|
||||
•答案:比作“实干工匠”,30余年坚守行业,身怀多元技能,持续突破技术难题
|
||||
8.康达新材涉及多个业务板块,要是用一种美食比喻它的业务布局,你觉得像什么?为什么?
|
||||
•答案:像“什锦火锅”,胶粘剂是锅底,各业务板块是食材,协同发力丰富产业生态
|
||||
9.要是给康达新材的研发人员写一句鼓励的话,你会怎么表达?
|
||||
•答案:以匠心攻技术,以创新破壁垒,为中国智造添力!
|
||||
10.假设康达新材要出一款周边产品送给客户,你觉得什么类型的产品既实用又能体现公司特色?
|
||||
•答案:定制多功能粘接工具套装,含迷你环保胶粘剂、ITO靶材图案书签
|
||||
11.用一句话形容康达新材从1988年成立到现在的发展历程,你会怎么说?
|
||||
•答案:从化工实验厂到“新材料+电子科技”集团,30余年坚守创新,领跑细分领域
|
||||
12.如果你要在社交平台分享康达新材的一则动态,配文会怎么写才能吸引关注?
|
||||
•答案:国企硬核!粘风电叶片、破ITO靶材“卡脖子”,覆盖新能源、电子科技,为中国制造打call~#康达新材 #国货之光
|
||||
13.康达新材在多个地区有生产基地,要是选一个基地推荐给朋友参观,你会选哪个?理由是什么?
|
||||
•答案:选上海奉贤基地,集研发生产于一体,能直观看到产品从研发到量产的全过程
|
||||
14.要是给康达新材设计一个吉祥物,你觉得它的外形和寓意应该是什么样的?
|
||||
•答案:蓝色分子造型小精灵,带翅膀(象征创新),头顶ITO靶材,寓意科技赋能
|
||||
15.想象康达新材要拍摄一支企业宣传片,你觉得开篇画面应该是什么场景?
|
||||
•答案:高空俯瞰全球生产基地,快速切换研发实验室、生产线、风电应用场景,定格企业口号
|
||||
16.如果你是康达新材的客服,接到客户咨询时,第一句暖心的开场白会怎么说?
|
||||
•答案:您好!康达新材客服为您服务,有任何产品或合作问题,我都会全力解答~
|
||||
17.用三个 emoji 代表康达新材的业务板块,你会选哪三个?分别对应什么业务?
|
||||
•答案:🧪(胶粘剂与特种树脂)、💻(电子科技)、📱(电子信息材料)
|
||||
18.康达新材有不少“专精特新”子公司,要是给这些子公司办一场表彰会,颁奖词可以怎么写?
|
||||
•答案:深耕细分领域,以硬科技破壁垒、填空白,为产业生态注入强劲动力,致敬每一份坚守!
|
||||
19.假设你要给康达新材的员工设计一件文化衫,正面和背面会印什么图案或文字?
|
||||
•答案:正面:康达新材logo+“以新材赋能”;背面:三大业务图标+“融合 协同 创新 超越”
|
||||
20.用简单的语言给小朋友解释康达新材生产的胶粘剂是做什么用的,你会怎么说?
|
||||
•答案:这是“超级胶水”,能把风电叶片、汽车零件粘得牢牢的,让它们更结实哦~
|
||||
21.要是康达新材要发起一个公益活动,结合它的业务特点,你觉得可以开展什么主题的活动?
|
||||
•答案:“绿色新材助环保”,用环保胶粘剂、可降解材料为乡村学校修补课桌椅
|
||||
22.如果你参加康达新材的年会,最期待看到哪个环节的表演?为什么?
|
||||
•答案:业务板块创意秀,能通过表演直观了解各部门工作,还能感受团队风采
|
||||
23.用对比的方式说说康达新材和你印象中的传统化工企业有什么不同?
|
||||
•答案:传统化工多单一业务、低技术,康达聚焦新兴产业,研发投入高、业务多元且绿色环保
|
||||
24.康达新材的使命是“服务国家战略,引领行业发展”,要是你是员工,会怎么理解这句话并落实到工作中?
|
||||
•答案:紧跟国家需求,研发时聚焦“卡脖子”技术,生产时严把质量关,助力产业升级
|
||||
25.假设你要给康达新材的官网设计一个新栏目,你会建议增加什么内容?
|
||||
•答案:“新材科普”栏目,用动画、图文讲解产品原理和应用,开设互动问答
|
||||
26.用一句幽默的话调侃康达新材的“硬核”业务(比如胶粘剂、ITO靶材),你会怎么说?
|
||||
•答案:康达业务太硬核,万物皆可粘、皆可造,就没有搞不定的技术难题?
|
||||
27.要是康达新材要和一所高校开展合作,你觉得可以在哪些领域合作共赢?
|
||||
•答案:①联合研发“卡脖子”技术;②开设定制班培养人才;③加速科研成果转化
|
||||
28.如果你是康达新材的hr,招聘时会用什么独特的理由吸引人才加入?
|
||||
•答案:参与热门领域技术攻关,国企保障+清晰晋升,与行业顶尖人才共事
|
||||
29.用比喻的手法说说康达新材的研发团队像什么?为什么?
|
||||
•答案:像“产业探路者”,探索前沿技术,开辟新路径,指引产业发展方向
|
||||
30.康达新材海外有布局,要是你负责海外业务推广,会先给海外客户推荐哪款产品?
|
||||
•答案:风电胶粘剂,市场占有率第一,技术领先,契合全球新能源需求
|
||||
31.假设康达新材要举办一场客户答谢会,你觉得现场布置可以融入哪些业务元素?
|
||||
•答案:①产品模型展示区;②分子结构装饰;③产品拼图、技术问答互动
|
||||
32.用一句话总结康达新材对国家新材料产业的意义,你会怎么说?
|
||||
•答案:打破进口依赖,树立国产标杆,为新材料产业高质量发展提供支撑
|
||||
33.要是你是康达新材的一名司机,开车运送产品时,会怎么跟别人介绍车上的货物?
|
||||
•答案:这是国产硬核科技产品,有风电专用胶粘剂、电子设备核心材料,助力多个行业发展~
|
||||
34.康达新材有很多生产基地,要是把这些基地比作一串珍珠,你觉得哪颗“珍珠”最亮眼?
|
||||
•答案:成都电子科技产业园,聚焦战略新兴产业,是西南地区电子信息特色产业园
|
||||
35.用简单的手势或动作比划康达新材某款产品的用途,你会怎么比划?
|
||||
•答案:比划胶粘剂:双手模拟“叶片”,合拢按压示意粘贴牢固
|
||||
36.假设康达新材要出一本企业内刊,你会给内刊起什么名字?设置哪些栏目?
|
||||
•答案:名字《康达新声》,栏目:总裁寄语、技术前沿、业务动态、员工风采
|
||||
37.用一句话形容康达新材员工的工作状态,你会怎么描述?
|
||||
•答案:勤学实干、协同创新,在各自岗位上全力冲刺目标
|
||||
38.要是康达新材要开展员工技能比赛,你觉得可以设置哪些比赛项目?
|
||||
•答案:①技术攻关挑战赛;②生产精准操作大赛;③产品推广创意赛
|
||||
39.用对比的方式说说康达新材20年前和现在的变化,你会怎么总结?
|
||||
•答案:20年前业务单一,如今成长为“新材料+电子科技”集团,技术、布局全面升级
|
||||
40.康达新材的核心价值观是“融合、协同、创新、超越”,要是你用生活中的例子解释“协同”,会选什么例子?
|
||||
•答案:像家庭做饭,分工不同但目标一致,相互配合才能快速做出丰盛饭菜
|
||||
41.假设你要给康达新材的会议室起名字,结合业务特色,你会起什么名字?
|
||||
•答案:粘合力厅、靶材阁、科创堂、协同馆
|
||||
42.用一句鼓励的话送给正在攻克“卡脖子”技术的康达新材研发团队,你会怎么说?
|
||||
•答案:坚守初心攻难关,国产技术定发光,为你们加油!
|
||||
43.要是康达新材要拍一支员工纪录片,你觉得应该采访哪些岗位的员工?
|
||||
•答案:研发人员、生产线工人、销售人员、子公司负责人、行政后勤人员
|
||||
44.用比喻的手法说说康达新材的产品像什么?比如胶粘剂像“工业胶水”,再具体描述一下
|
||||
•答案:①胶粘剂像“工业纽带”,连接各类部件;②ITO靶材像“电子基石”,支撑电子设备运行
|
||||
45.康达新材涉及电子科技领域,要是你给长辈解释什么是“微波组件”,会怎么说?
|
||||
•答案:这是电子设备的“信号中转站”,能让雷达、通讯设备精准收发信号,保障安全和监测
|
||||
46.假设康达新材要参加一场行业展会,你觉得展位设计要突出什么亮点才能吸引观众?
|
||||
•答案:突出科技感(分子结构造型、LED大屏)和互动性(产品体验、VR参观)
|
||||
47.用一句话说说你对康达新材未来5年发展的期待,你会怎么说?
|
||||
•答案:期待成为全球领先的“新材料+电子科技”集团,让更多国产硬核科技走向世界
|
||||
48.要是你是康达新材的食堂阿姨,会怎么用食堂菜品比喻公司的业务多元化?
|
||||
•答案:食堂菜像公司业务,有“核心硬菜”(胶粘剂)、“新鲜时蔬”(电子信息材料),搭配均衡、营养丰富~
|
||||
49.用简单的画画形式表现康达新材的业务,你会画哪些元素?
|
||||
•答案:画大圆圈代表公司,内部画风电叶片、手机、芯片、分子结构,用箭头连接体现协同
|
||||
50.康达新材有不少荣誉资质,要是你把这些荣誉比作公司的“勋章”,最想给别人展示哪枚“勋章”?为什么?
|
||||
•答案:国家级“专精特新”小巨人企业,体现公司技术实力和细分领域领先地位
|
||||
@ -1,13 +0,0 @@
|
||||
### QA问答的修改原则
|
||||
1. 子问题(sub_questions)原则 :
|
||||
|
||||
- 子问题的答案必须能在主答案中找到
|
||||
- 子问题不是对主问题的扩展或深入,而是从主答案中提取的、用户可能问的相关问题
|
||||
- 如果主答案内容简单(如只有年份、数字等),通常不需要子问题
|
||||
- 每个问题保留2-3个子问题(如果适用)
|
||||
|
||||
2. 变体(variations)原则 :
|
||||
|
||||
- 变体是主问题的不同表达方式
|
||||
- 只保留3个代表性的变体
|
||||
- 变体应覆盖常见的问法变化(如:去掉疑问词、简化表达、换种说法)
|
||||
@ -1,58 +0,0 @@
|
||||
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
|
||||
@ -1,71 +0,0 @@
|
||||
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))
|
||||
BIN
ruoyi-fastapi-backend/haotian1.jpg
Normal file
BIN
ruoyi-fastapi-backend/haotian1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 311 KiB |
File diff suppressed because it is too large
Load Diff
@ -1,14 +0,0 @@
|
||||
# 康达新材(002669.SZ)对外介绍(Q&A版)
|
||||
|
||||
> 本文与《2.0康达新材Q&A清单 251228.md》存在大量重合:公司概况/沿革/股权/战略/基地/板块介绍/部分经营与研发数据等。对外统一口径以清单为准。
|
||||
|
||||
## 仍保留(本文件独有或更适合对外的补充)
|
||||
|
||||
### 1)英文名
|
||||
**A:**KangDa New Materials。
|
||||
|
||||
### 2)对外免责声明/使用说明
|
||||
**A:**如与正式公告/披露存在差异,以公司披露为准。
|
||||
|
||||
### 3)(可选)对外呈现的结构建议
|
||||
**A:**对外问答建议直接引用清单中对应条目(公司基本信息→战略→基地→业务板块),避免出现多份口径。
|
||||
@ -1,11 +0,0 @@
|
||||
# 康达新材介绍文档(250826)(Q&A版)
|
||||
|
||||
> 本文内容与《2.0康达新材Q&A清单 251228.md》高度重合(公司概况/沿革/股权/战略/基地/业务板块/资质等),对外与对内统一口径以清单为准。
|
||||
|
||||
## 仍保留(本文件独有/更适合作为“介绍文档”的补充)
|
||||
|
||||
### 1)口号
|
||||
**A:**康达匠心赋能 引领制造新材。
|
||||
|
||||
### 2)原文口径不一致提示(用于内部校对)
|
||||
**A:**原文个别财务数据在不同位置存在不一致;如需对外口径,请以公司披露/正式财报为准。
|
||||
@ -1,243 +0,0 @@
|
||||
# 康达新材:2022年年度报告(Q&A版)
|
||||
|
||||
> 说明:本文为对 `md/康达新材:2022年年度报告.md` 的要点抽取与问答化重组,便于检索与知识库问答使用;问题为整理者归纳,答案以原文表述与数据为准。
|
||||
|
||||
---
|
||||
|
||||
## 1. 分配政策
|
||||
|
||||
**Q5:2022年度利润分配(现金分红/送股/转增)安排是什么?**
|
||||
**A:** 公司计划不派发现金红利、不送红股、不以公积金转增股本。
|
||||
|
||||
---
|
||||
|
||||
## 3. 业务板块与产品结构
|
||||
|
||||
**Q12:公司主要属于哪些行业/业务类型?**
|
||||
**A:** 胶粘剂新材料行业、军工电子科技行业、电子信息材料(含医药中间体与电子化学品等)相关领域。
|
||||
|
||||
**Q13:公司胶粘剂新材料板块有哪些主要产品?应用在哪些领域?**
|
||||
**A:** 产品包括环氧树脂胶、聚氨酯胶、丙烯酸酯胶、SBS胶、热熔胶、水性胶等,以及环氧/聚氨酯复合材料、聚酰亚胺材料等;应用于风电叶片制造、包装材料、轨道交通、船舶工程、汽车、电子电器、机械设备、建筑装饰、工业维修等。
|
||||
|
||||
**Q14:胶粘剂业务的经营模式是什么?**
|
||||
**A:** “产品+解决方案”。采购以订单与计划为导向、供应商体系管理;生产以销定产并部分常规备货;销售以直销为主,强调技术服务与客户认证。
|
||||
|
||||
**Q15:军工电子科技板块主要做什么?**
|
||||
**A:** 以应用于航空、航天、舰船、兵器等领域的滤波器/滤波组件与电源模块为主要方向,并覆盖电磁兼容相关设备与产品。
|
||||
|
||||
**Q16:军工电子科技板块的组织架构是什么?**
|
||||
**A:** 以晟璟科技为管理平台,下辖必控科技、力源兴达两家全资子公司,并参股成都铭瓷、成都立扬、上海汉未。
|
||||
|
||||
**Q17:必控科技与力源兴达分别主营什么?**
|
||||
**A:** 必控科技:电磁兼容设备/预测试系统/软件与电磁兼容加固产品,含滤波器、滤波组件、电源滤波模块等;力源兴达:电源变换器模块研发设计生产销售,面向军工与电网输配电/工业控制等。
|
||||
|
||||
**Q18:电子信息材料及医药中间体板块主要包含哪些方向?**
|
||||
**A:** 显示材料(液晶混晶/单体/中间体、OLED相关材料、光刻胶感光剂、LCD-PI导向膜单体、ITO靶材等——靶材项目起步阶段暂无规模化销售)、医药中间体、新能源电子化学品(如电解液添加剂、电子清洗剂)。
|
||||
|
||||
**Q19:彩晶光电的业务定位与销售方式有什么特点?**
|
||||
**A:** 通过下游认证后按客户材料结构式与指标定制研发与生产,以直销为主;部分日韩地区客户通过指定代理采购商销售。
|
||||
|
||||
---
|
||||
|
||||
## 5. 2022年经营回顾(管理层讨论与分析要点)
|
||||
|
||||
**Q24:2022年公司总体经营表现如何?**
|
||||
**A:** 营业收入246,636.18万元,同比增长8.57%;营业利润5,275.40万元,同比增长186.13%;归母净利润4,791.35万元,同比增长117.83%;研发投入12,641.94万元,占营业收入5.13%;期末资产总额53.33亿元,同比增长约43.04%;归母净资产30.02亿元,同比增长32.66%。
|
||||
|
||||
**Q25:风电叶片制造领域有哪些进展?**
|
||||
**A:** 风电胶粘剂保持市占率领先;产品成功应用于11款百米级叶片(100m–126m);海外拓展LM Windpower,批量采购环氧结构胶、环氧树脂、喷胶等多类型产品。
|
||||
|
||||
**Q26:软包装无溶剂胶粘剂领域有哪些进展?**
|
||||
**A:** 聚氨酯类胶粘剂销售量/额同比稳定增长,销售量突破11,000吨,覆盖客户超750家,国内无溶剂软包装胶粘剂市场占有率领先;东南亚/中亚销售网络布局推进;与客户联合申报的相关技术列入《国家清洁生产先进技术目录(2022)》。
|
||||
|
||||
**Q27:消费电子及家电用胶有哪些进展?**
|
||||
**A:** 形成以双组份丙烯酸酯结构胶、单组份结构胶、UV胶、PUR胶、双/单组份环氧胶等为主线的系列化产品(近200余个型号);与联想、华为、戴尔、华硕、立讯、韶音等合作;单组份环氧胶在柔性线路板保护/灌封/密封等领域进入中高端并打破国外垄断;电子胶粘剂销售收入稳定增长10%以上。
|
||||
|
||||
**Q28:军工电子板块有哪些经营与产能/模式探索?**
|
||||
**A:** 必控科技扩充电磁兼容产品型谱,探索“多品种、小批量”柔性化Cell-Line生产模式并开始试生产,获得四川省“专精特新”中小企业称号;力源兴达将民品电源事业部转为军品为重心,新增客户23家、新项目45项。
|
||||
|
||||
**Q29:研发与标准/专利方面有哪些成果?**
|
||||
**A:** 研发费用占营收5.13%;新增授权专利16项(发明13项、实用新型3项),受理发明专利10项;参与制定/修订国家标准2项、行业标准1项。
|
||||
|
||||
**Q30:产能基地建设与项目推进有哪些重点?**
|
||||
**A:** 募投项目唐山丰南与福建邵武基地推进;天津复材研究中心与军用电源生产基地完成设计并推进审批;成都高新区军工电子科技产业园进入规划并签署土地转让协议;彩晶光电推进液晶显示材料产业化(Ⅰ期二标段)项目(混合液晶30吨/年、液晶单体40吨/年、液晶中间体250吨/年,计划总投资37,350万元,预计2023年底主体建设完成)。
|
||||
|
||||
**Q31:2022年资本运作有哪些关键事项?**
|
||||
**A:** 2022年7月15日向特定对象发行股票52,910,052股,发行价13.23元/股,募集资金总额约7亿元;2022年8月收购彩晶光电60.9205%股权,打造“电子信息材料”第二增长极。
|
||||
|
||||
**Q32:员工激励与股份回购有什么信息?**
|
||||
**A:** 报告期内使用自有资金5,430.90万元回购股份,并实施第四期员工持股计划,授予股份469.14万股。
|
||||
|
||||
---
|
||||
|
||||
## 6. 主要财务指标(合并口径摘要)
|
||||
|
||||
**Q33:2022/2021/2020营业收入分别是多少?**
|
||||
**A:** 2022年2,466,361,756.29元;2021年2,271,612,976.74元;2020年1,932,135,499.94元。
|
||||
|
||||
**Q34:2022年归母净利润与扣非归母净利润是多少?同比如何?**
|
||||
**A:** 归母净利润47,913,477.29元(同比+117.83%);扣非归母净利润33,756,789.24元(同比+520.23%)。
|
||||
|
||||
**Q35:2022年经营活动现金流净额是多少?**
|
||||
**A:** -23,646,854.70元(同比-111.38%)。
|
||||
|
||||
**Q36:2022年基本/稀释每股收益是多少?**
|
||||
**A:** 基本每股收益0.179元/股;稀释每股收益0.179元/股。
|
||||
|
||||
**Q37:2022年末总资产与归母净资产是多少?**
|
||||
**A:** 总资产5,332,562,034.54元;归母净资产3,001,624,183.70元。
|
||||
|
||||
**Q38:2022年加权平均净资产收益率是多少?**
|
||||
**A:** 2.10%。
|
||||
|
||||
---
|
||||
|
||||
## 7. 分季度表现(合并口径)
|
||||
|
||||
**Q39:2022年分季度营业收入分别是多少?**
|
||||
**A:** Q1 598,664,229.98元;Q2 487,606,208.15元;Q3 543,442,867.29元;Q4 836,648,450.87元。
|
||||
|
||||
**Q40:2022年分季度归母净利润分别是多少?**
|
||||
**A:** Q1 -10,396,127.38元;Q2 -4,275,913.77元;Q3 -19,085,103.43元;Q4 81,670,621.87元。
|
||||
|
||||
**Q41:2022年分季度经营活动现金流净额分别是多少?**
|
||||
**A:** Q1 -75,340,459.93元;Q2 156,132,079.25元;Q3 -21,654,884.68元;Q4 -82,783,589.34元。
|
||||
|
||||
---
|
||||
|
||||
## 8. 非经常性损益
|
||||
|
||||
**Q42:2022年非经常性损益合计是多少?主要构成有哪些?**
|
||||
**A:** 合计14,156,688.05元。主要包括:政府补助、理财收益/公允价值变动、资产处置损益、其他营业外收支等,并扣除所得税影响与少数股东权益影响。
|
||||
|
||||
**Q43:“其他符合非经常性损益定义的损益项目”是什么?**
|
||||
**A:** 其他权益工具投资持有期间取得的股利收入237,658.95元。
|
||||
|
||||
---
|
||||
|
||||
## 9. 收入结构与毛利(2022年)
|
||||
|
||||
**Q44:按行业看,营业收入构成如何?**
|
||||
**A:** 胶粘剂15.80亿元(64.06%);电子产品2.76亿元(11.17%);复合材料4.32亿元(17.53%);显示材料0.19亿元(0.76%);医药中间体0.26亿元(1.06%);电子化学品0.35亿元(1.44%);其他业务0.98亿元(3.97%)。
|
||||
|
||||
**Q45:按产品看,主要产品收入有哪些?**
|
||||
**A:** 环氧胶类9.76亿元;聚氨酯胶类2.93亿元;轻木套材4.09亿元;滤波器及滤波组件1.38亿元;电源模块0.93亿元;电源滤波模块0.44亿元等。
|
||||
|
||||
**Q46:胶粘剂/电子产品/复合材料毛利率大致水平如何?**
|
||||
**A:** 胶粘剂毛利率17.74%;电子产品毛利率56.04%;复合材料毛利率1.69%(报告期披露口径)。
|
||||
|
||||
**Q47:按销售模式看,直销与代销占比如何?**
|
||||
**A:** 直销22.08亿元(89.52%);代销1.61亿元(6.51%);其他业务0.98亿元(3.97%)。
|
||||
|
||||
**Q48:前五大客户/供应商集中度如何?**
|
||||
**A:** 前五名客户合计销售金额987,551,997.77元,占年度销售总额41.70%;前五名供应商合计采购金额845,819,887.90元,占年度采购总额42.73%。
|
||||
|
||||
---
|
||||
|
||||
## 10. 费用与研发
|
||||
|
||||
**Q49:2022年主要期间费用是多少?同比如何?**
|
||||
**A:** 销售费用88,155,461.44元(-6.32%);管理费用156,228,970.50元(+14.17%);财务费用39,194,908.67元(+50.23%,主要因融资额增加);研发费用126,419,384.32元(+28.89%)。
|
||||
|
||||
**Q50:研发人员规模与结构如何?**
|
||||
**A:** 研发人员378人(同比+32.63%),占比24.77%;学历结构:本科164、硕士57、博士9;年龄:30岁以下110、30–40岁147等。
|
||||
|
||||
**Q51:研发人员增长的主要原因是什么?**
|
||||
**A:** 2022年收购彩晶光电完成后,将彩晶光电研发人员纳入统计。
|
||||
|
||||
---
|
||||
|
||||
## 11. 现金流与差异解释
|
||||
|
||||
**Q52:2022年三大现金流净额分别是多少?**
|
||||
**A:** 经营活动-23,646,854.70元;投资活动-844,050,079.34元;筹资活动+1,049,218,073.24元。
|
||||
|
||||
**Q53:经营活动现金流为何与净利润差异较大?**
|
||||
**A:** 报告说明主要由于购买商品、接受劳务支付的现金增加等因素。
|
||||
|
||||
---
|
||||
|
||||
## 12. 资产负债与受限资产
|
||||
|
||||
**Q54:2022年末资产结构的几个大项是多少?**
|
||||
**A:** 货币资金5.51亿元;应收账款11.54亿元;存货6.70亿元;固定资产7.45亿元;在建工程4.17亿元;商誉6.15亿元等(详见原表)。
|
||||
|
||||
**Q55:报告期末有哪些主要受限资产?合计多少?**
|
||||
**A:** 受限资产合计759,059,552.00元,主要包括票据保证金、质押票据、抵押的固定资产/无形资产/在建工程,以及质押的长期股权投资(以持有彩晶光电60.92%股权质押取得长期借款等)。
|
||||
|
||||
---
|
||||
|
||||
## 13. 投资并购与合并范围变动
|
||||
|
||||
**Q56:2022年合并范围有哪些变动?**
|
||||
**A:** 新设保定康达新材料(2022年1月纳入);出售天津易远通60%股权(2022年2月不再纳入);新设康达锦瑞(2022年8月纳入);收购彩晶光电60.9205%股权(2022年8月纳入)。
|
||||
|
||||
**Q57:重大股权投资中,彩晶光电收购的关键信息是什么?**
|
||||
**A:** 通过收购取得彩晶光电60.9205%股权;股权投资金额(披露为)372,530,500.00元;纳入合并报表范围。
|
||||
|
||||
**Q58:公司是否发生重大资产出售?**
|
||||
**A:** 报告期未出售重大资产。
|
||||
|
||||
**Q59:重大股权出售(天津易远通)情况如何?**
|
||||
**A:** 康达国际供应链以1,815万元向天津唐控科创集团出售天津易远通60%股权,披露为关联交易;对公司整体经营与业绩无重大影响。
|
||||
|
||||
---
|
||||
|
||||
## 14. 募集资金要点
|
||||
|
||||
**Q60:报告期涉及哪些募集资金项目/年份?**
|
||||
**A:** 2016年非公开发行募集资金8.5亿元;2022年非公开发行募集资金7亿元。
|
||||
|
||||
**Q61:截至2022年末,尚未使用的募集资金余额与去向是什么?**
|
||||
**A:** 2016年项目尚未使用余额1,375.01万元,存放专户;2022年项目尚未使用余额34,493.59万元,其中12,000万元用于现金管理、4,000万元用于暂时补充流动资金、其余存放募集资金专户。
|
||||
|
||||
**Q62:丁基材料项目为何结项并变更投向?**
|
||||
**A:** 报告说明市场前景不及预期,为优化资源配置与提高资金使用效率,公司对丁基材料项目结项,并将剩余募集资金18,558.08万元投向“高性能环氧结构胶粘剂扩产项目”和“研发中心扩建项目”。
|
||||
|
||||
---
|
||||
|
||||
## 15. 子公司/参股公司经营贡献(摘录)
|
||||
|
||||
**Q63:对公司净利润影响较大的子公司有哪些?表现如何?**
|
||||
**A:** 报告列示:
|
||||
* 必控科技:报告期营业收入19,642.62万元、净利润4,698.65万元,对整体业绩贡献较大。
|
||||
* 力源兴达:报告期营业收入10,059.81万元、净利润-818.43万元,对整体业绩有一定影响。
|
||||
|
||||
---
|
||||
|
||||
## 16. 资质与合规(节选)
|
||||
|
||||
**Q64:公司在安全/环保/质量体系方面有哪些主要证照或认证?**
|
||||
**A:** 报告列示包括安全生产许可证、安全生产标准化证书、危险化学品经营许可证、ISO9001/ISO14001/IATF16949、排污许可证、CNAS实验室认可、GL证书等,并披露有效期(详见原表)。
|
||||
|
||||
**Q65:报告期是否发生重大安全环保事故或非正常停产?**
|
||||
**A:** 报告披露全年未发生重大安全环保事故,且不存在非正常停产情形。
|
||||
|
||||
---
|
||||
|
||||
## 17. 税收优惠(财务报表附注摘录)
|
||||
|
||||
**Q66:哪些主体适用15%企业所得税优惠税率(高新/西部大开发等)?**
|
||||
**A:** 报告附注披露:母公司、本公司部分子公司(如必控科技、力源兴达、河北惟新、理日新材、彩晶光电等)适用15%税率(高新技术企业或西部大开发等政策口径)。
|
||||
|
||||
**Q67:哪些主体适用小型微利企业所得税优惠?**
|
||||
**A:** 康厦科技、深圳康达电子、康达国际供应链、微相邦、万斯先进新材料、晟宇科技、顺璟投资、璟创投资、天津瑞宏、天津三友、天津康达新材料、瑞贝斯、河北康达新材料、保定康达新材料、惟新半导体、理日新材、康达锦瑞等(以原文列示为准)。
|
||||
|
||||
---
|
||||
|
||||
## 18. 资产负债表日后事项(财务报表附注摘录)
|
||||
|
||||
**Q68:报告披露的资产负债表日后事项有哪些类型?**
|
||||
**A:** 主要包括部分借款信息披露,以及(1)受让成都赛英科技100%股权的进展披露、(2)为参股公司申请银行贷款提供担保、(3)控股子公司与关联方共同对外投资设立合资公司等。
|
||||
|
||||
---
|
||||
|
||||
## 19. 快速检索:关键数字一览
|
||||
|
||||
* 营业收入(2022):2,466,361,756.29 元
|
||||
* 归母净利润(2022):47,913,477.29 元
|
||||
* 扣非归母净利润(2022):33,756,789.24 元
|
||||
* 经营活动现金流净额(2022):-23,646,854.70 元
|
||||
* 总资产(2022年末):5,332,562,034.54 元
|
||||
* 归母净资产(2022年末):3,001,624,183.70 元
|
||||
* 研发费用(2022):126,419,384.32 元(占营收5.13%)
|
||||
@ -1,264 +0,0 @@
|
||||
# 康达新材:2023年年度报告(Q&A版)
|
||||
|
||||
> 说明:本文为对 `md/康达新材:2023年年度报告.md` 的要点抽取与问答化重组,便于检索与知识库问答使用;问题为整理者归纳,答案以原文表述与数据为准。
|
||||
|
||||
---
|
||||
|
||||
## 1. 利润分配
|
||||
|
||||
**Q5:2023年度利润分配预案是什么?**
|
||||
**A:** 以实施分配方案时股权登记日“总股本扣减回购专用证券账户股份数”为基数,向全体股东每10股派发现金股利0.70元(含税),不送红股,不以资本公积金转增股本。
|
||||
|
||||
**Q6:本次现金分红的股本基数与现金分红金额是多少?**
|
||||
**A:** 分配预案股本基数为297,160,573股;现金分红金额为20,801,240.11元(含税)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 业务版图与战略定位(管理层讨论与分析摘要)
|
||||
|
||||
**Q12:公司产品主要对应哪些行业?**
|
||||
**A:** 公司产品根据特性与终端应用领域/业务类型,分别属于胶粘剂新材料行业、合成树脂行业、电子信息材料行业、电子科技行业。
|
||||
|
||||
**Q13:公司“主链+第二增长极”的模式是什么?**
|
||||
**A:** 形成“以胶粘剂新材料系列产品为主链、高端电子信息材料为支撑的第二增长极”的发展模式,并在电子科技领域持续完善战略布局与资源联动。
|
||||
|
||||
**Q14:2023年公司在合成树脂领域有什么布局?**
|
||||
**A:** 向胶粘剂产业链上游收购并增资大连齐化,进入合成树脂领域;大连齐化以高品质环氧树脂为主,涵盖特种树脂新材料研发、生产、销售、服务。
|
||||
|
||||
**Q15:2023年公司在电子信息材料领域有哪些延伸?**
|
||||
**A:** 继2022年收购彩晶光电进入液晶显示材料、医药中间体及电子化学品等领域后,2023年收购晶材科技,业务涵盖陶瓷生料带、贵金属浆料、瓷粉等。
|
||||
|
||||
**Q16:2023年电子科技板块产业链如何延展?**
|
||||
**A:** 收购赛英科技后,形成涵盖电磁兼容、电源模块、电容、微波组件、雷达相关整机等元器件级/部件级/系统级/整机级的业务链条。
|
||||
|
||||
---
|
||||
|
||||
## 5. 2023年经营回顾(要点)
|
||||
|
||||
**Q22:2023年公司总体经营表现如何?**
|
||||
**A:** 营业收入2,792,525,024.71元,同比增长13.22%;归母净利润30,315,165.24元,同比下降36.66%;期末资产总额7,122,936,764.42元,同比增长33.57%。
|
||||
|
||||
**Q23:归母净利润下降的主要原因(报告说明)是什么?**
|
||||
**A:** 胶粘剂板块风电系列产品规模稳中有升、原材料趋稳后板块盈利能力较上年提升;但电子科技与电子信息材料板块行业环境阶段性变化、调整及订单延后等因素叠加,部分公司计提商誉减值准备,对归母净利润产生影响。
|
||||
|
||||
**Q24:风电胶粘剂与风电一体化材料供应方面有哪些进展?**
|
||||
**A:** 风电胶粘剂系列产品继续保持市占率领先,风电环氧结构胶、灌注树脂等各类产品销售总量近6万吨;海外市场在Siemens Gomesa应用稳定,并在LM Windpower批量采购并推进多工厂试用认证。
|
||||
|
||||
**Q25:包装领域业务表现与海外拓展如何?**
|
||||
**A:** 聚氨酯类胶粘剂销售量、销售额同比上升,报告期销售量超1万吨,覆盖客户超700家;海外销售范围扩展至东南亚、中亚等25个国家或地区,贸易额增幅达30%。
|
||||
|
||||
**Q26:消费电子、家电、轨道交通用胶有哪些进展?**
|
||||
**A:** 消费电子胶形成多产品线并按客户需求个性化调整;与联想、华硕、罗技、韶音等客户协同开发;家电用胶围绕美的、格力、海尔等目标客户开始批量供货;轨道交通领域通过TB/T2975-2018认证并逐步提升市占率。
|
||||
|
||||
**Q27:电子信息材料板块有哪些进展?**
|
||||
**A:** 彩晶光电混合液晶围绕京东方高世代产线,多款产品供货,“户外商显”首次小批量出货;医药板块受部分中间体停产与行业降价影响下滑,但推进新品转化与量产并开展高端中间体研发;惟新科技推进大尺寸ITO靶材产线升级扩建及高纯氧化铝靶材项目。
|
||||
|
||||
**Q28:电子科技板块在型号/研发与产能方面有哪些进展?**
|
||||
**A:** 赛英科技推进多个项目并于2023年7月彭州雷达测试基地投入使用;必控科技新研项目近190项;力源兴达全年完成项目开发370余项、约50%为国产化改造,并新增大量新需求。
|
||||
|
||||
**Q29:2023年研发投入与创新成果如何?**
|
||||
**A:** 研发费用177,605,054.56元,占营收6.36%;新增授权专利29项(发明16项、实用新型13项),受理发明专利30项,参与制定或修订行业标准1项。
|
||||
|
||||
---
|
||||
|
||||
## 6. 主要财务指标(合并口径)
|
||||
|
||||
**Q30:2023/2022营业收入分别是多少?**
|
||||
**A:** 2023年2,792,525,024.71元;2022年2,466,361,756.29元。
|
||||
|
||||
**Q31:2023年归母净利润与扣非归母净利润是多少?**
|
||||
**A:** 归母净利润30,315,165.24元;扣非归母净利润-150,073,608.10元。
|
||||
|
||||
**Q32:2023年经营活动现金流净额是多少?**
|
||||
**A:** 9,829,020.97元。
|
||||
|
||||
**Q33:2023年基本/稀释每股收益与加权ROE是多少?**
|
||||
**A:** 基本每股收益0.100元/股;稀释每股收益0.100元/股;加权平均净资产收益率1.03%。
|
||||
|
||||
**Q34:2023年末总资产与归母净资产是多少?**
|
||||
**A:** 总资产7,122,936,764.42元;归属于上市公司股东的净资产2,976,876,747.22元。
|
||||
|
||||
**Q35:营业收入扣除前后金额是多少?**
|
||||
**A:** 2023年营业收入扣除金额156,348,055.39元;扣除后金额2,636,176,969.32元。
|
||||
|
||||
---
|
||||
|
||||
## 7. 分季度主要财务指标(合并口径)
|
||||
|
||||
**Q36:2023年分季度营业收入分别是多少?**
|
||||
**A:** Q1 630,942,962.19元;Q2 648,773,198.50元;Q3 705,360,387.19元;Q4 807,448,476.83元。
|
||||
|
||||
**Q37:2023年分季度归母净利润分别是多少?**
|
||||
**A:** Q1 -14,847,352.02元;Q2 50,891,195.90元;Q3 -3,752,562.06元;Q4 -1,976,116.58元。
|
||||
|
||||
**Q38:2023年分季度经营活动现金流净额分别是多少?**
|
||||
**A:** Q1 -97,845,264.99元;Q2 -12,832,682.78元;Q3 31,779,967.81元;Q4 88,727,000.93元。
|
||||
|
||||
---
|
||||
|
||||
## 8. 非经常性损益(2023年)
|
||||
|
||||
**Q39:2023年非经常性损益合计是多少?**
|
||||
**A:** 180,388,773.34元。
|
||||
|
||||
**Q40:2023年非经常性损益的主要来源有哪些?**
|
||||
**A:** 主要包括:
|
||||
* 公允价值变动损益123,636,796.27元(报告说明:上海晶材原股东未完成业绩承诺所支付业绩补偿等)。
|
||||
* 计入当期损益的政府补助14,378,285.97元。
|
||||
* 收购赛英科技形成的“负商誉”收益44,946,508.24元。
|
||||
* 非流动资产处置损益-21,818.65元等。
|
||||
|
||||
**Q41:“其他符合非经常性损益定义的损益项目”是什么?**
|
||||
**A:** 本期注销子公司康达晟宇产生的投资收益,列示为4,186.14元。
|
||||
|
||||
---
|
||||
|
||||
## 9. 收入结构与毛利(2023年)
|
||||
|
||||
**Q42:按行业看,2023年营业收入构成如何?**
|
||||
**A:** 胶粘剂1,971,572,392.42元(70.60%);电子产品261,692,120.15元(9.37%);复合材料100,701,149.72元(3.61%);显示材料136,741,920.89元(4.90%);医药中间体130,962,131.42元(4.69%);电子化学品18,311,999.63元(0.66%);LTCC材料16,195,255.08元(0.58%);其他业务收入156,348,055.40元(5.60%)。
|
||||
|
||||
**Q43:按产品看,2023年主要产品收入有哪些?**
|
||||
**A:** 环氧胶类1,317,562,900.69元;聚氨酯胶类294,258,688.11元;电磁兼容产品130,855,841.37元;电源模块62,660,839.65元;显示材料136,741,920.89元;医药中间体130,962,131.42元;轻木套材74,331,868.48元等。
|
||||
|
||||
**Q44:占比10%以上的业务中,胶粘剂板块毛利率是多少?**
|
||||
**A:** 胶粘剂板块毛利率19.59%。
|
||||
|
||||
**Q45:环氧胶类与聚氨酯胶类毛利率分别是多少?**
|
||||
**A:** 环氧胶类毛利率15.64%;聚氨酯胶类毛利率22.16%。
|
||||
|
||||
**Q46:按销售模式看,直销与代销占比如何?**
|
||||
**A:** 直销2,470,109,362.83元(88.45%);代销166,067,606.48元(5.95%);其他业务156,348,055.40元(5.60%)。
|
||||
|
||||
---
|
||||
|
||||
## 10. 主要客户与供应商集中度
|
||||
|
||||
**Q47:前五名客户集中度如何?**
|
||||
**A:** 前五名客户合计销售金额959,309,115.17元,占年度销售总额比例34.35%。
|
||||
|
||||
**Q48:前五名供应商集中度如何?**
|
||||
**A:** 前五名供应商合计采购金额488,605,336.23元,占年度采购总额比例24.91%。
|
||||
|
||||
---
|
||||
|
||||
## 11. 费用、研发与人员
|
||||
|
||||
**Q49:2023年四项期间费用(销售/管理/财务/研发)分别是多少?**
|
||||
**A:** 销售费用103,138,352.65元;管理费用204,042,488.26元;财务费用68,578,978.79元;研发费用177,605,054.56元。
|
||||
|
||||
**Q50:上述费用同比大幅增长的主要原因(报告说明)是什么?**
|
||||
**A:** 主要因工资及福利费、股权激励费用摊销,以及股权投资形成的合并子公司增加、融资额增加等因素。
|
||||
|
||||
**Q51:2023年研发人员规模与占比是多少?**
|
||||
**A:** 研发人员486人,占比25.98%。
|
||||
|
||||
**Q52:研发人员增加的原因是什么?**
|
||||
**A:** 公司收购赛英科技、上海晶材、大连齐化后,将其研发人员纳入统计。
|
||||
|
||||
**Q53:2023年员工总数与结构如何?**
|
||||
**A:** 在职员工合计1,871人(母公司550人、主要子公司1,321人);专业构成含生产人员794人、技术人员547人、销售人员187人等;学历含博士16人、硕士90人、本科522人等(以原表为准)。
|
||||
|
||||
---
|
||||
|
||||
## 12. 现金流(合并口径)
|
||||
|
||||
**Q54:2023年三大现金流净额分别是多少?**
|
||||
**A:** 经营活动+9,829,020.97元;投资活动-871,553,996.98元;筹资活动+807,818,595.07元。
|
||||
|
||||
**Q55:现金及现金等价物净增加额是多少?主要原因是什么?**
|
||||
**A:** -53,861,976.40元;报告说明主要因归还贷款和股份回购增加等。
|
||||
|
||||
**Q56:经营活动现金流净额同比上升的原因(报告说明)是什么?**
|
||||
**A:** 主要系购买商品、接受劳务支付的现金减少所致。
|
||||
|
||||
---
|
||||
|
||||
## 13. 资产负债表要点(合并口径,2023年末)
|
||||
|
||||
**Q57:2023年末主要资产项目有哪些?**
|
||||
**A:** 货币资金522,558,226.31元;应收账款1,461,666,737.74元;存货792,860,649.81元;固定资产1,048,809,835.47元;在建工程843,036,382.32元;商誉776,599,927.54元;无形资产418,503,945.02元等。
|
||||
|
||||
**Q58:2023年末主要负债项目有哪些?**
|
||||
**A:** 短期借款1,017,747,629.74元;一年内到期的非流动负债620,721,942.15元;长期借款823,275,169.86元;应付账款650,273,652.63元;应付票据349,570,289.51元等。
|
||||
|
||||
---
|
||||
|
||||
## 14. 资产受限情况(节选)
|
||||
|
||||
**Q59:截至2023年末,受限资产合计是多少?**
|
||||
**A:** 受限资产合计(账面余额)968,128,955.76元,合计(账面价值)757,235,603.35元(表中同时列示期初对比数)。
|
||||
|
||||
**Q60:受限资产主要包括哪些类型?**
|
||||
**A:** 主要包括票据保证金/承兑汇票保证金、应收票据/应收款项融资质押、抵押的固定资产与无形资产、抵押的在建工程、质押的长期股权投资(如上海晶材67%股权、彩晶光电60.92%股权等融资安排)、以及应收账款质押等。
|
||||
|
||||
---
|
||||
|
||||
## 15. 投资并购与合并范围变动
|
||||
|
||||
**Q61:2023年合并范围有哪些重要变动?**
|
||||
**A:** 报告披露新增纳入:惟新半导体(唐山)(2023年2月起)、南平鑫天宇(2023年4月起)、惟新科技(唐山)(2023年5月起)、赛英科技(2023年5月起)、晟铭建筑新材料(2023年6月起)、裕隆数智(2023年8月起)、上海晶材(2023年9月起)、大连齐化及大连齐化国际物流(2023年11月起);同时晟宇科技注销(2023年6月起不再纳入)。
|
||||
|
||||
**Q62:报告期内三项重大股权投资(收购)金额与标的是什么?**
|
||||
**A:** 赛英科技100%股权:178,311,200.00元;上海晶材67%股权:388,600,000.00元;大连齐化51%股权:116,280,000.00元。
|
||||
|
||||
**Q63:资产减值与商誉相关情况的要点是什么?**
|
||||
**A:** 报告列示资产减值-161,189,905.31元,主要因计提存货跌价准备以及上海晶材、必控科技、力源兴达、天津瑞宏等的商誉减值损失。
|
||||
|
||||
---
|
||||
|
||||
## 16. 募集资金与现金管理(摘要)
|
||||
|
||||
**Q64:公司涉及哪些募集资金年度与规模?**
|
||||
**A:** 2016年非公开发行:募集资金总额85,000万元、净额82,743.85万元;2022年非公开发行:募集资金总额70,000万元、净额69,125万元。
|
||||
|
||||
**Q65:截至2023年末,尚未使用募集资金余额与去向是什么?**
|
||||
**A:** 2016年尚未使用余额559.11万元,存放募集资金专户;2022年尚未使用余额12,308.73万元,其中7,000万元用于暂时补充流动资金,剩余5,308.73万元存放募集资金专户。
|
||||
|
||||
**Q66:报告期内是否存在募集资金现金管理(委托理财)?**
|
||||
**A:** 报告披露存在,银行理财产品(资金来源为募集资金)委托理财发生额14,000万元,期末未到期余额为0。
|
||||
|
||||
**Q67:报告期内是否发生变更募集资金投向的重要事项?**
|
||||
**A:** 报告披露变更部分募集资金投向11,628万元用于收购大连齐化部分股权并增资,增资完成后合计持有大连齐化51%股权。
|
||||
|
||||
---
|
||||
|
||||
## 17. 公司治理与组织(摘录)
|
||||
|
||||
**Q68:2023年公司董事会结构有何变化?**
|
||||
**A:** 为匹配发展战略与业务需求,将董事会人数由9名增加至13名,并增补新材料、电子信息、战略管理等领域专家。
|
||||
|
||||
**Q69:2023年公司是否披露ESG相关信息?**
|
||||
**A:** 报告披露发布《2022年度环境、社会与公司治理报告》,万得ESG评级最新得分提升至A。
|
||||
|
||||
---
|
||||
|
||||
## 18. 风险提示(节选)
|
||||
|
||||
**Q70:报告提到的“市场竞争加剧风险”是什么?**
|
||||
**A:** 胶粘剂行业市场集中度不高、竞争充分,常规型产品竞争加剧,同时国际企业在国内布局;若公司不能保持技术/服务创新与品牌影响力,可能面临客户流失与市场份额下降风险。
|
||||
|
||||
---
|
||||
|
||||
## 19. 资产负债表日后事项(节选)
|
||||
|
||||
**Q71:资产负债表日后利润分配如何披露?**
|
||||
**A:** 披露拟每10股派息0.7元;并以总股本305,402,973股扣减回购专户8,242,400股后,基数297,160,573股测算,拟派发现金红利20,801,240.11元。
|
||||
|
||||
**Q72:资产负债表日后与上海晶材业绩补偿相关事项是什么?**
|
||||
**A:** 披露上海晶材2023年度未完成业绩承诺,交易对方补偿金额15,544万元(现金与股权形式,其中股权部分对应上海晶材13.4%股权);公司对相关商誉计提减值准备12,245.90万元,并将补偿款确认为以公允价值计量且其变动计入当期损益的金融资产,确认公允价值变动损益12,188.62万元(作为资产负债表日后调整事项)。
|
||||
|
||||
---
|
||||
|
||||
## 20. 快速检索:关键数字一览
|
||||
|
||||
* 营业收入(2023):2,792,525,024.71 元
|
||||
* 归母净利润(2023):30,315,165.24 元
|
||||
* 扣非归母净利润(2023):-150,073,608.10 元
|
||||
* 非经常性损益合计(2023):180,388,773.34 元
|
||||
* 经营活动现金流净额(2023):9,829,020.97 元
|
||||
* 投资活动现金流净额(2023):-871,553,996.98 元
|
||||
* 筹资活动现金流净额(2023):807,818,595.07 元
|
||||
* 总资产(2023年末):7,122,936,764.42 元
|
||||
* 归母净资产(2023年末):2,976,876,747.22 元
|
||||
* 研发费用(2023):177,605,054.56 元(占营收 6.36%)
|
||||
@ -1,317 +0,0 @@
|
||||
# 康达新材:2024年年度报告(Q&A版)
|
||||
|
||||
> 说明:本文为对 `md/康达新材:2024年年度报告.md` 的要点抽取与问答化重组,便于检索与知识库问答使用;问题为整理者归纳,答案以原文表述与数据为准。
|
||||
|
||||
---
|
||||
|
||||
## 1. 重要提示与利润分配
|
||||
|
||||
**Q1:年度报告由谁保证真实、准确、完整?**
|
||||
**A:** 公司董事会、监事会及董事、监事、高级管理人员保证年度报告内容真实、准确、完整,不存在虚假记载、误导性陈述或重大遗漏,并承担个别和连带的法律责任。
|
||||
|
||||
**Q2:公司负责人及财务负责人对财务报告作了什么声明?**
|
||||
**A:** 公司负责人王建祥、主管会计工作负责人宋兆庆及会计机构负责人(会计主管人员)张国强声明:保证本年度报告中财务报告真实、准确、完整。
|
||||
|
||||
**Q3:董事出席情况如何?**
|
||||
**A:** 所有董事均已出席审议本报告的董事会会议。
|
||||
|
||||
**Q4:前瞻性陈述意味着什么?**
|
||||
**A:** 报告所涉未来计划、发展战略等前瞻性陈述不构成公司对投资者的实质承诺,投资者及相关人士应保持风险认识,并理解计划、预测与承诺之间的差异。
|
||||
|
||||
**Q5:公司是否提示了经营风险?**
|
||||
**A:** 报告第三节“管理层讨论与分析”中“公司面临的风险和应对措施”部分描述了公司未来经营中可能面临的风险,提示投资者关注。
|
||||
|
||||
**Q6:2024年度利润分配(现金分红/送转)计划是什么?**
|
||||
**A:** 公司计划不派发现金红利,不送红股,不以公积金转增股本。
|
||||
|
||||
---
|
||||
|
||||
## 2. 公司概况与信息披露
|
||||
|
||||
**Q7:公司简称、股票代码与上市交易所是什么?**
|
||||
**A:** 股票简称:康达新材;股票代码:002669;上市交易所:深圳证券交易所。
|
||||
|
||||
**Q8:公司名称、法定代表人、注册地址/办公地址是什么?**
|
||||
**A:** 公司名称:康达新材料(集团)股份有限公司;法定代表人:王建祥;注册地址/办公地址:上海市奉贤区雷州路169号。
|
||||
|
||||
**Q9:公司网址与电子信箱是什么?**
|
||||
**A:** 公司网址:<http://www.shkdchem.com>;电子信箱:kdxc@shkdchem.com。
|
||||
|
||||
**Q10:董事会秘书与证券事务代表是谁?联系方式是什么?**
|
||||
**A:** 董事会秘书:沈一涛;证券事务代表:刘洁;联系地址:上海市浦东新区五星路707弄御河企业公馆A区3号楼;电话:021-50770196 / 021-50779159;传真:021-50770183;邮箱:kdxc@shkdchem.com。
|
||||
|
||||
**Q11:年度报告披露渠道与备置地点在哪里?**
|
||||
**A:** 披露网站:深圳证券交易所(https://www.szse.cn)与巨潮资讯网(http://www.cninfo.com.cn);披露媒体:《证券时报》《证券日报》《中国证券报》《上海证券报》;备置地点:上海市浦东新区五星路707弄御河企业公馆A区3号楼董事会办公室。
|
||||
|
||||
**Q12:公司控股股东与实际控制人是谁?**
|
||||
**A:** 控股股东为唐山工业控股集团有限公司(唐山工控,原名唐山金控产业发展集团有限公司);实际控制人为唐山市国资委。
|
||||
|
||||
---
|
||||
|
||||
## 3. 主营业务与战略定位(摘要)
|
||||
|
||||
**Q13:公司主要业务板块是什么?**
|
||||
**A:** 公司主要业务分为胶粘剂与特种树脂新材料、电子信息材料、电子科技三大板块。
|
||||
|
||||
**Q14:胶粘剂业务主要产品及应用领域有哪些?**
|
||||
**A:** 产品涵盖环氧树脂胶、聚氨酯胶、丙烯酸酯胶、SBS胶、热熔胶、水性胶等,并包括环氧树脂复合材料、聚氨酯复合材料及聚酰亚胺材料等新材料系列,应用于风电叶片制造、包装材料、轨道交通、船舶工程、汽车、电子电器、机械设备、建筑装饰及工业维修等。
|
||||
|
||||
**Q15:合成树脂业务(大连齐化)主要产品系列是什么?**
|
||||
**A:** 产品主要分为双酚A型环氧树脂、耐热型环氧树脂和特种环氧树脂三大系列,涵盖双酚A型液体/固体、溴化、邻甲酚醛等品种,可应用于复合材料、涂料与涂层、粘合剂、电子电气等领域。
|
||||
|
||||
**Q16:电子信息材料业务覆盖哪些产品方向?**
|
||||
**A:** 电子信息材料领域包含液晶显示材料、医药中间体、陶瓷生料带、贵金属浆料、特种显示材料及电子化学品等研发、生产与销售。
|
||||
|
||||
**Q17:电子科技业务覆盖哪些产品方向?**
|
||||
**A:** 电子科技领域主营为电磁兼容、电源模块、微波组件、雷达相关整机等电子产品的研发、生产与销售。
|
||||
|
||||
**Q18:公司总体战略(报告表述)如何概括?**
|
||||
**A:** 报告提出以“新材料+电子科技”为主线,打造国家级专精特新“小巨人”企业集群;推动从单一胶粘剂产品向高端新材料、从核心零部件向系统集成、从国内为主向国际化布局的转型。
|
||||
|
||||
---
|
||||
|
||||
## 4. 行业环境(摘录)
|
||||
|
||||
**Q19:胶粘剂行业“十四五”目标(报告引用)是什么?**
|
||||
**A:** 产量年均增长率约4.2%,销售额年均增长率约4.3%;到2025年末行业高附加值产品产值比例达到40%以上,并推动水基、热熔、无溶剂等绿色方向。
|
||||
|
||||
**Q20:环氧树脂行业的供需与国产替代机会(报告观点)是什么?**
|
||||
**A:** 电子工业、汽车产业等带动需求增长;适用性强、性能稳定的高品质环氧树脂仍供不应求,部分电子元件封装、覆铜板等领域产品仍依赖进口,具备国产替代机会。
|
||||
|
||||
**Q21:LTCC材料国产化为何被认为“需求迫切”?**
|
||||
**A:** LTCC材料在材料一致性、高频损耗率等指标上,上游原材料市场长期被海外厂商主导,具备规模化稳定供货能力并获市场认可的国内企业仍稀缺;在高性能雷达自主化、航天载荷轻量化等战略领域将释放市场空间。
|
||||
|
||||
**Q22:ITO靶材为何被描述为“卡脖子”技术之一?**
|
||||
**A:** 溅射靶材行业技术壁垒与客户认证壁垒高,全球高纯溅射靶材市场长期被海外厂商控制;大尺寸高性能ITO靶材被报道为“卡脖子”技术之一,并列入工信部重点新材料首批次应用示范指导目录(2019年版)。
|
||||
|
||||
**Q23:军工电子行业需求驱动(报告引用)是什么?**
|
||||
**A:** 国防支出稳步增长为装备发展提供支撑;报告引用“2025年全国一般公共预算安排国防支出1.81万亿元,同比增7.2%”(来源:2024年3月5日公布的预算执行情况与预算草案报告)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 2024年经营回顾(要点)
|
||||
|
||||
**Q24:2024年公司营业收入与利润总体表现如何?**
|
||||
**A:** 营业收入3,101,062,179.20元,同比增长11.05%;归母净利润-246,173,519.93元,同比下降-912.05%;扣非归母净利润-308,264,197.06元。
|
||||
|
||||
**Q25:公司解释的亏损主要原因有哪些?**
|
||||
**A:** 报告指出主要原因包括:对存在减值迹象的子公司商誉资产组组合进行减值测试并计提商誉减值;受市场环境影响,电子信息材料、电子科技板块下游客户订单交付减少或延期;新建生产基地处于产能爬坡阶段,对应固定资产折旧及融资贷款利息费用较大等。
|
||||
|
||||
**Q26:风电叶片制造领域(胶粘剂板块)有哪些关键进展?**
|
||||
**A:** 报告称国内市场份额第一地位稳固;风电结构胶全年销售量4万吨;高性能胶D-2新品全面推出,销量提升至结构胶整体销量的17%;风电环氧灌注树脂全年销售量4.5万吨,同比上升90%以上,连续三年实现翻倍增长,市场份额提升至12%,晋升行业前四;并推进海外工厂认证/验证(如LM Windpower、GE、Nordex等)。
|
||||
|
||||
**Q27:包装领域业务有哪些进展?**
|
||||
**A:** 报告称无溶剂复膜胶销售量超万吨、市场占有率保持领先;组建专业技术服务团队,为客户定制复膜胶解决方案;并持续推进油墨产品研发体系与整体解决方案能力。
|
||||
|
||||
**Q28:电子信息材料板块在国产替代方面有哪些进展?**
|
||||
**A:** 惟新科技建成30吨高纯度ITO靶材生产线,完成OLED面板头部企业G6代线ITO靶材生产验证,关键性能指标优于行业同等规格;并完成河北省重点研发计划课题验收;同时推进CMP(氧化铈)抛光液核心技术攻关。
|
||||
|
||||
**Q29:电子科技板块在“低空经济/安防”等方向有哪些描述?**
|
||||
**A:** 报告称赛英科技通过“军民协同、双向赋能”推进市场突破;FOD雷达在大兴机场应用多年并形成国家标准,国内机场将逐步列装;4D凝视雷达用于“低慢小”目标监控任务并在反无系统中大量应用。
|
||||
|
||||
---
|
||||
|
||||
## 6. 主要会计数据与财务指标(合并口径)
|
||||
|
||||
**Q30:2024/2023营业收入分别是多少?**
|
||||
**A:** 2024年3,101,062,179.20元;2023年2,792,525,024.71元。
|
||||
|
||||
**Q31:2024年归母净利润与扣非归母净利润是多少?**
|
||||
**A:** 归母净利润-246,173,519.93元;扣非归母净利润-308,264,197.06元。
|
||||
|
||||
**Q32:2024年经营活动现金流净额是多少?**
|
||||
**A:** 498,667,285.91元。
|
||||
|
||||
**Q33:2024年基本/稀释每股收益与加权ROE是多少?**
|
||||
**A:** 基本每股收益-0.824元/股;稀释每股收益-0.824元/股;加权平均净资产收益率-8.59%。
|
||||
|
||||
**Q34:2024年末总资产与归母净资产是多少?**
|
||||
**A:** 总资产6,940,150,195.69元;归属于上市公司股东的净资产2,727,894,289.21元。
|
||||
|
||||
**Q35:2024年营业收入扣除前后金额是多少?**
|
||||
**A:** 营业收入扣除金额132,825,438.14元;扣除后金额2,968,236,741.06元。
|
||||
|
||||
---
|
||||
|
||||
## 7. 分季度主要财务指标(合并口径)
|
||||
|
||||
**Q36:2024年分季度营业收入分别是多少?**
|
||||
**A:** Q1 535,607,900.26元;Q2 826,777,196.06元;Q3 816,474,450.24元;Q4 922,202,632.64元。
|
||||
|
||||
**Q37:2024年分季度归母净利润分别是多少?**
|
||||
**A:** Q1 -24,793,535.64元;Q2 -31,717,467.21元;Q3 -19,966,135.18元;Q4 -169,696,381.90元。
|
||||
|
||||
**Q38:2024年分季度经营活动现金流净额分别是多少?**
|
||||
**A:** Q1 73,400,210.80元;Q2 214,356,837.30元;Q3 139,941,585.62元;Q4 70,968,652.19元。
|
||||
|
||||
---
|
||||
|
||||
## 8. 非经常性损益(2024年)
|
||||
|
||||
**Q39:2024年非经常性损益合计是多少?**
|
||||
**A:** 62,090,677.13元。
|
||||
|
||||
**Q40:2024年非经常性损益主要由哪些项目构成?**
|
||||
**A:** 主要包括:
|
||||
* 非流动性资产处置损益40,430,252.31元(主要系报告期内转让彩晶光电产生的处置收益)。
|
||||
* 计入当期损益的政府补助25,980,131.98元。
|
||||
* 公允价值变动损益等269,346.81元。
|
||||
* 单独进行减值测试的应收款项减值准备转回3,177,638.34元。
|
||||
* 其他营业外收入和支出-3,759,143.23元。
|
||||
* 减:所得税影响额2,303,077.58元;少数股东权益影响额(税后)1,704,471.50元。
|
||||
|
||||
---
|
||||
|
||||
## 9. 营业收入结构与毛利(2024年)
|
||||
|
||||
**Q41:按行业看,2024年营业收入构成如何?**
|
||||
**A:** 胶粘剂2,252,028,435.31元(72.62%);合成树脂189,307,031.23元(6.10%);电子产品262,819,931.17元(8.48%);复合材料15,841,726.18元(0.51%);显示材料90,438,229.45元(2.92%);医药中间体102,507,074.51元(3.31%);电子化学品11,390,131.19元(0.37%);LTCC材料43,904,182.03元(1.42%);其他业务132,825,438.13元(4.28%)。
|
||||
|
||||
**Q42:按产品看,2024年主要产品收入有哪些?**
|
||||
**A:** 环氧胶类1,570,432,237.01元(50.64%);聚氨酯胶类312,452,673.98元(10.08%);丙烯酸胶类92,683,079.29元;SBS胶类110,022,477.65元;水性胶类56,581,632.93元;合成树脂189,307,031.23元;电源模块83,375,168.43元;电磁兼容产品86,020,868.14元;微波组件及系统93,423,894.60元;LTCC材料43,904,182.03元等。
|
||||
|
||||
**Q43:按地区看,2024年收入分布有哪些重点?**
|
||||
**A:** 华北地区895,953,680.06元(28.89%);华东地区782,609,545.75元(25.24%);华中地区338,200,626.93元(10.91%);国外地区91,915,079.33元(2.96%)等(以原表为准)。
|
||||
|
||||
**Q44:按销售模式看,直销与分销占比如何?**
|
||||
**A:** 直销2,809,752,244.68元(90.61%);分销158,484,496.39元(5.11%);其他业务132,825,438.13元(4.28%)。
|
||||
|
||||
**Q45:占比10%以上的业务/产品毛利率如何?**
|
||||
**A:** 胶粘剂毛利率17.84%;环氧胶类毛利率15.92%;聚氨酯胶类毛利率27.88%;直销毛利率16.18%(对应表中同比变动亦已披露)。
|
||||
|
||||
**Q46:报告期主营业务统计口径有什么变化?**
|
||||
**A:** 2024年将“合成树脂”从胶粘剂行业与环氧胶类产品中单独列示;口径变化原因之一是2024年合并大连齐化全年数据且列示为对外销售形成的营业收入。
|
||||
|
||||
---
|
||||
|
||||
## 10. 产销数据(同比大幅变动项)
|
||||
|
||||
**Q47:胶粘剂与合成树脂的销量同比为何变化较大?**
|
||||
**A:** 胶粘剂产品销量/产量增加主要为风电结构胶、灌注树脂及其他胶种市场开拓所致,库存量减少主要为年底销量增幅较快所致;合成树脂增幅较大主要为2023年11月大连齐化纳入合并范围,而2024年合并大连齐化全年数据所致。
|
||||
|
||||
---
|
||||
|
||||
## 11. 主要客户与供应商集中度
|
||||
|
||||
**Q48:前五名客户集中度如何?**
|
||||
**A:** 前五名客户合计销售金额1,170,113,577.70元,占年度销售总额比例37.73%。
|
||||
|
||||
**Q49:前五名供应商集中度与关联采购占比如何?**
|
||||
**A:** 前五名供应商合计采购金额840,525,802.96元,占年度采购总额比例35.30%;前五名供应商采购额中关联方采购额占年度采购总额比例21.93%。
|
||||
|
||||
---
|
||||
|
||||
## 12. 期间费用、研发与人员
|
||||
|
||||
**Q50:2024年销售/管理/财务/研发费用分别是多少?**
|
||||
**A:** 销售费用102,000,705.02元;管理费用232,581,720.53元;财务费用95,545,381.14元;研发费用203,525,998.94元。
|
||||
|
||||
**Q51:研发投入规模与占比如何?**
|
||||
**A:** 研发投入金额203,525,998.94元,占营业收入比例6.56%。
|
||||
|
||||
**Q52:研发人员规模与占比是多少?**
|
||||
**A:** 研发人员376人,占比22.97%。
|
||||
|
||||
**Q53:研发人员较上年减少的原因是什么?**
|
||||
**A:** 报告说明:公司出售彩晶光电股权,彩晶光电不再纳入公司合并报表范围,其研发人员未统计入2024年。
|
||||
|
||||
**Q54:报告期末员工总数与结构如何?**
|
||||
**A:** 在职员工合计1,637人(母公司546人、主要子公司1,091人);专业构成含生产人员662人、销售人员188人、技术人员444人等;学历含博士12人、硕士70人、本科483人等(以原表为准)。
|
||||
|
||||
---
|
||||
|
||||
## 13. 现金流(合并口径)
|
||||
|
||||
**Q55:2024年三大现金流净额分别是多少?**
|
||||
**A:** 经营活动+498,667,285.91元;投资活动-358,595,995.08元;筹资活动+137,480,803.15元。
|
||||
|
||||
**Q56:2024年现金及现金等价物净增加额是多少?**
|
||||
**A:** 276,194,234.34元。
|
||||
|
||||
**Q57:经营/投资/筹资现金流变化的主要原因(报告说明)是什么?**
|
||||
**A:** 经营性净流入增加主要系购买商品、接受劳务支付的现金减少;投资活动净流出同比减少主要系处置子公司取得现金收入增加、取得子公司及联营单位支付现金减少;筹资活动净流入同比减少主要系偿还银行借款及利息支出增加。
|
||||
|
||||
---
|
||||
|
||||
## 14. 资产负债表要点(合并口径,2024年末)
|
||||
|
||||
**Q58:2024年末主要资产项目有哪些?**
|
||||
**A:** 货币资金799,834,198.31元;应收账款1,877,790,764.09元;存货545,690,424.46元;固定资产1,116,686,210.01元;在建工程715,274,766.37元;商誉437,307,088.79元;无形资产328,426,810.26元等。
|
||||
|
||||
**Q59:2024年末主要负债项目有哪些?**
|
||||
**A:** 短期借款1,042,105,372.47元;应付票据570,729,889.37元;应付账款941,500,280.13元;一年内到期的非流动负债390,026,280.20元;长期借款772,284,891.01元等。
|
||||
|
||||
**Q60:报告期内重大资产减值情况是什么?**
|
||||
**A:** 报告列示资产减值损失-194,887,938.64元,并说明主要系计提存货跌价准备以及上海晶材、必控科技、力源兴达的商誉减值损失等。
|
||||
|
||||
---
|
||||
|
||||
## 15. 公允价值计量与业绩补偿(摘要)
|
||||
|
||||
**Q61:2024年末交易性金融资产(业绩补偿款)余额是多少?**
|
||||
**A:** 交易性金融资产121,886,200.00元(业绩承诺补偿款)。
|
||||
|
||||
**Q62:报告对交易性金融资产“其他变动”的说明是什么?**
|
||||
**A:** 报告说明:交易性金融资产(不含衍生金融资产)其他变动是上海晶材原股东未完成业绩承诺所支付业绩补偿。
|
||||
|
||||
---
|
||||
|
||||
## 16. 资产权利受限情况(摘要)
|
||||
|
||||
**Q63:截至2024年末,受限资产合计是多少?**
|
||||
**A:** 合计(账面余额)999,166,089.97元;合计(账面价值)848,820,322.87元。
|
||||
|
||||
**Q64:受限资产主要包括哪些类型?**
|
||||
**A:** 主要包括:抵押的固定资产/无形资产/在建工程,质押的长期股权投资、应收款项融资质押、应收账款质押,以及票据保证金等(明细见原表“期末说明”)。
|
||||
|
||||
---
|
||||
|
||||
## 17. 合并范围变动与重大股权交易
|
||||
|
||||
**Q65:2024年合并范围发生了哪些主要变动?**
|
||||
**A:** 报告披露:璟创投资、康达新材料(保定)、裕隆数智、顺璟投资、万斯新材料等注销或退出不再纳入;新增纳入康达新材(泰国);2024年12月增资取得贵州盛顺82%并间接取得贵州麟拓63%纳入合并;并披露出售彩晶光电股权后将自2025年1月起不再纳入。
|
||||
|
||||
**Q66:出售彩晶光电股权的交易对手、比例与价格是多少?**
|
||||
**A:** 公司全资子公司新材料科技向唐山裕隆光电科技有限公司出售所持西安彩晶光电科技股份有限公司66.9996%股权,对价为人民币40,588.00万元。
|
||||
|
||||
**Q67:出售彩晶光电股权对资金用途的影响(报告说明)是什么?**
|
||||
**A:** 报告说明:不影响正常生产经营活动;所得资金将用于归还银行贷款及补充流动资金,降低管理成本,提高盈利能力。
|
||||
|
||||
---
|
||||
|
||||
## 18. 募集资金使用(摘要)
|
||||
|
||||
**Q68:公司涉及哪些募集资金年度与规模?**
|
||||
**A:** 2016年向特定对象发行股票:募集资金总额85,000万元、净额82,743.85万元;2022年向特定对象发行股票:募集资金总额70,000万元、净额69,125万元。
|
||||
|
||||
**Q69:截至2024年末,尚未使用募集资金余额与去向是什么?**
|
||||
**A:** 2016年募集资金已使用完毕;2022年募集资金尚未使用余额4,953.67万元,其中4,500万元用于暂时补充流动资金,剩余453.67万元存放于募集资金专户。
|
||||
|
||||
---
|
||||
|
||||
## 19. 公司治理与ESG(摘录)
|
||||
|
||||
**Q70:公司在分红政策与股东回报规划方面有哪些安排?**
|
||||
**A:** 报告披露:修订《公司章程》中现金分红相关条款,并制定《未来三年股东分红回报规划(2024-2026年)》。
|
||||
|
||||
**Q71:报告披露的ESG相关信息与奖项有哪些?**
|
||||
**A:** 报告披露公司发布《2023年度环境、社会与公司治理报告》、万得ESG评级保持A级,并获得“ESG金牛奖百强”等。
|
||||
|
||||
**Q72:公司总股本在2024年是否发生变化?原因是什么?**
|
||||
**A:** 报告披露2024年6月完成注销部分回购股份2,002,973股(占注销前总股本0.66%),注销完成后总股本由305,402,973股变更为303,400,000股。
|
||||
|
||||
---
|
||||
|
||||
## 20. 快速检索:关键数字一览
|
||||
|
||||
* 营业收入(2024):3,101,062,179.20 元
|
||||
* 归母净利润(2024):-246,173,519.93 元
|
||||
* 扣非归母净利润(2024):-308,264,197.06 元
|
||||
* 非经常性损益合计(2024):62,090,677.13 元
|
||||
* 经营活动现金流净额(2024):498,667,285.91 元
|
||||
* 投资活动现金流净额(2024):-358,595,995.08 元
|
||||
* 筹资活动现金流净额(2024):137,480,803.15 元
|
||||
* 现金及现金等价物净增加额(2024):276,194,234.34 元
|
||||
* 总资产(2024年末):6,940,150,195.69 元
|
||||
* 归母净资产(2024年末):2,727,894,289.21 元
|
||||
* 研发费用(2024):203,525,998.94 元(占营收 6.56%)
|
||||
@ -1,220 +0,0 @@
|
||||
# 康达新材:2025年第一季度报告(Q&A版)
|
||||
|
||||
> 说明:本文为对 `md/康达新材:2025年一季度报告.md` 的要点抽取与问答化重组,便于检索与知识库问答使用;问题为整理者归纳,答案以原文表述与数据为准。
|
||||
|
||||
---
|
||||
|
||||
## 1. 重要提示与审计情况
|
||||
|
||||
**Q1:公司对一季度报告信息披露作了什么承诺?**
|
||||
**A:** 本公司及董事会全体成员保证信息披露内容真实、准确、完整,没有虚假记载、误导性陈述或重大遗漏。
|
||||
|
||||
**Q2:谁对季度报告的真实、准确、完整承担责任?**
|
||||
**A:** 董事会、监事会及董事、监事、高级管理人员保证季度报告真实、准确、完整,不存在虚假记载、误导性陈述或重大遗漏,并承担个别和连带的法律责任。
|
||||
|
||||
**Q3:公司负责人及财务负责人对财务信息作了什么声明?**
|
||||
**A:** 公司负责人、主管会计工作负责人及会计机构负责人(会计主管人员)声明:保证季度报告中财务信息真实、准确、完整。
|
||||
|
||||
**Q4:2025年第一季度报告是否经过审计?**
|
||||
**A:** 否,第一季度报告未经审计。
|
||||
|
||||
---
|
||||
|
||||
## 2. 主要会计数据与财务指标(合并口径)
|
||||
|
||||
**Q5:2025年第一季度营业收入是多少?同比如何?**
|
||||
**A:** 营业收入877,010,301.56元;上年同期535,607,900.26元;同比增长63.74%。
|
||||
|
||||
**Q6:2025年第一季度归母净利润是多少?同比如何?**
|
||||
**A:** 归属于上市公司股东的净利润6,371,842.98元;上年同期-24,793,535.64元;同比125.70%。
|
||||
|
||||
**Q7:2025年第一季度扣非归母净利润是多少?同比如何?**
|
||||
**A:** 扣除非经常性损益的归母净利润3,363,744.00元;上年同期-26,427,485.42元;同比112.73%。
|
||||
|
||||
**Q8:2025年第一季度经营活动现金流净额是多少?同比如何?**
|
||||
**A:** 经营活动产生的现金流量净额-243,787,510.86元;上年同期73,400,210.80元;同比-432.13%。
|
||||
|
||||
**Q9:2025年第一季度每股收益(基本/稀释)是多少?**
|
||||
**A:** 基本每股收益0.021元/股;稀释每股收益0.021元/股(上年同期均为-0.083元/股)。
|
||||
|
||||
**Q10:2025年第一季度加权平均净资产收益率是多少?**
|
||||
**A:** 0.24%(上年同期-0.84%)。
|
||||
|
||||
**Q11:2025年3月31日总资产与归母净资产是多少?较期初如何变化?**
|
||||
**A:** 总资产6,747,360,816.59元,较2024年末6,940,150,195.69元下降2.78%;归属于上市公司股东的所有者权益2,724,565,896.80元,较2024年末2,727,894,289.21元下降0.12%。
|
||||
|
||||
---
|
||||
|
||||
## 3. 非经常性损益(2025年一季度)
|
||||
|
||||
**Q12:本报告期非经常性损益合计是多少?**
|
||||
**A:** 3,008,098.98元。
|
||||
|
||||
**Q13:非经常性损益中“非流动性资产处置损益”是多少?**
|
||||
**A:** 707,479.18元。
|
||||
|
||||
**Q14:计入当期损益的政府补助是多少?**
|
||||
**A:** 1,064,112.95元。
|
||||
|
||||
**Q15:本期金融资产/金融负债相关公允价值变动及处置损益(报告说明)是什么?**
|
||||
**A:** 2,000,000.00元,报告说明为“收到参股子公司上年度分红款”。
|
||||
|
||||
**Q16:单独进行减值测试的应收款项减值准备转回是多少?**
|
||||
**A:** 20,000.00元。
|
||||
|
||||
**Q17:其他营业外收入和支出净额是多少?**
|
||||
**A:** -252,725.49元。
|
||||
|
||||
**Q18:非经常性损益的所得税影响额与少数股东权益影响额(税后)分别是多少?**
|
||||
**A:** 所得税影响额529,763.22元;少数股东权益影响额(税后)1,004.44元。
|
||||
|
||||
**Q19:是否存在其他符合非经常性损益定义的项目或将其界定为经常性损益的情形?**
|
||||
**A:** 不存在。
|
||||
|
||||
---
|
||||
|
||||
## 4. 主要指标变动原因(报告披露)
|
||||
|
||||
### 4.1 资产负债表项目变动
|
||||
|
||||
**Q20:货币资金为何下降?下降多少?**
|
||||
**A:** 2025年3月31日495,660,929.09元,较2024年末799,834,198.31元下降38.03%;主要系报告期内归还银行贷款和支付原材料采购款增加所致。
|
||||
|
||||
**Q21:预付款项为何上升?上升多少?**
|
||||
**A:** 32,868,552.07元,较2024年末20,329,269.33元上升61.68%;主要系预付原材料采购款增加所致。
|
||||
|
||||
**Q22:其他应收款为何大幅下降?**
|
||||
**A:** 16,975,655.72元,较2024年末170,367,423.35元下降90.04%;主要系收到出售子公司股权转让款所致。
|
||||
|
||||
**Q23:投资性房地产为何为0?**
|
||||
**A:** 期末为0.00元,较2024年末4,606,245.07元下降100.00%;主要系对外出租房屋减少所致。
|
||||
|
||||
**Q24:固定资产为何上升、在建工程为何下降?**
|
||||
**A:** 固定资产1,497,341,452.78元,较2024年末1,116,686,210.01元上升34.09%;在建工程318,514,634.19元,较2024年末715,274,766.37元下降55.47%;主要系在建工程转固所致。
|
||||
|
||||
**Q25:其他非流动资产为何上升?**
|
||||
**A:** 22,099,433.13元,较2024年末9,721,396.37元上升127.33%;主要系预付设备款、工程款增加所致。
|
||||
|
||||
**Q26:预收款项为何大幅下降、合同负债为何大幅上升?**
|
||||
**A:** 预收款项178,176.12元,较2024年末70,466,949.54元下降99.75%,主要系预收客户货款减少(合同未签订);合同负债120,156,064.23元,较2024年末35,733,227.19元上升236.26%,主要系预收客户货款增加(合同已签订)。
|
||||
|
||||
**Q27:其他应付款为何下降?**
|
||||
**A:** 9,858,085.28元,较2024年末14,836,889.80元下降33.56%;主要系往来款减少所致。
|
||||
|
||||
**Q28:库存股为何上升?**
|
||||
**A:** 36,909,368.73元,较2024年末23,668,633.73元上升55.94%;主要系报告期内公司回购股份增加所致。
|
||||
|
||||
**Q29:其他综合收益为何上升?**
|
||||
**A:** 446,738.51元,较2024年末142,776.11元上升212.89%;主要系外币财务报表折算差额增加所致。
|
||||
|
||||
### 4.2 利润表项目变动
|
||||
|
||||
**Q30:营业收入同比大幅增长的原因是什么?**
|
||||
**A:** 主要系胶粘剂产品中风电胶系列产品订单增加等因素影响,公司销售规模有所上升。
|
||||
|
||||
**Q31:营业成本同比上升的原因是什么?**
|
||||
**A:** 主要系营业收入增加,营业成本随之增加所致;本期737,923,975.48元,上年同期436,730,929.55元,同比+68.97%。
|
||||
|
||||
**Q32:信用减值损失为何由正转负?**
|
||||
**A:** 本期信用减值损失-3,694,467.19元,上年同期6,345,252.90元;主要系期末应收款项增减变动导致计提减值同步变动所致。
|
||||
|
||||
**Q33:资产处置收益为何增加?**
|
||||
**A:** 本期资产处置收益707,844.97元,上年同期217,756.26元,同比+225.06%;主要系处置无法使用的资产增加所致。
|
||||
|
||||
**Q34:所得税费用为何增长?**
|
||||
**A:** 本期所得税费用8,903,252.97元,上年同期2,562,419.33元,同比+247.45%;主要系利润总额增加导致所得税费用增加。
|
||||
|
||||
**Q35:归母净利润同比转正的原因是什么?**
|
||||
**A:** 报告说明主要系营业收入大幅增加,导致净利润同时增加所致。
|
||||
|
||||
**Q36:其他综合收益的税后净额为何大幅增加?**
|
||||
**A:** 本期303,962.40元,上年同期286.65元;主要系外币财务报表折算差额增加所致。
|
||||
|
||||
---
|
||||
|
||||
## 5. 股东信息(截至2025年3月31日)
|
||||
|
||||
**Q37:报告期末普通股股东总数是多少?**
|
||||
**A:** 22,893户。
|
||||
|
||||
**Q38:控股股东是谁?持股比例与数量是多少?是否存在质押?**
|
||||
**A:** 唐山工业控股集团有限公司;持股87,421,621股,占28.81%;质押39,918,579股。
|
||||
|
||||
**Q39:报告披露的前10名股东(部分)有哪些?**
|
||||
**A:** 例如:第四期员工持股计划(4,673,900股,1.54%)、耿殿根(4,578,168股,1.51%)、张立岗(4,324,750股,1.43%)、第五期员工持股计划(4,289,000股,1.41%)、第三期员工持股计划(3,241,100股,1.07%)等(以原表为准)。
|
||||
|
||||
**Q40:前十名股东之间是否存在关联关系或一致行动关系?**
|
||||
**A:** 公司未知前十名股东之间是否存在关联关系,也未知是否属于一致行动人。
|
||||
|
||||
**Q41:是否披露融资融券参与情况?**
|
||||
**A:** 披露:股东程洁通过信用账户持有1,667,000股;股东周新勇通过信用账户持有1,567,800股。
|
||||
|
||||
---
|
||||
|
||||
## 6. 其他重要事项(报告披露)
|
||||
|
||||
**Q42:公司董事会、监事会换届及高管选举的时间节点是什么?**
|
||||
**A:** 2025年1月24日召开第五届董事会第四十一次会议、第五届监事会第三十三次会议审议换届相关议案,并提交2025年2月10日临时股东大会审议通过;2025年2月11日召开第六届董事会第一次会议、第六届监事会第一次会议选举董事长、监事会主席及高级管理人员。
|
||||
|
||||
**Q43:第八期股份回购方案的核心参数是什么?**
|
||||
**A:** 回购价格不超过15.00元/股(含);回购资金总额不低于10,000万元(含)、不高于20,000万元(含);资金来源为自有资金及回购专项贷款。
|
||||
|
||||
**Q44:首次实施回购发生在何时?回购了多少股、成交金额多少?**
|
||||
**A:** 2025年3月4日首次实施回购;回购284,000股(占总股本0.0936%),最高9.87元/股、最低9.57元/股;成交金额2,760,130元(不含交易费用)。
|
||||
|
||||
**Q45:截至2025年3月31日累计回购多少股、金额多少?**
|
||||
**A:** 累计回购1,372,300股(占总股本0.4523%),最高9.99元/股、最低9.29元/股;成交金额13,240,735元(不含交易费用)。
|
||||
|
||||
**Q46:报告披露的“回购比例达到2%”事项是什么?**
|
||||
**A:** 公司于2025年4月15日披露《关于回购股份比例达到2%暨回购进展公告》,累计回购股份数量已超过公司总股本的2%,后续将根据市场情况继续按方案实施。
|
||||
|
||||
**Q47:控股股东增持计划(2024/2025两段)主要内容是什么?**
|
||||
**A:** 2024年7月9日至2025年1月8日,唐山工控以集中竞价累计增持3,266,500股,占总股本1.08%,增持金额约3,010.75万元;2025年1月18日起6个月内继续增持,计划金额不低于1,000万元且不超过2,000万元(方式包括集中竞价和大宗交易等);截至目前唐山工控持股87,421,621股,占28.81%。
|
||||
|
||||
**Q48:报告披露的关联交易/共同投资事项是什么?**
|
||||
**A:** 2025年1月24日审议通过:全资子公司必控科技与关联方以0元对价共同收购四川忠华智能科技有限公司股权并增资;唐控智能认购注册资本2,800万元持股35%,必控科技认购2,800万元持股35%(其中固定资产作价2,600万元),风范晶樱认购1,600万元持股20%,海南远领与海南可为分别认购400万元各持股5%;截至目前工商变更登记手续已办理完毕。
|
||||
|
||||
---
|
||||
|
||||
## 7. 财务报表关键数字速查(合并口径)
|
||||
|
||||
**Q49:合并资产负债表中,期末主要资产项目有哪些关键数值?**
|
||||
**A:** 货币资金495,660,929.09元;应收票据266,700,534.77元;应收账款2,034,639,856.21元;应收款项融资208,971,494.43元;存货619,751,020.17元;固定资产1,497,341,452.78元;在建工程318,514,634.19元;无形资产320,537,081.21元;商誉437,307,088.79元。
|
||||
|
||||
**Q50:合并资产负债表中,期末主要负债项目有哪些关键数值?**
|
||||
**A:** 短期借款1,070,196,811.34元;应付票据449,338,097.20元;应付账款1,080,690,587.96元;合同负债120,156,064.23元;一年内到期的非流动负债359,701,421.02元;长期借款560,844,522.99元。
|
||||
|
||||
**Q51:期末归母所有者权益合计是多少?少数股东权益是多少?**
|
||||
**A:** 归属于母公司所有者权益合计2,724,565,896.80元;少数股东权益173,340,070.82元。
|
||||
|
||||
**Q52:合并利润表中,营业利润、利润总额与净利润分别是多少?**
|
||||
**A:** 营业利润11,044,437.99元;利润总额10,791,346.71元;净利润1,888,093.74元。
|
||||
|
||||
**Q53:归属母公司净利润与少数股东损益分别是多少?**
|
||||
**A:** 归属于母公司所有者的净利润6,371,842.98元;少数股东损益-4,483,749.24元。
|
||||
|
||||
**Q54:合并现金流量表中,三大现金流净额分别是多少?**
|
||||
**A:** 经营活动现金流净额-243,787,510.86元;投资活动现金流净额92,049,529.25元;筹资活动现金流净额-164,746,680.98元。
|
||||
|
||||
**Q55:投资活动现金流中,处置子公司及其他营业单位收到的现金净额是多少?**
|
||||
**A:** 162,352,000.00元。
|
||||
|
||||
**Q56:筹资活动现金流中,取得借款收到的现金、偿还债务支付的现金分别是多少?**
|
||||
**A:** 取得借款收到的现金478,085,262.74元;偿还债务支付的现金660,256,118.51元。
|
||||
|
||||
**Q57:本期现金及现金等价物净增加额、期末余额分别是多少?**
|
||||
**A:** 净增加额-316,378,429.93元;期末余额405,856,258.35元。
|
||||
|
||||
---
|
||||
|
||||
## 8. 快速检索:关键指标一览
|
||||
|
||||
* 营业收入(2025Q1):877,010,301.56 元
|
||||
* 归母净利润(2025Q1):6,371,842.98 元
|
||||
* 扣非归母净利润(2025Q1):3,363,744.00 元
|
||||
* 非经常性损益合计(2025Q1):3,008,098.98 元
|
||||
* 经营活动现金流净额(2025Q1):-243,787,510.86 元
|
||||
* 总资产(2025-03-31):6,747,360,816.59 元
|
||||
* 归母净资产(2025-03-31):2,724,565,896.80 元
|
||||
* 普通股股东总数:22,893
|
||||
* 控股股东持股:87,421,621 股(28.81%)
|
||||
@ -1,25 +0,0 @@
|
||||
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=['*'],
|
||||
)
|
||||
@ -1,17 +0,0 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
def add_gzip_middleware(app: FastAPI):
|
||||
"""
|
||||
添加gzip压缩中间件 - 已禁用
|
||||
|
||||
注意: 由于SSE流式响应需要实时传输,gzip压缩被禁用
|
||||
SSE数据流很小,压缩意义不大,反而会影响实时性
|
||||
|
||||
:param app: FastAPI对象
|
||||
:return:
|
||||
"""
|
||||
# 暂时禁用GZip中间件以支持SSE流式响应
|
||||
# 如果需要重新启用,请取消下面的注释
|
||||
# app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=9)
|
||||
pass
|
||||
@ -1,16 +0,0 @@
|
||||
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)
|
||||
@ -1,17 +0,0 @@
|
||||
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)
|
||||
@ -1,23 +0,0 @@
|
||||
# -*- 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()
|
||||
@ -1,47 +0,0 @@
|
||||
# -*- 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)
|
||||
@ -1,52 +0,0 @@
|
||||
# -*- 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)
|
||||
@ -1,266 +0,0 @@
|
||||
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
|
||||
@ -1,84 +0,0 @@
|
||||
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
|
||||
@ -1,75 +0,0 @@
|
||||
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
|
||||
@ -1,68 +0,0 @@
|
||||
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='该用户无此接口权限')
|
||||
@ -1,89 +0,0 @@
|
||||
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)
|
||||
@ -1,40 +0,0 @@
|
||||
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
|
||||
)
|
||||
)
|
||||
@ -1,36 +0,0 @@
|
||||
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)
|
||||
@ -1,58 +0,0 @@
|
||||
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)
|
||||
@ -1,123 +0,0 @@
|
||||
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))
|
||||
@ -1,132 +0,0 @@
|
||||
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)
|
||||
@ -1,239 +0,0 @@
|
||||
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))
|
||||
@ -1,162 +0,0 @@
|
||||
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))
|
||||
@ -1,143 +0,0 @@
|
||||
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))
|
||||
@ -1,111 +0,0 @@
|
||||
# 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))
|
||||
@ -1,105 +0,0 @@
|
||||
# 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))
|
||||
@ -1,130 +0,0 @@
|
||||
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))
|
||||
@ -1,81 +0,0 @@
|
||||
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]}")
|
||||
@ -1,106 +0,0 @@
|
||||
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))
|
||||
@ -1,131 +0,0 @@
|
||||
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))
|
||||
@ -1,131 +0,0 @@
|
||||
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))
|
||||
@ -1,194 +0,0 @@
|
||||
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))
|
||||
@ -1,150 +0,0 @@
|
||||
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))
|
||||
@ -1,155 +0,0 @@
|
||||
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='退出成功')
|
||||
@ -1,114 +0,0 @@
|
||||
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)
|
||||
@ -1,163 +0,0 @@
|
||||
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))
|
||||
@ -1,89 +0,0 @@
|
||||
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)
|
||||
@ -1,40 +0,0 @@
|
||||
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)
|
||||
@ -1,105 +0,0 @@
|
||||
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))
|
||||
@ -1,630 +0,0 @@
|
||||
"""
|
||||
简化的RAGFlow控制器 - 集成语义缓存
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import hashlib
|
||||
import json
|
||||
import time
|
||||
from typing import Any, AsyncGenerator, Dict, Generator, Tuple
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
from module_admin.service.login_service import LoginService
|
||||
from module_admin.service.kb_es_service import KBESService
|
||||
from module_admin.service.ragflow_service import RAGFlowService
|
||||
from module_admin.service.search_service import SearchService
|
||||
from module_admin.entity.vo.ragflow_vo import (
|
||||
ConverseWithChatAssistantModel,
|
||||
CreateSessionWithChatModel,
|
||||
UpdateChatAssistantModel,
|
||||
RagflowListQueryModel
|
||||
)
|
||||
from utils.log_util import logger
|
||||
from utils.response_util import ResponseUtil
|
||||
from utils.semantic_cache_service import get_semantic_cache_service, store_qa_pair, get_question_hash
|
||||
from utils.static_qa_service import get_static_qa_service
|
||||
from utils.deepseek_client import DeepSeekAPIClient
|
||||
from config.env import KBConfig
|
||||
|
||||
|
||||
async def _async_store_qa(chat_id: str, question: str, answer: str, redis) -> None:
|
||||
"""
|
||||
异步存储问答对到语义缓存
|
||||
"""
|
||||
store_hash = get_question_hash(question)
|
||||
logger.info(f"[SemanticCache] 存储QA | chat_id={chat_id} | question={question} | hash={store_hash} | answer_length={len(answer)}")
|
||||
try:
|
||||
await store_qa_pair(chat_id, question, answer, redis)
|
||||
except Exception as e:
|
||||
logger.warning(f"[SemanticCache] 异步存储失败: {e}")
|
||||
|
||||
|
||||
# 使用标准的APIRouter,增加认证依赖
|
||||
ragflowController = APIRouter(prefix="/system/ragflow", dependencies=[Depends(LoginService.get_current_user)])
|
||||
|
||||
|
||||
def format_sse(data: dict, event: str | None = None) -> str:
|
||||
"""格式化SSE数据"""
|
||||
payload = json.dumps(data, ensure_ascii=False)
|
||||
prefix = f'event: {event}\n' if event else ''
|
||||
return f'{prefix}data: {payload}\n\n'
|
||||
|
||||
|
||||
def parse_result(result: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""统一结果解析"""
|
||||
code = result.get('code', 0)
|
||||
if code != 0:
|
||||
msg = result.get('message') or result.get('msg') or '接口异常'
|
||||
return ResponseUtil.error(msg=msg, data=result.get('data', None))
|
||||
return ResponseUtil.success(data=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 remove_style_hint(question: str) -> Tuple[str, bool]:
|
||||
"""
|
||||
移除语言风格提示词
|
||||
|
||||
Args:
|
||||
question: 原始问题
|
||||
|
||||
Returns:
|
||||
(处理后的问题, 是否移除了风格提示词)
|
||||
"""
|
||||
if not question:
|
||||
return question, False
|
||||
|
||||
import re
|
||||
|
||||
pattern = r'语言风格\s*[::]\s*[^,。!?\n]*'
|
||||
match = re.search(pattern, question)
|
||||
|
||||
if match:
|
||||
style_hint = match.group(0)
|
||||
cleaned_question = re.sub(pattern, '', question).strip()
|
||||
cleaned_question = re.sub(r'\s+', ' ', cleaned_question)
|
||||
logger.info(f'[StyleHint] 移除风格提示词: {style_hint} | 原始问题: {question} | 处理后: {cleaned_question}')
|
||||
return cleaned_question, True
|
||||
|
||||
return question, False
|
||||
|
||||
|
||||
# 非流式对话接口
|
||||
@ragflowController.post('/converse_with_chat_assistant')
|
||||
async def converse_with_chat_assistant(
|
||||
request: Request,
|
||||
converse_params: ConverseWithChatAssistantModel,
|
||||
):
|
||||
"""
|
||||
与聊天助手进行对话 - 集成语义缓存版本(支持流式和非流式)
|
||||
|
||||
匹配流程:
|
||||
1. 移除语言风格提示词
|
||||
2. 静态问答匹配 (threshold=0.70)
|
||||
3. 搜索服务判断
|
||||
4. RAG历史缓存匹配 (threshold=0.60)
|
||||
5. RAG服务调用(使用原始问题)
|
||||
"""
|
||||
start_time = time.perf_counter()
|
||||
|
||||
original_question = converse_params.question
|
||||
cleaned_question, style_removed = remove_style_hint(original_question)
|
||||
|
||||
if style_removed:
|
||||
converse_params.question = cleaned_question
|
||||
|
||||
# 获取redis实例
|
||||
redis = getattr(request.app.state, 'redis', None)
|
||||
cached_answer = None
|
||||
cache_similarity = 0.0
|
||||
|
||||
# ========== 1. 静态问答匹配 ==========
|
||||
try:
|
||||
static_qa_service = get_static_qa_service()
|
||||
logger.info(f'[StaticQA] 开始匹配 | chat_id={converse_params.chat_id} | question={converse_params.question} | threshold=0.70')
|
||||
static_match, static_sim = static_qa_service.find_match(converse_params.question, threshold=0.70)
|
||||
logger.info(f'[StaticQA] 匹配结果 | similarity={static_sim:.2f} | matched={static_match is not None}')
|
||||
|
||||
if static_match:
|
||||
cached_answer = static_match.get('answer', '')
|
||||
cache_similarity = static_sim
|
||||
logger.info(f'[RAG_SOURCE] 命中静态FAQ | chat_id={converse_params.chat_id} | question={converse_params.question} | similarity={cache_similarity:.2f} | answer_length={len(cached_answer)}')
|
||||
logger.info(f'[StaticQA] 流式响应使用静态问答答案,chat_id={converse_params.chat_id}')
|
||||
return StreamingResponse(
|
||||
stream_cached_response(cached_answer, converse_params.chat_id, start_time, cache_source='static_qa'),
|
||||
media_type='text/event-stream',
|
||||
headers={
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
'X-Accel-Buffering': 'no',
|
||||
'Transfer-Encoding': 'chunked'
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f'[StaticQA] 静态问答匹配失败: {e}')
|
||||
|
||||
# ========== 2. 检查是否应该使用搜索服务 ==========
|
||||
try:
|
||||
intent = await SearchService.classify_intent(converse_params.question)
|
||||
logger.info(f"[RAG_PROFILE] Intent Router ({converse_params.chat_id}): {intent}")
|
||||
|
||||
if intent == 'SEARCH':
|
||||
return await SearchService.handle_search_chat(converse_params, redis)
|
||||
except Exception as e:
|
||||
logger.warning(f"意图分类失败,使用RAG服务: {e}")
|
||||
|
||||
# ========== 3. RAG历史缓存查找 ==========
|
||||
# 注意:当 KB_PROVIDER=es_bm25 时,内部KB答案应直接来源于 ES 检索结果,
|
||||
# 避免 rag:semantic:cache 抢答导致“curl 与 API 不一致”。
|
||||
if KBConfig.KB_PROVIDER != 'es_bm25':
|
||||
logger.info(f'[SemanticCache] 准备执行RAG历史缓存查找 | redis={redis is not None} | chat_id={converse_params.chat_id}')
|
||||
if redis:
|
||||
lookup_hash = get_question_hash(converse_params.question)
|
||||
logger.info(f'[SemanticCache] 开始查找 | chat_id={converse_params.chat_id} | question={converse_params.question} | hash={lookup_hash} | threshold=0.60')
|
||||
|
||||
service = get_semantic_cache_service()
|
||||
logger.info(f'[SemanticCache] service实例: {service}')
|
||||
cache_result = await service.lookup(
|
||||
converse_params.chat_id,
|
||||
converse_params.question,
|
||||
redis
|
||||
)
|
||||
logger.info(f'[SemanticCache] 查找结果 | found={cache_result is not None}')
|
||||
if cache_result:
|
||||
cached_answer, cache_similarity = cache_result
|
||||
logger.info(f'[RAG_SOURCE] 命中RAG会话历史 | chat_id={converse_params.chat_id} | question={converse_params.question} | similarity={cache_similarity:.2f} | answer_length={len(cached_answer)}')
|
||||
logger.info(f'[SemanticCache] 流式响应使用RAG历史缓存答案,chat_id={converse_params.chat_id}')
|
||||
return StreamingResponse(
|
||||
stream_cached_response(cached_answer, converse_params.chat_id, start_time, cache_source='rag_history'),
|
||||
media_type='text/event-stream',
|
||||
headers={
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
'X-Accel-Buffering': 'no',
|
||||
'Transfer-Encoding': 'chunked'
|
||||
}
|
||||
)
|
||||
else:
|
||||
logger.info(f"[SemanticCache] skip lookup because KB_PROVIDER=es_bm25 | chat_id={converse_params.chat_id}")
|
||||
|
||||
# ========== 4. RAG服务调用(在这里根据 KB_PROVIDER 选择 ragflow / es_bm25) ==========
|
||||
try:
|
||||
# 恢复原始问题(包含语言风格提示词)
|
||||
if style_removed:
|
||||
converse_params.question = original_question
|
||||
logger.info(f'[StyleHint] 恢复原始问题: {original_question}')
|
||||
|
||||
if KBConfig.KB_PROVIDER == 'es_bm25':
|
||||
try:
|
||||
chunks, metrics = await KBESService.retrieve(converse_params.question, redis=redis)
|
||||
logger.info(
|
||||
f"[KB_ES] retrieve_ms={metrics.retrieve_ms:.1f} | top1_score={metrics.top1_score:.3f} | score_gap={metrics.score_gap:.3f} | "
|
||||
f"hit_count={metrics.hit_count} | keyword_coverage={metrics.keyword_coverage:.2f} | cache_hit={metrics.cache_hit} | "
|
||||
f"chat_id={converse_params.chat_id}"
|
||||
)
|
||||
|
||||
if KBESService.is_confident(metrics):
|
||||
# KB_PROVIDER=es_bm25 时不写入 rag:semantic:cache,避免污染后续命中
|
||||
cache_store_func = None
|
||||
|
||||
return StreamingResponse(
|
||||
stream_kb_es_response(
|
||||
question=converse_params.question,
|
||||
chunks=chunks,
|
||||
chat_id=converse_params.chat_id,
|
||||
start_time=start_time,
|
||||
cache_store_func=cache_store_func,
|
||||
retrieve_ms=metrics.retrieve_ms,
|
||||
),
|
||||
media_type='text/event-stream',
|
||||
headers={
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
'X-Accel-Buffering': 'no',
|
||||
'Transfer-Encoding': 'chunked'
|
||||
}
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"[KB_ES] 低置信度,准备fallback | fallback={KBConfig.KB_FALLBACK} | chat_id={converse_params.chat_id}"
|
||||
)
|
||||
if KBConfig.KB_FALLBACK == 'none':
|
||||
return StreamingResponse(
|
||||
stream_cached_response(
|
||||
'我目前不太确定答案。请补充更多关键词或换种问法。',
|
||||
converse_params.chat_id,
|
||||
start_time,
|
||||
cache_source='kb_es_low_confidence'
|
||||
),
|
||||
media_type='text/event-stream',
|
||||
headers={
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
'X-Accel-Buffering': 'no',
|
||||
'Transfer-Encoding': 'chunked'
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"[KB_ES] 内部KB异常: {e}")
|
||||
if KBConfig.KB_FALLBACK == 'none':
|
||||
return StreamingResponse(
|
||||
stream_cached_response(
|
||||
'内部知识库检索失败,请稍后再试或补充更多关键词。',
|
||||
converse_params.chat_id,
|
||||
start_time,
|
||||
cache_source='kb_es_error'
|
||||
),
|
||||
media_type='text/event-stream',
|
||||
headers={
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
'X-Accel-Buffering': 'no',
|
||||
'Transfer-Encoding': 'chunked'
|
||||
}
|
||||
)
|
||||
|
||||
logger.info(f'[RAG_SOURCE] 调用原生RAG服务 | chat_id={converse_params.chat_id} | question={converse_params.question}')
|
||||
result = RAGFlowService.converse_with_chat_assistant_services(converse_params)
|
||||
|
||||
cache_question = cleaned_question if style_removed else converse_params.question
|
||||
store_hash = get_question_hash(cache_question)
|
||||
logger.info(f'[RAG_CACHE] 准备存储 | chat_id={converse_params.chat_id} | question={cache_question} | hash={store_hash}')
|
||||
|
||||
async def cache_store_func(answer: str):
|
||||
if redis and answer and len(answer.strip()) >= 10:
|
||||
try:
|
||||
await _async_store_qa(converse_params.chat_id, cache_question, answer, redis)
|
||||
logger.info(f'[RAG_CACHE] 语义缓存存储成功 | chat_id={converse_params.chat_id} | answer_length={len(answer)}')
|
||||
except Exception as e:
|
||||
logger.warning(f'[RAG_CACHE] 语义缓存存储失败: {e}')
|
||||
|
||||
return StreamingResponse(
|
||||
stream_ragflow_response(result, converse_params.chat_id, start_time, cache_store_func=cache_store_func),
|
||||
media_type='text/event-stream',
|
||||
headers={
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
'X-Accel-Buffering': 'no',
|
||||
'Transfer-Encoding': 'chunked'
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f'[RAG_PROFILE] ragflow对话异常: {e}')
|
||||
return ResponseUtil.error(msg=f'对话服务异常: {str(e)}')
|
||||
|
||||
|
||||
async def stream_ragflow_response(result: Generator, chat_id: str, start_time: float, cache_store_func=None) -> AsyncGenerator[str, None]:
|
||||
"""
|
||||
流式处理RAGFlow响应 - 异步版本,修复首token延迟
|
||||
"""
|
||||
import time
|
||||
|
||||
server_stream_start = time.time()
|
||||
logger.info(f"[RAG_SERVER {server_stream_start:.3f}] 🚀 开始流式响应处理,chat_id: {chat_id}")
|
||||
|
||||
last_answer = ""
|
||||
first_token_received = False
|
||||
start_stream_time = time.perf_counter()
|
||||
|
||||
# 立即发送连接建立消息,解决首token延迟
|
||||
connection_time = time.time()
|
||||
ping_message = "event: ping\ndata: {\"status\": \"connected\"}\n\n"
|
||||
yield ping_message
|
||||
logger.info(f"[RAG_SERVER {connection_time:.3f}] 📡 连接建立ping发送,耗时: {connection_time - server_stream_start:.3f}s")
|
||||
|
||||
chunk_count = 0
|
||||
try:
|
||||
for chunk in result:
|
||||
chunk_time = time.time()
|
||||
chunk_count += 1
|
||||
|
||||
# 让出控制权,确保流式响应
|
||||
await asyncio.sleep(0)
|
||||
|
||||
# 检查第一个token的延迟
|
||||
if not first_token_received:
|
||||
first_token_received = True
|
||||
latency = time.perf_counter() - start_stream_time
|
||||
logger.info(f"[RAG_SERVER {chunk_time:.3f}] 🎯 首token到达,耗时: {latency:.3f}s")
|
||||
|
||||
# 处理chunk数据
|
||||
payload = chunk.get('data') if isinstance(chunk, dict) else chunk
|
||||
if not payload:
|
||||
logger.info(f"[RAG_SERVER {chunk_time:.3f}] ⚠️ 空chunk跳过")
|
||||
continue
|
||||
|
||||
body = payload if isinstance(payload, dict) else {'data': payload}
|
||||
|
||||
# 处理错误
|
||||
if isinstance(chunk, dict) and chunk.get('code') and chunk.get('code') != 0:
|
||||
logger.error(f"[RAG_SERVER {chunk_time:.3f}] ❌ RAGFlow Stream Error: {chunk}")
|
||||
error_message = format_sse({'message': chunk.get('message', '流式处理异常')}, event='error')
|
||||
yield error_message
|
||||
continue
|
||||
|
||||
# 处理answer字段的增量更新
|
||||
if isinstance(body, dict) and 'answer' in body:
|
||||
current_answer = body['answer']
|
||||
|
||||
# 安全检查:确保current_answer不为None且为字符串
|
||||
if current_answer is None:
|
||||
logger.warning(f"[RAG_SERVER {chunk_time:.3f}] ⚠️ current_answer为None,跳过处理")
|
||||
continue
|
||||
|
||||
if not isinstance(current_answer, str):
|
||||
logger.warning(f"[RAG_SERVER {chunk_time:.3f}] ⚠️ current_answer不是字符串,类型: {type(current_answer)}")
|
||||
current_answer = str(current_answer)
|
||||
|
||||
# 计算增量内容
|
||||
if current_answer.startswith(last_answer):
|
||||
delta = current_answer[len(last_answer):]
|
||||
if delta:
|
||||
body['answer'] = delta
|
||||
last_answer = current_answer
|
||||
logger.info(f"[RAG_SERVER {chunk_time:.3f}] 📝 Chunk #{chunk_count} 处理完成,delta长度: {len(delta)}")
|
||||
yield format_sse(body)
|
||||
else:
|
||||
# 上下文重置的备用处理
|
||||
last_answer = current_answer
|
||||
logger.info(f"[RAG_SERVER {chunk_time:.3f}] 🔄 Chunk #{chunk_count} 上下文重置")
|
||||
yield format_sse(body)
|
||||
else:
|
||||
logger.info(f"[RAG_SERVER {chunk_time:.3f}] 📦 Chunk #{chunk_count} 其他数据: {body}")
|
||||
yield format_sse(body)
|
||||
|
||||
# 流结束
|
||||
stream_end_time = time.time()
|
||||
end_message = format_sse({'status': 'completed'}, event='end')
|
||||
yield end_message
|
||||
logger.info(f'[RAG_SERVER {stream_end_time:.3f}] 流式响应完成,chat_id: {chat_id}')
|
||||
logger.info(f'[RAG_SERVER {stream_end_time:.3f}] 总共处理chunk数量: {chunk_count}')
|
||||
logger.info(f'[RAG_SOURCE] 原生RAG流式响应完成 | chat_id={chat_id} | total_chunks={chunk_count} | answer_length={len(last_answer)}')
|
||||
|
||||
except Exception as exc:
|
||||
error_time = time.time()
|
||||
logger.exception(f'[RAG_SERVER {error_time:.3f}] ragflow流式对话异常: {exc}')
|
||||
error_message = format_sse({'message': str(exc)}, event='error')
|
||||
yield error_message
|
||||
finally:
|
||||
total_time = time.perf_counter() - start_time
|
||||
logger.info(f'[RAG_SERVER {time.time():.3f}] ⏱️ Total Stream Duration ({chat_id}): {total_time:.3f}s')
|
||||
|
||||
if cache_store_func and last_answer and len(last_answer.strip()) >= 10:
|
||||
try:
|
||||
await cache_store_func(last_answer)
|
||||
logger.info(f'[RAG_CACHE] 缓存存储完成 | chat_id={chat_id} | answer_length={len(last_answer)}')
|
||||
except Exception as cache_err:
|
||||
logger.warning(f'[RAG_CACHE] 缓存存储失败: {cache_err}')
|
||||
|
||||
|
||||
# 聊天助手列表
|
||||
@ragflowController.post('/get_chat_assistant_list')
|
||||
async def get_chat_assistant_list(
|
||||
query_params: RagflowListQueryModel, # 使用正确的Pydantic模型
|
||||
):
|
||||
"""获取聊天助手列表"""
|
||||
result = RAGFlowService.get_chat_assistant_list_services(query_params)
|
||||
return parse_result(result)
|
||||
|
||||
|
||||
# 创建会话
|
||||
@ragflowController.post('/create_session_with_chat')
|
||||
async def create_session_with_chat(
|
||||
create_params: CreateSessionWithChatModel, # 使用正确的Pydantic模型
|
||||
):
|
||||
"""创建聊天会话"""
|
||||
result = RAGFlowService.create_session_with_chat_services(create_params)
|
||||
return parse_result(result)
|
||||
|
||||
|
||||
# 更新聊天助手
|
||||
@ragflowController.post('/update_chat_assistant')
|
||||
async def update_chat_assistant(
|
||||
update_params: UpdateChatAssistantModel, # 使用正确的Pydantic模型
|
||||
):
|
||||
"""更新聊天助手"""
|
||||
result = RAGFlowService.update_chat_assistant_services(update_params)
|
||||
return parse_result(result)
|
||||
|
||||
|
||||
# 数据集相关接口
|
||||
@ragflowController.post('/dataset_list')
|
||||
async def get_system_ragflow_list(
|
||||
request: Request,
|
||||
ragflow_list_query: Any, # 使用Any避免导入具体模型类
|
||||
):
|
||||
"""获取数据集列表"""
|
||||
result = RAGFlowService.get_ragflow_dataset_list_services(None, ragflow_list_query)
|
||||
return parse_result(result)
|
||||
|
||||
|
||||
@ragflowController.post('/create_dataset')
|
||||
async def create_dataset(
|
||||
create_dataset_params: Any, # 使用Any避免导入具体模型类
|
||||
):
|
||||
"""创建数据集"""
|
||||
result = RAGFlowService.create_dataset_services(create_dataset_params)
|
||||
return parse_result(result)
|
||||
|
||||
|
||||
@ragflowController.post('/delete_datasets')
|
||||
async def delete_datasets(
|
||||
delete_params: Any, # 使用Any避免导入具体模型类
|
||||
):
|
||||
"""删除数据集"""
|
||||
result = RAGFlowService.delete_datasets_services(delete_params)
|
||||
return parse_result(result)
|
||||
|
||||
|
||||
def stream_cached_response(cached_answer: str, chat_id: str, start_time: float, cache_source: str = 'cache') -> Generator[str, None, None]:
|
||||
"""
|
||||
流式返回缓存的答案
|
||||
|
||||
Args:
|
||||
cached_answer: 缓存的答案文本
|
||||
chat_id: 会话ID
|
||||
start_time: 请求开始时间
|
||||
cache_source: 缓存来源 ('static_qa' 或 'rag_history')
|
||||
"""
|
||||
import time
|
||||
|
||||
server_stream_start = time.time()
|
||||
logger.info(f"[CACHE_STREAM {server_stream_start:.3f}] 🚀 开始流式返回缓存答案,chat_id: {chat_id}")
|
||||
|
||||
# 立即发送连接建立消息
|
||||
connection_time = time.time()
|
||||
ping_message = "event: ping\ndata: {\"status\": \"connected\"}\n\n"
|
||||
yield ping_message
|
||||
logger.info(f"[CACHE_STREAM {connection_time:.3f}] 📡 连接建立ping发送,耗时: {connection_time - server_stream_start:.3f}s")
|
||||
|
||||
# 模拟流式输出效果(可选:如果是完整答案,可以选择立即返回或模拟流式)
|
||||
# 这里我们选择模拟流式输出,每次发送一小部分,模拟打字效果
|
||||
if cached_answer:
|
||||
# 将答案分块发送,模拟打字效果
|
||||
chunk_size = 10 # 每个块10个字符
|
||||
answer_chunks = [cached_answer[i:i+chunk_size] for i in range(0, len(cached_answer), chunk_size)]
|
||||
|
||||
for i, chunk in enumerate(answer_chunks):
|
||||
chunk_time = time.time()
|
||||
|
||||
# 首token延迟
|
||||
if i == 0:
|
||||
latency = time.perf_counter() - start_time
|
||||
logger.info(f"[CACHE_STREAM {chunk_time:.3f}] 🎯 首token到达(缓存),耗时: {latency:.3f}s")
|
||||
|
||||
body = {'answer': chunk, 'chunk_index': i, 'total_chunks': len(answer_chunks)}
|
||||
yield format_sse(body)
|
||||
|
||||
# 小延迟模拟打字效果(可调整或移除)
|
||||
# time.sleep(0.01)
|
||||
|
||||
logger.info(f"[CACHE_STREAM {chunk_time:.3f}] 📝 缓存答案流式发送完成,共 {len(answer_chunks)} 个chunk")
|
||||
|
||||
# 流结束
|
||||
stream_end_time = time.time()
|
||||
end_message = format_sse({
|
||||
'status': 'completed',
|
||||
'from_cache': True,
|
||||
'source': cache_source,
|
||||
'total_time': stream_end_time - server_stream_start
|
||||
}, event='end')
|
||||
yield end_message
|
||||
|
||||
logger.info(f"[CACHE_STREAM {stream_end_time:.3f}] 🏁 缓存流式响应完成")
|
||||
if cached_answer:
|
||||
logger.info(f"[CACHE_STREAM {stream_end_time:.3f}] 📊 缓存答案长度: {len(cached_answer)} 字符")
|
||||
|
||||
total_time = time.perf_counter() - start_time
|
||||
logger.info(f'[CACHE_STREAM {time.time():.3f}] ⏱️ Total Cache Stream Duration ({chat_id}): {total_time:.3f}s')
|
||||
|
||||
|
||||
async def stream_kb_es_response(
|
||||
*,
|
||||
question: str,
|
||||
chunks: list[dict],
|
||||
chat_id: str,
|
||||
start_time: float,
|
||||
cache_store_func=None,
|
||||
retrieve_ms: float | None = None,
|
||||
) -> AsyncGenerator[str, None]:
|
||||
import time
|
||||
|
||||
server_stream_start = time.time()
|
||||
logger.info(f"[KB_ES_STREAM {server_stream_start:.3f}] 开始流式响应处理,chat_id: {chat_id}")
|
||||
|
||||
ping_message = 'event: ping\ndata: {"status": "connected"}\n\n'
|
||||
yield ping_message
|
||||
|
||||
# 结构化Q&A命中时,直接返回 answer(最快,不走 LLM)
|
||||
if chunks:
|
||||
top = chunks[0]
|
||||
top_answer = (top.get('answer') or '').strip()
|
||||
if top_answer:
|
||||
chunk_size = 12
|
||||
for i in range(0, len(top_answer), chunk_size):
|
||||
if i == 0:
|
||||
latency = time.perf_counter() - start_time
|
||||
logger.info(
|
||||
f"[KB_ES_STREAM {time.time():.3f}] 首token到达(抽取式),ttft={latency:.3f}s | retrieve_ms={retrieve_ms} | chat_id={chat_id}"
|
||||
)
|
||||
yield format_sse({'answer': top_answer[i : i + chunk_size], 'source': 'kb_es'})
|
||||
yield format_sse({'status': 'completed', 'source': 'kb_es'}, event='end')
|
||||
if cache_store_func and len(top_answer.strip()) >= 10:
|
||||
try:
|
||||
await cache_store_func(top_answer)
|
||||
except Exception as e:
|
||||
logger.warning(f"[KB_ES_STREAM] 缓存存储失败: {e}")
|
||||
return
|
||||
|
||||
context_parts = []
|
||||
for i, c in enumerate(chunks[: KBConfig.KB_TOP_K], 1):
|
||||
title = (c.get('title') or '').strip()
|
||||
source = (c.get('source') or '').strip()
|
||||
content = (c.get('content') or '').strip()
|
||||
header = f"[{i}]"
|
||||
if title:
|
||||
header += f" {title}"
|
||||
if source:
|
||||
header += f" ({source})"
|
||||
context_parts.append(f"{header}\n{content}")
|
||||
|
||||
context = "\n\n".join(context_parts)
|
||||
system_prompt = (
|
||||
"你是公司内部知识库助手。请严格基于【已知资料】回答;如果资料不足以回答,直接说明不知道并提示用户补充关键词。"
|
||||
"回答要简洁。"
|
||||
)
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": f"{system_prompt}\n\n【已知资料】\n{context}"},
|
||||
{"role": "user", "content": question},
|
||||
]
|
||||
|
||||
full_answer = ''
|
||||
first_token_sent = False
|
||||
|
||||
try:
|
||||
client = DeepSeekAPIClient()
|
||||
stream = await client.chat_completion(
|
||||
messages=messages,
|
||||
temperature=0.2,
|
||||
max_tokens=512,
|
||||
stream=True,
|
||||
)
|
||||
|
||||
async for chunk in stream:
|
||||
await asyncio.sleep(0)
|
||||
try:
|
||||
delta = chunk.choices[0].delta.content or ''
|
||||
except Exception:
|
||||
delta = ''
|
||||
if not delta:
|
||||
continue
|
||||
if not first_token_sent:
|
||||
first_token_sent = True
|
||||
latency = time.perf_counter() - start_time
|
||||
logger.info(
|
||||
f"[KB_ES_STREAM {time.time():.3f}] 首token到达,ttft={latency:.3f}s | retrieve_ms={retrieve_ms} | chat_id={chat_id}"
|
||||
)
|
||||
full_answer += delta
|
||||
yield format_sse({'answer': delta, 'source': 'kb_es'})
|
||||
|
||||
yield format_sse({'status': 'completed', 'source': 'kb_es'}, event='end')
|
||||
|
||||
except Exception as exc:
|
||||
logger.exception(f"[KB_ES_STREAM] 流式对话异常: {exc}")
|
||||
yield format_sse({'message': str(exc)}, event='error')
|
||||
finally:
|
||||
total_time = time.perf_counter() - start_time
|
||||
logger.info(f"[KB_ES_STREAM {time.time():.3f}] Total Stream Duration ({chat_id}): {total_time:.3f}s")
|
||||
if cache_store_func and full_answer and len(full_answer.strip()) >= 10:
|
||||
try:
|
||||
await cache_store_func(full_answer)
|
||||
except Exception as e:
|
||||
logger.warning(f"[KB_ES_STREAM] 缓存存储失败: {e}")
|
||||
@ -1,376 +0,0 @@
|
||||
# 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.search_service import SearchService, SearchServiceError
|
||||
|
||||
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
|
||||
import asyncio
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
# 检查是否应该使用搜索服务 (Router Strategy)
|
||||
# Using 'glm-4-flash' to classify intent: SEARCH vs RAG
|
||||
t0 = time.perf_counter()
|
||||
intent = await SearchService.classify_intent(converse_params.question)
|
||||
logger.info(f"[RAG_PROFILE] Intent Router ({converse_params.chat_id}): {intent} | Time: {time.perf_counter() - t0:.3f}s | Query: {converse_params.question}")
|
||||
|
||||
if intent == 'SEARCH':
|
||||
return await SearchService.handle_search_chat(converse_params, redis)
|
||||
|
||||
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))
|
||||
|
||||
# Revert to Native RAGFlow Service (OpenAI endpoint failed with Auth error)
|
||||
t1 = time.perf_counter()
|
||||
# 使用同步Generator,更简单
|
||||
result = RAGFlowService.converse_with_chat_assistant_services(converse_params)
|
||||
logger.info(f"[RAG_PROFILE] RAG Init/Connect ({converse_params.chat_id}): {time.perf_counter() - t1:.3f}s")
|
||||
|
||||
if converse_params.stream:
|
||||
async def stream_response():
|
||||
last_answer = ""
|
||||
first_token_received = False
|
||||
start_stream_time = time.perf_counter()
|
||||
|
||||
try:
|
||||
# 将同步Generator转换为异步迭代
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
for chunk in result:
|
||||
# 在事件循环中运行CPU密集型操作
|
||||
await asyncio.sleep(0) # 让出控制权,允许其他协程运行
|
||||
|
||||
if not first_token_received:
|
||||
first_token_received = True
|
||||
latency = time.perf_counter() - start_stream_time
|
||||
logger.info(f"[RAG_PROFILE] Time to First Token ({converse_params.chat_id}): {latency:.3f}s")
|
||||
|
||||
# Native RAGFlow returns cumulative text in 'answer' field
|
||||
# Chunk structure: {'data': {'answer': 'Cumulative Text...'}}
|
||||
payload = chunk.get('data') if isinstance(chunk, dict) else chunk
|
||||
if not payload:
|
||||
continue
|
||||
|
||||
body = payload if isinstance(payload, dict) else {'data': payload}
|
||||
|
||||
if isinstance(body, dict) and 'answer' in body:
|
||||
# Inspect for errors even in stream
|
||||
if isinstance(chunk, dict) and chunk.get('code') and chunk.get('code') != 0:
|
||||
logger.error(f"RAGFlow Stream Error: {chunk}")
|
||||
|
||||
current_answer = body['answer']
|
||||
# Calculate Delta
|
||||
if current_answer.startswith(last_answer):
|
||||
delta = current_answer[len(last_answer):]
|
||||
if delta:
|
||||
body['answer'] = delta # Send only the new part
|
||||
last_answer = current_answer
|
||||
yield format_sse(body)
|
||||
else:
|
||||
# Context reset (unlikely but safe fallback)
|
||||
last_answer = current_answer
|
||||
yield format_sse(body)
|
||||
else:
|
||||
yield format_sse(body)
|
||||
|
||||
yield format_sse({'status': 'completed'}, event='end')
|
||||
except Exception as exc:
|
||||
logger.exception('[RAG_PROFILE] ragflow流式对话异常: %s', exc)
|
||||
yield format_sse({'message': str(exc)}, event='error')
|
||||
finally:
|
||||
total_time = time.perf_counter() - start_time
|
||||
logger.info(f'[RAG_PROFILE] Total Stream Duration ({converse_params.chat_id}): {total_time:.3f}s')
|
||||
|
||||
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(f'[RAG_PROFILE] Total Sync Duration ({converse_params.chat_id}): {time.perf_counter() - start_time:.3f}s')
|
||||
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:
|
||||
msg = result.get('message') or result.get('msg') or '接口异常'
|
||||
return ResponseUtil.error(msg=msg, data=result.get('data', None))
|
||||
return ResponseUtil.success(data=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'
|
||||
|
||||
|
||||
async def aiter_sync_generator(sync_gen):
|
||||
"""将同步 Generator 转换为异步迭代器"""
|
||||
loop = asyncio.get_event_loop()
|
||||
try:
|
||||
while True:
|
||||
# 在线程池中获取下一个值
|
||||
value = await loop.run_in_executor(None, next, sync_gen, None)
|
||||
if value is None:
|
||||
break
|
||||
yield value
|
||||
except StopIteration:
|
||||
pass
|
||||
import asyncio
|
||||
import hashlib
|
||||
import json
|
||||
import time
|
||||
from typing import AsyncIterator, Union
|
||||
|
||||
import aiofiles
|
||||
import redis
|
||||
from fastapi import Depends, Request, Response
|
||||
from fastapi.responses import StreamingResponse
|
||||
from fastapi_controller.decorators import Controller
|
||||
from fastapi_controller.dependencies import LoginService
|
||||
from fastapi_controller.response import ResponseUtil
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from module_admin.service.ragflow_service import RAGFlowService
|
||||
from module_admin.service.search_service import SearchService
|
||||
from module_admin.service.login_service import CheckRoleInterfaceAuth
|
||||
from module_admin.model.ragflow_models import (
|
||||
ConverseWithChatAssistantModel,
|
||||
CreateSessionWithChatModel,
|
||||
DeleteFileModel,
|
||||
DocumentIdsModel,
|
||||
RagflowListQueryModel,
|
||||
UpdateChatAssistantModel,
|
||||
)
|
||||
|
||||
import logger as logger_module
|
||||
@ -1,106 +0,0 @@
|
||||
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))
|
||||
@ -1,110 +0,0 @@
|
||||
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))
|
||||
@ -1,155 +0,0 @@
|
||||
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))
|
||||
@ -1,283 +0,0 @@
|
||||
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)
|
||||
@ -1,79 +0,0 @@
|
||||
"""
|
||||
系统定时任务
|
||||
"""
|
||||
|
||||
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分钟执行一次)')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
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)
|
||||
@ -1,106 +0,0 @@
|
||||
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))
|
||||
@ -1,240 +0,0 @@
|
||||
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))
|
||||
@ -1,58 +0,0 @@
|
||||
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"
|
||||
@ -1,399 +0,0 @@
|
||||
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)
|
||||
@ -1,105 +0,0 @@
|
||||
# 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))
|
||||
@ -1,173 +0,0 @@
|
||||
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))
|
||||
@ -1,116 +0,0 @@
|
||||
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])))
|
||||
@ -1,307 +0,0 @@
|
||||
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
|
||||
@ -1,281 +0,0 @@
|
||||
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
|
||||
@ -1,154 +0,0 @@
|
||||
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])))
|
||||
@ -1,135 +0,0 @@
|
||||
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])))
|
||||
|
||||
@ -1,120 +0,0 @@
|
||||
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])))
|
||||
|
||||
@ -1,131 +0,0 @@
|
||||
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])))
|
||||
|
||||
@ -1,194 +0,0 @@
|
||||
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])))
|
||||
|
||||
@ -1,129 +0,0 @@
|
||||
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])))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user