first commit
This commit is contained in:
51
code/utils/core_initialize.py
Normal file
51
code/utils/core_initialize.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# 初始化基类
|
||||
from fuadmin import settings
|
||||
|
||||
|
||||
class CoreInitialize:
|
||||
"""
|
||||
使用方法:继承此类,重写 run方法,在 run 中调用 save 进行数据初始化
|
||||
"""
|
||||
creator_id = None
|
||||
reset = False
|
||||
|
||||
def __init__(self, reset=False, creator_id=None):
|
||||
"""
|
||||
reset: 是否重置初始化数据
|
||||
creator_id: 创建人id
|
||||
"""
|
||||
self.reset = reset or self.reset
|
||||
self.creator_id = creator_id or self.creator_id
|
||||
|
||||
def save(self, obj, data: list, name=None, no_reset=False):
|
||||
name = name or obj._meta.verbose_name
|
||||
print(f"正在初始化[{obj._meta.label} => {name}]")
|
||||
if not no_reset and self.reset and obj not in settings.INITIALIZE_RESET_LIST:
|
||||
try:
|
||||
obj.objects.all().delete()
|
||||
settings.INITIALIZE_RESET_LIST.append(obj)
|
||||
except Exception:
|
||||
pass
|
||||
for ele in data:
|
||||
m2m_dict = {}
|
||||
new_data = {}
|
||||
for key, value in ele.items():
|
||||
# 判断传的 value 为 list 的多对多进行抽离,使用set 进行更新
|
||||
if isinstance(value, list):
|
||||
m2m_dict[key] = value
|
||||
else:
|
||||
new_data[key] = value
|
||||
object, _ = obj.objects.get_or_create(id=ele.get("id"), defaults=new_data)
|
||||
for key, m2m in m2m_dict.items():
|
||||
m2m = list(set(m2m))
|
||||
if m2m and len(m2m) > 0 and m2m[0]:
|
||||
exec(f"""
|
||||
if object.{key}:
|
||||
values_list = object.{key}.all().values_list('id', flat=True)
|
||||
values_list = list(set(list(values_list) + {m2m}))
|
||||
object.{key}.set(values_list)
|
||||
""")
|
||||
print(f"初始化完成[{obj._meta.label} => {name}]")
|
||||
|
||||
def run(self):
|
||||
raise NotImplementedError('.run() must be overridden')
|
||||
113
code/utils/fu_auth.py
Normal file
113
code/utils/fu_auth.py
Normal file
@@ -0,0 +1,113 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/2 23:19
|
||||
# @Author : 臧成龙
|
||||
# @FileName: fu_auth.py
|
||||
# @Software: PyCharm
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
# from django.core.cache import cache
|
||||
from fuadmin.settings import DEMO, SECRET_KEY, WHITE_LIST
|
||||
from ninja.security import HttpBearer
|
||||
from system.models import MenuButton, Users
|
||||
|
||||
from .fu_jwt import FuJwt
|
||||
from .fu_ninja import FuFilters
|
||||
from .usual import get_dept, get_user_info_from_token
|
||||
|
||||
METHOD = {
|
||||
'GET': 0,
|
||||
'POST': 1,
|
||||
'PUT': 2,
|
||||
'DELETE': 3,
|
||||
}
|
||||
|
||||
|
||||
class GlobalAuth(HttpBearer):
|
||||
def authenticate(self, request, token):
|
||||
# 直接放行所有请求
|
||||
return token
|
||||
# jwt = FuJwt(SECRET_KEY)
|
||||
# value = jwt.decode(SECRET_KEY, token)
|
||||
# time_now = int(datetime.now().timestamp())
|
||||
# # 判断token是否过期
|
||||
# if value.valid_to >= time_now:
|
||||
# token_user = value.payload
|
||||
# token_user_id = token_user['id']
|
||||
# user = Users.objects.get(id=token_user_id)
|
||||
# request_path = request.path
|
||||
# request_method = request.method
|
||||
# if DEMO:
|
||||
# # 判断是否在白名单中
|
||||
# if request_path in WHITE_LIST:
|
||||
# return token
|
||||
# if request_method == 'GET':
|
||||
# return token
|
||||
# else:
|
||||
# raise TimeoutError(403, '演示环境')
|
||||
# else:
|
||||
# # 判断是否是超级管理员
|
||||
# if not token_user['is_superuser']:
|
||||
# # 判断是path是否是‘/数字’结尾
|
||||
# result = re.search(r'/\d+$', request_path)
|
||||
# if result:
|
||||
# match_value = result.group()
|
||||
# # 将数字结尾的接口替换成.*? 因为接口中是/{id}
|
||||
# request_path = request_path.replace(match_value, '/*')
|
||||
# # 判断是否在白名单中
|
||||
# if request_path in WHITE_LIST:
|
||||
# return token
|
||||
# else:
|
||||
# menuIds = user.role.values_list('permission__id', flat=True)
|
||||
# queryset = MenuButton.objects.filter(id__in=menuIds, api__regex=request_path,
|
||||
# method=METHOD[request_method])
|
||||
# if queryset.exists():
|
||||
# return token
|
||||
# else:
|
||||
# raise TimeoutError(403, '没有权限')
|
||||
# # cache_token = cache.get(token_user_id)
|
||||
# # if token == cache_token:
|
||||
# return token
|
||||
# else:
|
||||
# raise TimeoutError(401, 'token时间过期')
|
||||
|
||||
|
||||
def data_permission(request, filters: FuFilters):
|
||||
return filters # 直接返回不进行任何权限过滤
|
||||
# user_info = get_user_info_from_token(request)
|
||||
# if user_info['is_superuser']:
|
||||
# return filters
|
||||
# user = Users.objects.get(id=user_info['id'])
|
||||
# data_range_qs = user.role.values_list('data_range', flat=True)
|
||||
# dept_ids = user.role.values_list('dept__id', flat=True)
|
||||
|
||||
# # 如果有多个角色,取数据权限最大的角色
|
||||
# data_range = max(list(data_range_qs))
|
||||
|
||||
# # 仅本人数据权限
|
||||
# if data_range == 0:
|
||||
# filters.creator_id = user_info['id']
|
||||
|
||||
# # 本部门数据权限
|
||||
# if data_range == 1:
|
||||
# filters.belong_dept = user_info['dept']
|
||||
|
||||
# # 本部门及以下数据权限
|
||||
# if data_range == 2:
|
||||
# dept_and_below_ids = get_dept(user_info['dept'])
|
||||
# filters.belong_dept__in = dept_and_below_ids
|
||||
|
||||
# # 自定义数据权限
|
||||
# if data_range == 3:
|
||||
# filters.belong_dept__in = list(dept_ids)
|
||||
|
||||
# # 所有数据权限
|
||||
# if data_range == 4:
|
||||
# pass
|
||||
|
||||
# return filters
|
||||
|
||||
|
||||
|
||||
242
code/utils/fu_crud.py
Normal file
242
code/utils/fu_crud.py
Normal file
@@ -0,0 +1,242 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/6 14:22
|
||||
# @Author : 臧成龙
|
||||
# @FileName: usual.py
|
||||
# @Software: PyCharm
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from datetime import datetime
|
||||
from urllib.parse import unquote
|
||||
|
||||
import openpyxl
|
||||
from django.http import FileResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from fuadmin.settings import BASE_DIR, STATIC_URL
|
||||
from ninja import Schema
|
||||
from openpyxl import load_workbook
|
||||
|
||||
from .fu_auth import data_permission
|
||||
from .fu_ninja import FuFilters
|
||||
from .fu_response import FuResponse
|
||||
from .usual import get_user_info_from_token
|
||||
|
||||
|
||||
class ImportSchema(Schema):
|
||||
path: str
|
||||
|
||||
|
||||
def create(request, data, model):
|
||||
"""
|
||||
创建新记录的函数。
|
||||
|
||||
参数:
|
||||
- request: 请求对象,用于获取用户信息。
|
||||
- data: 创建记录的数据,可以是字典或者支持dict()方法的对象。
|
||||
- model: Django ORM模型,指定要创建的对象类型。
|
||||
|
||||
返回值:
|
||||
- 创建成功的对象查询集合。
|
||||
"""
|
||||
if not isinstance(data, dict):
|
||||
# 如果data不是字典类型,则转换为字典
|
||||
data = data.dict()
|
||||
user_info = get_user_info_from_token(request)
|
||||
# 从请求中提取用户信息,并添加到数据中作为创建人、修改者和所属部门信息
|
||||
data['creator_id'] = user_info['id']
|
||||
data['modifier'] = user_info['name']
|
||||
data['belong_dept'] = user_info['dept']
|
||||
# 使用提供的模型和数据创建新记录
|
||||
query_set = model.objects.create(**data)
|
||||
return query_set
|
||||
|
||||
|
||||
def batch_create(request, data, model):
|
||||
"""
|
||||
批量创建模型实例。
|
||||
|
||||
参数:
|
||||
- request: HTTP请求对象,用于获取用户信息。
|
||||
- data: 一个包含创建数据的列表,每个元素可以是字典或者具有dict方法的对象。
|
||||
- model: Django模型类,用于实例化和批量创建。
|
||||
|
||||
返回值:
|
||||
- query_set: 批量创建后的模型实例查询集。
|
||||
"""
|
||||
user_info = get_user_info_from_token(request) # 从请求中获取用户信息
|
||||
data_list = []
|
||||
for item in data:
|
||||
if not isinstance(item, dict):
|
||||
item = item.dict() # 如果item不是字典,则转换为字典
|
||||
|
||||
# 为每个创建的数据项添加默认的创建人、修改者和所属部门信息
|
||||
item['creator_id'] = user_info['id']
|
||||
item['modifier'] = user_info['name']
|
||||
item['belong_dept'] = user_info['dept']
|
||||
data_list.append(model(**item)) # 根据字典内容实例化模型对象并添加到列表中
|
||||
query_set = model.objects.bulk_create(data_list) # 批量创建模型实例
|
||||
return query_set
|
||||
|
||||
|
||||
def delete(id, model):
|
||||
"""
|
||||
根据提供的ID和模型删除对象实例。
|
||||
|
||||
参数:
|
||||
- id: 要删除的对象的ID。
|
||||
- model: 对象所属的模型类。
|
||||
|
||||
返回值:
|
||||
- 无返回值。
|
||||
"""
|
||||
# 根据ID和模型获取对象实例,如果找不到则返回404错误页面
|
||||
instance = get_object_or_404(model, id=id)
|
||||
# 删除对象实例
|
||||
instance.delete()
|
||||
pass
|
||||
|
||||
|
||||
def update(request, id, data, model):
|
||||
"""
|
||||
更新给定模型实例的数据。
|
||||
|
||||
参数:
|
||||
- request: HTTP请求对象,用于获取用户信息。
|
||||
- id: 要更新的模型实例的ID。
|
||||
- data: 包含更新数据的实例,应能转换为字典格式。
|
||||
- model: 要更新的模型类。
|
||||
|
||||
返回值:
|
||||
- 更新后的模型实例。
|
||||
"""
|
||||
dict_data = data.dict() # 将data转换为字典格式
|
||||
user_info = get_user_info_from_token(request) # 从请求中获取用户信息
|
||||
# 为更新的数据添加修改者信息
|
||||
dict_data['modifier'] = user_info['name']
|
||||
instance = get_object_or_404(model, id=id) # 获取指定ID的模型实例
|
||||
# 遍历字典,将更新的数据设置到模型实例上
|
||||
for attr, value in dict_data.items():
|
||||
setattr(instance, attr, value)
|
||||
instance.save() # 保存更新
|
||||
return instance # 返回更新后的实例
|
||||
|
||||
|
||||
def retrieve(request, model, filters: FuFilters = FuFilters()):
|
||||
"""
|
||||
根据提供的过滤条件从数据库中检索模型实例。
|
||||
|
||||
参数:
|
||||
- request: HttpRequest对象,用于获取请求信息。
|
||||
- model: Django模型类,指定要检索的数据模型。
|
||||
- filters: FuFilters类的实例,包含过滤条件。默认为FuFilters(),即无条件过滤。
|
||||
|
||||
返回值:
|
||||
- query_set: 一个Django QuerySet对象,包含根据过滤条件检索到的模型实例。
|
||||
"""
|
||||
# 根据请求和过滤条件应用数据权限控制
|
||||
filters = data_permission(request, filters)
|
||||
if filters is not None:
|
||||
# 将filters中的空字符串值转换为None
|
||||
for attr, value in filters.__dict__.items():
|
||||
if getattr(filters, attr) == '':
|
||||
setattr(filters, attr, None)
|
||||
# 使用过滤条件查询模型实例
|
||||
query_set = model.objects.filter(**filters.dict(exclude_none=True))
|
||||
else:
|
||||
# 如果没有有效的过滤条件,则返回所有模型实例
|
||||
query_set = model.objects.all()
|
||||
return query_set
|
||||
|
||||
|
||||
def export_data(request, model, scheme, export_fields):
|
||||
"""
|
||||
导出数据为Excel文件。
|
||||
|
||||
参数:
|
||||
- request: HttpRequest对象,表示客户端请求。
|
||||
- model: Django模型类,指定要导出数据的模型。
|
||||
- scheme: 表示数据转换规则的对象,用于将ORM对象转换为字典。
|
||||
- export_fields: 包含要导出的字段名的列表。
|
||||
|
||||
返回值:
|
||||
- FileResponse对象,提供下载Excel文件。
|
||||
"""
|
||||
|
||||
title_dict = {}
|
||||
# 根据export_fields列表获取字段的显示名称
|
||||
for field in export_fields:
|
||||
field_obj = getattr(model, field).field
|
||||
title_dict[field] = field_obj.help_text
|
||||
|
||||
qs = retrieve(request, model)
|
||||
list_data = []
|
||||
# 将查询集中的每一项转换为指定格式的字典
|
||||
for qs_item in qs:
|
||||
qs_item = scheme.from_orm(qs_item)
|
||||
dict_data = {}
|
||||
for item, value in title_dict.items():
|
||||
dict_data[value] = getattr(qs_item, item)
|
||||
list_data.append(dict_data)
|
||||
|
||||
# 创建并初始化Excel工作簿
|
||||
wb = openpyxl.Workbook()
|
||||
ws = wb.active
|
||||
# 向Excel中写入数据
|
||||
for index, data in enumerate(list_data):
|
||||
if index == 0:
|
||||
# 写入表头
|
||||
ws.append(list(data.keys()))
|
||||
ws.append(list(data.values()))
|
||||
|
||||
# 生成唯一的文件名并设置文件路径
|
||||
file_name = datetime.now().strftime('%Y%m%d%H%M%S%f') + '.xlsx'
|
||||
current_ymd = datetime.now().strftime('%Y%m%d')
|
||||
file_path = os.path.join(STATIC_URL, current_ymd)
|
||||
file_url = os.path.join(file_path, file_name)
|
||||
# 确保导出文件的目录存在
|
||||
if not os.path.exists(file_path):
|
||||
os.makedirs(file_path)
|
||||
|
||||
# 保存Excel文件到指定路径
|
||||
wb.save(file_url)
|
||||
# 返回供下载的文件响应
|
||||
return FileResponse(open(file_url, "rb"), as_attachment=True)
|
||||
|
||||
|
||||
def import_data(request, model, scheme, data, import_fields):
|
||||
"""
|
||||
导入数据到指定模型
|
||||
|
||||
参数:
|
||||
- request: HttpRequest对象,表示客户端请求
|
||||
- model: Django模型类,数据将被导入到这个模型
|
||||
- scheme: 一个函数,用于根据给定的数据字典创建模型实例
|
||||
- data: 包含要导入文件信息的对象,比如上传的Excel文件
|
||||
- import_fields: 一个列表,指定模型中需要导入的字段名
|
||||
|
||||
返回值:
|
||||
- FuResponse对象,包含导入结果的消息
|
||||
"""
|
||||
title_dict = {} # 字段名与Excel列对应的字典
|
||||
for field in import_fields:
|
||||
field_obj = getattr(model, field).field
|
||||
title_dict[field_obj.help_text] = field_obj.column
|
||||
# 文件路径处理
|
||||
file_path = str(BASE_DIR) + unquote(data.path)
|
||||
# 加载Excel工作簿
|
||||
wb = load_workbook(file_path)
|
||||
ws = wb.active # 获取活动工作表
|
||||
title_value = []
|
||||
for index_row, row in enumerate(ws.values):
|
||||
if index_row == 0:
|
||||
title_value = row # 读取Excel表头
|
||||
else:
|
||||
dict_data = {} # 存储每一行数据转换后的字典
|
||||
for index, cell in enumerate(row):
|
||||
title_cell = title_value[index]
|
||||
value = title_dict.get(title_cell) # 根据表头查找对应字段
|
||||
if value is not None:
|
||||
dict_data[value] = cell
|
||||
print(dict_data) # 打印处理后的数据,用于调试
|
||||
data = scheme(**dict_data) # 根据处理后的字典创建模型实例
|
||||
create(request, data, model) # 在数据库中创建模型实例
|
||||
return FuResponse(msg='导入成功') # 返回成功消息
|
||||
58
code/utils/fu_jwt.py
Normal file
58
code/utils/fu_jwt.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/5/14 16:19
|
||||
# @Author : 臧成龙
|
||||
# @FileName: fu_jwt.py
|
||||
# @Software: PyCharm
|
||||
from typing import Union
|
||||
from simplejwt import util
|
||||
from simplejwt.jwt import default_alg, _hash, Jwt
|
||||
import datetime
|
||||
import json
|
||||
|
||||
|
||||
class DateEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj, datetime.datetime):
|
||||
return obj.strftime("%Y-%m-%d %H:%M:%S")
|
||||
else:
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
|
||||
|
||||
class FuJwt(Jwt):
|
||||
|
||||
def encode(self) -> str:
|
||||
payload = {}
|
||||
payload.update(self.registered_claims)
|
||||
payload.update(self.payload)
|
||||
return encode(self.secret, payload, self.alg, self.header)
|
||||
|
||||
|
||||
def encode(secret: Union[str, bytes], payload: dict = None, alg: str = default_alg, header: dict = None) -> str:
|
||||
"""
|
||||
:param secret: The secret used to encode the token.
|
||||
:type secret: Union[str, bytes]
|
||||
:param payload: The payload to be encoded in the token.
|
||||
:type payload: dict
|
||||
:param alg: The algorithm used to hash the token.
|
||||
:type alg: str
|
||||
:param header: The header to be encoded in the token.
|
||||
:type header: dict
|
||||
:return: A new token
|
||||
:rtype: str
|
||||
"""
|
||||
secret = util.to_bytes(secret)
|
||||
|
||||
payload = payload or {}
|
||||
header = header or {}
|
||||
|
||||
header_json = util.to_bytes(json.dumps(header))
|
||||
header_b64 = util.b64_encode(header_json)
|
||||
payload_json = util.to_bytes(json.dumps(payload, cls=DateEncoder))
|
||||
payload_b64 = util.b64_encode(payload_json)
|
||||
|
||||
pre_signature = util.join(header_b64, payload_b64)
|
||||
signature = _hash(secret, pre_signature, alg)
|
||||
signature_b64 = util.b64_encode(signature)
|
||||
|
||||
token = util.join(pre_signature, signature_b64)
|
||||
return util.from_bytes(token)
|
||||
66
code/utils/fu_ninja.py
Normal file
66
code/utils/fu_ninja.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/5/13 23:41
|
||||
# @Author : 臧成龙
|
||||
# @FileName: fu_ninja.py
|
||||
# @Software: PyCharm
|
||||
from typing import Any, List
|
||||
|
||||
from django.db.models import QuerySet
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from ninja import Field, ModelSchema, NinjaAPI, Query, Router, Schema
|
||||
from ninja.orm.metaclass import ModelSchemaMetaclass
|
||||
from ninja.pagination import PaginationBase
|
||||
from ninja.types import DictStrAny
|
||||
|
||||
from .fu_response import FuResponse
|
||||
from .usual import get_user_info_from_token
|
||||
|
||||
|
||||
class FuNinjaAPI(NinjaAPI):
|
||||
def create_response(
|
||||
self, request: HttpRequest, data: Any, *, status: int = 200, code: int = 2000, msg: str = "success",
|
||||
temporal_response: HttpResponse = None,
|
||||
) -> HttpResponse:
|
||||
std_data = {
|
||||
"code": code,
|
||||
"result": data,
|
||||
"message": msg,
|
||||
"success": True
|
||||
}
|
||||
content = self.renderer.render(request, std_data, response_status=status)
|
||||
content_type = "{}; charset={}".format(
|
||||
self.renderer.media_type, self.renderer.charset
|
||||
)
|
||||
|
||||
return HttpResponse(content, status=status, content_type=content_type)
|
||||
|
||||
|
||||
class MyPagination(PaginationBase):
|
||||
class Input(Schema):
|
||||
pageSize: int = Field(10, gt=0)
|
||||
page: int = Field(1, gt=-1)
|
||||
|
||||
class Output(Schema):
|
||||
items: List[Any]
|
||||
total: int
|
||||
|
||||
def paginate_queryset(
|
||||
self,
|
||||
queryset: QuerySet,
|
||||
pagination: Input,
|
||||
**params: DictStrAny,
|
||||
) -> Any:
|
||||
offset = pagination.pageSize * (pagination.page - 1)
|
||||
limit: int = pagination.pageSize
|
||||
return {
|
||||
"page": offset,
|
||||
"limit": limit,
|
||||
"items": queryset[offset: offset + limit],
|
||||
"total": self._items_count(queryset),
|
||||
} # noqa: E203
|
||||
|
||||
|
||||
class FuFilters(Schema):
|
||||
creator_id: int = Field(None, alias="creator_id")
|
||||
belong_dept: int = Field(None, alias="belong_dept")
|
||||
belong_dept__in: List[int] = Field(None, alias="belong_dept__in")
|
||||
42
code/utils/fu_response.py
Normal file
42
code/utils/fu_response.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/5/14 15:27
|
||||
# @Author : 臧成龙
|
||||
# @FileName: fu_response.py.py
|
||||
# @Software: PyCharm
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
|
||||
from django.http import HttpResponse
|
||||
|
||||
from .fu_jwt import DateEncoder
|
||||
|
||||
# class JsonResponse(HttpResponse):
|
||||
#
|
||||
# def __init__(
|
||||
# self,
|
||||
# data,
|
||||
# safe=True,
|
||||
# **kwargs,
|
||||
# ):
|
||||
# if safe and not isinstance(data, dict):
|
||||
# raise TypeError(
|
||||
# "In order to allow non-dict objects to be serialized set the "
|
||||
# "safe parameter to False."
|
||||
# )
|
||||
# kwargs.setdefault("content_type", "application/json")
|
||||
# data = json.dumps(data, cls=DateEncoder)
|
||||
# super().__init__(content=data, **kwargs)
|
||||
|
||||
|
||||
class FuResponse(HttpResponse):
|
||||
|
||||
def __init__(self, data=None, msg='success', code=2000, *args, **kwargs):
|
||||
std_data = {
|
||||
"code": code,
|
||||
"result": data,
|
||||
"message": msg,
|
||||
"success": True
|
||||
}
|
||||
data = json.dumps(std_data, cls=DateEncoder)
|
||||
super().__init__(data, *args, **kwargs)
|
||||
76
code/utils/list_to_tree.py
Normal file
76
code/utils/list_to_tree.py
Normal file
@@ -0,0 +1,76 @@
|
||||
# Author 臧成龙
|
||||
# coding=utf-8
|
||||
# @Time : 2022/5/16 21:09
|
||||
# @File : list_to_tree.py
|
||||
# @Software: PyCharm
|
||||
# @qq: 939589097
|
||||
|
||||
def add_node(p, node):
|
||||
# ⼦节点list
|
||||
p["children"] = []
|
||||
for n in node:
|
||||
if n.get("parent_id") == p.get("id"):
|
||||
p["children"].append(n)
|
||||
# 递归⼦节点,查找⼦节点的节点
|
||||
for t in p["children"]:
|
||||
if not t.get("children"):
|
||||
t["children"] = []
|
||||
t["children"].append(add_node(t, node))
|
||||
# 退出递归的条件
|
||||
if len(p["children"]) == 0:
|
||||
p.pop('children')
|
||||
p["choice"] = 1
|
||||
return
|
||||
|
||||
|
||||
def list_to_route(data):
|
||||
root = []
|
||||
node = []
|
||||
# 初始化数据,获取根节点和其他子节点list
|
||||
for d in data:
|
||||
d['meta'] = {
|
||||
'title': d.pop('title'),
|
||||
'ignoreKeepAlive': d.pop('keepalive'),
|
||||
'orderNo': d.pop('sort'),
|
||||
'hideMenu': d.pop('hide_menu'),
|
||||
'icon': d.pop('icon')
|
||||
}
|
||||
|
||||
d["choice"] = 0
|
||||
if d.get("parent_id") is None:
|
||||
root.append(d)
|
||||
else:
|
||||
node.append(d)
|
||||
# print("root----",root)
|
||||
# print("node----",node)
|
||||
# 查找子节点
|
||||
for p in root:
|
||||
add_node(p, node)
|
||||
# 无子节点
|
||||
if len(root) == 0:
|
||||
return node
|
||||
|
||||
return root
|
||||
|
||||
|
||||
def list_to_tree(data):
|
||||
root = []
|
||||
node = []
|
||||
# 初始化数据,获取根节点和其他子节点list
|
||||
|
||||
for d in data:
|
||||
d["choice"] = 0
|
||||
if d.get("parent_id") is None:
|
||||
root.append(d)
|
||||
else:
|
||||
node.append(d)
|
||||
# print("root----",root)
|
||||
# print("node----",node)
|
||||
# 查找子节点
|
||||
for p in root:
|
||||
add_node(p, node)
|
||||
# 无子节点
|
||||
if len(root) == 0:
|
||||
return node
|
||||
|
||||
return root
|
||||
99
code/utils/middleware.py
Normal file
99
code/utils/middleware.py
Normal file
@@ -0,0 +1,99 @@
|
||||
"""
|
||||
日志 django中间件
|
||||
"""
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
from system.models import OperationLog, Users
|
||||
|
||||
from .request_util import (
|
||||
get_browser,
|
||||
get_os,
|
||||
get_request_data,
|
||||
get_request_ip,
|
||||
get_request_path,
|
||||
get_request_user,
|
||||
get_verbose_name,
|
||||
)
|
||||
|
||||
|
||||
class ApiLoggingMiddleware(MiddlewareMixin):
|
||||
"""
|
||||
用于记录API访问日志中间件
|
||||
"""
|
||||
|
||||
def __init__(self, get_response=None):
|
||||
super().__init__(get_response)
|
||||
self.enable = getattr(settings, 'API_LOG_ENABLE', None) or False
|
||||
self.methods = getattr(settings, 'API_LOG_METHODS', None) or set()
|
||||
self.operation_log_id = None
|
||||
|
||||
@classmethod
|
||||
def __handle_request(cls, request):
|
||||
request.request_ip = get_request_ip(request)
|
||||
request.request_data = get_request_data(request)
|
||||
request.request_path = get_request_path(request)
|
||||
|
||||
def __handle_response(self, request, response):
|
||||
# request_data,request_ip由PermissionInterfaceMiddleware中间件中添加的属性
|
||||
body = getattr(request, 'request_data', {})
|
||||
# 请求含有password则用*替换掉(暂时先用于所有接口的password请求参数)
|
||||
if isinstance(body, dict) and body.get('password', ''):
|
||||
body['password'] = '*' * len(body['password'])
|
||||
if not hasattr(response, 'data') or not isinstance(response.data, dict):
|
||||
response.data = {}
|
||||
try:
|
||||
if not response.data and response.content:
|
||||
content = json.loads(response.content.decode())
|
||||
response.data = content if isinstance(content, dict) else {}
|
||||
except Exception:
|
||||
return
|
||||
user = get_request_user(request)
|
||||
if isinstance(user, AnonymousUser):
|
||||
return
|
||||
info = {
|
||||
'request_username': user.username if isinstance(user, Users) else user['username'],
|
||||
'request_ip': getattr(request, 'request_ip', 'unknown'),
|
||||
'creator_id': user.id if isinstance(user, Users) else user['id'],
|
||||
'belong_dept': user.dept_id if isinstance(user, Users) else user['dept'],
|
||||
'request_method': request.method,
|
||||
'request_path': request.request_path,
|
||||
'request_body': body,
|
||||
'response_code': response.data.get('code'),
|
||||
'request_os': get_os(request),
|
||||
'request_browser': get_browser(request),
|
||||
'request_msg': request.session.get('request_msg'),
|
||||
'status': True if response.data.get('code') in [2000, ] else False,
|
||||
'json_result': {"code": response.data.get('code'), "msg": response.data.get('result')},
|
||||
}
|
||||
operation_log, creat = OperationLog.objects.update_or_create(defaults=info, id=self.operation_log_id)
|
||||
if not operation_log.request_modular and settings.API_MODEL_MAP.get(request.request_path, None):
|
||||
operation_log.request_modular = settings.API_MODEL_MAP[request.request_path]
|
||||
operation_log.save()
|
||||
|
||||
def process_view(self, request, view_func, view_args, view_kwargs):
|
||||
if hasattr(view_func, 'cls') and hasattr(view_func.cls, 'queryset'):
|
||||
if self.enable:
|
||||
if self.methods == 'ALL' or request.method in self.methods:
|
||||
log = OperationLog(request_modular=get_verbose_name(view_func.cls.queryset))
|
||||
log.save()
|
||||
self.operation_log_id = log.id
|
||||
|
||||
return
|
||||
|
||||
def process_request(self, request):
|
||||
self.__handle_request(request)
|
||||
|
||||
def process_response(self, request, response):
|
||||
"""
|
||||
主要请求处理完之后记录
|
||||
:param request:
|
||||
:param response:
|
||||
:return:
|
||||
"""
|
||||
if self.enable:
|
||||
if self.methods == 'ALL' or request.method in self.methods:
|
||||
self.__handle_response(request, response)
|
||||
return response
|
||||
62
code/utils/models.py
Normal file
62
code/utils/models.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
@author: 猿小天
|
||||
@contact: QQ:1638245306
|
||||
@Created on: 2021/5/31 031 22:08
|
||||
@Remark: 公共基础model类
|
||||
"""
|
||||
import uuid
|
||||
|
||||
from django.apps import apps
|
||||
from django.db import models
|
||||
|
||||
from fuadmin import settings
|
||||
|
||||
|
||||
class CoreModel(models.Model):
|
||||
"""
|
||||
核心标准抽象模型模型,可直接继承使用
|
||||
增加审计字段, 覆盖字段时, 字段名称请勿修改, 必须统一审计字段名称
|
||||
"""
|
||||
id = models.BigAutoField(primary_key=True, help_text="Id", verbose_name="Id")
|
||||
remark = models.CharField(max_length=255, verbose_name="描述", null=True, blank=True, help_text="描述")
|
||||
creator = models.ForeignKey(to=settings.AUTH_USER_MODEL, related_query_name='creator_query', null=True,
|
||||
verbose_name='创建人', help_text="创建人", on_delete=models.SET_NULL, db_constraint=False)
|
||||
modifier = models.CharField(max_length=255, null=True, blank=True, help_text="修改人", verbose_name="修改人")
|
||||
belong_dept = models.IntegerField(help_text="数据归属部门", null=True, blank=True, verbose_name="数据归属部门")
|
||||
update_datetime = models.DateTimeField(auto_now=True, null=True, blank=True, help_text="修改时间", verbose_name="修改时间")
|
||||
create_datetime = models.DateTimeField(auto_now_add=True, null=True, blank=True, help_text="创建时间",
|
||||
verbose_name="创建时间")
|
||||
sort = models.IntegerField(default=1, null=True, blank=True, verbose_name="显示排序", help_text="显示排序")
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
verbose_name = '核心模型'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
|
||||
def get_all_models_objects(model_name=None):
|
||||
"""
|
||||
获取所有 models 对象
|
||||
:return: {}
|
||||
"""
|
||||
settings.ALL_MODELS_OBJECTS = {}
|
||||
if not settings.ALL_MODELS_OBJECTS:
|
||||
all_models = apps.get_models()
|
||||
for item in list(all_models):
|
||||
table = {
|
||||
"tableName": item._meta.verbose_name,
|
||||
"table": item.__name__,
|
||||
"tableFields": []
|
||||
}
|
||||
for field in item._meta.fields:
|
||||
fields = {
|
||||
"title": field.verbose_name,
|
||||
"field": field.name
|
||||
}
|
||||
table['tableFields'].append(fields)
|
||||
settings.ALL_MODELS_OBJECTS.setdefault(item.__name__, {"table": table, "object": item})
|
||||
if model_name:
|
||||
return settings.ALL_MODELS_OBJECTS[model_name] or {}
|
||||
return settings.ALL_MODELS_OBJECTS or {}
|
||||
1
code/utils/pca-code.json
Normal file
1
code/utils/pca-code.json
Normal file
File diff suppressed because one or more lines are too long
216
code/utils/request_util.py
Normal file
216
code/utils/request_util.py
Normal file
@@ -0,0 +1,216 @@
|
||||
"""
|
||||
Request工具类
|
||||
"""
|
||||
import json
|
||||
|
||||
import requests
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractBaseUser, AnonymousUser
|
||||
from django.urls.resolvers import ResolverMatch
|
||||
from system.models import LoginLog
|
||||
from user_agents import parse
|
||||
|
||||
from .usual import get_user_info_from_token
|
||||
|
||||
|
||||
def get_request_user(request):
|
||||
"""
|
||||
获取请求user
|
||||
(1)如果request里的user没有认证,那么则手动认证一次
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
user: AbstractBaseUser = getattr(request, 'user', None)
|
||||
if user and user.is_authenticated:
|
||||
return user
|
||||
try:
|
||||
user = get_user_info_from_token(request)
|
||||
except Exception as e:
|
||||
pass
|
||||
return user or AnonymousUser()
|
||||
|
||||
|
||||
def get_request_ip(request):
|
||||
"""
|
||||
获取请求IP
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '')
|
||||
if x_forwarded_for:
|
||||
ip = x_forwarded_for.split(',')[-1].strip()
|
||||
return ip
|
||||
ip = request.META.get('REMOTE_ADDR', '') or getattr(request, 'request_ip', None)
|
||||
return ip or 'unknown'
|
||||
|
||||
|
||||
def get_request_data(request):
|
||||
"""
|
||||
获取请求参数
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
request_data = getattr(request, 'request_data', None)
|
||||
if request_data:
|
||||
return request_data
|
||||
data: dict = {**request.GET.dict(), **request.POST.dict()}
|
||||
if not data:
|
||||
try:
|
||||
body = request.body
|
||||
if body:
|
||||
data = json.loads(body)
|
||||
except Exception as e:
|
||||
pass
|
||||
if not isinstance(data, dict):
|
||||
data = {'data': data}
|
||||
return data
|
||||
|
||||
|
||||
def get_request_path(request, *args, **kwargs):
|
||||
"""
|
||||
获取请求路径
|
||||
:param request:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
request_path = getattr(request, 'request_path', None)
|
||||
if request_path:
|
||||
return request_path
|
||||
values = []
|
||||
for arg in args:
|
||||
if len(arg) == 0:
|
||||
continue
|
||||
if isinstance(arg, str):
|
||||
values.append(arg)
|
||||
elif isinstance(arg, (tuple, set, list)):
|
||||
values.extend(arg)
|
||||
elif isinstance(arg, dict):
|
||||
values.extend(arg.values())
|
||||
if len(values) == 0:
|
||||
return request.path
|
||||
path: str = request.path
|
||||
for value in values:
|
||||
path = path.replace('/' + value, '/' + '{id}')
|
||||
return path
|
||||
|
||||
|
||||
def get_request_canonical_path(request, ):
|
||||
"""
|
||||
获取请求路径
|
||||
:param request:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
request_path = getattr(request, 'request_canonical_path', None)
|
||||
if request_path:
|
||||
return request_path
|
||||
path: str = request.path
|
||||
resolver_match: ResolverMatch = request.resolver_match
|
||||
for value in resolver_match.args:
|
||||
path = path.replace(f"/{value}", "/{id}")
|
||||
for key, value in resolver_match.kwargs.items():
|
||||
if key == 'pk':
|
||||
path = path.replace(f"/{value}", f"/{{id}}")
|
||||
continue
|
||||
path = path.replace(f"/{value}", f"/{{{key}}}")
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def get_browser(request, ):
|
||||
"""
|
||||
获取浏览器名
|
||||
:param request:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
ua_string = request.META['HTTP_USER_AGENT']
|
||||
user_agent = parse(ua_string)
|
||||
return user_agent.get_browser()
|
||||
|
||||
|
||||
def get_os(request, ):
|
||||
"""
|
||||
获取操作系统
|
||||
:param request:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
ua_string = request.META['HTTP_USER_AGENT']
|
||||
user_agent = parse(ua_string)
|
||||
return user_agent.get_os()
|
||||
|
||||
|
||||
def get_verbose_name(queryset=None, view=None, model=None):
|
||||
"""
|
||||
获取 verbose_name
|
||||
:param request:
|
||||
:param view:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
if queryset and hasattr(queryset, 'model'):
|
||||
model = queryset.model
|
||||
elif view and hasattr(view.get_queryset(), 'model'):
|
||||
model = view.get_queryset().model
|
||||
elif view and hasattr(view.get_serializer(), 'Meta') and hasattr(view.get_serializer().Meta, 'model'):
|
||||
model = view.get_serializer().Meta.model
|
||||
if model:
|
||||
return getattr(model, '_meta').verbose_name
|
||||
else:
|
||||
model = queryset.model._meta.verbose_name
|
||||
except Exception as e:
|
||||
pass
|
||||
return model if model else ""
|
||||
|
||||
|
||||
def get_ip_analysis(ip):
|
||||
"""
|
||||
获取ip详细概略
|
||||
:param ip: ip地址
|
||||
:return:
|
||||
"""
|
||||
data = {
|
||||
"continent": "",
|
||||
"country": "",
|
||||
"province": "",
|
||||
"city": "",
|
||||
"district": "",
|
||||
"isp": "",
|
||||
"area_code": "",
|
||||
"country_english": "",
|
||||
"country_code": "",
|
||||
"longitude": "",
|
||||
"latitude": ""
|
||||
}
|
||||
if ip != 'unknown' and ip:
|
||||
if getattr(settings, 'ENABLE_LOGIN_ANALYSIS_LOG', True):
|
||||
res = requests.get(url='https://ip.django-vue-admin.com/ip/analysis', params={"ip": ip}, verify=False)
|
||||
|
||||
if res.status_code == 200:
|
||||
res_data = res.json()
|
||||
if res_data.get('code') == 0:
|
||||
data = res_data.get('data')
|
||||
return data
|
||||
return data
|
||||
|
||||
|
||||
def save_login_log(request):
|
||||
"""
|
||||
保存登录日志
|
||||
:return:
|
||||
"""
|
||||
ip = get_request_ip(request=request)
|
||||
analysis_data = get_ip_analysis(ip)
|
||||
analysis_data['username'] = request.user.username
|
||||
analysis_data['ip'] = ip
|
||||
analysis_data['agent'] = str(parse(request.META['HTTP_USER_AGENT']))
|
||||
analysis_data['browser'] = get_browser(request)
|
||||
analysis_data['os'] = get_os(request)
|
||||
analysis_data['creator_id'] = request.user.id
|
||||
analysis_data['belong_dept'] = getattr(request.user, 'dept_id', '')
|
||||
LoginLog.objects.create(**analysis_data)
|
||||
27
code/utils/ru_convert.py
Normal file
27
code/utils/ru_convert.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import re
|
||||
|
||||
|
||||
class RuleConvert:
|
||||
"""
|
||||
命名规则转换 Tips:大小驼峰及下划线互转
|
||||
@descript 大驼峰: 首字母大写其余每一个逻辑断点(单词)都用大写字母标记,同帕斯卡命名法
|
||||
@descript 小驼峰: 首字母小写其余每一个逻辑断点(单词)都用大写字母标记
|
||||
@descript 下划线: 逻辑断点(单词)用的是下划线隔开
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def to_underline(x):
|
||||
"""转下划线命名"""
|
||||
return re.sub('(?<=[a-z])[A-Z]|(?<!^)[A-Z](?=[a-z])', '_\g<0>', x).lower()
|
||||
|
||||
@staticmethod
|
||||
def to_upper_camel_case(x):
|
||||
"""转大驼峰法命名"""
|
||||
s = re.sub('_([a-zA-Z])', lambda m: (m.group(1).upper()), x.lower())
|
||||
return s[0].upper() + s[1:]
|
||||
|
||||
@staticmethod
|
||||
def to_lower_camel_case(x):
|
||||
"""转小驼峰法命名"""
|
||||
s = re.sub('_([a-zA-Z])', lambda m: (m.group(1).upper()), x.lower())
|
||||
return s[0].lower() + s[1:]
|
||||
529
code/utils/server/linux.py
Normal file
529
code/utils/server/linux.py
Normal file
@@ -0,0 +1,529 @@
|
||||
#!/bin/python
|
||||
# coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | django-vue3-lyadmin
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: lybbn
|
||||
# +-------------------------------------------------------------------
|
||||
# | QQ: 1042594286
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
# ------------------------------
|
||||
# linux系统命令工具类封装
|
||||
# ------------------------------
|
||||
import os, sys, re, time, json
|
||||
import psutil
|
||||
from django.core.cache import cache
|
||||
from pathlib import Path
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
|
||||
PUBLIC_DICT = os.path.join(BASE_DIR, 'public.json')
|
||||
|
||||
|
||||
def Md5(strings):
|
||||
"""
|
||||
@name 生成MD5
|
||||
@author hwliang<hwl@bt.cn>
|
||||
@param strings 要被处理的字符串
|
||||
@return string(32)
|
||||
"""
|
||||
if type(strings) != bytes:
|
||||
strings = strings.encode()
|
||||
import hashlib
|
||||
m = hashlib.md5()
|
||||
m.update(strings)
|
||||
return m.hexdigest()
|
||||
|
||||
|
||||
def md5(strings):
|
||||
return Md5(strings)
|
||||
|
||||
|
||||
def get_preexec_fn(run_user):
|
||||
'''
|
||||
@name 获取指定执行用户预处理函数
|
||||
@author hwliang<2021-08-19>
|
||||
@param run_user<string> 运行用户
|
||||
@return 预处理函数
|
||||
'''
|
||||
import pwd
|
||||
pid = pwd.getpwnam(run_user)
|
||||
uid = pid.pw_uid
|
||||
gid = pid.pw_gid
|
||||
|
||||
def _exec_rn():
|
||||
os.setgid(gid)
|
||||
os.setuid(uid)
|
||||
|
||||
return _exec_rn
|
||||
|
||||
|
||||
def get_error_info():
|
||||
import traceback
|
||||
errorMsg = traceback.format_exc()
|
||||
return errorMsg
|
||||
|
||||
|
||||
def ExecShell(cmdstring, timeout=None, shell=True, cwd=None, env=None, user=None):
|
||||
'''
|
||||
@name 执行命令
|
||||
@author hwliang<2021-08-19>
|
||||
@param cmdstring 命令 [必传]
|
||||
@param timeout 超时时间
|
||||
@param shell 是否通过shell运行
|
||||
@param cwd 进入的目录
|
||||
@param env 环境变量
|
||||
@param user 执行用户名
|
||||
@return 命令执行结果
|
||||
'''
|
||||
a = ''
|
||||
e = ''
|
||||
import subprocess, tempfile
|
||||
preexec_fn = None
|
||||
tmp_dir = '/dev/shm'
|
||||
if user:
|
||||
preexec_fn = get_preexec_fn(user)
|
||||
tmp_dir = '/tmp'
|
||||
try:
|
||||
rx = md5(cmdstring)
|
||||
succ_f = tempfile.SpooledTemporaryFile(max_size=4096, mode='wb+', suffix='_succ', prefix='btex_' + rx,
|
||||
dir=tmp_dir)
|
||||
err_f = tempfile.SpooledTemporaryFile(max_size=4096, mode='wb+', suffix='_err', prefix='btex_' + rx,
|
||||
dir=tmp_dir)
|
||||
sub = subprocess.Popen(cmdstring, close_fds=True, shell=shell, bufsize=128, stdout=succ_f, stderr=err_f,
|
||||
cwd=cwd, env=env, preexec_fn=preexec_fn)
|
||||
if timeout:
|
||||
s = 0
|
||||
d = 0.01
|
||||
while sub.poll() == None:
|
||||
time.sleep(d)
|
||||
s += d
|
||||
if s >= timeout:
|
||||
if not err_f.closed: err_f.close()
|
||||
if not succ_f.closed: succ_f.close()
|
||||
return 'Timed out'
|
||||
else:
|
||||
sub.wait()
|
||||
|
||||
err_f.seek(0)
|
||||
succ_f.seek(0)
|
||||
a = succ_f.read()
|
||||
e = err_f.read()
|
||||
if not err_f.closed: err_f.close()
|
||||
if not succ_f.closed: succ_f.close()
|
||||
except:
|
||||
return '', get_error_info()
|
||||
try:
|
||||
# 编码修正
|
||||
if type(a) == bytes: a = a.decode('utf-8')
|
||||
if type(e) == bytes: e = e.decode('utf-8')
|
||||
except:
|
||||
a = str(a)
|
||||
e = str(e)
|
||||
|
||||
return a, e
|
||||
|
||||
|
||||
def ReadFile(filename, mode='r'):
|
||||
"""
|
||||
读取文件内容
|
||||
@filename 文件名
|
||||
return string(bin) 若文件不存在,则返回None
|
||||
"""
|
||||
import os
|
||||
if not os.path.exists(filename): return False
|
||||
fp = None
|
||||
try:
|
||||
fp = open(filename, mode)
|
||||
f_body = fp.read()
|
||||
except Exception as ex:
|
||||
if sys.version_info[0] != 2:
|
||||
try:
|
||||
fp = open(filename, mode, encoding="utf-8")
|
||||
f_body = fp.read()
|
||||
except:
|
||||
fp = open(filename, mode, encoding="GBK")
|
||||
f_body = fp.read()
|
||||
else:
|
||||
return False
|
||||
finally:
|
||||
if fp and not fp.closed:
|
||||
fp.close()
|
||||
return f_body
|
||||
|
||||
|
||||
def readFile(filename, mode='r'):
|
||||
'''
|
||||
@name 读取指定文件数据
|
||||
@author hwliang<2021-06-09>
|
||||
@param filename<string> 文件名
|
||||
@param mode<string> 文件打开模式,默认r
|
||||
@return string or bytes or False 如果返回False则说明读取失败
|
||||
'''
|
||||
return ReadFile(filename, mode)
|
||||
|
||||
|
||||
# xss 防御
|
||||
def xsssec(text):
|
||||
return text.replace('&', '&').replace('"', '"').replace('<', '<').replace('>', '>')
|
||||
|
||||
|
||||
def get_os_version():
|
||||
'''
|
||||
@name 取操作系统版本
|
||||
@author hwliang<2021-08-07>
|
||||
@return string
|
||||
'''
|
||||
p_file = '/etc/.productinfo'
|
||||
if os.path.exists(p_file):
|
||||
s_tmp = readFile(p_file).split("\n")
|
||||
if s_tmp[0].find('Kylin') != -1 and len(s_tmp) > 1:
|
||||
version = s_tmp[0] + ' ' + s_tmp[1].split('/')[0].strip()
|
||||
else:
|
||||
version = readFile('/etc/redhat-release')
|
||||
if not version:
|
||||
version = readFile('/etc/issue').strip().split("\n")[0].replace('\\n', '').replace('\l', '').strip()
|
||||
else:
|
||||
version = version.replace('release ', '').replace('Linux', '').replace('(Core)', '').strip()
|
||||
v_info = sys.version_info
|
||||
try:
|
||||
version = "{} {}(Py{}.{}.{})".format(version, os.uname().machine, v_info.major, v_info.minor, v_info.micro)
|
||||
except:
|
||||
version = "{} (Py{}.{}.{})".format(version, v_info.major, v_info.minor, v_info.micro)
|
||||
return xsssec(version)
|
||||
|
||||
|
||||
def GetSystemVersion():
|
||||
# 取操作系统版本
|
||||
key = 'lybbn_sys_version'
|
||||
version = cache.get(key)
|
||||
if version: return version
|
||||
version = get_os_version()
|
||||
cache.set(key, version, 10000)
|
||||
return version
|
||||
|
||||
|
||||
def GetLoadAverage():
|
||||
try:
|
||||
c = os.getloadavg()
|
||||
except:
|
||||
c = [0, 0, 0]
|
||||
data = {}
|
||||
data['one'] = float(c[0])
|
||||
data['five'] = float(c[1])
|
||||
data['fifteen'] = float(c[2])
|
||||
data['max'] = psutil.cpu_count() * 2
|
||||
data['limit'] = data['max']
|
||||
data['safe'] = data['max'] * 0.75
|
||||
temppercent = round(data['one'] / data['max'] * 100)
|
||||
data['percent'] = 100 if temppercent > 100 else temppercent
|
||||
return data
|
||||
|
||||
|
||||
# 取内存信息
|
||||
def GetMemInfo():
|
||||
mem = psutil.virtual_memory()
|
||||
memInfo = {}
|
||||
# memInfo['percent'] = mem.percent
|
||||
memInfo2 = {'memTotal': int(mem.total / 1024 / 1024), 'memFree': int(mem.free / 1024 / 1024),
|
||||
'memBuffers': int(mem.buffers / 1024 / 1024), 'memCached': int(mem.cached / 1024 / 1024)}
|
||||
memInfo['total'] = round(float(mem.total) / 1024 / 1024 / 1024, 2)
|
||||
memInfo['free'] = round((memInfo2['memFree'] + memInfo2['memBuffers'] + memInfo2['memCached']) / 1024, 2)
|
||||
memInfo['used'] = round(float(mem.used) / 1024 / 1024 / 1024, 2)
|
||||
memInfo['percent'] = round((int(mem.used) / 1024 / 1024) / memInfo2['memTotal'] * 100, 1)
|
||||
# memInfo['realUsed'] = round((memInfo2['memTotal'] - memInfo2['memFree'] - memInfo2['memBuffers'] - memInfo2['memCached']) / 1024,2) # memRealUsed
|
||||
return memInfo
|
||||
|
||||
|
||||
# 获取磁盘IO开销数据
|
||||
def get_disk_iostat():
|
||||
iokey = 'iostat'
|
||||
diskio = cache.get(iokey)
|
||||
mtime = int(time.time())
|
||||
if not diskio:
|
||||
diskio = {}
|
||||
diskio['info'] = None
|
||||
diskio['time'] = mtime
|
||||
diskio_1 = diskio['info']
|
||||
stime = mtime - diskio['time']
|
||||
if not stime: stime = 1
|
||||
diskInfo = {}
|
||||
diskInfo['ALL'] = {}
|
||||
diskInfo['ALL']['read_count'] = 0
|
||||
diskInfo['ALL']['write_count'] = 0
|
||||
diskInfo['ALL']['read_bytes'] = 0
|
||||
diskInfo['ALL']['write_bytes'] = 0
|
||||
diskInfo['ALL']['read_time'] = 0
|
||||
diskInfo['ALL']['write_time'] = 0
|
||||
diskInfo['ALL']['read_merged_count'] = 0
|
||||
diskInfo['ALL']['write_merged_count'] = 0
|
||||
try:
|
||||
if os.path.exists('/proc/diskstats'):
|
||||
diskio_2 = psutil.disk_io_counters(perdisk=True)
|
||||
if not diskio_1:
|
||||
diskio_1 = diskio_2
|
||||
for disk_name in diskio_2.keys():
|
||||
diskInfo[disk_name] = {}
|
||||
diskInfo[disk_name]['read_count'] = int(
|
||||
(diskio_2[disk_name].read_count - diskio_1[disk_name].read_count) / stime)
|
||||
diskInfo[disk_name]['write_count'] = int(
|
||||
(diskio_2[disk_name].write_count - diskio_1[disk_name].write_count) / stime)
|
||||
diskInfo[disk_name]['read_bytes'] = int(
|
||||
(diskio_2[disk_name].read_bytes - diskio_1[disk_name].read_bytes) / stime)
|
||||
diskInfo[disk_name]['write_bytes'] = int(
|
||||
(diskio_2[disk_name].write_bytes - diskio_1[disk_name].write_bytes) / stime)
|
||||
diskInfo[disk_name]['read_time'] = int(
|
||||
(diskio_2[disk_name].read_time - diskio_1[disk_name].read_time) / stime)
|
||||
diskInfo[disk_name]['write_time'] = int(
|
||||
(diskio_2[disk_name].write_time - diskio_1[disk_name].write_time) / stime)
|
||||
diskInfo[disk_name]['read_merged_count'] = int(
|
||||
(diskio_2[disk_name].read_merged_count - diskio_1[disk_name].read_merged_count) / stime)
|
||||
diskInfo[disk_name]['write_merged_count'] = int(
|
||||
(diskio_2[disk_name].write_merged_count - diskio_1[disk_name].write_merged_count) / stime)
|
||||
|
||||
diskInfo['ALL']['read_count'] += diskInfo[disk_name]['read_count']
|
||||
diskInfo['ALL']['write_count'] += diskInfo[disk_name]['write_count']
|
||||
diskInfo['ALL']['read_bytes'] += diskInfo[disk_name]['read_bytes']
|
||||
diskInfo['ALL']['write_bytes'] += diskInfo[disk_name]['write_bytes']
|
||||
if diskInfo['ALL']['read_time'] < diskInfo[disk_name]['read_time']:
|
||||
diskInfo['ALL']['read_time'] = diskInfo[disk_name]['read_time']
|
||||
if diskInfo['ALL']['write_time'] < diskInfo[disk_name]['write_time']:
|
||||
diskInfo['ALL']['write_time'] = diskInfo[disk_name]['write_time']
|
||||
diskInfo['ALL']['read_merged_count'] += diskInfo[disk_name]['read_merged_count']
|
||||
diskInfo['ALL']['write_merged_count'] += diskInfo[disk_name]['write_merged_count']
|
||||
|
||||
cache.set(iokey, {'info': diskio_2, 'time': mtime})
|
||||
except:
|
||||
return diskInfo
|
||||
return diskInfo
|
||||
|
||||
|
||||
# 获取网卡数据
|
||||
def GetNetWork():
|
||||
cache_timeout = 86400
|
||||
otime = cache.get("lybbn_otime")
|
||||
ntime = time.time()
|
||||
networkInfo = {}
|
||||
networkInfo['network'] = {}
|
||||
networkInfo['upTotal'] = 0
|
||||
networkInfo['downTotal'] = 0
|
||||
networkInfo['up'] = 0
|
||||
networkInfo['down'] = 0
|
||||
networkInfo['downPackets'] = 0
|
||||
networkInfo['upPackets'] = 0
|
||||
networkIo_list = psutil.net_io_counters(pernic=True)
|
||||
for net_key in networkIo_list.keys():
|
||||
networkIo = networkIo_list[net_key][:4]
|
||||
up_key = "{}_up".format(net_key)
|
||||
down_key = "{}_down".format(net_key)
|
||||
otime_key = "lybbn_otime"
|
||||
|
||||
if not otime:
|
||||
otime = time.time()
|
||||
|
||||
cache.set(up_key, networkIo[0], cache_timeout)
|
||||
cache.set(down_key, networkIo[1], cache_timeout)
|
||||
cache.set(otime_key, otime, cache_timeout)
|
||||
|
||||
networkInfo['network'][net_key] = {}
|
||||
up = cache.get(up_key)
|
||||
down = cache.get(down_key)
|
||||
if not up:
|
||||
up = networkIo[0]
|
||||
if not down:
|
||||
down = networkIo[1]
|
||||
networkInfo['network'][net_key]['upTotal'] = networkIo[0]
|
||||
networkInfo['network'][net_key]['downTotal'] = networkIo[1]
|
||||
networkInfo['network'][net_key]['up'] = round(float(networkIo[0] - up) / 1024 / (ntime - otime), 2)
|
||||
networkInfo['network'][net_key]['down'] = round(float(networkIo[1] - down) / 1024 / (ntime - otime), 2)
|
||||
networkInfo['network'][net_key]['downPackets'] = networkIo[3]
|
||||
networkInfo['network'][net_key]['upPackets'] = networkIo[2]
|
||||
|
||||
networkInfo['upTotal'] += networkInfo['network'][net_key]['upTotal']
|
||||
networkInfo['downTotal'] += networkInfo['network'][net_key]['downTotal']
|
||||
networkInfo['up'] += networkInfo['network'][net_key]['up']
|
||||
networkInfo['down'] += networkInfo['network'][net_key]['down']
|
||||
networkInfo['downPackets'] += networkInfo['network'][net_key]['downPackets']
|
||||
networkInfo['upPackets'] += networkInfo['network'][net_key]['upPackets']
|
||||
|
||||
cache.set(up_key, networkIo[0], cache_timeout)
|
||||
cache.set(down_key, networkIo[1], cache_timeout)
|
||||
cache.set(otime_key, time.time(), cache_timeout)
|
||||
|
||||
networkInfo['up'] = round(float(networkInfo['up']), 2)
|
||||
networkInfo['down'] = round(float(networkInfo['down']), 2)
|
||||
networkInfo['iostat'] = get_disk_iostat()
|
||||
|
||||
return networkInfo
|
||||
|
||||
|
||||
# 取系统启动时间
|
||||
def GetBootTime():
|
||||
key = 'lybbn_sys_time'
|
||||
sys_time = cache.get(key)
|
||||
if sys_time: return sys_time
|
||||
import math
|
||||
conf = readFile('/proc/uptime').split()
|
||||
tStr = float(conf[0])
|
||||
min = tStr / 60
|
||||
hours = min / 60
|
||||
days = math.floor(hours / 24)
|
||||
hours = math.floor(hours - (days * 24))
|
||||
min = math.floor(min - (days * 60 * 24) - (hours * 60))
|
||||
sys_time = "{}天".format(int(days))
|
||||
cache.set(key, sys_time, 1800)
|
||||
return sys_time
|
||||
|
||||
|
||||
# 取CPU类型
|
||||
def getCpuType():
|
||||
cpuinfo = open('/proc/cpuinfo', 'r').read()
|
||||
rep = "model\s+name\s+:\s+(.+)"
|
||||
tmp = re.search(rep, cpuinfo, re.I)
|
||||
cpuType = ''
|
||||
if tmp:
|
||||
cpuType = tmp.groups()[0]
|
||||
else:
|
||||
cpuinfo = ExecShell('LANG="en_US.UTF-8" && lscpu')[0]
|
||||
rep = "Model\s+name:\s+(.+)"
|
||||
tmp = re.search(rep, cpuinfo, re.I)
|
||||
if tmp: cpuType = tmp.groups()[0]
|
||||
return cpuType
|
||||
|
||||
|
||||
# 取CPU信息
|
||||
def GetCpuInfo(interval=1):
|
||||
# 取CPU信息
|
||||
cpuCount = psutil.cpu_count()
|
||||
cpuNum = psutil.cpu_count(logical=False)
|
||||
c_tmp = readFile('/proc/cpuinfo')
|
||||
d_tmp = re.findall("physical id.+", c_tmp)
|
||||
cpuW = len(set(d_tmp))
|
||||
import threading
|
||||
p = threading.Thread(target=get_cpu_percent_thead, args=(interval,))
|
||||
# p.setDaemon(True)
|
||||
p.start()
|
||||
|
||||
used = cache.get('lybbn_cpu_used_all')
|
||||
if not used: used = get_cpu_percent_thead(interval)
|
||||
|
||||
used_all = psutil.cpu_percent(percpu=True)
|
||||
cpu_name = getCpuType() + " * {}".format(cpuW)
|
||||
|
||||
return used, cpuCount, used_all, cpu_name, cpuNum, cpuW
|
||||
|
||||
|
||||
def get_cpu_percent_thead(interval):
|
||||
used = psutil.cpu_percent(interval)
|
||||
cache.set('lybbn_cpu_used_all', used, 10)
|
||||
return used
|
||||
|
||||
|
||||
def get_cpu_time():
|
||||
cpu_time = 0.00
|
||||
cpu_times = psutil.cpu_times()
|
||||
for s in cpu_times: cpu_time += s
|
||||
return cpu_time
|
||||
|
||||
|
||||
def GetDiskInfo(human=True):
|
||||
# 取磁盘分区信息
|
||||
key = 'lybbn_sys_disk'
|
||||
diskInfo = cache.get(key)
|
||||
if diskInfo: return diskInfo
|
||||
if human:
|
||||
temp = ExecShell("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")[0]
|
||||
else:
|
||||
temp = ExecShell("df -T -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")[0]
|
||||
tempInodes = ExecShell("df -i -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")[0]
|
||||
temp1 = temp.split('\n')
|
||||
tempInodes1 = tempInodes.split('\n')
|
||||
diskInfo = []
|
||||
n = 0
|
||||
cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev', '/dev/shm', '/run/lock', '/run', '/run/shm', '/run/user']
|
||||
for tmp in temp1:
|
||||
n += 1
|
||||
try:
|
||||
inodes = tempInodes1[n - 1].split()
|
||||
disk = re.findall(
|
||||
r"^(.+)\s+([\w\.]+)\s+([\w\.]+)\s+([\w\.]+)\s+([\w\.]+)\s+([\d%]{2,4})\s+(/.{0,100})$", tmp.strip())
|
||||
if disk: disk = disk[0]
|
||||
if len(disk) < 6: continue
|
||||
if disk[2].find('M') != -1: continue
|
||||
if disk[2].find('K') != -1: continue
|
||||
if len(disk[6].split('/')) > 10: continue
|
||||
if disk[6] in cuts: continue
|
||||
if disk[6].find('docker') != -1: continue
|
||||
if disk[1].strip() in ['tmpfs']: continue
|
||||
arr = {}
|
||||
arr['filesystem'] = disk[0].strip()
|
||||
arr['type'] = disk[1].strip()
|
||||
arr['path'] = disk[6].replace('/usr/local/lighthouse/softwares/btpanel', '/www')
|
||||
tmp1 = [disk[2], disk[3], disk[4], disk[5].split('%')[0]]
|
||||
arr['size'] = tmp1
|
||||
arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]]
|
||||
diskInfo.append(arr)
|
||||
except Exception as ex:
|
||||
continue
|
||||
cache.set(key, diskInfo, 10)
|
||||
return diskInfo
|
||||
|
||||
|
||||
def GetMsg(key, args=()):
|
||||
"""
|
||||
根据key获取内置消息返回
|
||||
@key 指定消息的key
|
||||
@args 消息内容中的参数
|
||||
"""
|
||||
try:
|
||||
log_message = json.loads(ReadFile(PUBLIC_DICT));
|
||||
keys = log_message.keys();
|
||||
msg = None;
|
||||
if key in keys:
|
||||
msg = log_message[key];
|
||||
for i in range(len(args)):
|
||||
rep = '{' + str(i + 1) + '}'
|
||||
msg = msg.replace(rep, args[i]);
|
||||
return msg;
|
||||
except:
|
||||
return key
|
||||
|
||||
|
||||
def getMsg(key, args=()):
|
||||
return GetMsg(key, args)
|
||||
|
||||
|
||||
def ReturnMsg(status, msg, args=()):
|
||||
"""
|
||||
@name 取通用dict返回
|
||||
@author hwliang<hwl@bt.cn>
|
||||
@param status 返回状态
|
||||
@param msg 返回消息
|
||||
@return dict {"status":bool,"msg":string}
|
||||
"""
|
||||
log_message = json.loads(ReadFile(PUBLIC_DICT))
|
||||
keys = log_message.keys()
|
||||
if type(msg) == str:
|
||||
if msg in keys:
|
||||
msg = log_message[msg]
|
||||
for i in range(len(args)):
|
||||
rep = '{' + str(i + 1) + '}'
|
||||
msg = msg.replace(rep, args[i])
|
||||
return {'status': status, 'msg': msg}
|
||||
|
||||
|
||||
def returnMsg(status, msg, args=()):
|
||||
"""
|
||||
@name 取通用dict返回
|
||||
@author hwliang<hwl@bt.cn>
|
||||
@param status 返回状态
|
||||
@param msg 返回消息
|
||||
@return dict {"status":bool,"msg":string}
|
||||
"""
|
||||
return ReturnMsg(status, msg, args)
|
||||
|
||||
|
||||
def RestartServer():
|
||||
ExecShell("sync && init 6 &")
|
||||
return returnMsg(True, 'SYS_REBOOT')
|
||||
12
code/utils/server/public.json
Normal file
12
code/utils/server/public.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ERROR": "操作失败",
|
||||
"SUCCESS": "操作成功",
|
||||
"START": "启动",
|
||||
"STOP": "停止",
|
||||
"OFF": "停用",
|
||||
"ON": "启用",
|
||||
"OPEN": "打开",
|
||||
"CLOSE": "关闭",
|
||||
"SYS_EXEC_SUCCESS": "执行成功!",
|
||||
"SYS_REBOOT": "命令发送成功!"
|
||||
}
|
||||
78
code/utils/server/system.py
Normal file
78
code/utils/server/system.py
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/bin/python
|
||||
#coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | django-vue3-lyadmin
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: lybbn
|
||||
# +-------------------------------------------------------------------
|
||||
# | QQ: 1042594286
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
# ------------------------------
|
||||
# 系统命令封装
|
||||
# ------------------------------
|
||||
|
||||
import platform
|
||||
|
||||
plat = platform.system().lower()
|
||||
if plat == 'windows':
|
||||
import windows as myos
|
||||
else:
|
||||
import linux as myos
|
||||
|
||||
|
||||
class system:
|
||||
|
||||
isWindows = False
|
||||
|
||||
def __init__(self):
|
||||
self.isWindows = self.isWindows()
|
||||
|
||||
def isWindows(self):
|
||||
plat = platform.system().lower()
|
||||
if plat == 'windows':
|
||||
return True
|
||||
return False
|
||||
|
||||
def GetSystemAllInfo(self,isCache=False):
|
||||
"""
|
||||
获取系统所有信息
|
||||
"""
|
||||
data = {}
|
||||
data['mem'] = self.GetMemInfo()
|
||||
data['load_average'] = self.GetLoadAverage()
|
||||
data['network'] = self.GetNetWork()
|
||||
data['cpu'] = self.GetCpuInfo(1)
|
||||
data['disk'] = self.GetDiskInfo()
|
||||
data['time'] = self.GetBootTime()
|
||||
data['system'] = self.GetSystemVersion()
|
||||
data['is_windows'] = self.isWindows
|
||||
return data
|
||||
|
||||
def GetMemInfo(self):
|
||||
memInfo = myos.GetMemInfo()
|
||||
return memInfo
|
||||
|
||||
def GetLoadAverage(self):
|
||||
data = myos.GetLoadAverage()
|
||||
return data
|
||||
|
||||
def GetNetWork(self):
|
||||
data = myos.GetNetWork()
|
||||
return data
|
||||
|
||||
def GetCpuInfo(self,interval=1):
|
||||
data = myos.GetCpuInfo(interval)
|
||||
return data
|
||||
|
||||
def GetBootTime(self):
|
||||
data = myos.GetBootTime()
|
||||
return data
|
||||
|
||||
def GetDiskInfo(self):
|
||||
data = myos.GetDiskInfo()
|
||||
return data
|
||||
|
||||
def GetSystemVersion(self):
|
||||
data = myos.GetSystemVersion()
|
||||
return data
|
||||
818
code/utils/server/windows.py
Normal file
818
code/utils/server/windows.py
Normal file
@@ -0,0 +1,818 @@
|
||||
#!/bin/python
|
||||
# coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | django-vue3-lyadmin
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: lybbn
|
||||
# +-------------------------------------------------------------------
|
||||
# | QQ: 1042594286
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
# ------------------------------
|
||||
# windows系统命令工具类封装
|
||||
# ------------------------------
|
||||
|
||||
import platform
|
||||
import os, time, re, json, socket
|
||||
import psutil
|
||||
import winreg
|
||||
from random import Random
|
||||
from django.core.cache import cache
|
||||
from pathlib import Path
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
|
||||
PUBLIC_DICT = os.path.join(BASE_DIR, 'public.json')
|
||||
|
||||
|
||||
def ReadFile(filename, mode='r'):
|
||||
"""
|
||||
读取文件内容
|
||||
@filename 文件名
|
||||
return string(bin) 若文件不存在,则返回None
|
||||
"""
|
||||
|
||||
import os, chardet
|
||||
if not os.path.exists(filename): return False
|
||||
if not os.path.isfile(filename): return False
|
||||
|
||||
f_body = '';
|
||||
try:
|
||||
fp = open(filename, mode)
|
||||
f_body = fp.read()
|
||||
except:
|
||||
try:
|
||||
fp.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
encoding = 'utf8'
|
||||
fp = open(filename, mode, encoding=encoding)
|
||||
f_body = fp.read()
|
||||
except:
|
||||
try:
|
||||
fp.close()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
encoding = 'gbk'
|
||||
fp = open(filename, mode, encoding=encoding)
|
||||
f_body = fp.read()
|
||||
except:
|
||||
try:
|
||||
fp.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
encoding = 'ansi'
|
||||
fp = open(filename, mode, encoding=encoding)
|
||||
f_body = fp.read()
|
||||
|
||||
try:
|
||||
if f_body[0] == '\ufeff':
|
||||
# 处理带bom格式
|
||||
new_code = chardet.detect(f_body.encode(encoding))["encoding"]
|
||||
f_body = f_body.encode(encoding).decode(new_code);
|
||||
except:
|
||||
pass
|
||||
|
||||
fp.close()
|
||||
return f_body
|
||||
|
||||
|
||||
def readFile(filename, mode='r'):
|
||||
return ReadFile(filename, mode)
|
||||
|
||||
|
||||
def ReadReg(path, key):
|
||||
"""
|
||||
读取注册表
|
||||
@path 注册表路径
|
||||
@key 注册表键值
|
||||
"""
|
||||
import winreg
|
||||
try:
|
||||
newKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path)
|
||||
value, type = winreg.QueryValueEx(newKey, key)
|
||||
return value
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def DelReg(path, key):
|
||||
"""
|
||||
删除注册表
|
||||
@path 注册表路径
|
||||
@key 注册表键值
|
||||
"""
|
||||
import winreg
|
||||
try:
|
||||
newKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path)
|
||||
winreg.DeleteKey(newKey, key)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def WriteReg(path, key, value, type=winreg.REG_SZ):
|
||||
"""
|
||||
写入/创建注册表
|
||||
@path 注册表路径
|
||||
@key 注册表键值
|
||||
@value 注册表值
|
||||
@type 注册表值类型
|
||||
"""
|
||||
import winreg
|
||||
try:
|
||||
newKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_ALL_ACCESS)
|
||||
except:
|
||||
newKey = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, path)
|
||||
try:
|
||||
# SetValue 只支持字符串,其他类型使用SetValueEx
|
||||
if isinstance(value, int):
|
||||
winreg.SetValueEx(newKey, key, 0, winreg.REG_DWORD, value)
|
||||
else:
|
||||
winreg.SetValueEx(newKey, key, 0, type, value)
|
||||
return True
|
||||
except Exception as ex:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_mac_address():
|
||||
"""
|
||||
获取mac
|
||||
"""
|
||||
import uuid
|
||||
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
|
||||
return ":".join([mac[e:e + 2] for e in range(0, 11, 2)])
|
||||
|
||||
|
||||
def process_exists(pname, exe=None):
|
||||
"""
|
||||
检查进程是否存在
|
||||
@pname 进程名称
|
||||
@exe 进程路径
|
||||
"""
|
||||
try:
|
||||
pids = psutil.pids()
|
||||
for pid in pids:
|
||||
try:
|
||||
p = psutil.Process(pid)
|
||||
if p.name() == pname:
|
||||
if not exe:
|
||||
return True;
|
||||
else:
|
||||
if p.exe() == exe: return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
except:
|
||||
return True
|
||||
|
||||
|
||||
def GetRandomString(length):
|
||||
"""
|
||||
@name 取随机字符串
|
||||
@author hwliang<hwl@bt.cn>
|
||||
@param length 要获取的长度
|
||||
@return string(length)
|
||||
"""
|
||||
strings = ''
|
||||
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
|
||||
chrlen = len(chars) - 1
|
||||
random = Random()
|
||||
for i in range(length):
|
||||
strings += chars[random.randint(0, chrlen)]
|
||||
return strings
|
||||
|
||||
|
||||
def Md5(strings):
|
||||
"""
|
||||
@name 生成MD5
|
||||
@author hwliang<hwl@bt.cn>
|
||||
@param strings 要被处理的字符串
|
||||
@return string(32)
|
||||
"""
|
||||
if type(strings) != bytes:
|
||||
strings = strings.encode()
|
||||
import hashlib
|
||||
m = hashlib.md5()
|
||||
m.update(strings)
|
||||
return m.hexdigest()
|
||||
|
||||
|
||||
def md5(strings):
|
||||
return Md5(strings)
|
||||
|
||||
|
||||
def ExecShell(cmdstring, cwd=None, timeout=None, shell=True):
|
||||
"""
|
||||
通过管道执行SHELL
|
||||
@cmdstring 需要执行的cmd命令
|
||||
@cwd 设置工作目录
|
||||
@timeout 超时时间
|
||||
@shell 是否通过shell输出
|
||||
@output 是否通过文件重定向输出结果(当结果数据量过大必须使用此参数,否则造成管道阻塞)
|
||||
"""
|
||||
import shlex
|
||||
import datetime
|
||||
import subprocess
|
||||
import time, tempfile
|
||||
|
||||
a = ''
|
||||
e = ''
|
||||
try:
|
||||
rx = md5(cmdstring)
|
||||
succ_f = tempfile.SpooledTemporaryFile(max_size=4096000, mode='wb+', suffix='_succ', prefix='btex_' + rx)
|
||||
err_f = tempfile.SpooledTemporaryFile(max_size=4096000, mode='wb+', suffix='_err', prefix='btex_' + rx)
|
||||
|
||||
sub = subprocess.Popen(cmdstring, shell=shell, bufsize=128, stdout=succ_f, stderr=err_f)
|
||||
sub.wait()
|
||||
|
||||
err_f.seek(0)
|
||||
succ_f.seek(0)
|
||||
a = succ_f.read()
|
||||
e = err_f.read()
|
||||
|
||||
if not err_f.closed: err_f.close()
|
||||
if not succ_f.closed: succ_f.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
if type(a) == bytes:
|
||||
try:
|
||||
a = a.decode('utf-8')
|
||||
except:
|
||||
try:
|
||||
a = a.decode('gb2312')
|
||||
except:
|
||||
try:
|
||||
a = a.decode('utf-16')
|
||||
except:
|
||||
a = a.decode('gb2312', 'ignore')
|
||||
|
||||
if type(e) == bytes:
|
||||
try:
|
||||
e = e.decode('utf-8')
|
||||
except:
|
||||
try:
|
||||
a = a.decode('utf-16')
|
||||
except:
|
||||
e = e.decode('gb2312', 'ignore')
|
||||
return a, e
|
||||
|
||||
|
||||
class ExcepError(Exception):
|
||||
'''
|
||||
通用异常对像
|
||||
'''
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return ("运行时发生错误: {}".format(repr(self.value)))
|
||||
|
||||
|
||||
def exists_args(args, get):
|
||||
'''
|
||||
@name 检查参数是否存在
|
||||
@author hwliang<2021-06-08>
|
||||
@param args<list or str> 参数列表 允许是列表或字符串
|
||||
@param get<dict_obj> 参数对像
|
||||
@return bool 都存在返回True,否则抛出KeyError异常
|
||||
'''
|
||||
if type(args) == str:
|
||||
args = args.split(',')
|
||||
for arg in args:
|
||||
if not arg in get:
|
||||
raise KeyError('缺少必要参数: {}'.format(arg))
|
||||
return True
|
||||
|
||||
|
||||
# xss 防御
|
||||
def xssencode(text):
|
||||
from html import escape, unescape
|
||||
list = ['`', '~', '&', '#', '*', '$', '@', '<', '>', '\"', '\'', ';', '%', ',', '\\u']
|
||||
ret = []
|
||||
for i in text:
|
||||
if i in list:
|
||||
i = ''
|
||||
ret.append(i)
|
||||
str_convert = ''.join(ret)
|
||||
text2 = escape(str_convert, quote=True)
|
||||
return text2
|
||||
|
||||
|
||||
def path_safe_check(path, force=True):
|
||||
"""
|
||||
校验路径安全
|
||||
@path 校验路径
|
||||
@force
|
||||
"""
|
||||
if len(path) > 256: return False
|
||||
checks = ['..', './', '\\', '%', '$', '^', '&', '*', '~', '#', '"', "'", ';', '|', '{', '}', '`']
|
||||
for c in checks:
|
||||
if path.find(c) != -1: return False
|
||||
if force:
|
||||
rep = r"^[\w\s\.\/-]+$"
|
||||
if not re.match(rep, path): return False
|
||||
return True
|
||||
|
||||
|
||||
def is_ipv4(ip):
|
||||
"""
|
||||
判断是否IPV4地址
|
||||
@ip ip地址
|
||||
return bool
|
||||
"""
|
||||
try:
|
||||
socket.inet_pton(socket.AF_INET, ip)
|
||||
except AttributeError:
|
||||
try:
|
||||
socket.inet_aton(ip)
|
||||
except socket.error:
|
||||
return False
|
||||
return ip.count('.') == 3
|
||||
except socket.error:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_ipv6(ip):
|
||||
"""
|
||||
判断是否IPV6地址
|
||||
@ip ip地址
|
||||
return bool
|
||||
"""
|
||||
try:
|
||||
socket.inet_pton(socket.AF_INET6, ip)
|
||||
except socket.error:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def to_size(size):
|
||||
"""
|
||||
字节单位转换
|
||||
@size 字节大小
|
||||
return 返回带单位的格式(如:1 GB)
|
||||
"""
|
||||
if not size: return '0.00 b'
|
||||
size = float(size)
|
||||
|
||||
d = ('b', 'KB', 'MB', 'GB', 'TB');
|
||||
s = d[0];
|
||||
for b in d:
|
||||
if size < 1024: return ("%.2f" % size) + ' ' + b;
|
||||
size = size / 1024;
|
||||
s = b;
|
||||
return ("%.2f" % size) + ' ' + b;
|
||||
|
||||
|
||||
# 取通用对象
|
||||
re_key_match = re.compile(r'^[\w\s\[\]\-]+$')
|
||||
re_key_match2 = re.compile(r'^\.?__[\w\s[\]\-]+__\.?$')
|
||||
key_filter_list = ['get', 'set', 'get_items', 'exists', '__contains__', '__setitem__', '__getitem__', '__delitem__',
|
||||
'__delattr__', '__setattr__', '__getattr__', '__class__']
|
||||
|
||||
|
||||
class dict_obj:
|
||||
def __contains__(self, key):
|
||||
return getattr(self, key, None)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if key in key_filter_list:
|
||||
raise ExcepError("错误的字段名")
|
||||
if not re_key_match.match(key) or re_key_match2.match(key):
|
||||
raise ExcepError("错误的字段名")
|
||||
setattr(self, key, value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return getattr(self, key, None)
|
||||
|
||||
def __delitem__(self, key):
|
||||
delattr(self, key)
|
||||
|
||||
def __delattr__(self, key):
|
||||
delattr(self, key)
|
||||
|
||||
def get_items(self):
|
||||
return self
|
||||
|
||||
def exists(self, keys):
|
||||
return exists_args(keys, self)
|
||||
|
||||
def set(self, key, value):
|
||||
if not isinstance(value, str) or not isinstance(key, str): return False
|
||||
if key in key_filter_list:
|
||||
raise ExcepError("错误的字段名")
|
||||
if not re_key_match.match(key) or re_key_match2.match(key):
|
||||
raise ExcepError("错误的字段名")
|
||||
return setattr(self, key, value)
|
||||
|
||||
def get(self, key, default='', format='', limit=[]):
|
||||
'''
|
||||
@name 获取指定参数
|
||||
@param key<string> 参数名称,允许在/后面限制参数格式,请参考参数值格式(format)
|
||||
@param default<string> 默认值,默认空字符串
|
||||
@param format<string> 参数值格式(int|str|port|float|json|xss|path|url|ip|ipv4|ipv6|letter|mail|phone|正则表达式|>1|<1|=1),默认为空
|
||||
@param limit<list> 限制参数值内容
|
||||
@param return mixed
|
||||
'''
|
||||
if key.find('/') != -1:
|
||||
key, format = key.split('/')
|
||||
result = getattr(self, key, default)
|
||||
if isinstance(result, str): result = result.strip()
|
||||
if format:
|
||||
if format in ['str', 'string', 's']:
|
||||
result = str(result)
|
||||
elif format in ['int', 'd']:
|
||||
try:
|
||||
result = int(result)
|
||||
except:
|
||||
raise ValueError("参数:{},要求int类型数据".format(key))
|
||||
elif format in ['float', 'f']:
|
||||
try:
|
||||
result = float(result)
|
||||
except:
|
||||
raise ValueError("参数:{},要求float类型数据".format(key))
|
||||
elif format in ['json', 'j']:
|
||||
try:
|
||||
result = json.loads(result)
|
||||
except:
|
||||
raise ValueError("参数:{}, 要求JSON字符串".format(key))
|
||||
elif format in ['xss', 'x']:
|
||||
result = xssencode(result)
|
||||
elif format in ['path', 'p']:
|
||||
if not path_safe_check(result):
|
||||
raise ValueError("参数:{},要求正确的路径格式".format(key))
|
||||
result = result.replace('//', '/')
|
||||
elif format in ['url', 'u']:
|
||||
regex = re.compile(
|
||||
r'^(?:http|ftp)s?://'
|
||||
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
|
||||
r'localhost|'
|
||||
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
|
||||
r'(?::\d+)?'
|
||||
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
|
||||
if not re.match(regex, result):
|
||||
raise ValueError('参数:{},要求正确的URL格式'.format(key))
|
||||
elif format in ['ip', 'ipaddr', 'i', 'ipv4', 'ipv6']:
|
||||
if format == 'ipv4':
|
||||
if not is_ipv4(result):
|
||||
raise ValueError('参数:{},要求正确的ipv4地址'.format(key))
|
||||
elif format == 'ipv6':
|
||||
if not is_ipv6(result):
|
||||
raise ValueError('参数:{},要求正确的ipv6地址'.format(key))
|
||||
else:
|
||||
if not is_ipv4(result) and not is_ipv6(result):
|
||||
raise ValueError('参数:{},要求正确的ipv4/ipv6地址'.format(key))
|
||||
elif format in ['w', 'letter']:
|
||||
if not re.match(r'^\w+$', result):
|
||||
raise ValueError('参数:{},要求只能是英文字母或数据组成'.format(key))
|
||||
elif format in ['email', 'mail', 'm']:
|
||||
if not re.match(r"^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$)", result):
|
||||
raise ValueError("参数:{},要求正确的邮箱地址格式".format(key))
|
||||
elif format in ['phone', 'mobile', 'p']:
|
||||
if not re.match("^[0-9]{11,11}$", result):
|
||||
raise ValueError("参数:{},要求手机号码格式".format(key))
|
||||
elif format in ['port']:
|
||||
result_port = int(result)
|
||||
if result_port > 65535 or result_port < 0:
|
||||
raise ValueError("参数:{},要求端口号为0-65535".format(key))
|
||||
result = result_port
|
||||
elif re.match(r"^[<>=]\d+$", result):
|
||||
operator = format[0]
|
||||
length = int(format[1:].strip())
|
||||
result_len = len(result)
|
||||
error_obj = ValueError("参数:{},要求长度为{}".format(key, format))
|
||||
if operator == '=':
|
||||
if result_len != length:
|
||||
raise error_obj
|
||||
elif operator == '>':
|
||||
if result_len < length:
|
||||
raise error_obj
|
||||
else:
|
||||
if result_len > length:
|
||||
raise error_obj
|
||||
elif format[0] in ['^', '(', '[', '\\', '.'] or format[-1] in ['$', ')', ']', '+', '}']:
|
||||
if not re.match(format, result):
|
||||
raise ValueError("指定参数格式不正确, {}:{}".format(key, format))
|
||||
|
||||
if limit:
|
||||
if not result in limit:
|
||||
raise ValueError("指定参数值范围不正确, {}:{}".format(key, limit))
|
||||
return result
|
||||
|
||||
|
||||
def is_64bitos():
|
||||
"""
|
||||
判断是否x64系统(windows)
|
||||
"""
|
||||
bites = {'AMD64': 64, 'x86_64': 64, 'i386': 32, 'x86': 32}
|
||||
info = platform.uname()
|
||||
if bites.get(info.machine) == 64:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_registry_value(key, subkey, value):
|
||||
"""
|
||||
读取注册表信息
|
||||
@key 注册表类型
|
||||
@subkey 注册表路径
|
||||
@value 注册表具体key值
|
||||
"""
|
||||
key = getattr(winreg, key)
|
||||
handle = winreg.OpenKey(key, subkey)
|
||||
(value, type) = winreg.QueryValueEx(handle, value)
|
||||
return value
|
||||
|
||||
|
||||
def GetSystemVersion():
|
||||
"""
|
||||
取操作系统版本
|
||||
"""
|
||||
try:
|
||||
key = 'lybbn_sys_version'
|
||||
version = cache.get(key)
|
||||
if version: return version
|
||||
bit = 'x86';
|
||||
if is_64bitos(): bit = 'x64'
|
||||
|
||||
def get(key):
|
||||
return get_registry_value("HKEY_LOCAL_MACHINE", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", key)
|
||||
|
||||
os = get("ProductName")
|
||||
build = get("CurrentBuildNumber")
|
||||
|
||||
version = "%s (build %s) %s (Py%s)" % (os, build, bit, platform.python_version())
|
||||
cache.set(key, version, 10000)
|
||||
return version
|
||||
except Exception as ex:
|
||||
version = "未知系统版本."
|
||||
cache.set(key, version, 10000)
|
||||
return version
|
||||
|
||||
|
||||
# 取负载信息
|
||||
def GetLoadAverage():
|
||||
data = {}
|
||||
data['one'] = 0
|
||||
data['five'] = 0
|
||||
data['fifteen'] = 0
|
||||
data['max'] = psutil.cpu_count() * 2
|
||||
data['limit'] = data['max']
|
||||
data['safe'] = data['max'] * 0.75
|
||||
data['percent'] = 0
|
||||
return data
|
||||
|
||||
|
||||
# 取内存信息
|
||||
def GetMemInfo():
|
||||
mem = psutil.virtual_memory()
|
||||
memInfo = {}
|
||||
memInfo['percent'] = mem.percent
|
||||
memInfo['total'] = round(float(mem.total) / 1024 / 1024 / 1024, 2)
|
||||
memInfo['free'] = round(float(mem.free) / 1024 / 1024 / 1024, 2)
|
||||
memInfo['used'] = round(float(mem.used) / 1024 / 1024 / 1024, 2)
|
||||
return memInfo
|
||||
|
||||
|
||||
# 获取网卡信息
|
||||
def GetNetWork():
|
||||
cache_timeout = 86400
|
||||
otime = cache.get("otime")
|
||||
ntime = time.time()
|
||||
networkInfo = {}
|
||||
networkInfo['network'] = {}
|
||||
networkInfo['upTotal'] = 0
|
||||
networkInfo['downTotal'] = 0
|
||||
networkInfo['up'] = 0
|
||||
networkInfo['down'] = 0
|
||||
networkInfo['downPackets'] = 0
|
||||
networkInfo['upPackets'] = 0
|
||||
networkIo_list = psutil.net_io_counters(pernic=True)
|
||||
|
||||
for net_key in networkIo_list.keys():
|
||||
if net_key.find('Loopback') >= 0 or net_key.find('Teredo') >= 0 or net_key.find('isatap') >= 0: continue
|
||||
|
||||
networkIo = networkIo_list[net_key][:4]
|
||||
up_key = "{}_up".format(net_key)
|
||||
down_key = "{}_down".format(net_key)
|
||||
otime_key = "otime"
|
||||
|
||||
if not otime:
|
||||
otime = time.time()
|
||||
|
||||
cache.set(up_key, networkIo[0], cache_timeout)
|
||||
cache.set(down_key, networkIo[1], cache_timeout)
|
||||
cache.set(otime_key, otime, cache_timeout)
|
||||
|
||||
networkInfo['network'][net_key] = {}
|
||||
up = cache.get(up_key)
|
||||
down = cache.get(down_key)
|
||||
if not up:
|
||||
up = networkIo[0]
|
||||
if not down:
|
||||
down = networkIo[1]
|
||||
networkInfo['network'][net_key]['upTotal'] = networkIo[0]
|
||||
networkInfo['network'][net_key]['downTotal'] = networkIo[1]
|
||||
try:
|
||||
networkInfo['network'][net_key]['up'] = round(float(networkIo[0] - up) / 1024 / (ntime - otime), 2)
|
||||
networkInfo['network'][net_key]['down'] = round(float(networkIo[1] - down) / 1024 / (ntime - otime), 2)
|
||||
except:
|
||||
networkInfo['up'] = 0
|
||||
networkInfo['down'] = 0
|
||||
|
||||
networkInfo['network'][net_key]['up'] = 0
|
||||
networkInfo['network'][net_key]['down'] = 0
|
||||
|
||||
networkInfo['network'][net_key]['downPackets'] = networkIo[3]
|
||||
networkInfo['network'][net_key]['upPackets'] = networkIo[2]
|
||||
|
||||
networkInfo['upTotal'] += networkInfo['network'][net_key]['upTotal']
|
||||
networkInfo['downTotal'] += networkInfo['network'][net_key]['downTotal']
|
||||
networkInfo['up'] += networkInfo['network'][net_key]['up']
|
||||
networkInfo['down'] += networkInfo['network'][net_key]['down']
|
||||
networkInfo['downPackets'] += networkInfo['network'][net_key]['downPackets']
|
||||
networkInfo['upPackets'] += networkInfo['network'][net_key]['upPackets']
|
||||
|
||||
cache.set(up_key, networkIo[0], cache_timeout)
|
||||
cache.set(down_key, networkIo[1], cache_timeout)
|
||||
cache.set(otime_key, time.time(), cache_timeout)
|
||||
|
||||
networkInfo['up'] = round(float(networkInfo['up']), 2)
|
||||
networkInfo['down'] = round(float(networkInfo['down']), 2)
|
||||
networkInfo['iostat'] = {}
|
||||
|
||||
return networkInfo
|
||||
|
||||
|
||||
# 取系统启动时间
|
||||
def GetBootTime():
|
||||
key = 'lybbn_sys_time'
|
||||
sys_time = cache.get(key)
|
||||
if sys_time: return sys_time
|
||||
import math
|
||||
tStr = time.time() - psutil.boot_time()
|
||||
min = tStr / 60;
|
||||
hours = min / 60;
|
||||
days = math.floor(hours / 24);
|
||||
hours = math.floor(hours - (days * 24));
|
||||
min = math.floor(min - (days * 60 * 24) - (hours * 60));
|
||||
sys_time = "{}天".format(int(days))
|
||||
cache.set(key, sys_time, 1800)
|
||||
return sys_time
|
||||
|
||||
|
||||
def getCpuType():
|
||||
"""
|
||||
取CPU类型
|
||||
"""
|
||||
try:
|
||||
cpuType = ReadReg(r'HARDWARE\DESCRIPTION\System\CentralProcessor\0', 'ProcessorNameString')
|
||||
except:
|
||||
cpuType = ''
|
||||
return cpuType;
|
||||
|
||||
|
||||
# 取CPU详细信息
|
||||
def GetCpuInfo(interval=1):
|
||||
cpuCount = psutil.cpu_count()
|
||||
cpuNum = psutil.cpu_count(logical=False)
|
||||
|
||||
import threading
|
||||
p = threading.Thread(target=get_cpu_percent_thead, args=(interval,))
|
||||
# p.setDaemon(True)
|
||||
p.start()
|
||||
|
||||
used = cache.get('lybbn_cpu_used_all')
|
||||
if not used: used = get_cpu_percent_thead(interval)
|
||||
|
||||
used_all = psutil.cpu_percent(percpu=True)
|
||||
|
||||
used_total = 0
|
||||
for x in used_all: used_total += x
|
||||
|
||||
ret = ExecShell('wmic cpu get NumberOfCores')[0]
|
||||
cpuW = 0
|
||||
|
||||
arrs = ret.strip().split('\r\n')
|
||||
for x in arrs:
|
||||
val = x.strip()
|
||||
if not val: continue;
|
||||
try:
|
||||
val = int(val)
|
||||
cpuW += 1;
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
cpu_name = '{} * {}'.format(
|
||||
ReadReg(r'HARDWARE\DESCRIPTION\System\CentralProcessor\0', 'ProcessorNameString').strip(), cpuW);
|
||||
except:
|
||||
cpu_name = ''
|
||||
|
||||
tmp = 0
|
||||
if cpuW: tmp = cpuNum / cpuW
|
||||
|
||||
used = 0
|
||||
if used_total: used = round(used_total / cpuCount, 2)
|
||||
return used, cpuCount, used_all, cpu_name, tmp, cpuW
|
||||
|
||||
|
||||
def get_cpu_percent_thead(interval):
|
||||
used = psutil.cpu_percent(interval)
|
||||
cache.set('lybbn_cpu_used_all', used, 10)
|
||||
return used
|
||||
|
||||
|
||||
# 取磁盘分区信息
|
||||
def GetDiskInfo():
|
||||
key = 'lybbn_sys_disk'
|
||||
diskInfo = cache.get(key)
|
||||
if diskInfo: return diskInfo
|
||||
try:
|
||||
diskIo = psutil.disk_partitions();
|
||||
except:
|
||||
import string
|
||||
diskIo = []
|
||||
for c in string.ascii_uppercase:
|
||||
disk = c + ':'
|
||||
if os.path.isdir(disk):
|
||||
data = dict_obj()
|
||||
data['mountpoint'] = disk + '/'
|
||||
diskIo.append(data)
|
||||
|
||||
diskInfo = []
|
||||
for disk in diskIo:
|
||||
try:
|
||||
tmp = {}
|
||||
tmp['path'] = disk.mountpoint.replace("\\", "/")
|
||||
usage = psutil.disk_usage(disk.mountpoint)
|
||||
tmp['size'] = [to_size(usage.total), to_size(usage.used), to_size(usage.free), usage.percent]
|
||||
tmp['inodes'] = False
|
||||
diskInfo.append(tmp)
|
||||
except:
|
||||
pass
|
||||
cache.set(key, diskInfo, 10)
|
||||
return diskInfo
|
||||
|
||||
|
||||
def GetMsg(key, args=()):
|
||||
"""
|
||||
根据key获取内置消息返回
|
||||
@key 指定消息的key
|
||||
@args 消息内容中的参数
|
||||
"""
|
||||
try:
|
||||
log_message = json.loads(ReadFile(PUBLIC_DICT));
|
||||
keys = log_message.keys();
|
||||
msg = None;
|
||||
if key in keys:
|
||||
msg = log_message[key];
|
||||
for i in range(len(args)):
|
||||
rep = '{' + str(i + 1) + '}'
|
||||
msg = msg.replace(rep, args[i]);
|
||||
return msg;
|
||||
except:
|
||||
return key
|
||||
|
||||
|
||||
def getMsg(key, args=()):
|
||||
return GetMsg(key, args)
|
||||
|
||||
|
||||
def ReturnMsg(status, msg, args=()):
|
||||
"""
|
||||
@name 取通用dict返回
|
||||
@param status 返回状态
|
||||
@param msg 返回消息
|
||||
@return dict {"status":bool,"msg":string}
|
||||
"""
|
||||
log_message = json.loads(ReadFile(PUBLIC_DICT));
|
||||
|
||||
keys = log_message.keys();
|
||||
if type(msg) == str:
|
||||
if msg in keys:
|
||||
msg = log_message[msg];
|
||||
for i in range(len(args)):
|
||||
rep = '{' + str(i + 1) + '}'
|
||||
msg = msg.replace(rep, args[i]);
|
||||
return {'status': status, 'msg': msg}
|
||||
|
||||
|
||||
def returnMsg(status, msg, args=()):
|
||||
return ReturnMsg(status, msg, args)
|
||||
|
||||
|
||||
# 重启系统
|
||||
def RestartServer():
|
||||
try:
|
||||
os.system("shutdown /r /f /t 0");
|
||||
except:
|
||||
pass
|
||||
return returnMsg(True, 'SYS_REBOOT');
|
||||
78
code/utils/system.py
Normal file
78
code/utils/system.py
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/bin/python
|
||||
#coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | django-vue3-lyadmin
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: lybbn
|
||||
# +-------------------------------------------------------------------
|
||||
# | QQ: 1042594286
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
# ------------------------------
|
||||
# 系统命令封装
|
||||
# ------------------------------
|
||||
|
||||
import platform
|
||||
|
||||
plat = platform.system().lower()
|
||||
if plat == 'windows':
|
||||
from .server import windows as myos
|
||||
else:
|
||||
from .server import linux as myos
|
||||
|
||||
|
||||
class system:
|
||||
|
||||
isWindows = False
|
||||
|
||||
def __init__(self):
|
||||
self.isWindows = self.isWindows()
|
||||
|
||||
def isWindows(self):
|
||||
plat = platform.system().lower()
|
||||
if plat == 'windows':
|
||||
return True
|
||||
return False
|
||||
|
||||
def GetSystemAllInfo(self,isCache=False):
|
||||
"""
|
||||
获取系统所有信息
|
||||
"""
|
||||
data = {}
|
||||
data['mem'] = self.GetMemInfo()
|
||||
data['load_average'] = self.GetLoadAverage()
|
||||
data['network'] = self.GetNetWork()
|
||||
data['cpu'] = self.GetCpuInfo(1)
|
||||
data['disk'] = self.GetDiskInfo()
|
||||
data['time'] = self.GetBootTime()
|
||||
data['system'] = self.GetSystemVersion()
|
||||
data['is_windows'] = self.isWindows
|
||||
return data
|
||||
|
||||
def GetMemInfo(self):
|
||||
memInfo = myos.GetMemInfo()
|
||||
return memInfo
|
||||
|
||||
def GetLoadAverage(self):
|
||||
data = myos.GetLoadAverage()
|
||||
return data
|
||||
|
||||
def GetNetWork(self):
|
||||
data = myos.GetNetWork()
|
||||
return data
|
||||
|
||||
def GetCpuInfo(self,interval=1):
|
||||
data = myos.GetCpuInfo(interval)
|
||||
return data
|
||||
|
||||
def GetBootTime(self):
|
||||
data = myos.GetBootTime()
|
||||
return data
|
||||
|
||||
def GetDiskInfo(self):
|
||||
data = myos.GetDiskInfo()
|
||||
return data
|
||||
|
||||
def GetSystemVersion(self):
|
||||
data = myos.GetSystemVersion()
|
||||
return data
|
||||
67
code/utils/usual.py
Normal file
67
code/utils/usual.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/4 23:32
|
||||
# @Author : 臧成龙
|
||||
# @FileName: usual.py
|
||||
# @Software: PyCharm
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from fuadmin.settings import SECRET_KEY
|
||||
from system.models import Dept
|
||||
|
||||
from .fu_jwt import FuJwt
|
||||
|
||||
|
||||
def get_user_info_from_token(request):
|
||||
token = request.META.get("HTTP_AUTHORIZATION")
|
||||
token = token.split(" ")[1]
|
||||
jwt = FuJwt(SECRET_KEY)
|
||||
value = jwt.decode(SECRET_KEY, token)
|
||||
user_info = value.payload
|
||||
return user_info
|
||||
|
||||
|
||||
def get_dept(dept_id: int, dept_all_list=None, dept_list=None):
|
||||
"""
|
||||
递归获取部门的所有下级部门
|
||||
:param dept_id: 需要获取的部门id
|
||||
:param dept_all_list: 所有部门列表
|
||||
:param dept_list: 递归部门list
|
||||
:return:
|
||||
"""
|
||||
if not dept_all_list:
|
||||
dept_all_list = Dept.objects.all().values('id', 'parent')
|
||||
if dept_list is None:
|
||||
dept_list = [dept_id]
|
||||
for ele in dept_all_list:
|
||||
if ele.get('parent') == dept_id:
|
||||
dept_list.append(ele.get('id'))
|
||||
get_dept(ele.get('id'), dept_all_list, dept_list)
|
||||
return list(set(dept_list))
|
||||
|
||||
|
||||
def insert_content_after_line(filename, target_line, content_to_insert):
|
||||
try:
|
||||
# 打开文件并读取内容
|
||||
with open(filename, 'r', encoding='utf-8') as file:
|
||||
lines = file.readlines()
|
||||
|
||||
# 找到目标行的索引位置
|
||||
target_line_index = None
|
||||
for i, line in enumerate(lines):
|
||||
if target_line in line:
|
||||
target_line_index = i
|
||||
break
|
||||
|
||||
if target_line_index is not None:
|
||||
# 在目标行后面插入内容
|
||||
lines.insert(target_line_index + 1, content_to_insert + '\n')
|
||||
|
||||
# 将更新后的内容写回文件中
|
||||
with open(filename, 'w', encoding='utf-8') as file:
|
||||
file.writelines(lines)
|
||||
print("内容已成功插入到目标行后。")
|
||||
else:
|
||||
print("未找到目标行。")
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"文件 '{filename}' 未找到。")
|
||||
85
code/utils/vdes_curd.py
Normal file
85
code/utils/vdes_curd.py
Normal file
@@ -0,0 +1,85 @@
|
||||
import logging
|
||||
from django.db import transaction, IntegrityError
|
||||
from time import perf_counter
|
||||
from system.models import ShipLatestStatus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def safe_bulk_upsert(ship_data_list):
|
||||
start_time = perf_counter()
|
||||
created = updated = 0
|
||||
try:
|
||||
# 验证数据格式
|
||||
valid_data = []
|
||||
for data in ship_data_list:
|
||||
if 'mmsi' not in data:
|
||||
logger.warning("Missing MMSI in data: %s", data)
|
||||
continue
|
||||
valid_data.append(data)
|
||||
|
||||
if not valid_data:
|
||||
return 0, 0
|
||||
|
||||
# 执行批量操作
|
||||
with transaction.atomic():
|
||||
current_mmsi = {item['mmsi'] for item in valid_data}
|
||||
existing_map = ShipLatestStatus.objects.in_bulk(
|
||||
current_mmsi, field_name='mmsi'
|
||||
)
|
||||
|
||||
to_create = []
|
||||
to_update = []
|
||||
|
||||
for data in valid_data:
|
||||
mmsi = data['mmsi']
|
||||
if mmsi in existing_map:
|
||||
ship = existing_map[mmsi]
|
||||
for field, value in data.items():
|
||||
setattr(ship, field, value)
|
||||
to_update.append(ship)
|
||||
else:
|
||||
to_create.append(ShipLatestStatus(**data))
|
||||
|
||||
if to_create:
|
||||
created = len(ShipLatestStatus.objects.bulk_create(to_create))
|
||||
|
||||
if to_update:
|
||||
update_fields = [f.name for f in ShipLatestStatus._meta.fields
|
||||
if f.name not in ['id', 'mmsi']]
|
||||
ShipLatestStatus.objects.bulk_update(to_update, update_fields)
|
||||
updated = len(to_update)
|
||||
|
||||
except IntegrityError as e:
|
||||
logger.error("Integrity error in bulk upsert: %s", str(e))
|
||||
# 这里可以添加重试或更精细的错误处理
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.exception("Unexpected error in bulk upsert")
|
||||
raise
|
||||
|
||||
elapsed = (perf_counter() - start_time) * 1000
|
||||
logger.info(f"Processed {len(valid_data)} ships in {elapsed:.2f}ms: "
|
||||
f"{created} created, {updated} updated")
|
||||
|
||||
return created, updated
|
||||
|
||||
|
||||
|
||||
def get_nearby_ships(latitude: float, longitude: float, radius_nm: float = 20.0):
|
||||
# 将海里转换为公里(Haversine公式使用公里)
|
||||
radius_km = radius_nm * 1.852
|
||||
|
||||
# 使用原生SQL计算
|
||||
return ShipLatestStatus.objects.raw("""
|
||||
SELECT *,
|
||||
(6371 * acos(
|
||||
cos(radians(%s))
|
||||
* cos(radians(latitude))
|
||||
* cos(radians(longitude) - radians(%s))
|
||||
+ sin(radians(%s))
|
||||
* sin(radians(latitude))
|
||||
)) AS distance
|
||||
FROM ship_latest_status
|
||||
HAVING distance <= %s
|
||||
ORDER BY distance
|
||||
""", [latitude, longitude, latitude, radius_km])
|
||||
Reference in New Issue
Block a user