增加目标追踪的模块
This commit is contained in:
parent
da1f5a874e
commit
939e29c2d6
@ -6,7 +6,7 @@ import torch
|
||||
from utils.yolov5.models.common import DetectMultiBackend
|
||||
from utils.yolov5.utils.torch_utils import select_device
|
||||
from utils.yolov5.utils.dataloaders import LoadImages, LoadStreams
|
||||
from utils.yolov5.utils.general import check_img_size, non_max_suppression, cv2, scale_coords, xyxy2xywh, Profile
|
||||
from utils.yolov5.utils.general import check_img_size, non_max_suppression, cv2, scale_coords, xyxy2xywh
|
||||
from deep_sort.deep_sort import DeepSort
|
||||
from deep_sort.utils.draw import draw_boxes
|
||||
|
||||
@ -69,22 +69,17 @@ async def run_deepsort(
|
||||
|
||||
time.sleep(2) # 等待2s,等待websocket进入
|
||||
|
||||
dt = (Profile(device=device), Profile(device=device), Profile(device=device))
|
||||
|
||||
for path, im, im0s, vid_cap, s in dataset:
|
||||
if room_manager.rooms.get(room):
|
||||
with dt[0]:
|
||||
im0 = im0s[0]
|
||||
im = torch.from_numpy(im).to(model.device)
|
||||
im = im.half() if model.fp16 else im.float() # uint8 to fp16/32
|
||||
im /= 255 # 0 - 255 to 0.0 - 1.0
|
||||
if len(im.shape) == 3:
|
||||
im = im[None] # expand for batch dim
|
||||
with dt[1]:
|
||||
pred = model(im, augment=False, visualize=False)
|
||||
with dt[2]:
|
||||
# NMS
|
||||
pred = non_max_suppression(pred, 0.25, 0.45, None, False, max_det=1000)[0]
|
||||
im0 = im0s[0]
|
||||
im = torch.from_numpy(im).to(model.device)
|
||||
im = im.half() if model.fp16 else im.float() # uint8 to fp16/32
|
||||
im /= 255 # 0 - 255 to 0.0 - 1.0
|
||||
if len(im.shape) == 3:
|
||||
im = im[None] # expand for batch dim
|
||||
pred = model(im, augment=False, visualize=False)
|
||||
# NMS
|
||||
pred = non_max_suppression(pred, 0.25, 0.45, None, False, max_det=1000)[0]
|
||||
|
||||
pred[:, :4] = scale_coords(im.shape[2:], pred[:, :4], im0.shape).round()
|
||||
|
||||
|
@ -8,7 +8,7 @@ from utils.yolov5.utils.torch_utils import select_device
|
||||
from ultralytics.utils.plotting import Annotator, colors
|
||||
from utils.yolov5.models.common import DetectMultiBackend
|
||||
from apps.business.deepsort import service as deepsort_service
|
||||
from utils.yolov5.utils.general import check_img_size, Profile, non_max_suppression, cv2, scale_boxes
|
||||
from utils.yolov5.utils.general import check_img_size, non_max_suppression, cv2, scale_boxes
|
||||
|
||||
import time
|
||||
import torch
|
||||
@ -97,7 +97,7 @@ async def run_detect_img(
|
||||
room = 'detect_' + str(detect_id)
|
||||
await room_manager.send_to_room(room, f"AiCheck: 模型训练开始,请稍等。。。\n")
|
||||
commend = ["python", '-u', yolo_path, "--weights", weights, "--source", source, "--name", name, "--project",
|
||||
project, "--save-txt", "--conf-thres", "0.4"]
|
||||
project, "--save-txt", "--conf-thres", "0.6"]
|
||||
# 判断是否存在cuda版本
|
||||
if is_gpu == 'True':
|
||||
commend.append("--device=0")
|
||||
@ -176,25 +176,20 @@ async def run_detect_rtsp(weights_pt: str, rtsp_url: str, data: str, detect_id:
|
||||
|
||||
model.warmup(imgsz=(1 if pt or model.triton else bs, 3, *img_sz))
|
||||
|
||||
dt = (Profile(device=device), Profile(device=device), Profile(device=device))
|
||||
|
||||
time.sleep(3) # 等待2s,等待websocket进入
|
||||
time.sleep(3) # 等待3s,等待websocket进入
|
||||
|
||||
for path, im, im0s, vid_cap, s in dataset:
|
||||
if room_manager.rooms.get(room):
|
||||
with dt[0]:
|
||||
im = torch.from_numpy(im).to(model.device)
|
||||
im = im.half() if model.fp16 else im.float() # uint8 to fp16/32
|
||||
im /= 255 # 0 - 255 to 0.0 - 1.0
|
||||
if len(im.shape) == 3:
|
||||
im = im[None] # expand for batch dim
|
||||
im = torch.from_numpy(im).to(model.device)
|
||||
im = im.half() if model.fp16 else im.float() # uint8 to fp16/32
|
||||
im /= 255 # 0 - 255 to 0.0 - 1.0
|
||||
if len(im.shape) == 3:
|
||||
im = im[None] # expand for batch dim
|
||||
|
||||
# Inference
|
||||
with dt[1]:
|
||||
pred = model(im, augment=False, visualize=False)
|
||||
pred = model(im, augment=False, visualize=False)
|
||||
# NMS
|
||||
with dt[2]:
|
||||
pred = non_max_suppression(pred, 0.25, 0.45, None, False, max_det=1000)
|
||||
pred = non_max_suppression(pred, 0.25, 0.45, None, False, max_det=1000)
|
||||
|
||||
# Process predictions
|
||||
for i, det in enumerate(pred): # per image
|
||||
|
@ -80,8 +80,6 @@ class LoginValidation:
|
||||
await db.flush()
|
||||
elif not user.is_active:
|
||||
self.result.msg = "此手机号已被冻结!"
|
||||
elif data.platform in ["0", "1"]:
|
||||
self.result.msg = "此手机号无权限!"
|
||||
else:
|
||||
if not DEMO and count:
|
||||
await count.delete()
|
||||
|
@ -7,20 +7,16 @@
|
||||
# @desc : 数据库 增删改查操作
|
||||
|
||||
import json
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
from redis.asyncio import Redis
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||
from sqlalchemy import select
|
||||
import os
|
||||
from sqlalchemy import select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import joinedload
|
||||
from application.settings import SUBSCRIBE
|
||||
from core.mongo_manage import MongoManage
|
||||
from application.settings import STATIC_ROOT, REDIS_DB_ENABLE
|
||||
from core.database import redis_getter
|
||||
from utils.file.file_manage import FileManage
|
||||
from . import models, schemas
|
||||
from core.crud import DalBase
|
||||
from core.exception import CustomException
|
||||
from utils import status
|
||||
from fastapi import Request
|
||||
|
||||
|
||||
class DictTypeDal(DalBase):
|
||||
@ -67,323 +63,116 @@ class DictDetailsDal(DalBase):
|
||||
self.schema = schemas.DictDetailsSimpleOut
|
||||
|
||||
|
||||
class TaskDal(MongoManage):
|
||||
class SettingsDal(DalBase):
|
||||
|
||||
class JobOperation(Enum):
|
||||
add = "add_job"
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(SettingsDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.VadminSystemSettings
|
||||
self.schema = schemas.SettingsSimpleOut
|
||||
|
||||
def __init__(self, db: AsyncIOMotorDatabase):
|
||||
super(TaskDal, self).__init__(db, "vadmin_system_task", schemas.TaskSimpleOut)
|
||||
|
||||
async def get_task(
|
||||
self,
|
||||
_id: str = None,
|
||||
v_return_none: bool = False,
|
||||
v_schema: Any = None,
|
||||
**kwargs
|
||||
) -> dict | None:
|
||||
async def get_tab_values(self, tab_id: int) -> dict:
|
||||
"""
|
||||
获取单个数据,默认使用 ID 查询,否则使用关键词查询
|
||||
|
||||
包括临时字段 last_run_datetime,is_active
|
||||
is_active: 只有在 scheduler_task_jobs 任务运行表中存在相同 _id 才表示任务添加成功,任务状态才为 True
|
||||
last_run_datetime: 在 scheduler_task_record 中获取该任务最近一次执行完成的时间
|
||||
|
||||
:param _id: 数据 ID
|
||||
:param v_return_none: 是否返回空 None,否则抛出异常,默认抛出异常
|
||||
:param v_schema: 指定使用的序列化对象
|
||||
获取系统配置标签下的信息
|
||||
"""
|
||||
if _id:
|
||||
kwargs["_id"] = ("ObjectId", _id)
|
||||
|
||||
params = self.filter_condition(**kwargs)
|
||||
pipeline = [
|
||||
{
|
||||
'$addFields': {
|
||||
'str_id': {'$toString': '$_id'}
|
||||
}
|
||||
},
|
||||
{
|
||||
'$lookup': {
|
||||
'from': 'scheduler_task_jobs',
|
||||
'localField': 'str_id',
|
||||
'foreignField': '_id',
|
||||
'as': 'matched_jobs'
|
||||
}
|
||||
},
|
||||
{
|
||||
'$lookup': {
|
||||
'from': 'scheduler_task_record',
|
||||
'localField': 'str_id',
|
||||
'foreignField': 'job_id',
|
||||
'as': 'matched_records'
|
||||
}
|
||||
},
|
||||
{
|
||||
'$addFields': {
|
||||
'is_active': {
|
||||
'$cond': {
|
||||
'if': {'$ne': ['$matched_jobs', []]},
|
||||
'then': True,
|
||||
'else': False
|
||||
}
|
||||
},
|
||||
'last_run_datetime': {
|
||||
'$ifNull': [
|
||||
{'$arrayElemAt': ['$matched_records.create_datetime', -1]},
|
||||
None
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'$business': {
|
||||
'matched_records': 0,
|
||||
'matched_jobs': 0
|
||||
}
|
||||
},
|
||||
{
|
||||
'$match': params
|
||||
},
|
||||
{
|
||||
'$facet': {
|
||||
'documents': [
|
||||
{'$limit': 1},
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
# 执行聚合查询
|
||||
cursor = self.collection.aggregate(pipeline)
|
||||
result = await cursor.to_list(length=None)
|
||||
data = result[0]['documents']
|
||||
if not data and v_return_none:
|
||||
return None
|
||||
elif not data:
|
||||
raise CustomException("未查找到对应数据", code=status.HTTP_404_NOT_FOUND)
|
||||
data = data[0]
|
||||
if data and v_schema:
|
||||
return jsonable_encoder(v_schema(**data))
|
||||
return data
|
||||
|
||||
async def get_tasks(
|
||||
self,
|
||||
page: int = 1,
|
||||
limit: int = 10,
|
||||
v_schema: Any = None,
|
||||
v_order: str = None,
|
||||
v_order_field: str = None,
|
||||
**kwargs
|
||||
) -> tuple:
|
||||
"""
|
||||
获取任务信息列表
|
||||
|
||||
添加了两个临时字段
|
||||
is_active: 只有在 scheduler_task_jobs 任务运行表中存在相同 _id 才表示任务添加成功,任务状态才为 True
|
||||
last_run_datetime: 在 scheduler_task_record 中获取该任务最近一次执行完成的时间
|
||||
"""
|
||||
v_order_field = v_order_field if v_order_field else 'create_datetime'
|
||||
v_order = -1 if v_order in self.ORDER_FIELD else 1
|
||||
params = self.filter_condition(**kwargs)
|
||||
pipeline = [
|
||||
{
|
||||
'$addFields': {
|
||||
'str_id': {'$toString': '$_id'}
|
||||
}
|
||||
},
|
||||
{
|
||||
'$lookup': {
|
||||
'from': 'scheduler_task_jobs',
|
||||
'localField': 'str_id',
|
||||
'foreignField': '_id',
|
||||
'as': 'matched_jobs'
|
||||
}
|
||||
},
|
||||
{
|
||||
'$lookup': {
|
||||
'from': 'scheduler_task_record',
|
||||
'localField': 'str_id',
|
||||
'foreignField': 'job_id',
|
||||
'as': 'matched_records'
|
||||
}
|
||||
},
|
||||
{
|
||||
'$addFields': {
|
||||
'is_active': {
|
||||
'$cond': {
|
||||
'if': {'$ne': ['$matched_jobs', []]},
|
||||
'then': True,
|
||||
'else': False
|
||||
}
|
||||
},
|
||||
'last_run_datetime': {
|
||||
'$ifNull': [
|
||||
{'$arrayElemAt': ['$matched_records.create_datetime', -1]},
|
||||
None
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'$business': {
|
||||
'matched_records': 0,
|
||||
'matched_jobs': 0
|
||||
}
|
||||
},
|
||||
{
|
||||
'$match': params
|
||||
},
|
||||
{
|
||||
'$facet': {
|
||||
'documents': [
|
||||
{'$sort': {v_order_field: v_order}},
|
||||
{'$limit': limit},
|
||||
{'$skip': (page - 1) * limit}
|
||||
],
|
||||
'count': [{'$count': 'total'}]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
# 执行聚合查询
|
||||
cursor = self.collection.aggregate(pipeline)
|
||||
result = await cursor.to_list(length=None)
|
||||
datas = result[0]['documents']
|
||||
count = result[0]['count'][0]['total'] if result[0]['count'] else 0
|
||||
if count == 0:
|
||||
return [], 0
|
||||
elif v_schema:
|
||||
datas = [jsonable_encoder(v_schema(**data)) for data in datas]
|
||||
elif self.schema:
|
||||
datas = [jsonable_encoder(self.schema(**data)) for data in datas]
|
||||
return datas, count
|
||||
|
||||
async def add_task(self, rd: Redis, data: dict) -> int:
|
||||
"""
|
||||
添加任务到消息队列
|
||||
|
||||
使用消息无保留策略:无保留是指当发送者向某个频道发送消息时,如果没有订阅该频道的调用方,就直接将该消息丢弃。
|
||||
|
||||
:param rd: redis 对象
|
||||
:param data: 行数据字典
|
||||
:return: 接收到消息的订阅者数量。
|
||||
"""
|
||||
exec_strategy = data.get("exec_strategy")
|
||||
job_params = {
|
||||
"name": data.get("_id"),
|
||||
"job_class": data.get("job_class"),
|
||||
"expression": data.get("expression")
|
||||
}
|
||||
if exec_strategy == "interval" or exec_strategy == "cron":
|
||||
job_params["start_date"] = data.get("start_date")
|
||||
job_params["end_date"] = data.get("end_date")
|
||||
message = {
|
||||
"operation": self.JobOperation.add.value,
|
||||
"task": {
|
||||
"exec_strategy": data.get("exec_strategy"),
|
||||
"job_params": job_params
|
||||
}
|
||||
}
|
||||
return await rd.publish(SUBSCRIBE, json.dumps(message).encode('utf-8'))
|
||||
|
||||
async def create_task(self, rd: Redis, data: schemas.Task) -> dict:
|
||||
"""
|
||||
创建任务
|
||||
"""
|
||||
data_dict = data.model_dump()
|
||||
is_active = data_dict.pop('is_active')
|
||||
insert_result = await super().create_data(data_dict)
|
||||
obj = await self.get_task(insert_result.inserted_id, v_schema=schemas.TaskSimpleOut)
|
||||
|
||||
# 如果分组不存在则新增分组
|
||||
group = await TaskGroupDal(self.db).get_data(value=data.group, v_return_none=True)
|
||||
if not group:
|
||||
await TaskGroupDal(self.db).create_data({"value": data.group})
|
||||
|
||||
result = {
|
||||
"subscribe_number": 0,
|
||||
"is_active": is_active
|
||||
}
|
||||
|
||||
if is_active:
|
||||
# 创建任务成功后, 如果任务状态为 True,则向消息队列中发送任务
|
||||
result['subscribe_number'] = await self.add_task(rd, obj)
|
||||
datas = await self.get_datas(limit=0, tab_id=tab_id, v_return_objs=True)
|
||||
result = {}
|
||||
for data in datas:
|
||||
if not data.disabled:
|
||||
result[data.config_key] = data.config_value
|
||||
return result
|
||||
|
||||
async def put_task(self, rd: Redis, _id: str, data: schemas.Task) -> dict:
|
||||
async def update_datas(self, datas: dict, request: Request) -> None:
|
||||
"""
|
||||
更新任务
|
||||
更新系统配置信息
|
||||
|
||||
更新ico图标步骤:先将文件上传到本地,然后点击提交后,获取到文件地址,将上传的新文件覆盖原有文件
|
||||
原因:ico图标的路径是在前端的index.html中固定的,所以目前只能改变图片,不改变路径
|
||||
"""
|
||||
data_dict = data.model_dump()
|
||||
is_active = data_dict.pop('is_active')
|
||||
await super(TaskDal, self).put_data(_id, data)
|
||||
obj: dict = await self.get_task(_id, v_schema=schemas.TaskSimpleOut)
|
||||
for key, value in datas.items():
|
||||
if key == "web_ico":
|
||||
continue
|
||||
elif key == "web_ico_local_path":
|
||||
if not value:
|
||||
continue
|
||||
ico = await self.get_data(config_key="web_ico", tab_id=1)
|
||||
web_ico = datas.get("web_ico")
|
||||
if ico.config_value == web_ico:
|
||||
continue
|
||||
# 将上传的ico路径替换到static/system/favicon.ico文件
|
||||
await FileManage.async_copy_file(value, os.path.join(STATIC_ROOT, "system/favicon.ico"))
|
||||
sql = update(self.model).where(self.model.config_key == "web_ico").values(config_value=web_ico)
|
||||
await self.db.execute(sql)
|
||||
else:
|
||||
sql = update(self.model).where(self.model.config_key == str(key)).values(config_value=value)
|
||||
await self.db.execute(sql)
|
||||
if "wx_server_app_id" in datas and REDIS_DB_ENABLE:
|
||||
rd = redis_getter(request)
|
||||
await rd.client().set("wx_server", json.dumps(datas))
|
||||
elif "sms_access_key" in datas and REDIS_DB_ENABLE:
|
||||
rd = redis_getter(request)
|
||||
await rd.client().set('aliyun_sms', json.dumps(datas))
|
||||
|
||||
# 如果分组不存在则新增分组
|
||||
group = await TaskGroupDal(self.db).get_data(value=data.group, v_return_none=True)
|
||||
if not group:
|
||||
await TaskGroupDal(self.db).create_data({"value": data.group})
|
||||
|
||||
try:
|
||||
# 删除正在运行中的 Job
|
||||
await SchedulerTaskJobsDal(self.db).delete_data(_id)
|
||||
except CustomException as e:
|
||||
pass
|
||||
|
||||
result = {
|
||||
"subscribe_number": 0,
|
||||
"is_active": is_active
|
||||
}
|
||||
|
||||
if is_active:
|
||||
# 更新任务成功后, 如果任务状态为 True,则向消息队列中发送任务
|
||||
result['subscribe_number'] = await self.add_task(rd, obj)
|
||||
async def get_base_config(self) -> dict:
|
||||
"""
|
||||
获取系统基本信息
|
||||
"""
|
||||
ignore_configs = ["wx_server_app_id", "wx_server_app_secret"]
|
||||
datas = await self.get_datas(limit=0, tab_id=("in", ["1", "9"]), disabled=False, v_return_objs=True)
|
||||
result = {}
|
||||
for config in datas:
|
||||
if config.config_key not in ignore_configs:
|
||||
result[config.config_key] = config.config_value
|
||||
return result
|
||||
|
||||
async def delete_task(self, _id: str) -> bool:
|
||||
"""
|
||||
删除任务
|
||||
"""
|
||||
result = await super(TaskDal, self).delete_data(_id)
|
||||
|
||||
try:
|
||||
# 删除正在运行中的 Job
|
||||
await SchedulerTaskJobsDal(self.db).delete_data(_id)
|
||||
except CustomException as e:
|
||||
pass
|
||||
return result
|
||||
class SettingsTabDal(DalBase):
|
||||
|
||||
async def run_once_task(self, rd: Redis, _id: str) -> int:
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(SettingsTabDal, self).__init__(db, models.VadminSystemSettingsTab, schemas.SettingsTabSimpleOut)
|
||||
|
||||
async def get_classify_tab_values(self, classify: list[str], hidden: bool | None = False) -> dict:
|
||||
"""
|
||||
执行一次任务
|
||||
获取系统配置分类下的标签信息
|
||||
"""
|
||||
obj: dict = await self.get_data(_id, v_schema=schemas.TaskSimpleOut)
|
||||
message = {
|
||||
"operation": self.JobOperation.add.value,
|
||||
"task": {
|
||||
"exec_strategy": "once",
|
||||
"job_params": {
|
||||
"name": obj.get("_id"),
|
||||
"job_class": obj.get("job_class")
|
||||
}
|
||||
model = models.VadminSystemSettingsTab
|
||||
options = [joinedload(model.settings)]
|
||||
datas = await self.get_datas(
|
||||
limit=0,
|
||||
v_options=options,
|
||||
classify=("in", classify),
|
||||
disabled=False,
|
||||
v_return_objs=True,
|
||||
hidden=hidden
|
||||
)
|
||||
return self.__generate_values(datas)
|
||||
|
||||
async def get_tab_name_values(self, tab_names: list[str], hidden: bool | None = False) -> dict:
|
||||
"""
|
||||
获取系统配置标签下的标签信息
|
||||
"""
|
||||
model = models.VadminSystemSettingsTab
|
||||
options = [joinedload(model.settings)]
|
||||
datas = await self.get_datas(
|
||||
limit=0,
|
||||
v_options=options,
|
||||
tab_name=("in", tab_names),
|
||||
disabled=False,
|
||||
v_return_objs=True,
|
||||
hidden=hidden
|
||||
)
|
||||
return self.__generate_values(datas)
|
||||
|
||||
@classmethod
|
||||
def __generate_values(cls, datas: list[models.VadminSystemSettingsTab]) -> dict:
|
||||
"""
|
||||
生成字典值
|
||||
"""
|
||||
return {
|
||||
tab.tab_name: {
|
||||
item.config_key: item.config_value
|
||||
for item in tab.settings
|
||||
if not item.disabled
|
||||
}
|
||||
for tab in datas
|
||||
}
|
||||
return await rd.publish(SUBSCRIBE, json.dumps(message).encode('utf-8'))
|
||||
|
||||
|
||||
class TaskGroupDal(MongoManage):
|
||||
|
||||
def __init__(self, db: AsyncIOMotorDatabase):
|
||||
super(TaskGroupDal, self).__init__(db, "vadmin_system_task_group")
|
||||
|
||||
|
||||
class TaskRecordDal(MongoManage):
|
||||
|
||||
def __init__(self, db: AsyncIOMotorDatabase):
|
||||
super(TaskRecordDal, self).__init__(db, "scheduler_task_record")
|
||||
|
||||
|
||||
class SchedulerTaskJobsDal(MongoManage):
|
||||
|
||||
def __init__(self, db: AsyncIOMotorDatabase):
|
||||
super(SchedulerTaskJobsDal, self).__init__(db, "scheduler_task_jobs", is_object_id=False)
|
||||
|
@ -1 +1,2 @@
|
||||
from .dict import VadminDictType, VadminDictDetails
|
||||
from .settings import VadminSystemSettings, VadminSystemSettingsTab
|
||||
|
42
apps/vadmin/system/models/settings.py
Normal file
42
apps/vadmin/system/models/settings.py
Normal file
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/7/7 13:41
|
||||
# @File : settings.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 系统字典模型
|
||||
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
from db.db_base import BaseModel
|
||||
from sqlalchemy import String, Integer, ForeignKey, Boolean, Text
|
||||
|
||||
|
||||
class VadminSystemSettingsTab(BaseModel):
|
||||
__tablename__ = "vadmin_system_settings_tab"
|
||||
__table_args__ = ({'comment': '系统配置分类表'})
|
||||
|
||||
title: Mapped[str] = mapped_column(String(255), comment="标题")
|
||||
classify: Mapped[str] = mapped_column(String(255), index=True, nullable=False, comment="分类键")
|
||||
tab_label: Mapped[str] = mapped_column(String(255), comment="tab标题")
|
||||
tab_name: Mapped[str] = mapped_column(String(255), index=True, nullable=False, unique=True, comment="tab标识符")
|
||||
hidden: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否隐藏")
|
||||
disabled: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否禁用")
|
||||
|
||||
settings: Mapped[list["VadminSystemSettings"]] = relationship(back_populates="tab")
|
||||
|
||||
|
||||
class VadminSystemSettings(BaseModel):
|
||||
__tablename__ = "vadmin_system_settings"
|
||||
__table_args__ = ({'comment': '系统配置表'})
|
||||
|
||||
config_label: Mapped[str] = mapped_column(String(255), comment="配置表标签")
|
||||
config_key: Mapped[str] = mapped_column(String(255), index=True, nullable=False, unique=True, comment="配置表键")
|
||||
config_value: Mapped[str | None] = mapped_column(Text, comment="配置表内容")
|
||||
remark: Mapped[str | None] = mapped_column(String(255), comment="备注信息")
|
||||
disabled: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否禁用")
|
||||
|
||||
tab_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vadmin_system_settings_tab.id", ondelete='CASCADE'),
|
||||
comment="关联tab标签"
|
||||
)
|
||||
tab: Mapped[VadminSystemSettingsTab] = relationship(foreign_keys=tab_id, back_populates="settings")
|
@ -1,3 +1,2 @@
|
||||
from .dict_type import DictTypeParams
|
||||
from .dict_detail import DictDetailParams
|
||||
from .task import TaskParams
|
||||
from .dict_detail import DictDetailParams
|
@ -1,32 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/6/25 14:50
|
||||
# @File : task.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
from fastapi import Depends
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class TaskParams(QueryParams):
|
||||
"""
|
||||
列表分页
|
||||
"""
|
||||
def __init__(self, name: str = None, _id: str = None, group: str = None, params: Paging = Depends()):
|
||||
super().__init__(params)
|
||||
self.name = ("like", name)
|
||||
self.group = group
|
||||
self._id = ("ObjectId", _id)
|
||||
self.v_order = "desc"
|
||||
|
||||
|
||||
class TaskRecordParams(QueryParams):
|
||||
"""
|
||||
列表分页
|
||||
"""
|
||||
def __init__(self, job_id: str = None, name: str = None, params: Paging = Depends()):
|
||||
super().__init__(params)
|
||||
self.job_id = ("like", job_id)
|
||||
self.name = ("like", name)
|
||||
self.v_order = "desc"
|
@ -1,2 +1,3 @@
|
||||
from .dict import DictType, DictDetails, DictTypeSimpleOut, DictDetailsSimpleOut, DictTypeOptionsOut
|
||||
from .task import Task, TaskSimpleOut
|
||||
from .settings_tab import SettingsTab, SettingsTabSimpleOut
|
||||
from .settings import Settings, SettingsSimpleOut
|
||||
|
29
apps/vadmin/system/schemas/settings.py
Normal file
29
apps/vadmin/system/schemas/settings.py
Normal file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/18 22:19
|
||||
# @File : settings.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
|
||||
|
||||
class Settings(BaseModel):
|
||||
config_label: str | None = None
|
||||
config_key: str
|
||||
config_value: str | None = None
|
||||
remark: str | None = None
|
||||
disabled: bool | None = None
|
||||
tab_id: int
|
||||
|
||||
|
||||
class SettingsSimpleOut(Settings):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
create_datetime: DatetimeStr
|
||||
update_datetime: DatetimeStr
|
||||
|
28
apps/vadmin/system/schemas/settings_tab.py
Normal file
28
apps/vadmin/system/schemas/settings_tab.py
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/10/18 22:19
|
||||
# @File : settings_tab.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
|
||||
|
||||
class SettingsTab(BaseModel):
|
||||
title: str
|
||||
classify: str
|
||||
tab_label: str
|
||||
tab_name: str
|
||||
hidden: bool
|
||||
|
||||
|
||||
class SettingsTabSimpleOut(SettingsTab):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
create_datetime: DatetimeStr
|
||||
update_datetime: DatetimeStr
|
||||
|
@ -1,32 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/6/25 15:08
|
||||
# @File : task.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from core.data_types import DatetimeStr, ObjectIdStr
|
||||
|
||||
|
||||
class Task(BaseModel):
|
||||
name: str
|
||||
group: str | None = None
|
||||
job_class: str
|
||||
exec_strategy: str
|
||||
expression: str
|
||||
is_active: bool | None = True # 临时字段,不在表中创建
|
||||
remark: str | None = None
|
||||
start_date: DatetimeStr | None = None
|
||||
end_date: DatetimeStr | None = None
|
||||
|
||||
|
||||
class TaskSimpleOut(Task):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: ObjectIdStr = Field(..., alias='_id')
|
||||
create_datetime: DatetimeStr
|
||||
update_datetime: DatetimeStr
|
||||
last_run_datetime: DatetimeStr | None = None # 临时字段,不在表中创建
|
||||
|
@ -5,19 +5,16 @@
|
||||
# @IDE : PyCharm
|
||||
# @desc : 主要接口文件
|
||||
|
||||
from redis.asyncio import Redis
|
||||
from fastapi import APIRouter, Depends, Body
|
||||
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||
from core.database import redis_getter, mongo_getter
|
||||
from utils.response import SuccessResponse, ErrorResponse
|
||||
from utils.sms.code import CodeSMS
|
||||
from fastapi import APIRouter, Depends, Body, Request
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from core.database import db_getter
|
||||
from utils.response import SuccessResponse
|
||||
from . import schemas, crud
|
||||
from core.dependencies import IdList
|
||||
from apps.vadmin.auth.utils.current import AllUserAuth, OpenAuth
|
||||
from apps.vadmin.auth.utils.current import AllUserAuth, FullAdminAuth, OpenAuth
|
||||
from apps.vadmin.auth.utils.validation.auth import Auth
|
||||
from .params import DictTypeParams, DictDetailParams, TaskParams
|
||||
from apps.vadmin.auth import crud as vadmin_auth_crud
|
||||
from .params.task import TaskRecordParams
|
||||
from .params import DictTypeParams, DictDetailParams
|
||||
|
||||
|
||||
app = APIRouter()
|
||||
|
||||
@ -99,96 +96,39 @@ async def get_dict_detail(data_id: int, auth: Auth = Depends(AllUserAuth())):
|
||||
|
||||
|
||||
###########################################################
|
||||
# 短信服务管理
|
||||
# 系统配置管理
|
||||
###########################################################
|
||||
@app.post("/sms/send", summary="发送短信验证码(阿里云服务)")
|
||||
async def sms_send(telephone: str, rd: Redis = Depends(redis_getter), auth: Auth = Depends(OpenAuth())):
|
||||
user = await vadmin_auth_crud.UserDal(auth.db).get_data(telephone=telephone, v_return_none=True)
|
||||
if not user:
|
||||
return ErrorResponse("手机号不存在!")
|
||||
sms = CodeSMS(telephone, rd)
|
||||
return SuccessResponse(await sms.main_async())
|
||||
@app.post("/settings/tabs", summary="获取系统配置标签列表")
|
||||
async def get_settings_tabs(classifys: list[str] = Body(...), auth: Auth = Depends(FullAdminAuth())):
|
||||
return SuccessResponse(await crud.SettingsTabDal(auth.db).get_datas(limit=0, classify=("in", classifys)))
|
||||
|
||||
|
||||
###########################################################
|
||||
# 定时任务管理
|
||||
###########################################################
|
||||
@app.get("/tasks", summary="获取定时任务列表")
|
||||
async def get_tasks(
|
||||
p: TaskParams = Depends(),
|
||||
db: AsyncIOMotorDatabase = Depends(mongo_getter),
|
||||
auth: Auth = Depends(AllUserAuth())
|
||||
@app.get("/settings/tabs/values", summary="获取系统配置标签下的信息")
|
||||
async def get_settings_tabs_values(tab_id: int, auth: Auth = Depends(FullAdminAuth())):
|
||||
return SuccessResponse(await crud.SettingsDal(auth.db).get_tab_values(tab_id=tab_id))
|
||||
|
||||
|
||||
@app.put("/settings/tabs/values", summary="更新系统配置信息")
|
||||
async def put_settings_tabs_values(
|
||||
request: Request,
|
||||
datas: dict = Body(...),
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
datas, count = await crud.TaskDal(db).get_tasks(**p.dict())
|
||||
return SuccessResponse(datas, count=count)
|
||||
return SuccessResponse(await crud.SettingsDal(auth.db).update_datas(datas, request))
|
||||
|
||||
|
||||
@app.post("/tasks", summary="添加定时任务")
|
||||
async def post_tasks(
|
||||
data: schemas.Task,
|
||||
db: AsyncIOMotorDatabase = Depends(mongo_getter),
|
||||
rd: Redis = Depends(redis_getter),
|
||||
auth: Auth = Depends(AllUserAuth())
|
||||
):
|
||||
return SuccessResponse(await crud.TaskDal(db).create_task(rd, data))
|
||||
@app.get("/settings/base/config", summary="获取系统基础配置", description="每次进入系统中时使用")
|
||||
async def get_setting_base_config(db: AsyncSession = Depends(db_getter)):
|
||||
return SuccessResponse(await crud.SettingsDal(db).get_base_config())
|
||||
|
||||
|
||||
@app.put("/tasks", summary="更新定时任务")
|
||||
async def put_tasks(
|
||||
_id: str,
|
||||
data: schemas.Task,
|
||||
db: AsyncIOMotorDatabase = Depends(mongo_getter),
|
||||
rd: Redis = Depends(redis_getter),
|
||||
auth: Auth = Depends(AllUserAuth())
|
||||
):
|
||||
return SuccessResponse(await crud.TaskDal(db).put_task(rd, _id, data))
|
||||
@app.get("/settings/privacy", summary="获取隐私协议")
|
||||
async def get_settings_privacy(auth: Auth = Depends(OpenAuth())):
|
||||
return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_privacy")).config_value)
|
||||
|
||||
|
||||
@app.delete("/tasks", summary="删除单个定时任务")
|
||||
async def delete_task(
|
||||
_id: str,
|
||||
db: AsyncIOMotorDatabase = Depends(mongo_getter),
|
||||
auth: Auth = Depends(AllUserAuth())
|
||||
):
|
||||
return SuccessResponse(await crud.TaskDal(db).delete_task(_id))
|
||||
@app.get("/settings/agreement", summary="获取用户协议")
|
||||
async def get_settings_agreement(auth: Auth = Depends(OpenAuth())):
|
||||
return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_agreement")).config_value)
|
||||
|
||||
|
||||
@app.get("/task", summary="获取定时任务详情")
|
||||
async def get_task(
|
||||
_id: str,
|
||||
db: AsyncIOMotorDatabase = Depends(mongo_getter),
|
||||
auth: Auth = Depends(AllUserAuth())
|
||||
):
|
||||
return SuccessResponse(await crud.TaskDal(db).get_task(_id, v_schema=schemas.TaskSimpleOut))
|
||||
|
||||
|
||||
@app.post("/task", summary="执行一次定时任务")
|
||||
async def run_once_task(
|
||||
_id: str,
|
||||
db: AsyncIOMotorDatabase = Depends(mongo_getter),
|
||||
rd: Redis = Depends(redis_getter),
|
||||
auth: Auth = Depends(AllUserAuth())
|
||||
):
|
||||
return SuccessResponse(await crud.TaskDal(db).run_once_task(rd, _id))
|
||||
|
||||
|
||||
###########################################################
|
||||
# 定时任务分组管理
|
||||
###########################################################
|
||||
@app.get("/task/group/options", summary="获取定时任务分组选择项列表")
|
||||
async def get_task_group_options(db: AsyncIOMotorDatabase = Depends(mongo_getter), auth: Auth = Depends(AllUserAuth())):
|
||||
return SuccessResponse(await crud.TaskGroupDal(db).get_datas(limit=0))
|
||||
|
||||
|
||||
###########################################################
|
||||
# 定时任务调度日志
|
||||
###########################################################
|
||||
@app.get("/task/records", summary="获取定时任务调度日志列表")
|
||||
async def get_task_records(
|
||||
p: TaskRecordParams = Depends(),
|
||||
db: AsyncIOMotorDatabase = Depends(mongo_getter),
|
||||
auth: Auth = Depends(AllUserAuth())
|
||||
):
|
||||
count = await crud.TaskRecordDal(db).get_count(**p.to_count())
|
||||
datas = await crud.TaskRecordDal(db).get_datas(**p.dict())
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user