Compare commits

...

38 Commits

Author SHA1 Message Date
周平 103ee99d26 添加rsa函数,oaep填充 12 months ago
周平 171f891d63 更新docker文档 1 year ago
周平 1c04b32028 修复业务模型无法更新的bug 1 year ago
周平 a034aa4bf6 更行代码 1 year ago
周平 6931e7914e 更新代码 1 year ago
周平 baef5ee8a9 修复bug 1 year ago
周平 734ba9818c 完成服务器模块的功能 1 year ago
周平 af2d037577 企业设备功能接口 1 year ago
周平 7a765dd1ac 更新代码 1 year ago
周平 9fe7ff8382 修复bug 1 year ago
周平 5c524390b9 更新代码 1 year ago
周平 ada8cb2053 更新代码 1 year ago
周平 7a44899f2f 设备状态模块功能 1 year ago
周平 bd92e72d5f 联调bug修复 1 year ago
周平 7a4212140f 功能联调 1 year ago
周平 bc77bc5ce9 更新首页统计数据实现 1 year ago
周平 b82bccc95d 更新代码,优化异步连接 1 year ago
周平 c1f4dc7778 添加异步数据库处理 1 year ago
周平 85b1e044d0 添加告警配置 1 year ago
周平 469b04438c 更新代码 1 year ago
周平 1bb705c5c4 获取节点下所有设备的简单信息, id, name, classification_name 1 year ago
周平 6a4100d612 完成企业节点模型部署的功能编写 1 year ago
周平 6b10daf0fe 更新代码 1 year ago
周平 46e8e308f2 更新代码 1 year ago
周平 ead7b8fc94 update code 1 year ago
周平 809ef6f7b4 更新代码,完成企业模型的功能实现 1 year ago
周平 85daf8c320 更新代码,调整逻辑结构 1 year ago
周平 48c14a9651 完成设备功能自测 1 year ago
周平 a2abb09955 更新代码结构 1 year ago
周平 c5e4d44827 更新代码 1 year ago
周平 b7a3c0fb94 添加企业设备模块 1 year ago
周平 57ce3d896e 完成企业节点的增删改查接口 1 year ago
周平 ae4ac605c0 代码更新 1 year ago
周平 9f25d21303 添加镜像仓库代码 1 year ago
周平 7e77fbe67b 完成模型功能的api 1 year ago
周平 933b1878af 模型版本的部分功能 1 year ago
周平 eee871a2da 1、完成首页的接口开发
2、完成文件上传下载
3、模型的部分接口实现
1 year ago
周平 2406ef5c95 添加工程文件 1 year ago

@ -0,0 +1,55 @@
FROM 192.168.10.94:5000/generalai:v1
# Set timezone
ENV TZ=Asia/Shanghai
# Install necessary packages
RUN apt-get update && apt-get install -y \
# python3-pip \
# mysql-client \
default-mysql-client \
redis-tools \
&& apt-get clean
# ENV APP_HOME=/app/lemon
RUN mkdir -p /app/lemon
RUN mkdir -p /app/fileupload
RUN mkdir -p /app/log
# Set working directory
WORKDIR /app/lemon
# Copy application files
COPY website/ ./website/
COPY dist/ ./dist/
# COPY mysql_app.sql /app/lemon/
# Copy init script and make it executable
# COPY mysql_init.sh /app/lemon/
# RUN chmod +x /app/lemon/mysql_init.sh
# Install Python dependencies
# RUN pip3 install -r /app/lemon/website/requirements.txt
# Copy supervisor configuration
COPY supervisor.conf /etc/supervisor/conf.d/lemon.conf
# Copy nginx configuration
# COPY nginx.conf /etc/nginx/sites-available/lemon
# RUN ln -s /etc/nginx/sites-available/lemon /etc/nginx/sites-enabled/
# COPY entrypoint.sh /app/lemon/entrypoint.sh
# RUN chmod +x /app/lemon/entrypoint.sh
COPY entrypoint.sh /
RUN sed -i 's/\r//' /entrypoint.sh
RUN chmod +x /entrypoint.sh
# Expose port
EXPOSE 80
# VOLUME /data
# Start services
# ENTRYPOINT ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/supervisord.conf"]
ENTRYPOINT []

@ -0,0 +1,295 @@
## 工程目录
```commandline
├── Dockerfile
├── docker-compose.yml
├── entrypoint.sh
├── mysql_app.sql
├── mysql_auth.sql
├── nginx.conf
├── supervisord.conf
└── website
├── db
├── handlers
├── service
├── util
├── app.py
├── url.py
├── handler.py
└── dist
├── index.html
├── ...
```
## Dockerfile
```bash
# generalai:v1是镜像名192.168.10.94:5000是镜像仓库docker registry本地地址
FROM 192.168.10.94:5000/generalai:v1
# Set timezone
ENV TZ=Asia/Shanghai
# Install necessary packages
RUN apt-get update && apt-get install -y \
# python3-pip \
# mysql-client \
default-mysql-client \
redis-tools \
&& apt-get clean
RUN mkdir -p /app/lemon
RUN mkdir -p /app/fileupload
RUN mkdir -p /app/log
# Set working directory设置工作目录之后COPY命令的目标目录就是workdir指定的目录
WORKDIR /app/lemon
# Copy application files
# 拷贝工程文件,将工程文件拷贝到镜像的指定目录下,如果拷贝完整目录,需要使用"website/",不能使用"website/*"
COPY website/ ./website/
COPY dist/ ./dist/
# COPY mysql_app.sql /app/lemon/
# Install Python dependencies
# RUN pip3 install -r /app/lemon/website/requirements.txt
# Copy supervisor configuration
COPY supervisor.conf /etc/supervisor/conf.d/lemon.conf
# Copy nginx configuration
# COPY nginx.conf /etc/nginx/sites-available/lemon
# RUN ln -s /etc/nginx/sites-available/lemon /etc/nginx/sites-enabled/
# COPY entrypoint.sh /app/lemon/entrypoint.sh
# RUN chmod +x /app/lemon/entrypoint.sh
# 拷贝entrypoint.sh文件将entrypoint.sh文件拷贝到镜像根目录下
COPY entrypoint.sh /
RUN sed -i 's/\r//' /entrypoint.sh
RUN chmod +x /entrypoint.sh
# Expose port
EXPOSE 80
# VOLUME /data
# Start services
# ENTRYPOINT留空入口文件entrypoint.sh配置在docker-compose.yml中由docker-compose执行
ENTRYPOINT []
```
编译
```shell
# docker build便于移植
docker build -t lemon:latest .
docker images 查看生成的镜像 lemon:latest
```
## docker-compose.yml
```shell
version: "3.3"
services:
web:
# build: .
working_dir: /app/lemon/website # 设置工作目录不设置会导致程序import错误
image: lemon:latest
tty:
true
container_name: lemon_web
privileged: true
# ports:
# - "80:80"
# - "8989:8989"
volumes:
# - /data/app/lemon:/app/lemon # 如果由相同的设置则会覆盖Dockfile中COPY的文件
- /data/app/log:/app/log
- /data/app/fileupload:/app/fileupload
environment:
- TZ=Asia/Shanghai
networks: # 配置网络
- lemon_network
depends_on:
- mysql
- redis
# 执行entrypoint.sh文件并执行supervisord启动服务
command: sh -c "/entrypoint.sh && /usr/bin/supervisord -n -c /etc/supervisor/supervisord.conf"
nginx:
image: nginx:latest
container_name: lemon_nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/lemon.conf # 挂载配置文件
- ./dist:/app/lemon/dist # 挂载前端目录
depends_on:
- web
networks:
- lemon_network
mysql:
image: mysql:8.0
container_name: lemon_mysql
ports:
- "13306:3306"
environment:
MYSQL_ROOT_PASSWORD: SghjdA887# # 设置root用户密码
MYSQL_DATABASE: aiplatform # 设置数据库
volumes:
- /data/mysql_data:/var/lib/mysql # 挂载数据目录
- ./mysql_auth.sql:/docker-entrypoint-initdb.d/auth.sql # 挂载权限初始化sql
- ./mysql_app.sql:/docker-entrypoint-initdb.d/app.sql # 挂载数据库初始化sql
networks:
- lemon_network
redis:
image: redis:latest
container_name: lemon_redis
ports:
- "16379:6379"
command: redis-server --requirepass hgkiYY87 # 设置密码
volumes:
- /data/redis_data:/data
networks:
- lemon_network
#volumes:
# mysql_data:
# redis_data:
networks:
lemon_network:
driver: bridge # 如果不设置则默认使用bridge
```
## entrypoint.sh
```bash
#!/bin/bash
# 初始化数据库mysql_app.sql通过挂载的方式初始化所以本处的代码注释掉了
#if [ -f "/app/lemon/mysql_app.sql" ];then
# echo "Initializing MySQL database..."
# # 初始化数据库, -p 表示使用密码登录,请使用实际的密码
# mysql -h lemon_mysql -u root -pxxx aiplatform < /app/lemon/mysql_app.sql
# echo "MySQL initialization completed."
#else
# echo "Mysql init errorinit sql file does not exist"
#fi
# 更新配置文件
if [ ! -f "/app/lemon/website/settings_local.py" ];then
echo "@@init settings_local file"
cat > /app/lemon/website/settings_local.py << EOF
mysql_app = {
"host": "lemon_mysql:3306", # docker-compose中设置了相同的networks可以直接使用lemon_mysql:3306访问
"database": "aiplatform",
"user": "root",
"password": "xxx", # 请替换为实际的密码
"time_zone": "+8:00"
}
redis_app = ("lemon_redis", 6379, 0, "xxxx") # 请替换为实际的密码
EOF
fi
# 设置要列出的目录
dir_path="/app/lemon"
# 函数,递归打印目录结构
function print_dir_structure() {
local dir="$1"
local indent="$2"
# 打印当前目录
echo "$indent$(basename "$dir")"
# 遍历目录下的所有项目
for item in "$dir"/*
do
# 如果是目录,递归打印
if [ -d "$item" ]; then
print_dir_structure "$item" "$indent "
# 如果是文件,打印文件名
elif [ -f "$item" ]; then
echo "$indent $(basename "$item")"
fi
done
}
# print_dir_structure "$dir_path" "-"
# 启动Tornado应用程序
#echo "启动Tornado应用程序"
#if [ -f "/app/lemon/website/app.py" ];then
# echo "@@app.py存在"
# python /app/lemon/website/app.py --port=8889 >> /app/log/lemon.log 2>&1
#else
# echo "app.py文件错误文件不存在"
#fi
echo "启动完成"
```
## mysql初始化
```
mysql_app.sql, 数据库的结构,以及默认账号的初始化
mysql_auth.sql 修改密码加密规格,修改密码
```
## nginx
```
server {
listen 80;
server_name 192.168.10.94;
root /app/lemon/dist; # 静态文件目录,请替换为实际的路径
location ^~ /api {
proxy_pass http://lemon_web:8989; # lemon_web为容器的默认host
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
try_files $uri $uri/ /index.html;
}
}
```
## supervisor
```
[program:lemon]
directory=/app/lemon/website
command=python /app/lemon/website/app.py --port=8989
numprocs=1
process_name=%(program_name)s_%(process_num)02d
autorestart=true
autostart=true
startsecs=5
redirect_stderr=true
stdout_logfile=/app/log/app_%(process_num)02d.log
```
```
command 可以使用动态参数,例如:--port=88%(process_num)02d默认进程使用8800端口
多进程则修改numproc的值如numproc=2则第一个进程使用8800端口第二个进程使用8801端口。
```
## 启动
```
1. 修改配置文件
2. 编译docker build -t lemon .
3. docker-compose up -d
```

@ -0,0 +1,74 @@
version: "3.3"
services:
web:
# build: .
working_dir: /app/lemon/website
image: lemon:latest
tty:
true
container_name: lemon_web
privileged: true
# ports:
# - "80:80"
# - "8989:8989"
volumes:
# - /data/app/lemon:/app/lemon
- /data/app/log:/app/log
- /data/app/fileupload:/app/fileupload
environment:
- TZ=Asia/Shanghai
networks:
- lemon_network
depends_on:
- mysql
- redis
command: sh -c "/entrypoint.sh && /usr/bin/supervisord -n -c /etc/supervisor/supervisord.conf"
nginx:
image: nginx:latest
container_name: lemon_nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/lemon.conf
- ./dist:/app/lemon/dist
depends_on:
- web
networks:
- lemon_network
mysql:
image: mysql:8.0
container_name: lemon_mysql
ports:
- "13306:3306"
environment:
MYSQL_ROOT_PASSWORD: SghjdA887#
MYSQL_DATABASE: aiplatform
volumes:
- /data/mysql_data:/var/lib/mysql
- ./mysql_auth.sql:/docker-entrypoint-initdb.d/auth.sql
- ./mysql_app.sql:/docker-entrypoint-initdb.d/app.sql
networks:
- lemon_network
redis:
image: redis:latest
container_name: lemon_redis
ports:
- "16379:6379"
command: redis-server --requirepass hgkiYY87
volumes:
- /data/redis_data:/data
networks:
- lemon_network
#volumes:
# mysql_data:
# redis_data:
networks:
lemon_network:
driver: bridge

@ -0,0 +1,72 @@
#!/bin/bash
# 初始化数据库
#if [ -f "/app/lemon/mysql_app.sql" ];then
# echo "Initializing MySQL database..."
# # 初始化数据库
# mysql -h lemon_mysql -u root -pSghjdA887# aiplatform < /app/lemon/mysql_app.sql
# echo "MySQL initialization completed."
#else
# echo "Mysql init errorinit sql file does not exist"
#fi
#search_dir="/app/lemon"
#for entry in `ls $search_dir`; do
# echo "正在加载文件:$entry"
#done
#for entry in "$search_dir"/*; do
# echo "正在加载文件:$(basename "$entry")"
#done
# 更新配置文件
if [ ! -f "/app/lemon/website/settings_local.py" ];then
echo "@@init settings_local file"
cat > /app/lemon/website/settings_local.py << EOF
mysql_app = {
"host": "lemon_mysql:3306",
"database": "aiplatform",
"user": "root",
"password": "SghjdA887#",
"time_zone": "+8:00"
}
redis_app = ("lemon_redis", 6379, 0, "hgkiYY87")
file_upload_dir = "/app/fileupload"
EOF
fi
# 设置要列出的目录
dir_path="/app/lemon"
# 递归打印目录结构
function print_dir_structure() {
local dir="$1"
local indent="$2"
# 打印当前目录
echo "$indent$(basename "$dir")"
# 遍历目录下的所有项目
for item in "$dir"/*
do
# 如果是目录,递归打印
if [ -d "$item" ]; then
print_dir_structure "$item" "$indent "
# 如果是文件,打印文件名
elif [ -f "$item" ]; then
echo "$indent $(basename "$item")"
fi
done
}
# print_dir_structure "$dir_path" "-"
# 启动Tornado应用程序
#echo "启动Tornado应用程序"
#if [ -f "/app/lemon/website/app.py" ];then
# echo "@@app.py存在"
# python /app/lemon/website/app.py --port=8889 >> /app/log/lemon.log 2>&1
#else
# echo "app.py文件错误文件不存在"
#fi
echo "启动完成"

@ -0,0 +1,337 @@
/*
Navicat Premium Data Transfer
Source Server :
Source Server Type : MySQL
Source Server Version : 80036 (8.0.36)
Source Host :
Source Schema :
Target Server Type : MySQL
Target Server Version : 80036 (8.0.36)
File Encoding : 65001
Date: 27/06/2024 14:49:51
*/
CREATE DATABASE /*!32312 IF NOT EXISTS*/`aiplatform` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `aiplatform`;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for device_classification
-- ----------------------------
DROP TABLE IF EXISTS `device_classification`;
CREATE TABLE `device_classification` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '名称',
`suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'short uuid',
`del` int NULL DEFAULT 0,
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for enterprise
-- ----------------------------
DROP TABLE IF EXISTS `enterprise`;
CREATE TABLE `enterprise` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`province` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`city` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`industry` int NOT NULL,
`contact` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`phone` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`summary` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`logo` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL,
`account` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`pwd` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`del` int NULL DEFAULT 0,
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_suid`(`suid` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '企业信息表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for enterprise_alert
-- ----------------------------
DROP TABLE IF EXISTS `enterprise_alert`;
CREATE TABLE `enterprise_alert` (
`id` int NOT NULL AUTO_INCREMENT,
`entity_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`node_id` int NULL DEFAULT NULL,
`node_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`is_sms` tinyint NULL DEFAULT 0,
`sms_to` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`is_email` tinyint NULL DEFAULT 0,
`email_to` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`freq` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ent_node`(`entity_suid` ASC, `node_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for enterprise_busi_model
-- ----------------------------
DROP TABLE IF EXISTS `enterprise_busi_model`;
CREATE TABLE `enterprise_busi_model` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'short uuid',
`entity_id` int NOT NULL COMMENT '企业id',
`entity_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '企业uuid',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`comment` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`base_models` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '关联的基础模型json list, [{\"id\":123,\"suid\":\"xxx\",\"name\":\"xxx\"},...]',
`business_logic` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '业务代码压缩包的md5',
`business_conf_file` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '业务配置参数压缩包的文件md5',
`business_conf_param` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '业务配置的参数json字符串eg: \'{\"a\":1, \"b\":2}\'',
`delete` tinyint(1) NULL DEFAULT 0,
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '企业业务模型表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for enterprise_busi_model_node
-- ----------------------------
DROP TABLE IF EXISTS `enterprise_busi_model_node`;
CREATE TABLE `enterprise_busi_model_node` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`entity_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '企业suid',
`busi_model_id` int NULL DEFAULT NULL,
`busi_model_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`node_id` int NULL DEFAULT NULL,
`node_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for enterprise_busi_model_node_device
-- ----------------------------
DROP TABLE IF EXISTS `enterprise_busi_model_node_device`;
CREATE TABLE `enterprise_busi_model_node_device` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`entity_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '企业suid',
`node_id` int NULL DEFAULT NULL COMMENT '节点id',
`node_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '节点suid',
`busi_model_id` int NULL DEFAULT NULL COMMENT '业务模型id',
`busi_model_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '业务模型suid',
`device_id` int NULL DEFAULT NULL COMMENT '设备id',
`device_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '设备suid',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '节点-业务模型-设备关联表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for enterprise_device
-- ----------------------------
DROP TABLE IF EXISTS `enterprise_device`;
CREATE TABLE `enterprise_device` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '设备suid',
`entity_id` int NOT NULL COMMENT '企业id',
`entity_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '企业suid',
`node_id` int NOT NULL COMMENT '节点id',
`node_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '节点suid',
`classification` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '分类suid',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`device_model` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '设备型号',
`param` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`comment` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`status` int NULL DEFAULT 1001 COMMENT '1000/all/默认, 1001/在线1002/离线1003/运行中1004/故障',
`del` int NULL DEFAULT 0,
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '企业设备表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for enterprise_node
-- ----------------------------
DROP TABLE IF EXISTS `enterprise_node`;
CREATE TABLE `enterprise_node` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
`entity_id` int NOT NULL,
`entity_suid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '企业uuid',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '企业name',
`parent` int NULL DEFAULT NULL,
`addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '地址',
`lola` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '经纬度',
`contact` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '联系人',
`phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '联系方式',
`comment` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '备注',
`del` int NULL DEFAULT 0,
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '企业节点表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for enterprise_node_device_bm_cus_conf
-- ----------------------------
DROP TABLE IF EXISTS `enterprise_node_device_bm_cus_conf`;
CREATE TABLE `enterprise_node_device_bm_cus_conf` (
`id` int NOT NULL AUTO_INCREMENT,
`entity_id` int NULL DEFAULT NULL,
`entity_suid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`node_id` int NULL DEFAULT NULL,
`node_suid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`device_id` int NULL DEFAULT NULL,
`device_suid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`busi_model_id` int NULL DEFAULT NULL,
`busi_model_suid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`base_model_id` int NULL DEFAULT NULL,
`base_model_suid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`busi_conf_file` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`busi_conf_str` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`model_hub_image` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`model_conf_file` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`model_conf_str` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '企业设备关联业务模型下的基础模型自定义配置' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for enterprise_server
-- ----------------------------
DROP TABLE IF EXISTS `enterprise_server`;
CREATE TABLE `enterprise_server` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`entity_id` int NULL DEFAULT NULL,
`entity_suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`port` int NULL DEFAULT NULL,
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`passwd` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`status` int NULL DEFAULT 1001 COMMENT '1000/all/默认, 1001/在线1002/离线,',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for files
-- ----------------------------
DROP TABLE IF EXISTS `files`;
CREATE TABLE `files` (
`id` int NOT NULL AUTO_INCREMENT,
`filename` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
`filepath` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
`md5_str` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
`filesize` int NULL DEFAULT NULL,
`filetype` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`user` int NULL DEFAULT 0,
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '文件表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for model
-- ----------------------------
DROP TABLE IF EXISTS `model`;
CREATE TABLE `model` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'short uuid',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
`model_type` int NULL DEFAULT 1002 COMMENT '模型类型1001/经典算法1002/深度学习',
`classification` int NULL DEFAULT 0 COMMENT '模型分类的id',
`comment` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '备注',
`default_version` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`del` tinyint(1) NULL DEFAULT 0 COMMENT '删除状态1/删除0/正常',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '模型表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for model_classification
-- ----------------------------
DROP TABLE IF EXISTS `model_classification`;
CREATE TABLE `model_classification` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '模型分类表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for model_hub
-- ----------------------------
DROP TABLE IF EXISTS `model_hub`;
CREATE TABLE `model_hub` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`host` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`port` int NULL DEFAULT 0,
`path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`comment` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`create_time` datetime NULL DEFAULT NULL,
`update_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '模型运行库表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for model_version
-- ----------------------------
DROP TABLE IF EXISTS `model_version`;
CREATE TABLE `model_version` (
`id` int NOT NULL AUTO_INCREMENT,
`model_id` int NOT NULL,
`version` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
`comment` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
`model_file` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '模型文件的md5',
`config_file` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '模型配置文件的md5',
`config_str` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '模型默认配置参数json字符串',
`is_default` tinyint(1) NULL DEFAULT 0 COMMENT '1/默认0/非默认',
`del` tinyint(1) NULL DEFAULT 0 COMMENT '1/删除0/正常',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '模型版本表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for sys_log
-- ----------------------------
DROP TABLE IF EXISTS `sys_log`;
CREATE TABLE `sys_log` (
`id` int NOT NULL AUTO_INCREMENT,
`user` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`ip` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`primary_menu` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`sub_menu` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`op_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`content` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`comment` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int NOT NULL AUTO_INCREMENT,
`uid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
`pwd` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
`available` tinyint(1) NULL DEFAULT 1,
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '系统用户表' ROW_FORMAT = Dynamic;
INSERT INTO `sys_user` VALUES (1, 'cbb27e2d10bf400f9044fae4ebdcfd5e', 'admin', 'Lkxs7P9x4ZE7ipU9uYMIJw==', 1, NULL);
SET FOREIGN_KEY_CHECKS = 1;

@ -0,0 +1,6 @@
USE mysql;
-- 修改密码加密规则
ALTER USER 'root'@'%' IDENTIFIED BY 'password' PASSWORD EXPIRE NEVER;
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'SghjdA887#';
-- 刷新权限
FLUSH PRIVILEGES;

@ -0,0 +1,16 @@
#!/bin/bash
# Wait for MySQL to be ready
echo "Waiting for MySQL to be ready..."
while ! mysqladmin ping -h"lemon_mysql" --silent; do
sleep 1
done
# Initialize MySQL database
echo "Initializing MySQL database..."
mysql -h lemon_mysql -u root -pSghjdA887# aiplatform < /app/lemon/mysql_app.sql
echo "MySQL initialization completed."
# Start the application
exec "$@"

@ -0,0 +1,18 @@
server {
listen 80;
server_name 192.168.10.94;
root /app/lemon/dist;
location ^~ /api {
proxy_pass http://lemon_web:8989;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
try_files $uri $uri/ /index.html;
}
}

@ -0,0 +1,10 @@
[program:lemon]
directory=/app/lemon/website
command=python /app/lemon/website/app.py --port=8989
numprocs=1
process_name=%(program_name)s_%(process_num)02d
autorestart=true
autostart=true
startsecs=5
redirect_stderr=true
stdout_logfile=/app/log/app_%(process_num)02d.log

@ -0,0 +1,230 @@
# -*- coding: utf-8 -*-
import logging
import os.path
import sys
import time
import redis
import tornado.escape
import tornado.ioloop
import tornado.options
import tornado.web
# import tornado.websocket
# import torndb
import importlib
# from confluent_kafka import Producer
# from rediscluster import StrictRedisCluster
# from redis import sentinel
# from rediscluster import RedisCluster
# from redis.sentinel import Sentinel
# from tornado.options import define, options
from tornado.options import options, define as _define, parse_command_line
# from elasticsearch import Elasticsearch
# from tornado_swagger import swagger
def define(name, default=None, type=None, help=None, metavar=None,
multiple=False, group=None, callback=None):
if name not in options._options:
return _define(name, default, type, help, metavar,
multiple, group, callback)
tornado.options.define = define
sys.dont_write_bytecode = True
define("port", default=8888, help="run on the given port", type=int)
define("debug", default=0)
_ROOT = os.path.dirname(os.path.abspath(__file__))
importlib.reload(sys)
# sys.setdefaultencoding('utf-8')
try:
import website
except ImportError:
print("app package import error and try to config sys.path")
logging.info("app import error")
# sys.path.append(os.path.join(_ROOT, "../.."))
sys.path.append(os.path.abspath(os.path.join(os.getcwd(), "..")))
sys.path.append(os.path.abspath(os.path.join(os.getcwd(), "../..")))
from website import settings
from website.db_mysql import app_engine
from website.handler import APIErrorHandler
from website.urls import handlers, page_handlers
# from website.urls import handlers_v2
# print(os.path.dirname(os.path.abspath(__file__)))
# class Connection(torndb.Connection):
# def __init__(self,
# host,
# database,
# user=None,
# password=None,
# max_idle_time=7 * 3600,
# connect_timeout=500,
# time_zone="+0:00"):
# self.host = host
# self.database = database
# self.max_idle_time = float(max_idle_time)
#
# args = dict(conv=torndb.CONVERSIONS,
# use_unicode=True,
# charset="utf8",
# db=database,
# init_command=('SET time_zone = "%s";' %
# time_zone),
# connect_timeout=connect_timeout,
# sql_mode="TRADITIONAL")
# if user is not None:
# args["user"] = user
# if password is not None:
# args["passwd"] = password
#
# # We accept a path to a MySQL socket file or a host(:port) string
# if "/" in host:
# args["unix_socket"] = host
# else:
# self.socket = None
# pair = host.split(":")
# if len(pair) == 2:
# args["host"] = pair[0]
# args["port"] = int(pair[1])
# else:
# args["host"] = host
# args["port"] = 3306
#
# self._db = None
# self._db_args = args
# self._last_use_time = time.time()
# try:
# self.reconnect()
# except Exception:
# logging.error("Cannot connect to MySQL on %s",
# self.host,
# exc_info=True)
# class NoCacheStaticFileHandler(tornado.web.StaticFileHandler):
# def set_extra_headers(self, path):
# self.set_header("Cache-control", "no-cache")
class Application(tornado.web.Application):
def __init__(self):
# from website.handlers import Model
handlers_ = []
for handler in handlers:
handlers_.append(("%s%s" % (settings.api_prefix, handler[0]),
handler[1]))
for handler in page_handlers:
handlers_.append((handler[0], handler[1]))
# for handler in handlers_v2:
# handlers_.append(("%s%s" % (settings.api_prefix_v2, handler[0]),
# handler[1]))
# handlers_.append((r"/wap/s", tornado.web.RedirectHandler, dict(url=r"//wap/s.html")))
handlers_.append((r".*", APIErrorHandler))
# handlers_.append((r"/static/(.*)", NoCacheStaticFileHandler, {"path": os.path.join(_ROOT, "static")}))
settings_ = dict(
debug=options.debug,
# login_url="/login",
login_url="",
cookie_secret=settings.cookie_secret,
template_path=os.path.join(_ROOT, "templates"),
static_path=os.path.join(_ROOT, "static"),
xsrf_cookies=False,
autoescape=None,
)
# self.db_app = Connection(
# settings.mysql_app["host"],
# settings.mysql_app["database"],
# user=settings.mysql_app["user"],
# password=settings.mysql_app["password"],
# time_zone=settings.mysql_app["time_zone"])
self.app_mysql = app_engine
# if settings.redis_sentinel == 1:
# rs = Sentinel(settings.redis_sentinel_nodes, socket_timeout=0.1)
# self.r_app = rs.master_for(settings.redis_sentinel_master,
# socket_timeout=0.1,
# password=settings.redis_sentinel_pwd)
# if settings.redis_cluster == 1:
# self.r_app = RedisCluster(startup_nodes=settings.redis_app_cluster_notes, decode_responses=True,
# password=settings.redis_cluster_pwd)
# else:
# self.r_app = redis.Redis(*settings.redis_app, decode_responses=True)
self.r_app = redis.Redis(*settings.redis_app, decode_responses=True)
# self.r_app = redis.Redis(*settings.redis_app)
# self.kafka_producer = Producer(**settings.kafka_conf)
# self.es = Elasticsearch(settings.es_nodes)
# Model.setup_dbs({"db_app": self.db_app,
# "r_app": self.r_app
# })
tornado.web.Application.__init__(self, handlers_, **settings_)
# swagger.Application.__init__(self, handlers_, **settings_)
def sig_handler(signum, frame):
tornado.ioloop.IOLoop.instance().stop()
class PwdFilter(logging.Filter):
def filter(self, record):
try:
print("##########")
print("{}, {}".format(record.name, record.msg))
except Exception as e:
print(e)
pass
return True
def main():
tornado.options.parse_command_line()
# options.parse_command_line()
formatter = logging.Formatter(
'[%(levelname)1.1s %(asctime)s.%(msecs)d '
'%(module)s:%(funcName)s:%(lineno)d] %(message)s',
"%Y-%m-%d %H:%M:%S"
) # creating own format
for handler in logging.getLogger().handlers: # setting format for all handlers
handler.setFormatter(formatter)
# handler.addFilter(PwdFilter())
app = Application()
# app.listen(options.port)
server = tornado.httpserver.HTTPServer(app, max_buffer_size=104857600*3)
server.listen(options.port)
# def ping():
# try:
# row = app.db_app.get("select id from user limit 1")
# if row:
# logging.info("db check ok")
# except Exception as e:
# logging.info(e)
# logging.info("db connection err, reconnect")
# app.db_app.reconnect()
logging.info("start app server...")
# tornado.ioloop.PeriodicCallback(ping, 600000).start()
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
"""静态配置"""
PAGE_SIZE = 10
# 行业分类
industry_map = {
1001: u"IT服务",
1002: u"制造业",
1003: u"批发/零售",
1004: u"生活服务",
1005: u"文化/体育/娱乐业",
1006: u"建筑/房地产",
1007: u"教育",
1008: u"运输/物流/仓储",
1009: u"医疗",
1010: u"政府",
1011: u"金融",
1012: u"能源/采矿",
1013: u"农林渔牧",
1014: u"其他行业",
}
model_type_classic = 1001
model_type_machine = 1002
model_type_map = {
model_type_classic: u"经典算法",
model_type_machine: u"机器学习",
}
# 1000/all/默认, 1001/在线1002/离线1003/运行中1004/故障
device_status_default = 1000
device_status_online = 1001
device_status_offline = 1002
device_status_ongoing = 1003
device_status_error = 1004
device_status_map = {
1000: u"全部",
1001: u"在线",
1002: u"离线",
1003: u"运行中",
1004: u"故障",
}

@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
import logging
from typing import Union
from sqlalchemy import Column, Integer, String, DateTime, func
from sqlalchemy.ext.declarative import declarative_base
from website.db_mysql import get_session
Base = declarative_base()
"""
CREATE TABLE `model` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(10) DEFAULT NULL COMMENT 'short uuid',
`name` varchar(255) NOT NULL DEFAULT '',
`model_type` int DEFAULT '1002' COMMENT '模型类型1001/经典算法1002/深度学习',
`classification` int DEFAULT '0' COMMENT '模型分类的id',
`comment` varchar(255) DEFAULT '' COMMENT '备注',
`default_version` varchar(100) DEFAULT '',
`del` tinyint(1) DEFAULT '0' COMMENT '删除状态1/删除0/正常',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='模型表';
"""
class Model(Base):
__tablename__ = "model"
id = Column(Integer, primary_key=True, autoincrement=True)
suid = Column(String(10), comment="short uuid")
name = Column(String(255), nullable=False, default="")
model_type = Column(Integer, default=1002, comment="模型类型1001/经典算法1002/深度学习")
classification = Column(Integer, default=0, comment="模型分类的id")
comment = Column(String(255), default="", comment="备注")
default_version = Column(String(100), default="")
delete = Column("del", Integer, default=0, comment="删除状态1/删除0/正常")
create_time = Column(DateTime, default=func.now())
update_time = Column(DateTime, onupdate=func.now())
def __repr__(self):
return f"Model(id={self.id}, name='{self.name}', model_type={self.model_type})"
class ModelRepositry(object):
def get_suid(self, model_id: int) -> str:
with get_session() as session:
model = session.query(Model).filter(Model.id == model_id).first()
if not model or not model.suid:
return ""
return model.suid
def get_model_by_id(self, model_id: int) -> Union[Model, None]:
with get_session() as session:
model = session.query(Model).filter(Model.id == model_id).first()
if not model:
return None
return model
def get_model_dict_by_id(self, model_id: int) -> dict:
with get_session() as session:
logging.info(f"model id is : {model_id}")
model = session.query(Model).filter(Model.id == model_id).first()
if not model:
return {}
model_dict = {
'id': model.id,
'suid': model.suid,
'name': model.name,
'model_type': model.model_type,
'classification': model.classification,
'comment': model.comment,
'default_version': model.default_version,
'delete': model.delete,
'create_time': model.create_time,
'update_time': model.update_time
}
logging.info(f"model dict is : {model_dict}")
return model_dict
def get_model_by_ids(self, model_ids: list) -> list:
with get_session() as session:
models = session.query(Model).filter(Model.id.in_(model_ids)).all()
if not models:
return []
return models
def get_model_count(self) -> int:
with get_session() as session:
return session.query(Model).count()

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, DateTime, func
from website.db_mysql import get_session
Base = declarative_base()
"""
设备分类表
"""
class DeviceClassification(Base):
__tablename__ = 'device_classification'
id = Column(Integer, primary_key=True)
name = Column(String(255), default='', comment='名称')
suid = Column(String(10), default='', comment='short uuid')
delete = Column("del", Integer, default=0)
create_time = Column(DateTime, default=func.now())
class DeviceClassificationReporitory(object):
def get_row_by_id(self, cid):
with get_session() as session:
return session.query(DeviceClassification).filter_by(id=cid).first()
def get_row_by_suid(self, suid):
with get_session() as session:
return session.query(DeviceClassification).filter_by(suid=suid).first()

@ -0,0 +1,136 @@
from website.handler import BaseHandler
from sqlalchemy import text
from typing import Any
from website.db_mysql import get_session, get_async_session, to_json, to_json_list
import json
# 获取企业模型数量
def get_enterprise_model_count(id: int) -> int:
return 0
# 获取企业设备数量
def get_enterprise_device_count(id: int) -> int:
return 0
# 获取所有企业实体数量
def get_enterprise_entity_count(engine: Any) -> int:
with engine.connect() as conn:
count_sql_text = "select count(*) from enterprise "
count = conn.execute(text(count_sql_text)).fetchone()
if count:
return count[0]
return 0
# 获取所有企业模型数量
def get_enterprise_model_count(entity_id: int = 0, entity_suid: str = "") -> int:
with get_session() as session:
sql = "select base_models from enterprise_busi_model where "
param = {}
if entity_id:
sql += "entity_id = :entity_id"
param["entity_id"] = entity_id
elif entity_suid:
sql += "entity_suid = :entity_suid"
param["entity_suid"] = entity_suid
cur = session.execute(text(sql), param)
res = to_json(cur)
if res:
base_model_list = json.loads(res)
return len(base_model_list)
return 0
# 获取所有企业设备数量
def get_enterprise_device_count(entity_id: int = 0, entity_suid: str = "") -> int:
with get_session() as session:
sql = "select count(id) as device_count from enterprise_device "
param = {}
if entity_id:
sql += "where entity_id = :entity_id"
param = {"entity_id": entity_id}
elif entity_suid:
sql += "where entity_suid = :entity_suid"
param = {"entity_suid": entity_suid}
cur = session.execute(text(sql), param)
res = to_json(cur)
if res:
return res["device_count"]
return 0
async def get_enterprise_model_and_device_count(
entity_id: int = 0, entity_suid: str = ""
) -> (int, int):
model_count = 0
device_count = 0
async with get_async_session() as session:
# sql_model = "select base_models from enterprise_busi_model where "
# param_model = {}
# if entity_id:
# sql_model += "entity_id = :entity_id"
# param_model["entity_id"] = entity_id
# elif entity_suid:
# sql_model += "entity_suid = :entity_suid"
# param_model["entity_suid"] = entity_suid
# cur_model = await session.execute(text(sql_model), param_model)
# res_model = to_json(cur_model)
# if res_model:
# base_model_list = json.loads(res_model)
# model_count = len(base_model_list)
# sql_device = "select count(id) as device_count from enterprise_device where "
# param_device = {}
# if entity_id:
# sql_device += "entity_id = :entity_id"
# param_device["entity_id"] = entity_id
# elif entity_suid:
# sql_device += "where entity_suid = :entity_suid"
# param_device = {"entity_suid": entity_suid}
# cur_device = await session.execute(text(sql_device), param_device)
# res_device = to_json(cur_device)
# if res_device:
# device_count = res_device["device_count"]
sql_device_count = "SELECT COUNT(*) AS device_count FROM enterprise_device WHERE {where_clause} "
sql_base_model = "SELECT base_models FROM enterprise_busi_model WHERE {where_clause} "
where_clause = ""
params = {}
if entity_id:
where_clause = "entity_id = :entity_id"
params["entity_id"] = entity_id
elif entity_suid:
where_clause = "entity_suid = :entity_suid"
params["entity_suid"] = entity_suid
sql_device_count = sql_device_count.format(where_clause=where_clause)
result_device_count = await session.execute(text(sql_device_count), params)
device_count = to_json(result_device_count)["device_count"]
sql_base_model = sql_base_model.format(where_clause=where_clause)
result_base_model = await session.execute(text(sql_base_model), params)
base_models = to_json_list(result_base_model)
base_model_ids = []
for item in base_models:
base_models = json.loads(item["base_models"])
for base_model in base_models:
if base_model["id"] not in base_model_ids:
base_model_ids.append(base_model["id"])
model_count = len(base_model_ids)
return model_count, device_count

@ -0,0 +1,295 @@
# -*- coding: utf-8 -*-
import copy
import json
import logging
from typing import Any, Dict, List, Optional
from sqlalchemy import Column, Integer, String, DateTime, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import text
from website.db.alg_model import alg_model as DB_alg_model
from website.db.enterprise_entity.enterprise_entity import EnterpriseEntityRepository
from website.db.enterprise_node import enterprise_node as DB_Node
from website.db_mysql import get_session, to_json_list, to_json, Row, dict_to_obj
from website.util import shortuuid
Base = declarative_base()
"""
CREATE TABLE `enterprise_busi_model` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(10) NOT NULL DEFAULT '' COMMENT 'short uuid',
`entity_id` int NOT NULL COMMENT '企业id',
`entity_suid` varchar(10) NOT NULL COMMENT '企业uuid',
`name` varchar(255) NOT NULL,
`comment` varchar(255) DEFAULT '',
`basemodel_ids` varchar(255) NOT NULL COMMENT '关联模型json list, [{"id":123,"suid":"xxx"},...]',
`business_logic` varchar(32) DEFAULT NULL COMMENT '业务代码压缩包的md5',
`business_conf_file` varchar(32) DEFAULT NULL COMMENT '业务配置参数压缩包的文件md5',
`business_conf_param` varchar(255) DEFAULT NULL COMMENT '业务配置的参数json字符串eg: ''{"a":1, "b":2}''',
`delete` tinyint(1) DEFAULT '0',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='企业业务模型表';
CREATE TABLE `enterprise_busi_model_node` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(10) NOT NULL,
`entity_suid` varchar(10) DEFAULT NULL COMMENT '企业suid',
`busi_model_id` int DEFAULT NULL,
`busi_model_suid` varchar(10) DEFAULT NULL,
`node_id` int DEFAULT NULL,
`node_suid` varchar(10) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
"""
class EnterpriseBusiModel(Base):
__tablename__ = 'enterprise_busi_model'
id = Column(Integer, primary_key=True, autoincrement=True)
suid = Column(String(10), nullable=False, default='', comment='short uuid')
entity_id = Column(Integer, nullable=False, comment='企业id')
entity_suid = Column(String(10), nullable=False, comment='企业uuid')
name = Column(String(255), nullable=False)
comment = Column(String(255), nullable=True, default='')
base_models = Column(String(255), nullable=False, comment='关联的基础模型json list, [{"id":123,"suid":"xxx"},...]')
business_logic = Column(String(32), nullable=True, comment='业务代码压缩包的md5')
business_conf_file = Column(String(32), nullable=True, comment='业务配置参数压缩包的文件md5')
business_conf_param = Column(String(255), nullable=True, comment='业务配置的参数json字符串eg: ''{"a":1, "b":2}''')
delete = Column(Integer, default=0)
create_time = Column(DateTime, nullable=False, default=func.now(), onupdate=func.now())
update_time = Column(DateTime, nullable=False, default=func.now(), onupdate=func.now())
def __repr__(self):
return f"EnterpriseBusiModel(id={self.id}, suid='{self.suid}', name='{self.name}')"
def __init__(self, **kwargs):
valid_columns = {col.name for col in self.__table__.columns}
filtered_data = {key: value for key, value in kwargs.items() if key in valid_columns}
super().__init__(**filtered_data)
class EnterpriseBusiModelRepository(object):
def get_by_id(self, id: int) -> Optional[EnterpriseBusiModel]:
with get_session() as session:
model = session.query(EnterpriseBusiModel).filter(EnterpriseBusiModel.id == id).first()
return model
def insert_busi_model(self, data: Dict):
entity_suid = EnterpriseEntityRepository().get_entity_suid(data['entity_id'])
data['suid'] = shortuuid.ShortUUID().random(10)
data['entity_suid'] = entity_suid
base_model_ids = [int(model_id) for model_id in data['basemodel_ids'].split(',')]
logging.info("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
logging.info(base_model_ids)
base_model = []
for base_model_id in base_model_ids:
base_model_db = DB_alg_model.ModelRepositry()
# base_model_suid = base_model_db.get_suid(base_model_id)
base_model_info = base_model_db.get_model_dict_by_id(base_model_id)
logging.info("#####################")
logging.info(base_model_info)
base_model.append({
'id': base_model_id,
'suid': base_model_info["suid"],
'name': base_model_info["name"],
})
data['base_models'] = json.dumps(base_model)
new_data = copy.copy(data)
with get_session() as session:
model = EnterpriseBusiModel(**new_data)
session.add(model)
session.commit()
return model.id, model.suid
def edit_busi_model(self, data: Dict):
base_model_ids = [int(model_id) for model_id in data['basemodel_ids'].split(',')]
base_model = []
for base_model_id in base_model_ids:
base_model_db = DB_alg_model.ModelRepositry()
# base_model_suid = base_model_db.get_suid(base_model_id)
base_model_info = base_model_db.get_model_dict_by_id(base_model_id)
base_model.append({
"id": base_model_id,
"suid": base_model_info["suid"],
"name": base_model_info["name"],
})
data['base_models'] = json.dumps(base_model)
with get_session() as session:
try:
# 过滤不必要的字段
valid_columns = {col.name for col in EnterpriseBusiModel.__table__.columns}
filtered_data = {key: value for key, value in data.items() if key in valid_columns}
session.query(EnterpriseBusiModel).filter(EnterpriseBusiModel.id == data['id']).update(filtered_data)
except Exception as e:
logging.error("Failed to edit device, error: {}".format(e))
session.commit()
return
def get_busi_model_by_id(self, id: int) -> Optional[EnterpriseBusiModel]:
with get_session() as session:
model = session.query(EnterpriseBusiModel).filter(EnterpriseBusiModel.id == id).first()
if model:
model_dict = model.__dict__
model_dict.pop('_sa_instance_state')
return dict_to_obj(model_dict)
return None
def list_enterprise_busi_model(self, entity_id: int, page_no: int, page_size: int) -> Dict[Any, Any]:
"""
获取企业部署的业务模型列表
Args:
entity_id (int): 企业 ID
page_no (int): 页码
page_size (int): 每页数量
Returns:
dict: 包含总数和数据列表的字典
"""
with get_session() as session:
total_count = session.query(func.count(EnterpriseBusiModel.id)).filter(
EnterpriseBusiModel.entity_id == entity_id).scalar()
models = (
session.query(
EnterpriseBusiModel.id.label("model_id"),
EnterpriseBusiModel.name.label("model_name"),
EnterpriseBusiModel.create_time
)
.filter(EnterpriseBusiModel.entity_id == entity_id)
.offset((page_no - 1) * page_size)
.limit(page_size)
.all()
)
return {
"count": total_count,
"data": [
{
"model_id": model.model_id,
"model_name": model.model_name,
"create_time": model.create_time.strftime("%Y-%m-%d %H:%M:%S"),
}
for model in models
]
}
def delete_by_id(self, model_id: int):
"""
删除业务模型
delete字段置为1
Args:
model_id (int): 业务模型 ID
"""
with get_session() as session:
model = session.query(EnterpriseBusiModel).filter(EnterpriseBusiModel.id == model_id).first()
if model:
model.delete = 1
session.commit()
return
class EnterpriseBusiModelNode(Base):
__tablename__ = 'enterprise_busi_model_node'
id = Column(Integer, primary_key=True)
suid = Column(String(10), nullable=False, default='')
entity_suid = Column(String(10))
busi_model_id = Column(Integer)
busi_model_suid = Column(String(10))
node_id = Column(Integer)
node_suid = Column(String(10))
create_time = Column(DateTime, default=func.current_timestamp())
def __repr__(self):
return f'<EnterpriseBusiModelNode(id={self.id}, suid={self.suid})>'
class EnterpriseBusiModelNodeRepository(object):
# def get_by_id(self, id: int) -> Optional[EnterpriseBusiModelNode]:
# return self.db.query(EnterpriseBusiModelNode).filter(EnterpriseBusiModelNode.id == id).first()
def insert_busi_model_nodes(self, data: Dict):
data['suid'] = shortuuid.ShortUUID().random(10)
link_node_ids = [int(node_id) for node_id in data['node_ids'].split(',')]
with get_session() as session:
for node_id in link_node_ids:
logging.info("node_id: %s")
node_db = DB_Node.EnterpriseNodeRepository()
node = node_db.get_entity_suid_by_node_id(node_id)
node_suid = node["suid"]
entity_suid = node["entity_suid"]
model_node = EnterpriseBusiModelNode(
suid=shortuuid.ShortUUID().random(10),
entity_suid=entity_suid,
busi_model_id=data['busimodel_id'],
busi_model_suid=data['busimodel_suid'],
node_id=node_id,
node_suid=node_suid,
)
session.add(model_node)
session.commit()
return
def get_nodes_by_busi_model(self, busi_model_id: int, entity_suid: str) -> Optional[List[Row]]:
with get_session() as session:
# nodes = session.query(EnterpriseBusiModelNode, ).filter(
# EnterpriseBusiModelNode.busi_model_id == busi_model_id).all()
sql = ("select mn.node_id, n.name node_name from enterprise_busi_model_node mn, enterprise_node n "
"where mn.node_id=n.id "
" and mn.busi_model_id = :busi_model_id"
" and mn.entity_suid = :entity_suid")
nodes = session.execute(text(sql), {"busi_model_id": busi_model_id, "entity_suid": entity_suid})
return to_json_list(nodes)
def delete_by_busi_model_id(self, busi_model_id: int) -> None:
with get_session() as session:
session.query(EnterpriseBusiModelNode).filter(
EnterpriseBusiModelNode.busi_model_id == busi_model_id).delete()
session.commit()
def get_busi_model_by_node_id(self, node_id: int, page_no: int = 1, page_size: int = 10) -> Dict[Any, Any]:
with get_session() as session:
models = (
session.query(
EnterpriseBusiModel.id.label("busi_model_id"),
EnterpriseBusiModel.name.label("busi_model_name"),
EnterpriseBusiModel.create_time
)
.outerjoin(EnterpriseBusiModelNode, EnterpriseBusiModel.id == EnterpriseBusiModelNode.busi_model_id)
.filter(EnterpriseBusiModelNode.node_id == node_id)
.offset((page_no - 1) * page_size)
.limit(page_size)
.all()
)
total_count = session.query(func.count(EnterpriseBusiModelNode.id)).filter(
EnterpriseBusiModelNode.node_id == node_id).scalar()
return {
"count": total_count,
"data": [
{
"busi_model_id": model.busi_model_id,
"busi_model_name": model.busi_model_name,
"create_time": model.create_time.strftime("%Y-%m-%d %H:%M:%S"),
} for model in models
]
}

@ -0,0 +1,156 @@
# -*- coding: utf-8 -*-
import logging
from typing import List, Union, Optional, Tuple
from sqlalchemy import Column, Integer, String, DateTime, func, text
from sqlalchemy.ext.declarative import declarative_base
from website.db_mysql import get_session, to_json, to_json_list, Row
"""
CREATE TABLE `enterprise_busi_model_node_device` (
`id` int NOT NULL,
`suid` varchar(10) DEFAULT NULL,
`entity_suid` varchar(10) DEFAULT NULL,
`node_id` int DEFAULT NULL,
`node_suid` varchar(10) DEFAULT NULL,
`busi_model_id` int DEFAULT NULL,
`busi_model_suid` varchar(10) DEFAULT NULL,
`device_id` int DEFAULT NULL,
`device_suid` varchar(10) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='节点-业务模型-设备关联表';
"""
Base = declarative_base()
class EnterpriseBusiModelNodeDevice(Base):
__tablename__ = "enterprise_busi_model_node_device"
id = Column(Integer, primary_key=True)
suid = Column(String(10))
entity_suid = Column(String(10))
node_id = Column(Integer)
node_suid = Column(String(10))
busi_model_id = Column(Integer)
busi_model_suid = Column(String(10))
device_id = Column(Integer)
device_suid = Column(String(10))
create_time = Column(
DateTime, default=func.current_timestamp(), onupdate=func.current_timestamp()
)
def __repr__(self):
return f"EnterpriseBusiModelNodeDevice(id={self.id}, suid='{self.suid}')"
class EnterpriseBusiModelNodeDeviceRepository(object):
def insert_record(self, records: List[EnterpriseBusiModelNodeDevice]):
with get_session() as session:
for record in records:
session.add(record)
session.commit()
def batch_insert_record(self, records: List[dict]):
with get_session() as session:
session.bulk_insert_mappings(EnterpriseBusiModelNodeDevice, records)
session.commit()
def delete_by_busi_model_and_node(self, node_id: int, busi_model_id: int) -> bool:
try:
with get_session() as session:
sql = text(
"delete from enterprise_busi_model_node_device "
"where node_id=:node_id and busi_model_id=:busi_model_id"
)
session.execute(
sql, {"node_id": node_id, "busi_model_id": busi_model_id}
)
session.commit()
except Exception as e:
logging.info("delete_by_busi_model_and_node error: %s", e)
return False
return True
def check_deployed(self, node_id: int, busi_model_id: int) -> int:
with get_session() as session:
count = (
session.query(EnterpriseBusiModelNodeDevice)
.filter(
EnterpriseBusiModelNodeDevice.node_id == node_id,
EnterpriseBusiModelNodeDevice.busi_model_id == busi_model_id,
)
.count()
)
return 1 if count > 0 else 0
def get_one_by_busimodel(self, busi_model_id: int):
with get_session() as session:
sql = text(
"select node_id, busi_model_id from enterprise_busi_model_node_device "
"where busi_model_id=:busi_model_id limit 1"
)
res = session.execute(sql, {"busi_model_id": busi_model_id})
return to_json(res)
def get_device_ids(self, node_id: int, busi_model_id: int) -> list:
with get_session() as session:
sql = text(
"select device_id from enterprise_busi_model_node_device "
"where node_id=:node_id and busi_model_id=:busi_model_id"
)
res = session.execute(
sql, {"node_id": node_id, "busi_model_id": busi_model_id}
)
return to_json_list(res)
def get_busi_model_by_device(self, device_id: int = 0, device_suid: str = "", pagination: bool = False,
page_no: int = 0,
page_size: int = 0) -> Union[list, Tuple[Optional[List[Row]], int]]:
if not device_id and not device_suid:
logging.error("get_busi_model_by_device error: device_id and device_suid is null")
return []
count = 0
with get_session() as session:
sql = """
select d.busi_model_id, d.node_id, m.name, m.base_models
from enterprise_busi_model_node_device d, enterprise_busi_model m
where d.busi_model_id=m.id"""
p = {}
sql_count = """
select count(1) from enterprise_busi_model_node_device d, enterprise_busi_model m where d.busi_model_id=m.id
"""
p_count = {}
if device_id:
sql += " and d.device_id=:device_id"
p.update({"device_id": device_id})
sql_count += " and d.device_id=:device_id"
p_count.update({"device_id": device_id})
if device_suid:
sql += " and d.device_suid=:device_suid"
p.update({"device_suid": device_suid})
sql_count += " and d.device_suid=:device_suid"
p_count.update({"device_suid": device_suid})
if pagination:
sql += " order by d.id desc"
if page_no > 0:
sql += " limit :pageno, :pagesize"
p.update({"pageno": (page_no - 1) * page_size, "pagesize": page_size})
count = session.execute(text(sql_count), p_count).scalar()
res = session.execute(text(sql), p)
res = to_json_list(res)
return res, count

@ -0,0 +1,385 @@
# -*- coding: utf-8 -*-
import logging
from sqlalchemy import Column, Integer, String, DateTime, func
from sqlalchemy.ext.declarative import declarative_base
from website import consts
from website import errors
from website.db.device_classification.device_classification import DeviceClassification
from website.db.enterprise_entity.enterprise_entity import EnterpriseEntityRepository
from website.db.enterprise_node.enterprise_node import EnterpriseNodeRepository
from website.db_mysql import get_session
from website.util import shortuuid
def row2dict(row):
d = {}
for column in row.__table__.columns:
d[column.name] = str(getattr(row, column.name))
return d
Base = declarative_base()
"""
CREATE TABLE `enterprise_device` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(10) DEFAULT NULL COMMENT '设备suid',
`entity_id` int NOT NULL COMMENT '企业id',
`entity_suid` varchar(10) NOT NULL COMMENT '企业suid',
`node_id` int NOT NULL COMMENT '节点id',
`node_suid` varchar(10) NOT NULL COMMENT '节点suid',
`classification` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '分类suid',
`name` varchar(255) NOT NULL,
`addr` varchar(255) DEFAULT '',
`device_model` varchar(255) DEFAULT '' COMMENT '设备型号',
`param` varchar(255) DEFAULT '',
`comment` varchar(255) DEFAULT '',
`del` int DEFAULT '0',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='企业设备表';
"""
class EnterpriseDevice(Base):
__tablename__ = "enterprise_device"
id = Column(Integer, primary_key=True)
suid = Column(String(length=10), default="")
entity_id = Column(Integer)
entity_suid = Column(String)
node_id = Column(Integer)
node_suid = Column(String)
classification = Column(String)
name = Column(String)
addr = Column(String)
device_model = Column(String)
param = Column(String)
comment = Column(String)
status = Column(Integer)
delete = Column("del", Integer, default=0)
create_time = Column(DateTime, default=func.now())
update_time = Column(DateTime, default=func.now())
class EnterpriseDeviceRepository(object):
def add_device(self, device):
entity_id = device["entity_id"]
node_id = device["node_id"]
entity_suid = EnterpriseEntityRepository().get_entity_suid(entity_id)
node = EnterpriseNodeRepository().get_node_by_id(node_id)
node_suid = node["suid"]
device["entity_suid"] = entity_suid
device["node_suid"] = node_suid
device["suid"] = shortuuid.ShortUUID().random(10)
name = device["name"]
with get_session() as session:
existing_device = (
session.query(EnterpriseDevice)
.filter_by(node_id=node_id, name=name, delete=0)
.first()
)
if existing_device:
logging.error(
f"Failed to add device: device with node_id={node_id} and name={name} already exists"
)
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备已存在")
new_device = EnterpriseDevice(**device)
try:
# session.execute(
# text(
# """INSERT INTO enterprise_device
# (suid, entity_id, entity_suid, node_id, node_suid, classification, name, addr, device_model, param, comment)
# values (:suid, :entity_id, :entity_suid, :node_id, :node_suid, :classification, :name, :addr, :device_model, :param, :comment)"""
# ),
# device)
session.add(new_device)
except Exception as e:
logging.error("Failed to add device")
raise e
return
def edit_device(self, device):
device_id = device["id"]
with get_session() as session:
try:
# valid_columns = {col.name for col in EnterpriseDevice.__table__.columns}
# filtered_data = {key: value for key, value in device.items() if key in valid_columns}
session.query(EnterpriseDevice).filter(
EnterpriseDevice.id == device_id
).update(device)
except Exception as e:
logging.error("Failed to edit device")
raise e
return
def delete_device(self, device_id):
with get_session() as session:
try:
session.query(EnterpriseDevice).filter(
EnterpriseDevice.id == device_id
).update({"delete": 1})
except Exception as e:
logging.error("Failed to delete device")
raise e
return
def list_devices(self, node_id: int, pageNo: int, pageSize: int) -> dict:
with get_session() as session:
try:
total_count = (
session.query(EnterpriseDevice)
.filter(
EnterpriseDevice.node_id == node_id,
EnterpriseDevice.delete != 1,
)
.count()
)
devices = (
session.query(
EnterpriseDevice,
DeviceClassification.name.label("classification_name"),
)
.join(
DeviceClassification,
EnterpriseDevice.classification == DeviceClassification.suid,
)
.filter(
EnterpriseDevice.node_id == node_id,
EnterpriseDevice.delete != 1,
)
.order_by(EnterpriseDevice.id.desc())
.limit(pageSize)
.offset((pageNo - 1) * pageSize)
.all()
)
except Exception as e:
logging.error("Failed to list devices")
raise e
device_dicts = []
for device, classification_name in devices:
device_dict = {
"id": device.id,
"suid": device.suid,
"entity_id": device.entity_id,
"entity_suid": device.entity_suid,
"node_id": device.node_id,
"node_suid": device.node_suid,
"classification": classification_name,
"name": device.name,
"addr": device.addr,
"device_model": device.device_model,
"param": device.param,
"comment": device.comment,
"delete": device.delete,
"create_time": device.create_time.strftime("%Y-%m-%d %H:%M:%S"),
"update_time": str(device.update_time),
}
device_dicts.append(device_dict)
# for row in devices:
# logging.info(row.name)
logging.info(device_dicts)
return {"devices": device_dicts, "total_count": total_count}
def list_simple_devices(self, node_id: int) -> list:
"""获取节点下所有设备的简单信息。"""
with get_session() as session:
try:
devices = (
session.query(
EnterpriseDevice,
DeviceClassification.name.label("classification_name"),
)
.join(
DeviceClassification,
EnterpriseDevice.classification == DeviceClassification.suid,
)
.filter(
EnterpriseDevice.node_id == node_id,
EnterpriseDevice.delete != 1,
)
.all()
)
except Exception as e:
logging.error("Failed to list devices")
raise e
device_dicts = []
for device, cname in devices:
device_dict = {
"id": device.id,
"name": device.name,
"classification_name": cname,
}
device_dicts.append(device_dict)
return device_dicts
def get_device(self, device_id: int) -> dict:
"""
根据设备ID获取设备信息
"""
with get_session() as session:
try:
device = (
session.query(EnterpriseDevice)
.filter(
EnterpriseDevice.id == device_id, EnterpriseDevice.delete != 1
)
.first()
)
except Exception as e:
logging.error("Failed to get device")
raise e
device_dict = {}
if device:
device_dict = {
"id": device.id,
"entity_id": device.entity_id,
"entity_suid": device.entity_suid,
"node_id": device.node_id,
"node_suid": device.node_suid,
"suid": device.suid,
"name": device.name,
"addr": device.addr,
"device_model": device.device_model,
"param": device.param,
"comment": device.comment,
"classification": device.classification,
}
return device_dict
def get_devices(self, device_ids: list) -> list:
"""
根据设备ID列表获取设备信息
"""
with get_session() as session:
try:
devices = (
session.query(EnterpriseDevice)
.filter(
EnterpriseDevice.id.in_(device_ids),
EnterpriseDevice.delete != 1,
)
.all()
)
except Exception as e:
logging.error("Failed to get devices")
raise e
device_dicts = []
for device in devices:
device_dict = {
"id": device.id,
"entity_id": device.entity_id,
"entity_suid": device.entity_suid,
"node_id": device.node_id,
"node_suid": device.node_suid,
"suid": device.suid,
"name": device.name,
"addr": device.addr,
"device_model": device.device_model,
"param": device.param,
"comment": device.comment,
"classification": device.classification,
}
device_dicts.append(device_dict)
return device_dicts
def get_all_device_count(self) -> int:
"""获取所有设备的数量"""
with get_session() as session:
try:
count = session.query(EnterpriseDevice).filter(EnterpriseDevice.delete != 1).count()
return count
except Exception as e:
logging.error("Failed to get all device count, error: {}".format(e))
return 0
def list_entity_devices(self, entity_id: int, pageno: int, pagesize: int, classification: str = "",
status: int = 0, group: int = 0) -> dict:
"""获取企业的设备列表"""
logging.info(status)
with get_session() as session:
try:
session_count = (
session.query(EnterpriseDevice)
.filter(
EnterpriseDevice.entity_id == entity_id,
EnterpriseDevice.delete != 1,
)
)
if classification:
session_count = session_count.filter(EnterpriseDevice.classification == classification)
if status in consts.device_status_map and status != consts.device_status_default:
logging.info("filter status")
session_count = session_count.filter(EnterpriseDevice.status == status)
if group:
session_count = session_count.filter(EnterpriseDevice.node_id == group)
count = session_count.count()
session_device = (
session.query(EnterpriseDevice, DeviceClassification.name.label("classification_name"))
.join(DeviceClassification, DeviceClassification.id == EnterpriseDevice.classification,
isouter=True)
.filter(
EnterpriseDevice.entity_id == entity_id,
EnterpriseDevice.delete != 1,
)
)
if classification:
session_device = session_device.filter(EnterpriseDevice.classification == classification)
if status in consts.device_status_map and status != consts.device_status_default:
session_device = session_device.filter(EnterpriseDevice.status == status)
if group:
session_device = session_count.filter(EnterpriseDevice.node_id == group)
devices = session_device.order_by(EnterpriseDevice.id.desc()).limit(pagesize).offset(
(pageno - 1) * pagesize).all()
except Exception as e:
logging.error("Failed to list devices")
raise e
logging.info(devices)
return {"count": count, "devices": devices}
def status_count(self, entity_id: int, classification: str = ""):
with get_session() as session:
try:
session_count = (
session.query(
EnterpriseDevice.status,
func.count(EnterpriseDevice.id).label("count")
)
.filter(
EnterpriseDevice.entity_id == entity_id,
EnterpriseDevice.delete != 1,
)
.group_by(EnterpriseDevice.status)
)
if classification:
session_count = session_count.filter(EnterpriseDevice.classification == classification)
result = session_count.all()
except Exception as e:
logging.error("Failed to get status count")
raise e
return dict(result)

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
import logging
from website.db_mysql import get_session, to_json
from sqlalchemy import text
"""
CREATE TABLE `enterprise` (
`id` int NOT NULL AUTO_INCREMENT,
`suid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '',
`name` varchar(255) NOT NULL,
`province` varchar(50) NOT NULL,
`city` varchar(50) NOT NULL,
`addr` varchar(255) NOT NULL,
`industry` int NOT NULL,
`contact` varchar(100) NOT NULL,
`phone` varchar(50) NOT NULL,
`summary` varchar(255) DEFAULT NULL,
`logo` text,
`account` varchar(20) DEFAULT NULL,
`pwd` varchar(100) DEFAULT NULL,
`del` int DEFAULT '0',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_suid` (`suid`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='企业信息表';
"""
class EnterpriseEntityRepository(object):
def __init__(self):
pass
def get_entity_suid(self, entity_id: int) -> str:
with get_session() as session:
res = session.execute(text("select suid from enterprise where id=:id"),
{"id": entity_id})
entity = to_json(res)
return entity["suid"] if entity else ""

@ -0,0 +1,194 @@
# -*- coding: utf-8 -*-
from typing import List, Dict, Any, Union
from sqlalchemy import text
from website.db_mysql import get_session, to_json_list, to_json
"""
CREATE TABLE `enterprise_node` (
`id` int NOT NULL AUTO_INCREMENT,
`entity_suid` int NOT NULL COMMENT '企业uuid',
`name` varchar(255) DEFAULT '' COMMENT '企业name',
`parent` int DEFAULT NULL,
`addr` varchar(255) DEFAULT '',
`lola` varchar(255) DEFAULT '',
`contact` varchar(255) DEFAULT '',
`phone` varchar(255) DEFAULT '',
`comment` varchar(255) DEFAULT '',
`del` int DEFAULT '0',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='企业节点表'
"""
class EnterpriseNodeRepository(object):
def insert_node(self, node: dict) -> int:
with get_session() as session:
session.execute(
text(
"insert into "
"enterprise_node(suid, entity_id, entity_suid, name, parent, addr, lola, contact, phone, comment) "
"values (:suid, :entity_id, :entity_suid, :name, :parent, :addr, :lola, :contact, :phone, :comment)"
),
node,
)
# last_insert_id = session.execute(text("SELECT LAST_INSERT_ID()")).scalar()
# logging.info(f"last_insert_id: {last_insert_id}")
return 0
def update_node(self, node: dict) -> int:
with get_session() as session:
sql = (
"update enterprise_node "
"set name=:name, parent=:parent, addr=:addr, lola=:lola, contact=:contact, phone=:phone, comment=:comment where id=:id"
)
param = {
"id": node["id"],
"name": node["name"],
"parent": node["parent"],
"addr": node["addr"],
"lola": node["lola"],
"contact": node["contact"],
"phone": node["phone"],
"comment": node["comment"],
}
session.execute(text(sql), param)
return 0
def select_tree(self, entity_id: int, name: str = "") -> List[Dict[str, Any]]:
roots = []
with get_session() as session:
# sql = "select id, name, suid, parent from enterprise_node where entity_id=:entity_id and del=0 and parent=0 "
sql = (
"""
SELECT
n.id, n.name, n.parent, p.name AS parent_name, n.suid
FROM enterprise_node n
LEFT JOIN enterprise_node p ON n.parent = p.id
WHERE n.entity_id=:entity_id AND n.del=0 and n.parent=0
"""
)
param = {"entity_id": entity_id}
if name:
sql += " and n.name like :name"
param["name"] = f"%{name}%"
res = session.execute(text(sql), param)
node_list = to_json_list(res)
node_list = node_list and node_list or []
for node in node_list:
root = {
"id": node["id"],
"name": node["name"],
"suid": node["suid"],
"parent": node["parent"],
"parent_name": node["parent_name"],
"children": self.build_tree(session, node, name),
}
roots.append(root)
# return node_list
return roots
def build_tree(
self, session: Any, node: Dict[str, Any], name: str = ""
) -> List[Any]:
# sql = (
# "select id, name, suid, parent from enterprise_node where del=0 and parent=:parent"
# )
sql = (
"""
SELECT
n.id, n.name, n.parent, p.name AS parent_name, n.suid
FROM enterprise_node n
LEFT JOIN enterprise_node p ON n.parent = p.id
WHERE n.parent=:parent AND n.del=0
"""
)
param = {"parent": node["id"]}
if name:
sql += " and n.name like :name"
param["name"] = f"%{name}%"
res = session.execute(text(sql), param)
node_list = to_json_list(res)
node_list = node_list and node_list or []
children = []
for node in node_list:
child = {
"id": node["id"],
"name": node["name"],
"suid": node["suid"],
"parent": node["parent"],
"parent_name": node["parent_name"],
"children": self.build_tree(session, node, name),
}
children.append(child)
return children
def select_node(self, node_id: int) -> Dict[str, Any]:
with get_session() as session:
# sql = (
# "select id, name, parent, addr, lola, contact, phone, comment, suid from enterprise_node "
# "where id=:id and del=0"
# )
sql = (
"""
SELECT
n.id, n.name, n.parent, p.name AS parent_name, n.addr, n.lola, n.contact, n.phone, n.comment, n.suid
FROM enterprise_node n
LEFT JOIN enterprise_node p ON n.parent = p.id
WHERE n.id=:id AND n.del=0
"""
)
param = {"id": node_id}
res = session.execute(text(sql), param)
node_list = to_json_list(res)
node_list = node_list and node_list or []
return node_list[0] if node_list else None
def delete_node(self, node_id: int) -> int:
with get_session() as session:
sql = "update enterprise_node set del=1 where id=:id"
param = {"id": node_id}
session.execute(text(sql), param)
session.commit()
return 0
def get_node_by_id(self, node_id: int) -> dict:
with get_session() as session:
sql = "select suid, name from enterprise_node where id=:id"
param = {"id": node_id}
res = session.execute(text(sql), param)
node = to_json(res)
return node
def get_entity_suid_by_node_id(self, node_id: int) -> dict:
with get_session() as session:
res = session.execute(
text("select suid, entity_suid from enterprise_node where id=:id"),
{"id": node_id},
)
entity = to_json(res)
return entity
def simple_list(self, entity_id: int) -> List[Any]:
with get_session() as session:
sql = (
"select id, name from enterprise_node where del=0 and entity_id=:entity_id"
)
param = {"entity_id": entity_id}
res = session.execute(text(sql), param)
node_list = to_json_list(res)
return node_list

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
from typing import List, Dict, Union
from sqlalchemy import text
from website.db_mysql import get_session, to_json_list, to_json
class EnterpriseNodeAlertRepository(object):
def get_all(self) -> List[Dict]:
with get_session() as session:
sql = text(
"""SELECT * FROM `enterprise_alert` WHERE `enterprise_node_id`=:enterprise_node_id ORDER BY `created_at` DESC;"""
)
cursor = session.execute(
sql, {"enterprise_node_id": self.enterprise_node_id}
)
return to_json_list(cursor)
def get_one(
self, entity_suid: str, node_id: int
) -> Union[Dict, None]:
with get_session() as session:
sql = text(
"""SELECT * FROM `enterprise_alert` WHERE `entity_suid`=:entity_suid and `node_id`=:node_id;"""
)
cursor = session.execute(
sql, {"entity_suid": entity_suid, "node_id": node_id}
)
return to_json(cursor)
def update_or_inert(self, data: Dict) -> None:
with get_session() as session:
sql = text(
"""
INSERT INTO `enterprise_alert` (`entity_suid`, `node_id`, `node_suid`, `is_sms`, `sms_to`, `is_email`, `email_to`, `freq`)
VALUES (:entity_suid, :node_id, :node_suid, :is_sms, :sms_to, :is_email, :email_to, :freq)
ON DUPLICATE KEY UPDATE
`node_suid`=:node_suid, `is_sms`=:is_sms, `sms_to`=:sms_to, `is_email`=:is_email, `email_to`=:email_to, `freq`=:freq
"""
)
session.execute(sql, data)
session.commit()

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
import copy
import json
import logging
from typing import Any, Dict, List, Optional
from sqlalchemy import Column, Integer, String, DateTime, func
from sqlalchemy.ext.declarative import declarative_base
from website.db.alg_model import alg_model as DB_alg_model
from website.db.enterprise_entity.enterprise_entity import EnterpriseEntityRepository
from website.db.enterprise_node import enterprise_node as DB_Node
from website.db_mysql import get_session
from website.util import shortuuid
Base = declarative_base()
class EnterpriseNodeDeviceBMCusConf(Base):
"""企业设备关联业务模型下的基础模型自定义配置"""
__tablename__ = 'enterprise_node_device_bm_cus_conf'
id = Column(Integer, primary_key=True, autoincrement=True)
entity_id = Column(Integer, nullable=True)
entity_suid = Column(String(255), nullable=True)
node_id = Column(Integer, nullable=True)
node_suid = Column(String(255), nullable=True)
device_id = Column(Integer, nullable=True)
device_suid = Column(String(255), nullable=True)
busi_model_id = Column(Integer, nullable=True)
busi_model_suid = Column(String(255), nullable=True)
base_model_id = Column(Integer, nullable=True)
base_model_suid = Column(String(255), nullable=True)
busi_conf_file = Column(String(255), nullable=True)
busi_conf_str = Column(String(255), nullable=True)
model_hub_image = Column(String(255), nullable=True)
model_conf_file = Column(String(255), nullable=True)
model_conf_str = Column(String(255), nullable=True)
create_time = Column(DateTime, server_default=func.now(), onupdate=func.now())
def __repr__(self):
return f"EnterpriseNodeDeviceBMCusConf(id={self.id}, entity_id={self.entity_id})"
class EnterpriseNodeDeviceBMCusConfRepository():
def create_busi_model_custom_config(self, data: Dict[str, Any]):
with get_session() as session:
session.add(EnterpriseNodeDeviceBMCusConf(**data))
session.commit()
return
def get_busi_model_custom_config(self, node_id: int, device_id: int, busi_model_id: int, base_model_id: int):
with get_session() as session:
data = session.query(EnterpriseNodeDeviceBMCusConf).filter(
EnterpriseNodeDeviceBMCusConf.node_id == node_id,
EnterpriseNodeDeviceBMCusConf.device_id == device_id,
EnterpriseNodeDeviceBMCusConf.busi_model_id == busi_model_id,
EnterpriseNodeDeviceBMCusConf.base_model_id == base_model_id
).first()
return data

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
from typing import List, Dict, Any, Type
from sqlalchemy import Column, Integer, String, DateTime, func, text
from sqlalchemy.ext.declarative import declarative_base
from website.db_mysql import get_session, to_json_list, to_json, dict_to_obj
"""
CREATE TABLE `files` (
`id` int NOT NULL AUTO_INCREMENT,
`filename` varchar(255) NOT NULL DEFAULT '',
`filepath` varchar(255) NOT NULL DEFAULT '',
`md5_str` varchar(32) NOT NULL DEFAULT '',
`filesize` int DEFAULT NULL,
`filetype` varchar(50) DEFAULT '',
`user` int DEFAULT '0',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='文件表';
"""
Base = declarative_base()
class File(Base):
__tablename__ = 'files'
id = Column(Integer, primary_key=True, autoincrement=True)
filename = Column(String(255), nullable=False, default='')
filepath = Column(String(255), nullable=False, default='')
md5_str = Column(String(32), nullable=False, default='')
filesize = Column(Integer, nullable=True)
filetype = Column(String(50), nullable=True, default='')
user = Column(Integer, nullable=True, default=0)
create_time = Column(DateTime, nullable=False, default=func.now())
def __repr__(self):
return f"File(id={self.id}, filename='{self.filename}', filepath='{self.filepath}')"
class FileRepository(object):
def get_file_by_md5(self, md5_list: List[str]) -> List[File]:
resp = []
with get_session() as session:
# return (
# session.query(File).filter(File.md5_str == md5_str).first()
# )
files = session.query(File).filter(File.md5_str.in_(md5_list)).all()
for file in files:
obj_dict = file.__dict__
del obj_dict['_sa_instance_state']
print(obj_dict)
resp.append(dict_to_obj(obj_dict))
# return session.query(File).filter(File.md5_str.in_(md5_list)).all()
return resp

@ -0,0 +1,107 @@
import contextlib
import itertools
from contextlib import asynccontextmanager
from typing import List, Any, Optional
from sqlalchemy import create_engine
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from website import settings
from website.handler import Row
# class Row(dict):
# """A dict that allows for object-like property access syntax."""
#
# def __getattr__(self, name):
# try:
# return self[name]
# except KeyError:
# raise AttributeError(name)
def dict_to_obj(d):
return type('GenericClass', (object,), d)()
def to_json_list(cursor: Any) -> Optional[List[Row]]:
column_names = list(cursor.keys())
result = cursor.fetchall()
if not result:
return []
return [Row(itertools.zip_longest(column_names, row)) for row in result]
def to_json(cursor):
column_names = list(cursor.keys())
result = cursor.fetchone()
if not result:
return {}
return Row(itertools.zip_longest(column_names, result))
app_engine = create_engine(
"mysql+pymysql://{}:{}@{}/{}?charset=utf8mb4".format(
settings.mysql_app["user"],
settings.mysql_app["password"],
settings.mysql_app["host"],
settings.mysql_app["database"],
), # SQLAlchemy 数据库连接串,格式见下面
echo=bool(settings.SQLALCHEMY_ECHO), # 是不是要把所执行的SQL打印出来一般用于调试
pool_pre_ping=True,
pool_size=int(settings.SQLALCHEMY_POOL_SIZE), # 连接池大小
max_overflow=int(settings.SQLALCHEMY_POOL_MAX_SIZE), # 连接池最大的大小
pool_recycle=int(settings.SQLALCHEMY_POOL_RECYCLE), # 多久时间回收连接
)
Session = sessionmaker(bind=app_engine, expire_on_commit=False)
# Base = declarative_base(app_engine)
@contextlib.contextmanager
def get_session():
s = Session()
try:
yield s
s.commit()
except Exception as e:
s.rollback()
raise e
finally:
s.close()
async_app_engine = create_async_engine(
"mysql+aiomysql://{}:{}@{}/{}?charset=utf8mb4".format(
settings.mysql_app["user"],
settings.mysql_app["password"],
settings.mysql_app["host"],
settings.mysql_app["database"],
),
echo=bool(settings.SQLALCHEMY_ECHO),
pool_pre_ping=True,
pool_size=int(settings.SQLALCHEMY_POOL_SIZE),
max_overflow=int(settings.SQLALCHEMY_POOL_MAX_SIZE),
pool_recycle=int(settings.SQLALCHEMY_POOL_RECYCLE),
)
AsyncSession = sessionmaker(bind=async_app_engine, class_=AsyncSession)
@asynccontextmanager
async def get_async_session():
async with AsyncSession() as s:
try:
yield s
await s.commit()
except Exception as e:
await s.rollback()
raise e
finally:
await s.close()

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
import logging
from tornado import escape
from tornado.web import HTTPError
# HTTP status code
HTTP_OK = 200
ERROR_BAD_REQUEST = 400
ERROR_UNAUTHORIZED = 401
ERROR_FORBIDDEN = 403
ERROR_NOT_FOUND = 404
ERROR_METHOD_NOT_ALLOWED = 405
ERROR_INTERNAL_SERVER_ERROR = 500
# Custom error code
ERROR_WARNING = 1001
ERROR_DEPRECATED = 1002
ERROR_MAINTAINING = 1003
ERROR_UNKNOWN_ERROR = 9999
ERROR_LICENSE_NOT_ACTIVE = 9000
ERROR_LICENSE_EXPIRE_ATALL = 9003
class HTTPAPIError(HTTPError):
"""API error handling exception
API server always returns formatted JSON to client even there is
an internal server error.
"""
def __init__(self, status_code=ERROR_UNKNOWN_ERROR, message=None,
error=None, data=None, log_message=None, *args):
assert isinstance(data, dict) or data is None
message = message if message else ""
assert isinstance(message, str)
super(HTTPAPIError, self).__init__(int(status_code),
log_message, *args)
self.error = error if error else \
_error_types.get(self.status_code, _unknow_error)
self.message = message
self.data = data if data is not None else {}
def __str__(self):
err = {"meta": {"code": self.status_code, "error": self.error}}
if self.data:
err["data"] = self.data
if self.message:
err["meta"]["message"] = self.message
return escape.json_encode(err)
# default errors
_unknow_error = "unknow_error"
_error_types = {400: "bad_request",
401: "unauthorized",
403: "forbidden",
404: "not_found",
405: "method_not_allowed",
500: "internal_server_error",
1001: "warning",
1002: "deprecated",
1003: "maintaining",
9000: "license_not_active",
9003: "license_expire_at_all",
9999: _unknow_error}

@ -0,0 +1,593 @@
# -*- coding: utf-8 -*-
import ast
import functools
import hashlib
import json
import logging
import re
import time
import traceback
import urllib
from typing import Any
# import urlparse
from urllib.parse import parse_qs, unquote
# from urllib import unquote
import tornado
from sqlalchemy import text
from tornado import escape
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from tornado.options import options
# from tornado.web import RequestHandler as BaseRequestHandler, HTTPError, asynchronous
from tornado.web import RequestHandler as BaseRequestHandler, HTTPError
from website import errors
from website import settings
from website.service.license import get_license_status
# from torndb import Row
if settings.enable_curl_async_http_client:
AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
else:
AsyncHTTPClient.configure(None, max_clients=settings.max_clients)
REMOVE_SLASH_RE = re.compile(".+/$")
class Row(dict):
"""A dict that allows for object-like property access syntax."""
def __getattr__(self, name):
try:
return self[name]
except KeyError:
raise AttributeError(name)
def _callback_wrapper(callback):
"""A wrapper to handling basic callback error"""
def _wrapper(response):
if response.error:
logging.error("call remote api err: %s" % response)
if isinstance(response.error, tornado.httpclient.HTTPError):
raise errors.HTTPAPIError(response.error.code, "网络连接失败")
else:
raise errors.HTTPAPIError(errors.ERROR_UNKNOWN_ERROR, "未知错误")
else:
callback(response)
return _wrapper
class BaseHandler(BaseRequestHandler):
# def __init__(self, application, request, **kwargs):
# super(BaseHandler, self).__init__(application, request, **kwargs)
# self.xsrf_form_html()
def set_default_headers(self):
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Headers",
"DNT,token,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,"
"Content-Type")
# self.set_header("Access-Control-Allow-Headers",
# "DNT,web-token,app-token,Authorization,Accept,Origin,Keep-Alive,User-Agent,X-Mx-ReqToken,"
# "X-Data-Type,X-Auth-Token,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range")
self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
# def _request_summary(self):
#
# return "%s %s %s(@%s)" % (self.request.method, self.request.uri, self.request.body.decode(),
# self.request.remote_ip)
def get(self, *args, **kwargs):
# enable GET request when enable delegate get to post
if settings.app_get_to_post:
self.post(*args, **kwargs)
else:
raise HTTPError(405)
def render(self, template_name, **kwargs):
if self.current_user:
if 'username' not in kwargs:
kwargs["username"] = self.current_user.name
if 'current_userid' not in kwargs:
kwargs["current_userid"] = self.current_user.id
if 'role' not in kwargs:
kwargs["role"] = self.current_user.role
self.set_header("Cache-control", "no-cache")
return super(BaseHandler, self).render(template_name, **kwargs)
# @property
# def db_app(self):
# return self.application.db_app
@property
def app_mysql(self):
return self.application.app_mysql
@property
def r_app(self):
return self.application.r_app
@property
def kafka_producer(self):
return self.application.kafka_producer
#
@property
def es(self):
return self.application.es
# @property
# def nsq(self):
# return self.application.nsq
def _call_api(self, url, headers, body, callback, method, callback_wrapper=_callback_wrapper):
start = 0
if callback_wrapper:
callback = callback_wrapper(callback)
try:
start = time.time()
AsyncHTTPClient().fetch(HTTPRequest(url=url,
method=method,
body=body,
headers=headers,
allow_nonstandard_methods=True,
connect_timeout=settings.remote_connect_timeout,
request_timeout=settings.remote_request_timeout,
follow_redirects=False),
callback)
except tornado.httpclient.HTTPError:
logging.error("requet from %s, take time: %s" % (url, (time.time() - start) * 1000))
# if hasattr(x, "response") and x.response:
# callback(x.response)
# else:
# logging.error("Tornado signalled HTTPError %s", x)
# raise x
# @asynchronous
async def call_api(self, url, headers=None, body=None, callback=None, method="POST"):
if callback is None:
callback = self.call_api_callback
if headers is None:
headers = self.request.headers
if body is None:
body = self.request.body
else:
# make sure it is a post request
headers["Content-Type"] = "application/x-www-form-urlencoded"
self._call_api(url, headers, body, callback, method)
def get_current_user(self, token_body=None):
# jid = self.get_secure_cookie(settings.cookie_key)
token = self.request.headers.get("token")
if token_body:
token = token_body
if not token:
return None
token = unquote(token)
jid = tornado.web.decode_signed_value(
settings.cookie_secret,
settings.secure_cookie_name,
token
)
jid = jid and str(jid, encoding="utf-8") or ""
key = settings.session_key_prefix % jid
user = self.r_app.get(key)
if user:
if "/user/info" in self.request.path:
pass
else:
self.r_app.expire(key, settings.session_ttl)
user = str(user, encoding='utf8') if isinstance(user, bytes) else user
# return Row(ast.literal_eval(str(user, encoding="utf-8")))
return Row(ast.literal_eval(user))
else:
return None
def md5compare(self, name):
string = unquote(name)
num1 = string.split("|")[0]
num2 = string.split("|")[1]
num3 = string.split("|")[2]
num = num1 + num2
md5string = hashlib.md5(num).hexdigest().upper()
if md5string == num3:
return True
else:
return False
def tostr(self, src):
return str(src, encoding='utf8') if isinstance(src, bytes) else src
def prepare(self):
self.remove_slash()
self.prepare_context()
self.set_default_jsonbody()
# self.traffic_threshold()
def set_default_jsonbody(self):
if self.request.headers.get('Content-Type') == 'application/json' and self.request.body:
# logging.info(self.request.headers.get('Content-Type'))
# if self.request.headers.get('Content-Type') == 'application/json; charset=UTF-8':
json_body = tornado.escape.json_decode(self.request.body)
for key, value in json_body.items():
if value is not None:
if type(value) is list:
self.request.arguments.setdefault(key, []).extend(value)
elif type(value) is dict:
self.request.arguments[key] = value
else:
self.request.arguments.setdefault(key, []).extend([bytes(str(value), 'utf-8')])
def traffic_threshold(self):
# if self.request.uri in ["/api/download"]:
# return
if not self.current_user:
user_id = self.request.remote_ip
else:
user_id = self.current_user.id
freq_key = "API:FREQ:%s:%s" % (user_id, int(time.time()) / 10)
send_count = self.r_app.incr(freq_key)
if send_count > settings.api_count_in_ten_second:
freq_key = "API:FREQ:%s:%s" % (user_id, int(time.time()) / 10 + 1)
self.r_app.setex(freq_key, send_count, 10)
raise errors.HTTPAPIError(
errors.ERROR_METHOD_NOT_ALLOWED, "请勿频繁操作")
if send_count == 1:
self.r_app.expire(freq_key, 10)
def prepare_context(self):
# self.nsq.pub(settings.nsq_topic_stats, escape.json_encode(self._create_stats_msg()))
pass
def remove_slash(self):
if self.request.method == "GET":
if REMOVE_SLASH_RE.match(self.request.path):
# remove trail slash in path
uri = self.request.path.rstrip("/")
if self.request.query:
uri += "?" + self.request.query
self.redirect(uri)
# def get_json_argument(self, name, default=BaseRequestHandler._ARG_DEFAULT):
def get_json_argument(self, name, default=object()):
json_body = tornado.escape.json_decode(self.request.body)
value = json_body.get(name, default)
return escape.utf8(value) if isinstance(value, str) else value
def get_int_json_argument(self, name, default=0):
try:
return int(self.get_json_argument(name, default))
except ValueError:
return default
def get_escaped_json_argument(self, name, default=None):
if default is not None:
return self.escape_string(self.get_json_argument(name, default))
else:
return self.get_json_argument(name, default)
# def get_argument(self, name, default=BaseRequestHandler._ARG_DEFAULT, strip=True):
def get_argument(self, name, default=object(), strip=True):
value = super(BaseHandler, self).get_argument(name, default, strip)
return escape.utf8(value) if isinstance(value, str) else value
# def get_int_argument(self, name, default=0):
def get_int_argument(self, name: Any, default: int = 0) -> int:
try:
return int(self.get_argument(name, default))
except ValueError:
return default
def get_float_argument(self, name, default=0.0):
try:
return float(self.get_argument(name, default))
except ValueError:
return default
def get_uint_arg(self, name, default=0):
try:
return abs(int(self.get_argument(name, default)))
except ValueError:
return default
def unescape_string(self, s):
return escape.xhtml_unescape(s)
def escape_string(self, s):
return escape.xhtml_escape(s)
def get_escaped_argument(self, name, default=None):
if default is not None:
return self.escape_string(self.get_argument(name, default))
else:
return self.get_argument(name, default)
def get_page_url(self, page, form_id=None, tab=None):
if form_id:
return "javascript:goto_page('%s',%s);" % (form_id.strip(), page)
path = self.request.path
query = self.request.query
# qdict = urlparse.parse_qs(query)
qdict = parse_qs(query)
for k, v in qdict.items():
if isinstance(v, list):
qdict[k] = v and v[0] or ''
qdict['page'] = page
if tab:
qdict['tab'] = tab
return path + '?' + urllib.urlencode(qdict)
def find_all(self, target, substring):
current_pos = target.find(substring)
while current_pos != -1:
yield current_pos
current_pos += len(substring)
current_pos = target.find(substring, current_pos)
class WebHandler(BaseHandler):
def finish(self, chunk=None, message=None):
callback = escape.utf8(self.get_argument("callback", None))
if callback:
self.set_header("Content-Type", "application/x-javascript")
if isinstance(chunk, dict):
chunk = escape.json_encode(chunk)
self._write_buffer = [callback, "(", chunk, ")"] if chunk else []
super(WebHandler, self).finish()
else:
self.set_header("Cache-control", "no-cache")
super(WebHandler, self).finish(chunk)
def write_error(self, status_code, **kwargs):
try:
exc_info = kwargs.pop('exc_info')
e = exc_info[1]
if isinstance(e, errors.HTTPAPIError):
pass
elif isinstance(e, HTTPError):
if e.status_code == 401:
self.redirect("/", permanent=True)
return
e = errors.HTTPAPIError(e.status_code)
else:
e = errors.HTTPAPIError(errors.ERROR_INTERNAL_SERVER_ERROR)
exception = "".join([ln for ln
in traceback.format_exception(*exc_info)])
if status_code == errors.ERROR_INTERNAL_SERVER_ERROR \
and not options.debug:
self.send_error_mail(exception)
if options.debug:
e.data["exception"] = exception
self.clear()
# always return 200 OK for Web errors
self.set_status(errors.HTTP_OK)
self.set_header("Content-Type", "application/json; charset=UTF-8")
self.finish(str(e))
except Exception:
logging.error(traceback.format_exc())
return super(WebHandler, self).write_error(status_code, **kwargs)
def send_error_mail(self, exception):
pass
class APIHandler(BaseHandler):
def finish(self, chunk=None, message=None):
if chunk is None:
chunk = {}
if isinstance(chunk, dict):
chunk = {"meta": {"code": errors.HTTP_OK}, "data": chunk}
if message:
chunk["message"] = message
callback = escape.utf8(self.get_argument("callback", None))
if callback:
self.set_header("Content-Type", "application/x-javascript")
if isinstance(chunk, dict):
chunk = escape.json_encode(chunk)
self._write_buffer = [callback, "(", chunk, ")"] if chunk else []
super(APIHandler, self).finish()
else:
self.set_header("Content-Type", "application/json; charset=UTF-8")
super(APIHandler, self).finish(chunk)
def write_error(self, status_code, **kwargs):
try:
exc_info = kwargs.pop('exc_info')
e = exc_info[1]
if isinstance(e, errors.HTTPAPIError):
pass
elif isinstance(e, HTTPError):
e = errors.HTTPAPIError(e.status_code)
else:
e = errors.HTTPAPIError(errors.ERROR_INTERNAL_SERVER_ERROR)
exception = "".join([ln for ln
in traceback.format_exception(*exc_info)])
if status_code == errors.ERROR_INTERNAL_SERVER_ERROR \
and not options.debug:
self.send_error_mail(exception)
if options.debug:
e.data["exception"] = exception
self.clear()
# always return 200 OK for API errors
self.set_status(errors.HTTP_OK)
self.set_header("Content-Type", "application/json; charset=UTF-8")
self.finish(str(e))
except Exception:
logging.error(traceback.format_exc())
return super(APIHandler, self).write_error(status_code, **kwargs)
def send_error_mail(self, exception):
"""Override to implement custom error mail"""
pass
class ErrorHandler(BaseHandler):
"""Default 404: Not Found handler."""
def prepare(self):
super(ErrorHandler, self).prepare()
raise HTTPError(errors.ERROR_NOT_FOUND)
class APIErrorHandler(APIHandler):
"""Default API 404: Not Found handler."""
def prepare(self):
super(APIErrorHandler, self).prepare()
raise errors.HTTPAPIError(errors.ERROR_NOT_FOUND)
def authenticated(method):
"""Decorate methods with this to require that the user be logged in.
Just raise 401
or be avaliable
"""
@functools.wraps(method)
def wrapper(self, *args, **kwargs):
if not self.current_user:
# raise HTTPError(401)
raise errors.HTTPAPIError(errors.ERROR_UNAUTHORIZED, "登录失效")
return method(self, *args, **kwargs)
return wrapper
def authenticated_admin(method):
@functools.wraps(method)
def wrapper(self, *args, **kwargs):
if int(self.current_user.role) != 1001:
# raise HTTPError(403)
raise errors.HTTPAPIError(errors.ERROR_FORBIDDEN, "permission denied")
return method(self, *args, **kwargs)
return wrapper
def operation_log(primary_menu, sub_menu, ope_type, content, comment):
"""
Add logging to a function. level is the logging
level, name is the logger name, and message is the
log message. If name and message aren't specified,
they default to the function's module and name.
"""
def decorate(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
with self.app_mysql.connect() as conn:
conn.execute(text(
"insert into sys_log(user, ip, primary_menu, sub_menu, op_type, content, comment) "
"values(:user, :ip, :primary_menu, :sub_menu, :op_type, :content, :comment)"
),
{"user": self.current_user.name,
"ip": self.request.headers[
"X-Forwarded-For"] if "X-Forwarded-For" in self.request.headers else self.request.remote_ip,
"primary_menu": primary_menu,
"sub_menu": sub_menu,
"op_type": ope_type,
"content": content,
"comment": comment
}
)
conn.commit()
return func(self, *args, **kwargs)
return wrapper
return decorate
def permission(codes):
def decorate(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
rows = self.db_app.query(
"select rp.permission from role_permission rp, user_role ur where rp.role=ur.role and ur.userid=%s",
self.current_user.id
)
permissions = [item["permission"] for item in rows]
for code in codes:
if code not in permissions:
raise errors.HTTPAPIError(errors.ERROR_FORBIDDEN, "permission denied")
return func(self, *args, **kwargs)
return wrapper
return decorate
def license_validate(codes):
def decorate(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
license_cache = self.r_app.get("system:license")
license_info = {}
if license_cache:
license_info = json.loads(self.tostr(license_cache))
else:
row = self.db_app.get("select syscode, expireat from license limit 1")
if row:
self.r_app.set("system:license",
json.dumps({"syscode": row["syscode"], "expireat": row["expireat"]}))
license_info = row
license_status = get_license_status(license_info)
# logging.info("license status is : {}, need : {}".format(license_status, codes))
if license_status not in codes:
raise errors.HTTPAPIError(errors.ERROR_LICENSE_NOT_ACTIVE, "系统License未授权")
# if not license_info:
# raise errors.HTTPAPIError(errors.ERROR_LICENSE_NOT_ACTIVE, "License未授权")
# expireat = int(license_info["expireat"])
# local_time = int(time.time())
# if local_time >= expireat:
# raise errors.HTTPAPIError(errors.ERROR_LICENSE_NOT_ACTIVE, "License授权过期")
return func(self, *args, **kwargs)
return wrapper
return decorate
def userlog(method):
@functools.wraps(method)
def wrapper(self, *args, **kwargs):
logging.info("[ip]:%s [user]:%s" % (self.request.remote_ip, self.current_user.id))
return method(self, *args, **kwargs)
return wrapper

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
class Model(object):
_dbs = {}
@classmethod
def setup_dbs(cls, dbs):
cls._dbs = dbs
@property
def db_app(self):
return self._dbs.get("db_app", None)
@property
def r_app(self):
return self._dbs.get("r_app", None)

@ -0,0 +1,697 @@
# -*- coding: utf-8 -*-
import logging
import os
from sqlalchemy import text
from website import consts
from website import db_mysql
from website import errors
from website import settings
from website.handler import APIHandler, authenticated
from website.util import md5, shortuuid
class ClassificationAddHandler(APIHandler):
"""
添加模型分类
"""
@authenticated
def post(self):
name = self.get_escaped_argument("name", "")
if not name:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
if len(name) > 128:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "分类名称过长")
with self.app_mysql.connect() as conn:
cur = conn.execute(
text("select id from model_classification where name=:name"),
{"name": name},
)
row = db_mysql.to_json(cur)
if row:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "分类名称重复")
conn.execute(
text(
"""insert into model_classification (name, create_time) values (:name, NOW())"""
),
{"name": name},
)
conn.commit()
self.finish()
class ClassificationEditHandler(APIHandler):
"""
编辑模型分类
"""
@authenticated
def post(self):
classification_id = self.get_int_argument("id")
name = self.get_escaped_argument("name", "")
if not classification_id or not name:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
with self.app_mysql.connect() as conn:
conn.execute(
text("""update model_classification set name=:name where id=:id"""),
{"name": name, "id": classification_id},
)
conn.commit()
self.finish()
class ClassificationListHandler(APIHandler):
"""
模型分类列表
"""
@authenticated
def post(self):
with self.app_mysql.connect() as conn:
cur = conn.execute(text("""select id, name from model_classification"""))
result = db_mysql.to_json_list(cur)
self.finish({"data": result})
class ClassificationDeleteHandler(APIHandler):
"""
删除模型分类
"""
@authenticated
def post(self):
classification_id = self.get_int_argument("id")
if not classification_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
with self.app_mysql.connect() as conn:
conn.execute(
text("""DELETE FROM model_classification WHERE id=:id"""),
{"id": classification_id},
)
conn.commit()
self.finish()
class ListHandler(APIHandler):
"""
模型列表
"""
@authenticated
def post(self):
pageNo = self.get_int_argument("pageNo", 1)
pageSize = self.get_int_argument("pageSize", consts.PAGE_SIZE)
name = self.get_escaped_argument("name", "")
result = []
with self.app_mysql.connect() as conn:
sql = (
"select m.id, m.name, m.model_type, m.default_version, mc.name classification_name, m.update_time "
"from model m left join model_classification mc on m.classification=mc.id where m.del=0 "
)
param = {}
sql_count = "select count(id) from model where del=0 "
param_count = {}
if name:
sql += "and m.name like :name"
param["name"] = "%{}%".format(name)
sql_count += "and m.name like :name"
param_count["name"] = "%{}%".format(name)
sql += " order by m.id desc limit :pageSize offset :offset"
param["pageSize"] = pageSize
param["offset"] = (pageNo - 1) * pageSize
cur = conn.execute(text(sql), param)
result = db_mysql.to_json_list(cur)
count = conn.execute(text(sql_count), param_count).fetchone()[0]
data = []
for item in result:
data.append(
{
"id": item["id"],
"name": item["name"],
"model_type": consts.model_type_map[item["model_type"]],
"classification_name": item["classification_name"],
"default_version": item["default_version"],
"update_time": str(item["update_time"]),
}
)
self.finish({"data": data, "count": count})
class ListSimpleHandler(APIHandler):
@authenticated
def post(self):
with self.app_mysql.connect() as conn:
sql = "select id, name from model where del=0"
cur = conn.execute(text(sql))
res = db_mysql.to_json_list(cur)
self.finish({"result": res})
class AddHandler(APIHandler):
"""
添加模型
"""
@authenticated
def post(self):
name = self.get_escaped_argument("name", "")
model_type = self.get_int_argument(
"model_type", consts.model_type_machine
) # 1001/经典算法1002/深度学习
classification = self.get_int_argument("classification")
comment = self.get_escaped_argument("comment", "")
if not name or not classification or model_type not in consts.model_type_map:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
with self.app_mysql.connect() as conn:
sql = text("select id from model_classification where id=:id")
cur = conn.execute(sql, {"id": classification})
if not cur.fetchone():
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "分类不存在")
conn.execute(
text(
"""insert into model (suid, name, model_type, classification, comment, create_time, update_time)
values (:suid, :name, :model_type, :classification, :comment, NOW(), NOW())"""
),
{
"suid": shortuuid.ShortUUID().random(10),
"name": name,
"model_type": model_type,
"classification": classification,
"comment": comment,
},
)
conn.commit()
self.finish()
class EditHandler(APIHandler):
"""
编辑模型
"""
@authenticated
def post(self):
mid = self.get_int_argument("id")
name = self.get_escaped_argument("name", "")
model_type = self.get_int_argument(
"model_type", consts.model_type_machine
) # 1001/经典算法1002/深度学习
classification = self.get_int_argument("classification")
comment = self.get_escaped_argument("comment", "")
if not name or not classification or model_type not in consts.model_type_map:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
with self.app_mysql.connect() as conn:
conn.execute(
text(
"""update model
set name=:name, model_type=:model_type, classification=:classification, comment=:comment,
update_time=NOW()
where id=:id"""
),
{
"name": name,
"model_type": model_type,
"classification": classification,
"comment": comment,
"id": mid,
},
)
conn.commit()
self.finish()
class InfoHandler(APIHandler):
"""
模型信息
"""
@authenticated
def post(self):
mid = self.get_int_argument("id")
if not mid:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
result = {}
with self.app_mysql.connect() as conn:
cur = conn.execute(
text(
"""
select
m.name, m.model_type, m.default_version, m.comment, m.update_time,
mc.id as classification_id, mc.name as classification_name
from model m, model_classification mc
where m.id=:id and m.classification=mc.id
"""
),
{"id": mid},
)
result = db_mysql.to_json(cur)
if not result:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "模型不存在")
data = {
"name": result["name"],
"model_type": result["model_type"],
"default_version": result["default_version"],
"classification_id": result["classification_id"],
"classification_name": result["classification_name"],
"comment": result["comment"],
"update_time": str(result["update_time"]),
}
self.finish(data)
class DeleteHandler(APIHandler):
"""
删除模型
"""
@authenticated
def post(self):
mid = self.get_int_argument("id")
if not mid:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
with self.app_mysql.connect() as conn:
conn.execute(text("update model set del=1 where id=:id"), {"id": mid})
conn.commit()
self.finish()
class VersionAddHandler(APIHandler):
"""
添加模型版本
- 描述 添加模型版本
- 请求方式post
- 请求参数
> - model_id, int, 模型id
> - version, string, 版本
> - comment, string备注
> - model_file, string, 模型文件的md5, 只允许上传一个文件
> - config_file, string, 模型配置文件的md5
> - config_str, string, 模型配置参数json格式字符串eg: '{"a":"xx","b":"xx"}'
- 返回值
"""
@authenticated
def post(self):
mid = self.get_int_argument("model_id")
version = self.get_escaped_argument("version", "")
comment = self.get_escaped_argument("comment", "")
model_file = self.get_escaped_argument("model_file", "")
config_file = self.get_escaped_argument("config_file", "")
config_str = self.get_escaped_argument("config_str", "")
if not mid or not version or not model_file:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
if not md5.md5_validate(model_file):
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "模型文件格式错误")
if config_file and not md5.md5_validate(config_file):
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "模型配置文件格式错误")
with self.app_mysql.connect() as conn:
conn.execute(
text(
"""
insert into model_version
(model_id, version, comment,model_file, config_file, config_str, create_time, update_time)
values (:model_id, :version, :comment, :model_file, :config_file, :config_str, NOW(), NOW())"""
),
{
"model_id": mid,
"version": version,
"comment": comment,
"model_file": model_file,
"config_file": config_file,
"config_str": config_str,
},
)
conn.commit()
self.finish()
class VersionEditHandler(APIHandler):
"""
编辑模型版本
- 描述 编辑模型版本
- 请求方式post
- 请求参数
> - version_id, int, 模型id
> - version, string, 版本
> - comment, string备注
> - model_file, string, 模型文件的md5, 只允许上传一个文件
> - config_file, string, 模型配置文件的md5
> - config_str, string, 模型配置参数json格式字符串eg: '{"a":"xx","b":"xx"}'
- 返回值
"""
@authenticated
def post(self):
version_id = self.get_int_argument("version_id")
version = self.get_escaped_argument("version", "")
comment = self.get_escaped_argument("comment", "")
model_file = self.get_escaped_argument("model_file", "")
config_file = self.get_escaped_argument("config_file", "")
config_str = self.get_escaped_argument("config_str", "")
if not version_id or not version or not model_file:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
if not md5.md5_validate(model_file):
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "模型文件格式错误")
if config_file and not md5.md5_validate(config_file):
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "模型配置文件格式错误")
with self.app_mysql.connect() as conn:
conn.execute(
text(
"update model_version "
"set version=:version, comment=:comment, model_file=:model_file, config_file=:config_file, "
"config_str=:config_str, update_time=NOW() where id=:id"
),
{
"version": version,
"comment": comment,
"model_file": model_file,
"config_file": config_file,
"config_str": config_str,
"id": version_id,
},
)
self.finish()
class VersionListHandler(APIHandler):
"""
模型版本列表
- 描述 模型版本列表
- 请求方式post
- 请求参数
> - model_id, int, 模型id
> - pageNo, int
> - pageSize, int
- 返回值
```
{
"count": 123,
"data": [
{
"version_id": 213, # 版本id
"version": "xx", # 版本号
"path": "xxx", # 文件路径
"size": 123,
"update_time": "xxx",
"is_default": 1 # 是否默认1/默认0/非默认
},
...
]
}
```
"""
@authenticated
def post(self):
model_id = self.get_int_argument("model_id")
pageNo = self.get_int_argument("pageNo", 1)
pageSize = self.get_int_argument("pageSize", 20)
if not model_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
count = 0
data = []
with self.app_mysql.connect() as conn:
# 查询数据从model_version, files查询version以及file的信息
cur = conn.execute(
text(
"""
select mv.id as version_id, mv.version, mv.model_file, mv.update_time, mv.is_default, f.filepath, f.filesize
from model_version mv
left join files f on mv.model_file=f.md5_str
where mv.model_id=:mid and mv.del=0
order by mv.id desc limit :offset, :limit
"""
),
{"mid": model_id, "offset": (pageNo - 1) * pageSize, "limit": pageSize},
)
result = db_mysql.to_json_list(cur)
# 获取记录数量
count = conn.execute(
text(
"""
select count(*)
from model_version mv
where mv.del=0 and model_id=:mid
"""
),
{"mid": model_id},
).scalar()
for item in result:
data.append(
{
"version_id": item["version_id"], # 版本id
"version": item["version"], # 版本号
"path": item["filepath"], # 文件路径
"size": item["filesize"],
"update_time": str(item["update_time"]),
"is_default": item["is_default"], # 是否默认1/默认0/非默认
}
)
self.finish({"count": count, "data": data})
class VersionInfoHandler(APIHandler):
"""
模型版本信息
- 描述 模型版本信息
- 请求方式post
- 请求参数
> - version_id, int, 模型id
- 返回值
```
{
"model_name": "xxx",
"version": "xxx", # 版本
"comment": "xxx", # 备注
"model_file_name": "xxx", # 模型文件名
"model_file_size": 123, # 模型文件大小
"model_file_md5": "xx", # 模型文件md5
"config_file_name": "xx", # 配置文件名
"config_file_size": "xx", # 配置文件大小
"config_file_md5": "xxx", # 配置文件md5
"config_str": "xxx" # 配置参数
}
```
"""
@authenticated
def post(self):
version_id = self.get_int_argument("version_id")
response = {
"model_name": "",
"version": "",
"comment": "",
"model_file_name": "",
"model_file_size": 0,
"model_file_md5": "",
"config_file_name": "",
"config_file_size": 0,
"config_file_md5": "",
"config_str": "",
}
with self.app_mysql.connect() as conn:
cur = conn.execute(
text(
"""select m.name as model_name, mv.version, mv.comment, mv.model_file, mv.config_file, mv.config_str
from model_version mv, model m where mv.id=:id and mv.model_id=m.id"""
),
{"id": version_id},
)
result = db_mysql.to_json(cur)
if not result:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "模型版本不存在")
model_file = result["model_file"]
config_file = result["config_file"]
response["model_name"] = result["model_name"]
response["version"] = result["version"]
response["comment"] = result["comment"]
response["model_file_md5"] = model_file
response["config_file_md5"] = config_file
# 获取文件信息
if model_file:
cur_model_file = conn.execute(
text("select filename, filesize from files where md5_str=:md5_str"),
{"md5_str": model_file},
)
model_file_info = db_mysql.to_json(cur_model_file)
response["model_file_name"] = (
model_file_info["filename"] if model_file_info else ""
)
response["model_file_size"] = (
model_file_info["filesize"] if model_file_info else 0
)
if config_file:
cur_config_file = conn.execute(
text("select filename, filesize from files where md5_str=:md5_str"),
{"md5_str": config_file},
)
config_file_info = db_mysql.to_json(cur_config_file)
response["config_file_name"] = (
config_file_info["filename"] if config_file_info else ""
)
response["config_file_size"] = (
config_file_info["filesize"] if config_file_info else 0
)
response["config_str"] = self.unescape_string(result["config_str"])
self.finish(response)
class VersionSetDefaultHandler(APIHandler):
"""
设置模型版本为默认版本
- 描述 设置模型默认版本
- 请求方式post
- 请求参数
> - version_id, int, 模型版本id
> - model_id, int, 模型id
- 返回值
"""
@authenticated
def post(self):
version_id = self.get_int_argument("version_id")
model_id = self.get_int_argument("model_id")
if not version_id or not model_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
with self.app_mysql.connect() as conn:
conn.execute(
text("update model_version set is_default=0 where model_id=:model_id"),
{"model_id": model_id},
)
conn.execute(
text("update model_version set is_default=1 where id=:id"),
{"id": version_id},
)
conn.execute(
text("update model m set m.default_version=(select version from model_version v where v.id=:vid) "
"where m.id=:mid"),
{"vid": version_id, "mid": model_id}
)
conn.commit()
self.finish()
class VersionDeleteHandler(APIHandler):
"""
删除模型版本
- 描述 删除模型版本
- 请求方式post
- 请求参数
> - version_id, int, 模型版本id
- 返回值
"""
@authenticated
def post(self):
version_id = self.get_int_argument("version_id")
if not version_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
row = {}
# 获取模型对应的model_file, config_file使用model_file, config_file删除对应的存储文件
with self.app_mysql.connect() as conn:
cur = conn.execute(
text(
"select model_id, model_file, config_file from model_version where id=:id"
),
{"id": version_id},
)
row = db_mysql.to_json(cur)
if not row:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "模型版本不存在")
model_file = row["model_file"]
config_file = row["config_file"]
model_id = row["model_id"]
# 清空模型默认版本
conn.execute(
text("update model set default_version='' where id=:id"),
{"id": model_id},
)
# 删除文件
try:
conn.execute(
text("delete from files where md5_str=:md5_str"),
{"md5_str": model_file},
)
conn.execute(
text("delete from files where md5_str=:md5_str"),
{"md5_str": config_file},
)
os.remove(settings.file_upload_dir + "model/" + model_file)
os.remove(settings.file_upload_dir + "model/" + config_file)
except Exception as e:
logging.info(e)
conn.execute(
text("delete from model_version where id=:id"), {"id": version_id}
)
conn.commit()
self.finish()

@ -0,0 +1,26 @@
from website.handlers.alg_model import handler
handlers = [
# ("/", handler.Handler),
("/model/classification/add", handler.ClassificationAddHandler),
("/model/classification/list", handler.ClassificationListHandler),
("/model/classification/delete", handler.ClassificationDeleteHandler),
("/model/list", handler.ListHandler),
("/model/list/simple", handler.ListSimpleHandler),
("/model/add", handler.AddHandler),
("/model/edit", handler.EditHandler),
("/model/info", handler.InfoHandler),
("/model/delete", handler.DeleteHandler),
("/model/version/add", handler.VersionAddHandler),
("/model/version/edit", handler.VersionEditHandler),
("/model/version/list", handler.VersionListHandler),
("/model/version/info", handler.VersionInfoHandler),
("/model/version/setdefault", handler.VersionSetDefaultHandler),
("/model/version/delete", handler.VersionDeleteHandler),
]
page_handlers = [
]

@ -0,0 +1,276 @@
# -*- coding: utf-8 -*-
import logging
import os
import requests
from sqlalchemy import text
from website import consts
from website import db_mysql
from website import errors
from website import settings
from website.handler import APIHandler, authenticated
class ListHandler(APIHandler):
"""
- 描述 模型运行库列表
- 请求方式post
- 请求参数
> - pageNo, int
> - pageSize, int
> - name, string, 名称
- 返回值
```
{
"count": 123,
"data": [
{
"id": 123,
"name": "xx",
"create_time": "xxx",
"update_time": "xxx"
},
...
]
}
```
"""
@authenticated
def post(self):
pageNo = self.get_int_argument("pageNo", 1)
pageSize = self.get_int_argument("pageSize", consts.PAGE_SIZE)
name = self.get_escaped_argument("name", "")
result = []
count = 0
with self.app_mysql.connect() as conn:
sql = "select id, name, path, create_time, update_time from model_hub where 1=1"
param = {}
sql_count = "select count(id) from model_hub where 1=1"
param_count = {}
if name:
sql += " and name like :name"
param["name"] = "%{}%".format(name)
sql_count += " and name like :name"
param_count["name"] = "%{}%".format(name)
sql += " order by id desc limit :pageSize offset :offset"
param["pageSize"] = pageSize
param["offset"] = (pageNo - 1) * pageSize
cur = conn.execute(text(sql), param)
result = db_mysql.to_json_list(cur)
count = conn.execute(text(sql_count), param_count).fetchone()[0]
data = []
for item in result:
data.append(
{
"id": item["id"],
"name": item["name"],
"path": item["path"],
"create_time": item["create_time"].strftime("%Y-%m-%d %H:%M:%S"),
"update_time": item["update_time"].strftime("%Y-%m-%d %H:%M:%S"),
}
)
self.finish({"count": count, "data": data})
class SyncHandler(APIHandler):
"""
- 描述 查询docker registry中的镜像
- 请求方式post
- 请求参数
> - host, string, ip地址
> - port, int, 端口
- 返回值
```
{
"data": [
"xxx", # docker registry中docker images的地址
"xxx",
...
]
}
```
"""
@authenticated
def post(self):
host = self.get_escaped_argument("host", "")
port = self.get_int_argument("port")
if not host or not port:
raise errors.HTTPAPIError(
errors.ERROR_BAD_REQUEST, "host and port must be provided."
)
images = []
# 查询docker registry中的镜像
repositories = requests.get(
"http://{}:{}/v2/_catalog".format(host, port)
).json()["repositories"]
for repository in repositories:
# 查询docker registry中的镜像的tag
tags = requests.get(
"http://{}:{}/v2/{}/tags/list".format(host, port, repository)
).json()["tags"]
for tag in tags:
image_name = "{}:{}/{}:{}".format(host, port, repository, tag)
images.append(image_name)
self.finish({"data": images})
class AddHandler(APIHandler):
"""
- 描述 新建模型运行库
- 请求方式post
- 请求参数
> - name, string, 名称
> - host, string,
> - port, int
> - path, string, 镜像路径
> - comment, string, 备注
- 返回值
"""
@authenticated
def post(self):
name = self.get_escaped_argument("name", "")
host = self.get_escaped_argument("host", "")
port = self.get_int_argument("port")
path = self.get_escaped_argument("path", "")
comment = self.get_escaped_argument("comment", "")
if not name or not host or not port:
raise errors.HTTPAPIError(
errors.ERROR_BAD_REQUEST, "name and host and port must be provided."
)
with self.app_mysql.connect() as conn:
conn.execute(
text(
"""insert into model_hub (name, host, port, path, comment, create_time, update_time)
values (:name, :host, :port, :path, :comment, NOW(), NOW())"""
),
{
"name": name,
"host": host,
"port": port,
"path": path,
"comment": comment,
},
)
conn.commit()
self.finish()
class EditHandler(APIHandler):
"""
- 描述 编辑模型运行库
- 请求方式post
- 请求参数
> - id, int
> - name, string, 名称
> - host, string,
> - port, int
> - path, string, 镜像路径
> - comment, string, 备注
- 返回值
"""
@authenticated
def post(self):
id = self.get_int_argument("id")
name = self.get_escaped_argument("name", "")
host = self.get_escaped_argument("host", "")
port = self.get_int_argument("port")
path = self.get_escaped_argument("path", "")
comment = self.get_escaped_argument("comment", "")
if not id or not name or not host or not port or path:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "parameter error")
with self.app_mysql.connect() as conn:
conn.execute(
text(
"""update model_hub set name=:name, host=:host, port=:port, path=:path, comment=:comment, update_time=NOW()
where id=:id"""
),
{
"id": id,
"name": name,
"host": host,
"port": port,
"path": path,
"comment": comment,
},
)
conn.commit()
self.finish()
class InfoHandler(APIHandler):
"""
- 描述 模型运行库信息
- 请求方式post
- 请求参数
> - id, int
- 返回值
```
{
"name": "xxx",
"host": "xxx",
"port": 123,
"path": "xxx",
"comment": "xxx",
}
```
"""
@authenticated
def post(self):
hid = self.get_int_argument("id")
if not id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "parameter error")
result = {}
with self.app_mysql.connect() as conn:
cur = conn.execute(
text(
"""select name, host, port, path, comment from model_hub where id=:id"""
),
{"id": hid},
)
result = db_mysql.to_json(cur)
if not result:
raise errors.HTTPAPIError(
errors.ERROR_BAD_REQUEST, "model hub not found"
)
self.finish(result)
class DeleteHandler(APIHandler):
"""
- 描述 删除模型运行库
- 请求方式post
- 请求参数
> - id, int
- 返回值
"""
@authenticated
def post(self):
hid = self.get_int_argument("id")
if not id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "parameter error")
with self.app_mysql.connect() as conn:
conn.execute(text("""delete from model_hub where id=:id"""), {"id": hid})
conn.commit()
self.finish()

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
from website.handlers.alg_model_hub import handler
handlers = [
# ("/", handler.Handler),
("/model/hub/list", handler.ListHandler),
("/model/hub/sync", handler.SyncHandler),
("/model/hub/add", handler.AddHandler),
("/model/hub/edit", handler.EditHandler),
("/model/hub/info", handler.InfoHandler),
("/model/hub/delete", handler.DeleteHandler),
]
page_handlers = [
]

@ -0,0 +1,274 @@
# -*- coding: utf-8 -*-
import json
import logging
from sqlalchemy import text
from website import db_mysql, errors
from website.handler import APIHandler, authenticated
from website.util import shortuuid
from website.db.enterprise_busi_model import enterprise_busi_model as DB_BusiModel
from website.db.enterprise_busi_model import enterprise_busi_model_node_device as DB_BusiModelNodeDevice
from website.db.file import file as DB_File
class ListHandler(APIHandler):
"""
- 描述企业部署的业务模型列表
- 请求方式post
- 请求参数
> - pageNo
> - pageSize
> - entity_id, int, 企业id
- 返回值
```
{
"count": 123,
"data": [
{
"model_id": 123, # 模型id
"model_name": "xxx", # 模型名称
"create_time": "xxxx", # 创建时间
},
...
]
}
```
"""
@authenticated
def post(self):
pageNo = self.get_int_argument("pageNo", 1)
pageSize = self.get_int_argument("pageSize", 10)
entity_id = self.get_int_argument("entity_id")
if not entity_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
db_busimodel = DB_BusiModel.EnterpriseBusiModelRepository()
result = db_busimodel.list_enterprise_busi_model(entity_id=entity_id, page_no=pageNo, page_size=pageSize)
self.finish({"count": result["count"], "data": result["data"]})
class AddHandler(APIHandler):
"""
- 描述添加企业部署的业务模型
- 请求方式post
- 请求参数
> - entity_id, int, 企业id
> - name, string, 业务模型名称
> - comment, string, 简介
> - basemodel_ids, string, 基础模型id, 多个使用逗号分割
> - business_logic, string, 业务代码压缩包的md5
> - business_conf_file, string, 业务配置压缩包的文件md5
> - business_conf_param, string, 业务配置的参数json字符串eg: '{"a":1, "b":2}'
> - link_node_ids, string, 关联节点id, 多个节点逗号分割
- 返回值
"""
@authenticated
def post(self):
entity_id = self.get_int_argument("entity_id")
name = self.get_escaped_argument("name", "")
comment = self.get_escaped_argument("comment", "")
basemodel_ids = self.get_escaped_argument("basemodel_ids", "")
business_logic = self.get_escaped_argument("business_logic", "")
business_conf_file = self.get_escaped_argument("business_conf_file", "")
business_conf_param = self.get_escaped_argument("business_conf_param", "")
link_node_ids = self.get_escaped_argument("link_node_ids", "")
if not entity_id or not name or not basemodel_ids:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
if not business_logic:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "业务代码参数错误")
if not business_conf_file and not business_conf_param:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "业务配置参数错误")
if not link_node_ids:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "关联节点参数错误")
model_data = {
"entity_id": entity_id,
"name": name,
"comment": comment,
"basemodel_ids": basemodel_ids,
"business_logic": business_logic,
"business_conf_file": business_conf_file,
"business_conf_param": business_conf_param,
"link_node_ids": link_node_ids,
}
db_busimodel = DB_BusiModel.EnterpriseBusiModelRepository()
busimodel_id, busimodel_suid = db_busimodel.insert_busi_model(model_data)
model_node_data = {
"entity_id": entity_id,
"busimodel_id": busimodel_id,
"busimodel_suid": busimodel_suid,
"node_ids": link_node_ids
}
db_model_node = DB_BusiModel.EnterpriseBusiModelNodeRepository()
db_model_node.insert_busi_model_nodes(model_node_data)
self.finish()
class InfoHandler(APIHandler):
"""
- 描述企业部署的业务模型详情
- 请求方式post
- 请求参数
> - id, int, 业务模型id
- 返回值
```
{
"name": "xxx",
"comment": "xxx",
"basemodel_list": "xxx,xx,xx",
"business_logic": "xxx",
"business_conf_file": "xxx",
"business_conf_param": "xxx",
"link_node_list": "xxx,xx,xx"
}
```
"""
@authenticated
def post(self):
busimodel_id = self.get_int_argument("id")
if not busimodel_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
db_busimodel = DB_BusiModel.EnterpriseBusiModelRepository()
busi_model_data = db_busimodel.get_busi_model_by_id(busimodel_id)
if not busi_model_data:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "业务模型不存在")
db_file = DB_File.FileRepository()
files = db_file.get_file_by_md5([busi_model_data.business_conf_file, busi_model_data.business_logic])
business_conf_name = files[0].filename
business_logic_name = files[1].filename
# business_conf_name = db_file.get_file_by_md5(busi_model_data.business_conf_file)
# business_logic_name = db_file.get_file_by_md5(busi_model_data.business_logic)
basemodel_list = json.loads(busi_model_data.base_models)
entity_suid = busi_model_data.entity_suid
db_model_node = DB_BusiModel.EnterpriseBusiModelNodeRepository()
link_nodes = db_model_node.get_nodes_by_busi_model(busimodel_id, entity_suid)
link_node_list = []
for node in link_nodes:
link_node_list.append({"node_id": node["node_id"], "node_name": node["node_name"]})
self.finish({
"name": busi_model_data.name,
"comment": busi_model_data.comment,
"basemodel_list": basemodel_list, # 关联模型
"business_logic_name": business_logic_name, # 业务代码压缩包文件名
"business_logic_md5": busi_model_data.business_logic, # 业务代码压缩包md5
"business_conf_name": business_conf_name, # 业务参数配置文件名
"business_conf_md5": busi_model_data.business_conf_file, # 业务参数配置md5
"business_conf_param": busi_model_data.business_conf_param, # 业务参数配置的字段
"link_node_list": link_node_list # 关联节点的id多个id逗号分割
})
class EditHandler(APIHandler):
"""
- 描述企业部署的业务模型编辑
- 请求方式post
- 请求参数
> - id, int, 业务模型的id
> - name, string, 业务模型名称
> - comment, string, 简介
> - basemodel_ids, string, 基础模型id, 多个使用逗号分割
> - business_logic, string, 业务代码压缩包的md5
> - business_conf_file, string, 业务配置压缩包的文件md5
> - business_conf_param, string, 业务配置的参数json字符串eg: '{"a":1, "b":2}'
> - link_node_ids, string, 关联节点id, 多个节点逗号分割
- 返回值
"""
@authenticated
def post(self):
busimodel_id = self.get_int_argument("id")
if not busimodel_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
db_busimodel = DB_BusiModel.EnterpriseBusiModelRepository()
busi_model_data = db_busimodel.get_busi_model_by_id(busimodel_id)
if not busi_model_data:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "业务模型不存在")
name = self.get_escaped_argument("name", "")
comment = self.get_escaped_argument("comment", "")
basemodel_ids = self.get_escaped_argument("basemodel_ids", "")
business_logic = self.get_escaped_argument("business_logic", "")
business_conf_file = self.get_escaped_argument("business_conf_file", "")
business_conf_param = self.get_escaped_argument("business_conf_param", "")
link_node_ids = self.get_escaped_argument("link_node_ids", "")
if not name or not basemodel_ids:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
if not business_logic:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "业务代码参数错误")
if not business_conf_file and not business_conf_param:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "业务配置参数错误")
if not link_node_ids:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "关联节点参数错误")
# 更新业务模型
model_data = {
"id": busimodel_id,
"name": name,
"comment": comment,
"basemodel_ids": basemodel_ids,
"business_logic": business_logic,
"business_conf_file": business_conf_file,
"business_conf_param": business_conf_param,
"link_node_ids": link_node_ids,
}
db_busimodel.edit_busi_model(model_data)
# 获取业务模型的数据
busi_model = db_busimodel.get_busi_model_by_id(busimodel_id)
db_model_node = DB_BusiModel.EnterpriseBusiModelNodeRepository()
# 删除关联节点
db_model_node.delete_by_busi_model_id(busimodel_id)
# 插入关联节点
model_node_data = {
"entity_id": busi_model.entity_id,
"busimodel_id": busimodel_id,
"busimodel_suid": busi_model.suid,
"node_ids": link_node_ids
}
db_model_node.insert_busi_model_nodes(model_node_data)
self.finish()
class DeleteHandler(APIHandler):
@authenticated
def post(self):
busimodel_id = self.get_int_argument("id")
if not busimodel_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
db_con = DB_BusiModelNodeDevice.EnterpriseBusiModelNodeDeviceRepository()
one = db_con.get_one_by_busimodel(busimodel_id)
if one:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "该业务模型已关联设备,无法删除")
db_busimodel = DB_BusiModel.EnterpriseBusiModelRepository()
db_busimodel.delete_by_id(busimodel_id)
db_model_node = DB_BusiModel.EnterpriseBusiModelNodeRepository()
db_model_node.delete_by_busi_model_id(busimodel_id)
self.finish()

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
from website.handlers.enterprise_busi_model import handler
handlers = [
("/enterprise/model/deployment/list", handler.ListHandler),
("/enterprise/model/deployment/add", handler.AddHandler),
("/enterprise/model/deployment/info", handler.InfoHandler),
("/enterprise/model/deployment/edit", handler.EditHandler),
("/enterprise/model/delete", handler.DeleteHandler),
]
page_handlers = []

@ -0,0 +1,722 @@
# -*- coding: utf-8 -*-
import json
import logging
import random
from sqlalchemy import text
from website import consts
from website import db_mysql, errors
from website.db.alg_model.alg_model import ModelRepositry as DB_AlgModel
from website.db.device_classification import device_classification as DB_DeviceClassification
from website.db.enterprise_busi_model import (
enterprise_busi_model as DB_BusiModel,
)
from website.db.enterprise_busi_model import enterprise_busi_model_node_device as DB_BusiModelNodeDevice
from website.db.enterprise_device import enterprise_device as DB_Device
from website.db.enterprise_node import enterprise_node as DB_Node
from website.db.enterprise_node import enterprise_node_base_model_conf as DB_NodeBaseModelConf
from website.handler import APIHandler, authenticated
from website.util import date_util
from website.util import shortuuid
class DeviceClassificationAddHandler(APIHandler):
"""
- 描述添加设备分类
- 请求方式post
- 请求参数
>- entity_id, int, 企业id
>- name, string, 设备分类名称
- 返回值
```
{
"id": 123
}
```
"""
@authenticated
def post(self):
name = self.get_escaped_argument("name", "")
with self.app_mysql.connect() as conn:
cur = conn.execute(
text("SELECT id FROM device_classification WHERE name=:name and del=0"),
{"name": name},
)
row = cur.fetchone()
if row:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备分类已存在")
result = conn.execute(
text(
"INSERT INTO device_classification (name, suid) VALUES (:name, :suid)"
),
{"name": name, "suid": shortuuid.ShortUUID().random(10)},
)
conn.commit()
last_id = result.lastrowid
self.finish({"id": last_id})
class DeviceClassificationHandler(APIHandler):
"""
- 描述设备分类列表
- 请求方式post
- 请求参数
>-
- 返回值
```
{
"data": [
{
"id": 123,
"suid": "xxx",
"name": "xxx"
},
...
]
}
```
"""
@authenticated
def post(self):
with self.app_mysql.connect() as conn:
cur = conn.execute(
text(
"SELECT id, suid, name FROM device_classification where del=0 ORDER BY id DESC"
)
)
res = db_mysql.to_json_list(cur)
res = res and res or []
self.finish({"data": res})
class DeviceClassificationDeleteHandler(APIHandler):
"""
- 描述删除设备分类
- 请求方式post
- 请求参数
>- id
- 返回值
"""
@authenticated
def post(self):
did = self.get_int_argument("id")
with self.app_mysql.connect() as conn:
cur = conn.execute(
text(
"""
select d.id from enterprise_device d, device_classification c
where d.classification=c.suid and c.id=:id and c.del=0
"""
),
{"id": did},
)
rows = cur.fetchall()
if rows:
raise errors.HTTPAPIError(
errors.ERROR_BAD_REQUEST, "该分类使用中,无法删除"
)
conn.execute(
text("update device_classification set del=1 WHERE id=:id"), {"id": did}
)
conn.commit()
self.finish()
class DeviceAddHandler(APIHandler):
"""
- 描述企业节点添加设备
- 请求方式post
- 请求参数
>- entity_id, int, 企业id
>- node_id, int
>- name, string, 设备名称
>- addr, string, 设备位置
>- classification string, 设备分类
>- device_model, string, 设备型号
>- param, string, 设备参数
>- comment, string, 备注
- 返回值
```
```
"""
@authenticated
def post(self):
entity_id = self.get_int_argument("entity_id")
node_id = self.get_int_argument("node_id")
name = self.get_escaped_argument("name", "")
addr = self.get_escaped_argument("addr", "")
classification = self.get_escaped_argument("classification", "")
device_model = self.get_escaped_argument("device_model", "")
param = self.get_escaped_argument("param", "")
comment = self.get_escaped_argument("comment", "")
if not name:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备名称不能为空")
if not entity_id or not node_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空")
# 判断设备分类是否存在
db_classification = DB_DeviceClassification.DeviceClassificationReporitory()
row = db_classification.get_row_by_suid(classification)
if not row:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备分类不存在")
device_data = {
"entity_id": entity_id,
"node_id": node_id,
"name": name,
"addr": addr,
"classification": classification,
"device_model": device_model,
"param": param,
"comment": comment,
}
db_device = DB_Device.EnterpriseDeviceRepository()
db_device.add_device(device_data)
self.finish()
class DeviceEditHandler(APIHandler):
"""
- 描述企业节点编辑设备
- 请求方式post
- 请求参数
>- device_id, int, 设备id
>- name, string, 设备名称
>- addr, string, 设备位置
>- classification string, 设备分类的short uuid
>- device_model, string, 设备型号
>- param, string, 设备参数
>- comment, string, 备注
- 返回值
"""
@authenticated
def post(self):
device_id = self.get_int_argument("device_id")
name = self.get_escaped_argument("name", "")
addr = self.get_escaped_argument("addr", "")
classification = self.get_escaped_argument("classification", "")
device_model = self.get_escaped_argument("device_model", "")
param = self.get_escaped_argument("param", "")
comment = self.get_escaped_argument("comment", "")
if not device_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备id不能为空")
if not name:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备名称不能为空")
device_data = {
"id": device_id,
"name": name,
"addr": addr,
"classification": classification,
"device_model": device_model,
"param": param,
"comment": comment,
}
db_device = DB_Device.EnterpriseDeviceRepository()
db_device.edit_device(device_data)
self.finish()
class DeviceDeleteHandler(APIHandler):
"""
- 描述企业节点删除设备
- 请求方式post
- 请求参数
>- node_id, int
>- device_id, int, 设备id
- 返回值
"""
@authenticated
def post(self):
# node_id = self.get_int_argument('node_id')
device_id = self.get_int_argument("device_id")
if not device_id:
raise errors.HTTPAPIError(
errors.ERROR_BAD_REQUEST, "企业节点或设备不能为空"
)
db_device = DB_Device.EnterpriseDeviceRepository()
db_device.delete_device(device_id)
self.finish()
class DeviceListHandler(APIHandler):
"""
- 描述企业节点设备列表
- 请求方式post
- 请求参数
> - pageNo
> - pageSize
> - node_id, int, 节点id
- 返回值
```
{
"count": 123,
"data": [
{
"device_id": 123,
"device_name": "xxx",
"device_class": "xxx", 设备类型
"deployed": 0, # 是否部署, 0/未部署, 1/已部署
},
...
]
}
```
"""
@authenticated
def post(self):
node_id = self.get_int_argument("node_id")
pageNo = self.get_int_argument("pageNo", 1)
pageSize = self.get_int_argument("pageSize", 20)
if not node_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空")
db_device = DB_Device.EnterpriseDeviceRepository()
devices = db_device.list_devices(
node_id=node_id, pageNo=pageNo, pageSize=pageSize
)
logging.info(devices)
self.finish({"count": devices["total_count"], "data": devices["devices"]})
class DeviceListSimpleHandler(APIHandler):
"""获取节点下所有设备的简单信息"""
@authenticated
def post(self):
node_id = self.get_int_argument("node_id")
if not node_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空")
db_device = DB_Device.EnterpriseDeviceRepository()
devices = db_device.list_simple_devices(node_id=node_id)
logging.info(devices)
self.finish({"data": devices})
class DeviceInfoHandler(APIHandler):
"""
- 描述企业节点设备信息
- 请求方式post
- 请求参数
> - device_id, int, 设备id
- 返回值
```
{
"name": "xxx",
"addr": "xxx",
"classification": "xxx",
"device_model": "xxx",
"param": "xxx",
"comment": "xxx",
}
```
"""
def post(self):
device_id = self.get_int_argument("device_id")
if not device_id:
raise errors.HTTPAPIError(
errors.ERROR_BAD_REQUEST, "企业节点或设备不能为空"
)
db_device = DB_Device.EnterpriseDeviceRepository()
device = db_device.get_device(device_id=device_id)
logging.info(device)
self.finish(device)
class DeviceBasemodelListHandler(APIHandler):
"""
- 描述企业节点节点信息 -> 设备列表 -> 基础模型配置 -> 模型列表
- 请求方式post
- 请求参数
> - pageNo
> - pageSize
> - device_id, int, 设备id
- 返回值
```
{
"count": 123,
"data": [
{
"busi_model": "xxx", # 业务模型的名称
"base_model": [
{
"model_id": 123, # 基础模型id
"model_name": "xxx" # 基础模型name
"model_version": "xxx", # 基础模型的版本
"model_hub_image": "xxx", # 运行库镜像
},
...
]
},
...
]
}
```
"""
@authenticated
def post(self):
device_id = self.get_int_argument("device_id")
if not device_id:
raise errors.HTTPAPIError(
errors.ERROR_BAD_REQUEST, "企业节点或设备不能为空"
)
pageNo = self.get_int_argument("pageNo", 1)
pageSize = self.get_int_argument("pageSize", 10)
db_busi_model = DB_BusiModelNodeDevice.EnterpriseBusiModelNodeDeviceRepository()
busi_models, count = db_busi_model.get_busi_model_by_device(device_id=device_id, pagination=True,
page_no=pageNo,
page_size=pageSize)
if count == 0:
self.finish({"count": count, "data": []})
return
res = []
for item in busi_models:
busi_model_id = item["busi_model_id"]
busi_model_name = item["name"]
base_model_list = json.loads(item["base_models"])
logging.info("##############################################################")
logging.info(base_model_list)
logging.info("##############################################################")
base_models = []
for base_model in base_model_list:
base_model_id = base_model["id"]
base_model_suid = base_model["suid"]
base_model_name = base_model["name"]
db_alg_model = DB_AlgModel()
base_model = db_alg_model.get_model_dict_by_id(base_model_id)
base_model_version = base_model["default_version"]
db_conf = DB_NodeBaseModelConf.EnterpriseNodeDeviceBMCusConfRepository()
conf = db_conf.get_busi_model_custom_config(
node_id=item["node_id"],
device_id=device_id,
busi_model_id=busi_model_id,
base_model_id=base_model_id
)
base_model_hub_image = ""
if conf:
base_model_hub_image = conf.model_hub_image
base_models.append({
"model_id": base_model_id,
"model_name": base_model_name,
"model_version": base_model_version,
"model_hub_image": base_model_hub_image,
})
res.append({
"busi_model_id": busi_model_id,
"busi_model_name": busi_model_name,
"base_models": base_models
})
self.finish({"count": count, "data": res})
class DeviceBaseModelCustomConfigHandler(APIHandler):
"""
- 描述企业节点节点信息 -> 设备列表 -> 基础模型配置 -> 基础模型参数配置
- 请求方式post
- 请求参数
> - device_id, int, 设备id
> - node_id, int, 节点id
> - base_model_id, int, 基础模型id
> - busi_conf_file, string, 业务参数配置文件md5
> - busi_conf_str, string, 业务参数配置json字符串eg:'{"key1": "value1", "key2": "value2"}'
> - model_hub_image, string, 运行库地址
> - model_conf_file, string, 模型参数配置文件md5
> - model_conf_str, string, 模型参数配置json字符串eg:'{"key1": "value1", "key2": "value2"}'
- 返回值
"""
@authenticated
def post(self):
device_id = self.get_int_argument("device_id")
node_id = self.get_int_argument("node_id")
busi_model_id = self.get_int_argument("busi_model_id")
base_model_id = self.get_int_argument("base_model_id")
busi_conf_file = self.get_escaped_argument("busi_conf_file")
busi_conf_str = self.get_escaped_argument("busi_conf_str")
model_hub_image = self.get_escaped_argument("model_hub_image")
model_conf_file = self.get_escaped_argument("model_conf_file")
model_conf_str = self.get_escaped_argument("model_conf_str")
db_device = DB_Device.EnterpriseDeviceRepository()
device = db_device.get_device(device_id=device_id)
if not device:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "device not exist")
if device["node_id"] != node_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "device not belong to this node")
db_alg_model = DB_AlgModel()
base_model = db_alg_model.get_model_dict_by_id(base_model_id)
if not base_model:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "base model not exist")
device_suid = device["suid"]
node_suid = device["node_suid"]
entity_id = device["entity_id"]
entity_suid = device["entity_suid"]
db_busi_model = DB_BusiModel.EnterpriseBusiModelRepository()
busi_model = db_busi_model.get_busi_model_by_id(busi_model_id)
busi_model_suid = busi_model.suid
data = dict()
data.update(
entity_id=entity_id,
entity_suid=entity_suid,
node_id=node_id,
node_suid=node_suid,
device_id=device_id,
device_suid=device_suid,
busi_model_id=busi_model_id,
busi_model_suid=busi_model_suid,
base_model_id=base_model_id,
base_model_suid=base_model["suid"],
busi_conf_file=busi_conf_file,
busi_conf_str=busi_conf_str,
model_hub_image=model_hub_image,
model_conf_file=model_conf_file,
model_conf_str=model_conf_str
)
db_conf = DB_NodeBaseModelConf.EnterpriseNodeDeviceBMCusConfRepository()
db_conf.create_busi_model_custom_config(data=data)
self.finish()
class StatusListHandler(APIHandler):
"""
- 描述设备状态列表
- 请求方式post
- 请求参数
> - entity_id, int, 企业id
> - classification_suid, string, 分类id
> - status, int, 状态1000/all/默认, 1001/在线1002/离线1003/运行中1004/故障
> - pageNo
> - pageSize
- 返回值
```
{
"count": 123,
"data": [
{
"id": 123,
"name": "xxx",
"status": 1001,
"cpu": 123,
"mem": 123,
"storage": 123,
"gpu": 123,
},
...
]
}
```
"""
@authenticated
def post(self):
entity_id = self.get_int_argument("entity_id")
group = self.get_int_argument("group")
classification = self.get_escaped_argument("classification", "")
status = self.get_int_argument("status", 1000)
pageNo = self.get_int_argument("pageNo", 1)
pageSize = self.get_int_argument("pageSize", 20)
if not entity_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空")
if status not in consts.device_status_map:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "状态参数错误")
db_device = DB_Device.EnterpriseDeviceRepository()
res = db_device.list_entity_devices(
entity_id=entity_id,
pageno=pageNo,
pagesize=pageSize,
classification=classification,
status=status,
group=group
)
count = res["count"]
devices = res["devices"]
data = []
for item, _ in devices:
logging.info(item)
data.append(
{
"id": item.id,
"name": item.name,
"status": item.status,
"cpu": random.randint(20, 30),
"mem": random.randint(20, 30),
"storage": random.randint(20, 30),
"gpu": random.randint(20, 30),
}
)
status_dic = {
consts.device_status_online: 0,
consts.device_status_offline: 0,
consts.device_status_ongoing: 0,
consts.device_status_error: 0,
}
status_count = db_device.status_count(entity_id=entity_id, classification=classification)
for key in status_count:
status_dic.update({key: status_count[key]})
self.finish({"count": count, "data": data, "status_count": status_dic})
class StatusGroupHandler(APIHandler):
@authenticated
def post(self):
entity_id = self.get_int_argument("entity_id")
db_node = DB_Node.EnterpriseNodeRepository()
res = db_node.simple_list(entity_id)
self.finish({"result": res})
class StatusInfoHandler(APIHandler):
""" """
@authenticated
def post(self):
device_id = self.get_int_argument("id")
if not device_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备id不能为空")
# 查询设备信息
db_device = DB_Device.EnterpriseDeviceRepository()
res = db_device.get_devices(device_ids=[device_id])
if not res:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备不存在")
res = res[0]
node_id = res["node_id"]
device_suid = res["suid"]
device_name = res["name"]
device_comment = res["comment"]
classification = res["classification"]
db_cls = DB_DeviceClassification.DeviceClassificationReporitory()
row_cls = db_cls.get_row_by_suid(suid=classification)
device_classification_name = row_cls.name
db_node = DB_Node.EnterpriseNodeRepository()
node_info = db_node.get_node_by_id(node_id)
node_name = node_info["name"]
db_busi_model = DB_BusiModelNodeDevice.EnterpriseBusiModelNodeDeviceRepository()
busi_models, _ = db_busi_model.get_busi_model_by_device(device_id=device_id)
models = []
for item in busi_models:
busi_model_id = item["busi_model_id"]
busi_model_name = item["name"]
base_model_list = json.loads(item["base_models"])
base_models = []
for base_model in base_model_list:
base_model_id = base_model["id"]
base_model_name = base_model["name"]
db_alg_model = DB_AlgModel()
base_model = db_alg_model.get_model_dict_by_id(base_model_id)
base_model_version = base_model["default_version"]
db_conf = DB_NodeBaseModelConf.EnterpriseNodeDeviceBMCusConfRepository()
conf = db_conf.get_busi_model_custom_config(busi_model_id=busi_model_id, device_id=device_id,
node_id=item["node_id"])
base_model_hub_image = ""
if conf:
base_model_hub_image = conf.model_hub_image
base_models.append({
"model_id": base_model_id,
"model_name": base_model_name,
"model_version": base_model_version,
"model_hub_image": base_model_hub_image,
})
models.append({
"busi_model": busi_model_name,
"base_models": base_models
})
self.finish({
"classification": device_classification_name,
"name": device_name,
"comment": device_comment,
"ID": device_suid,
"cpu": random.randint(20, 30),
"mem": random.randint(20, 30),
"storage": random.randint(20, 30),
"gpu": random.randint(20, 30),
"models": models,
"group": node_name
})
class StatusLogHandler(APIHandler):
"""
- 描述设备状态日志
- 请求方式post
- 请求参数
> - entity_id, int, 企业id
> - pageNo
> - pageSize
- 返回值
```
{
"count": 123,
"data": [
{
"ID": "xxx",
"name": "xxx",
"classification": "xxx",
"IP": "xxx",
"duration": "xxx",
}
]
}
```
"""
@authenticated
def post(self):
entity_id = self.get_int_argument("entity_id")
pageNo = self.get_int_argument("pageNo", 1)
pageSize = self.get_int_argument("pageSize", 10)
db_device = DB_Device.EnterpriseDeviceRepository()
res = db_device.list_entity_devices(entity_id=entity_id, pageno=pageNo, pagesize=pageSize)
count = res["count"]
devices = res["devices"]
result = []
for device, classification_name in devices:
device_name = device.name
device_suid = device.suid
device_ip = ""
duration = date_util.time_diff(str(device.create_time))
result.append({
"ID": device_suid,
"name": device_name,
"classification": classification_name,
"IP": device_ip,
"duration": duration,
})
self.finish({"count": count, "data": result})

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from website.handlers.enterprise_device import handler
handlers = [
("/enterprise/device/classification/add", handler.DeviceClassificationAddHandler),
("/enterprise/device/classification", handler.DeviceClassificationHandler),
(
"/enterprise/device/classification/delete",
handler.DeviceClassificationDeleteHandler,
),
("/enterprise/entity/nodes/device/add", handler.DeviceAddHandler),
("/enterprise/entity/nodes/device/edit", handler.DeviceEditHandler),
("/enterprise/entity/nodes/device/delete", handler.DeviceDeleteHandler),
("/enterprise/entity/nodes/device/list", handler.DeviceListHandler),
("/enterprise/entity/nodes/device/list/simple", handler.DeviceListSimpleHandler),
("/enterprise/entity/nodes/device/info", handler.DeviceInfoHandler),
("/enterprise/entity/nodes/device/basemodel/list", handler.DeviceBasemodelListHandler),
(
"/enterprise/entity/nodes/device/basemodel/custom/config",
handler.DeviceBaseModelCustomConfigHandler,
),
("/enterprise/device/status/list", handler.StatusListHandler),
("/enterprise/device/status/group", handler.StatusGroupHandler),
("/enterprise/device/status/info", handler.StatusInfoHandler),
("/enterprise/device/status/log", handler.StatusLogHandler),
]
page_handlers = []

@ -0,0 +1,395 @@
# -*- coding: utf-8 -*-
import asyncio
import json
import logging
from sqlalchemy import text
from website import consts
from website import db_mysql
from website import errors
from website import settings
from website.db.alg_model.alg_model import ModelRepositry
from website.db.enterprise import enterprise
from website.db.enterprise_device.enterprise_device import EnterpriseDeviceRepository
from website.handler import APIHandler, authenticated
from website.util import shortuuid, aes
# from concurrent.futures import ThreadPoolExecutor
# from functools import partial
class EntityIndexHandler(APIHandler):
"""首页"""
@authenticated
async def post(self):
pageNo = self.get_int_argument("pageNo", 1)
pageSize = self.get_int_argument("pageSize", 10)
name = self.tostr(self.get_escaped_argument("name", ""))
with self.app_mysql.connect() as conn:
sql_text = "select id, suid, name, industry, logo, create_time from enterprise where 1=1 "
param = {}
count_sql_text = "select count(id) c from enterprise where 1=1 "
count_param = {}
if name:
sql_text += "and name like :name"
param["name"] = "%{}%".format(name)
count_sql_text += "and name like :name"
count_param["name"] = "%{}%".format(name)
sql_text += " and del=0"
count_sql_text += " and del=0"
sql_text += " order by id desc limit :pageSize offset :offset"
param["pageSize"] = pageSize
param["offset"] = (pageNo - 1) * pageSize
cur = conn.execute(text(sql_text), param)
result = db_mysql.to_json_list(cur)
cur_count = conn.execute(text(count_sql_text), count_param)
count = db_mysql.to_json(cur_count)
count = count["c"] if count else 0
data = []
# for item in result:
# modelCount = enterprise.get_enterprise_model_count(item["id"])
# deviceCount = enterprise.get_enterprise_device_count(item["id"])
# data.append(
# {
# "id": item["id"],
# "name": item["name"],
# "industry": consts.industry_map[item["industry"]],
# "modelCount": modelCount,
# "deviceCount": deviceCount,
# "logo": item["logo"],
# "createTime": str(item["create_time"]),
# }
# )
# with ThreadPoolExecutor() as executor:
# get_count = partial(enterprise.get_enterprise_model_and_device_count)
# futures = [
# executor.submit(get_count, entity_id=item["id"], entity_suid="")
# for item in result
# ]
# results = [future.result() for future in futures]
# model_counts = [result[0] for result in results]
# device_counts = [result[1] for result in results]
# *用于解包参数列表将列表中的每个元素作为单独的参数传递给asyncio.gather函数
count_results = await asyncio.gather(
*[
enterprise.get_enterprise_model_and_device_count(entity_id=item["id"])
for item in result
]
)
model_counts = [result[0] for result in count_results]
device_counts = [result[1] for result in count_results]
data = [
{
"id": item["id"],
"name": item["name"],
"industry": consts.industry_map[item["industry"]],
"modelCount": model_count,
"deviceCount": device_count,
"logo": item["logo"],
"createTime": str(item["create_time"]),
}
for item, model_count, device_count in zip(
result, model_counts, device_counts
)
]
self.finish({"count": count, "data": data})
class EntityIndexBasecountHandler(APIHandler):
"""首页基础统计书记"""
@authenticated
def post(self):
entity_count = enterprise.get_enterprise_entity_count(self.app_mysql)
model_repository = ModelRepositry()
model_count = model_repository.get_model_count()
device_repository = EnterpriseDeviceRepository()
device_count = device_repository.get_all_device_count()
self.finish({"entity": entity_count, "model": model_count, "device": device_count})
class EntityAddHandler(APIHandler):
"""添加企业"""
@authenticated
def post(self):
name = self.tostr(self.get_escaped_argument("name", ""))
province = self.get_escaped_argument("province", "")
city = self.get_escaped_argument("city", "")
addr = self.get_escaped_argument("addr", "")
industry = self.get_int_argument("industry")
contact = self.get_escaped_argument("contact", "")
phone = self.get_escaped_argument("phone", "")
summary = self.get_escaped_argument("summary", "")
logo = self.get_escaped_argument("logo", "")
if (
not name
or not province
or not city
or not addr
or not industry
or not contact
or not phone
or not summary
):
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
if industry not in consts.industry_map:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "清选择行业类型")
if logo and len(logo) * 0.75 / 1024 / 1024 > 1.2:
raise errors.HTTPAPIError(
errors.ERROR_BAD_REQUEST, "Logo图标大小超出1M限制"
)
short_uid = shortuuid.ShortUUID().random(length=8)
pwd = aes.encrypt(settings.enterprise_aes_key, short_uid)
with self.app_mysql.connect() as conn:
conn.execute(
text(
"insert into enterprise(suid, name, province, city, addr, industry, contact, phone, summary, logo, account, pwd) "
"values(:suid, :name, :province, :city, :addr, :industry, :contact, :phone, :summary, :logo, :account, :pwd)"
),
{
"suid": shortuuid.ShortUUID().random(length=10),
"name": name,
"province": province,
"city": city,
"addr": addr,
"industry": industry,
"contact": contact,
"phone": phone,
"summary": summary,
"logo": logo,
"account": "admin",
"pwd": pwd,
},
)
conn.commit()
# self.db_app.insert(
# "insert into enterprise(name, province, city, addr, industry, contact, phone, summary, logo, account, pwd) "
# "values(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
# name, province, city, addr, industry, contact, phone, summary, logo, "admin", pwd,
# )
self.finish()
class EntityEditHandler(APIHandler):
"""编辑企业"""
@authenticated
def post(self):
eid = self.get_int_argument("id")
name = self.tostr(self.get_escaped_argument("name", ""))
province = self.get_escaped_argument("province", "")
city = self.get_escaped_argument("city", "")
addr = self.get_escaped_argument("addr", "")
industry = self.get_int_argument("industry")
contact = self.get_escaped_argument("contact", "")
phone = self.get_escaped_argument("phone", "")
summary = self.get_escaped_argument("summary", "")
logo = self.get_escaped_argument("logo", "")
account = self.get_escaped_argument("account", "")
if (
not name
or not province
or not city
or not addr
or not industry
or not contact
or not phone
or not summary
):
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
if industry not in consts.industry_map:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "清选择行业类型")
if logo and len(logo) * 0.75 / 1024 / 1024 > 1.2:
raise errors.HTTPAPIError(
errors.ERROR_BAD_REQUEST, "Logo图标大小超出1M限制"
)
with self.app_mysql.connect() as conn:
conn.execute(
text(
# "insert into enterprise(name, province, city, addr, industry, contact, phone, summary, logo, account, pwd) "
# "values(:name, :province, :city, :addr, :industry, :contact, :phone, :summary, :logo, :account, :pwd)"
"update enterprise set name=:name, province=:province, city=:city, addr=:addr, industry=:industry, contact"
"=:contact, phone=:phone, summary=:summary, logo=:logo, account=:account where id=:id",
),
{
"name": name,
"province": province,
"city": city,
"addr": addr,
"industry": industry,
"contact": contact,
"phone": phone,
"summary": summary,
"logo": logo,
"account": account,
"id": eid,
},
)
conn.commit()
self.finish()
class EntityInfoHandler(APIHandler):
"""企业信息"""
@authenticated
def post(self):
eid = self.get_int_argument("id")
row = {}
with self.app_mysql.connect() as conn:
cur = conn.execute(
text("select * from enterprise where id=:id"), {"id": eid}
)
# keys = list(cur.keys())
#
# one = cur.fetchone()
# row = dict(zip(keys, one))
# logging.info(db.Row(itertools.zip_longest(keys, one)))
row = db_mysql.to_json(cur)
cur.close()
if not row:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请求失败")
data = {
"name": row["name"],
"province": row["province"],
"city": row["city"],
"addr": row["addr"],
"industry": consts.industry_map[row["industry"]],
"expire_at": "",
"contact": row["contact"],
"phone": row["phone"],
"summary": row["summary"],
"logo": row["logo"],
"createTime": str(row["create_time"]),
"account": row["account"], # 企业账号
}
self.finish(data)
class ModelsHandler(APIHandler):
"""企业模型"""
@authenticated
def post(self):
eid = self.get_int_argument("id")
model_ids = []
with self.app_mysql.connect() as conn:
cur = conn.execute(
text("select base_models from enterprise_busi_model where entity_id=:eid"), {"eid": eid}
)
rows = db_mysql.to_json_list(cur)
for row in rows:
base_models = json.loads(row["base_models"])
model_ids.extend([item["id"] for item in base_models])
cur.close()
model_ids = list(set(model_ids))
cur = conn.execute(text(
"""
select m.name, m.model_type, mc.name as classification_name, m.default_version
from model m, model_classification mc
where m.classification=mc.id and m.id in :model_ids
"""
), {"model_ids": model_ids})
rows = db_mysql.to_json_list(cur)
cur.close()
data = []
for row in rows:
data.append({
"name": row["name"],
"model_type": consts.model_type_map[row["model_type"]],
"classification_name": row["classification_name"],
"default_version": row["default_version"]
})
self.finish({"count": len(model_ids), "data": data})
class EntityDeleteHandler(APIHandler):
"""删除企业"""
@authenticated
def post(self):
eid = self.get_int_argument("id")
with self.app_mysql.connect() as conn:
conn.execute(text("update enterprise set del=1 where id=:id"), {"id": eid})
conn.commit()
self.finish()
class EntityPwdcheckHandler(APIHandler):
"""查看企业密码"""
@authenticated
def post(self):
eid = self.get_int_argument("id")
with self.app_mysql.connect() as conn:
cur = conn.execute(
text("select pwd from enterprise where id=:id"), {"id": eid}
)
# row = cur.fetchone()
logging.info(cur)
row = db_mysql.to_json(cur)
if not row:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请求失败")
pwd = row["pwd"]
cur.close()
pwd_dcrypt = aes.decrypt(settings.enterprise_aes_key, pwd)
self.finish({"pwd": pwd_dcrypt})
class IndustryMapHandler(APIHandler):
@authenticated
def post(self):
self.finish(consts.industry_map)

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
from website.handlers.enterprise_entity import handler
handlers = [
("/enterprise/entity/index", handler.EntityIndexHandler),
("/enterprise/entity/index/basecount", handler.EntityIndexBasecountHandler),
("/enterprise/entity/add", handler.EntityAddHandler),
("/enterprise/entity/edit", handler.EntityEditHandler),
("/enterprise/entity/info", handler.EntityInfoHandler),
("/enterprise/entity/models", handler.ModelsHandler),
("/enterprise/entity/delete", handler.EntityDeleteHandler),
("/enterprise/entity/pwdcheck", handler.EntityPwdcheckHandler),
("/enterprise/industrymap", handler.IndustryMapHandler),
]
page_handlers = []

@ -0,0 +1,489 @@
# -*- coding: utf-8 -*-
import json
from website import errors
from website.db.enterprise_busi_model import enterprise_busi_model as DB_BusiModel
from website.db.enterprise_busi_model import (
enterprise_busi_model_node_device as DB_BusiModelNodeDevice,
)
from website.db.enterprise_device import enterprise_device as DB_Device
from website.db.enterprise_entity import enterprise_entity as DB_Entity
from website.db.enterprise_node import enterprise_node as DB_Node
from website.db.enterprise_node import enterprise_node_alert as DB_NodeAlert
from website.handler import APIHandler, authenticated
from website.util import shortuuid
class AddHandler(APIHandler):
"""
- 描述添加企业节点
- 请求方式post
- 请求参数
>- entity_suid, int, 企业id, short uuid
>- name, string, 名称
>- parent, int, 上级节点id
>- addr, string, 地址
>- lola, string, 经纬度longitude and latitude
>- contact, string, 负责人
>- phone, string, 联系方式
>- comment, string, 简介
- 返回值
"""
# @authenticated
def post(self):
entity_id = self.get_int_argument("entity_id")
entity_suid = self.get_escaped_argument("entity_suid", "")
name = self.get_escaped_argument("name", "")
parent = self.get_int_argument("parent", 0)
addr = self.get_escaped_argument("addr", "")
lola = self.get_escaped_argument("lola", "")
contact = self.get_escaped_argument("contact", "")
phone = self.get_escaped_argument("phone", "")
comment = self.get_escaped_argument("comment", "")
if not entity_id or not name:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
db_entity = DB_Entity.EnterpriseEntityRepository()
entity_suid = db_entity.get_entity_suid(entity_id)
db_node = DB_Node.EnterpriseNodeRepository()
db_node.insert_node(
{
"suid": shortuuid.ShortUUID().random(length=10),
"entity_id": entity_id,
"entity_suid": entity_suid,
"name": name,
"parent": parent,
"addr": addr,
"lola": lola,
"contact": contact,
"phone": phone,
"comment": comment,
}
)
self.finish()
class EditHandler(APIHandler):
"""
- 描述更新企业节点
- 请求方式post
- 请求参数
>- node_id, int, 节点id
>- name, string, 名称
>- parent, int, 上级节点id
>- addr, string, 地址
>- lola, string, 经纬度longitude and latitude
>- contact, string, 负责人
>- phone, string, 联系方式
>- comment, string, 简介
- 返回值
"""
# @authenticated
def post(self):
node_id = self.get_int_argument("node_id", 0)
name = self.get_escaped_argument("name", "")
parent = self.get_int_argument("parent", 0)
addr = self.get_escaped_argument("addr", "")
lola = self.get_escaped_argument("lola", "")
contact = self.get_escaped_argument("contact", "")
phone = self.get_escaped_argument("phone", "")
comment = self.get_escaped_argument("comment", "")
if not node_id or not name:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
db_node = DB_Node.EnterpriseNodeRepository()
db_node.update_node(
{
"id": node_id,
"name": name,
"parent": parent,
"addr": addr,
"lola": lola,
"contact": contact,
"phone": phone,
"comment": comment,
}
)
self.finish()
class TreeHandler(APIHandler):
"""
- 描述企业节点树
- 请求方式post
- 请求参数
>- entity_id, int, 企业id
>- name, string, 搜索内容
- 返回值
```
{
"data":[
{
"id": 123,
"name": "xxx",
"children": [
{
"id": 123,
"name": "xxx"
"children": [
{
"id": 123,
"name": "xxx"
},
]
},
...
]
},
...
]
}
```
"""
@authenticated
def post(self):
entity_id = self.get_escaped_argument("entity_id", "")
name = self.get_escaped_argument("name", "")
if not entity_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
db_node = DB_Node.EnterpriseNodeRepository()
nodes = db_node.select_tree(entity_id, name)
self.finish({"data": nodes})
class InfoHandler(APIHandler):
"""
- 描述企业节点信息
- 请求方式post
- 请求参数
>- node_id, int, 节点id
- 返回值
```
{
"name": "xxx",
"parent": "xxx",
"addr": "xxx",
"lola": "xxx",
"contact": "xxx",
"phone": "xxx",
"comment": "xxx",
}
```
"""
@authenticated
def post(self):
node_id = self.get_int_argument("node_id")
if not node_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
db_node = DB_Node.EnterpriseNodeRepository()
node = db_node.select_node(node_id)
node = node and node or {}
self.finish(node)
class DeleteHandler(APIHandler):
@authenticated
def post(self):
node_id = self.get_int_argument("node_id")
if not node_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
db_node = DB_Node.EnterpriseNodeRepository()
db_node.delete_node(node_id)
self.finish()
class BusimodelHandler(APIHandler):
"""
- 描述企业节点业务模型部署
- 请求方式post
- 请求参数
> - pageNo
> - pageSize
> - node_id, int, 节点id
- 返回值
```
{
"count": 123,
"data": [
{
"busi_model_id": 123, # 业务模型id
"busi_model_name": "xxx", # 业务模型name
"deployed": 0, # 是否部署
"create_time": "xxx" # 创建时间
},
...
]
}
```
"""
@authenticated
def post(self):
node_id = self.get_int_argument("node_id")
pageNo = self.get_int_argument("pageNo", 1)
pageSize = self.get_int_argument("pageSize", 20)
if not node_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空")
db_model_node = DB_BusiModel.EnterpriseBusiModelNodeRepository()
busi_models = db_model_node.get_busi_model_by_node_id(node_id, pageNo, pageSize)
count = busi_models["count"]
models = busi_models["data"]
db_mode_node_device = (
DB_BusiModelNodeDevice.EnterpriseBusiModelNodeDeviceRepository()
)
for busi_model in models:
deployed = db_mode_node_device.check_deployed(
node_id, busi_model["busi_model_id"]
)
busi_model["deployed"] = deployed
self.finish({"count": count, "data": models})
class BusimodelInfoHandler(APIHandler):
"""
- 描述企业节点业务模型部署 -> 业务模型信息
- 请求方式post
- 请求参数
> - node_id, int, 节点id
> - busi_model_id, int业务模型id
- 返回值
```
{
"busi_model_name": "xx", # 业务模型名称
"busi_model_comment": "xxx", # 简介
"base_models": "xxx,xx,xx,xx" # 关联基础模型
"devices": [
{
"device_id": 123,
"device_name": "xxx"
},
...
]
}
```
"""
@authenticated
def post(self):
node_id = self.get_int_argument("node_id")
busi_model_id = self.get_int_argument("busi_model_id")
if not node_id or not busi_model_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
db_busi_model = DB_BusiModel.EnterpriseBusiModelRepository()
busi_model = db_busi_model.get_busi_model_by_id(busi_model_id)
busi_model_name = busi_model.name
busi_model_comment = busi_model.comment
base_models = json.loads(busi_model.base_models)
base_model_names = ",".join([base_model["name"] for base_model in base_models])
db_mode_node_device = (
DB_BusiModelNodeDevice.EnterpriseBusiModelNodeDeviceRepository()
)
device_ids = db_mode_node_device.get_device_ids(node_id, busi_model_id)
device_ids = list(set([item["device_id"] for item in device_ids]))
db_device = DB_Device.EnterpriseDeviceRepository()
devices = db_device.get_devices(device_ids)
devices_return = [
{"device_id": item["id"], "device_name": item["name"]} for item in devices
]
self.finish(
{
"busi_model_name": busi_model_name,
"busi_model_comment": busi_model_comment,
"base_models": base_model_names,
"devices": devices_return,
}
)
class BusimodelDeployHandler(APIHandler):
"""
- 描述企业节点节点信息 -> 业务模型部署 -> 业务模型配置
- 请求方式post
- 请求参数
> - node_id, int, 节点id
> - busi_model_id, int业务模型id
> - device_ids, string, 设备id, 多个id逗号分割
- 返回值
"""
@authenticated
def post(self):
node_id = self.get_int_argument("node_id")
busi_model_id = self.get_int_argument("busi_model_id")
device_ids = self.get_escaped_argument("device_ids", "")
if not node_id or not busi_model_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
if not device_ids:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备不能为空")
db_node = DB_Node.EnterpriseNodeRepository()
node = db_node.get_node_by_id(node_id)
if not node:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "节点不存在")
node_suid = node["suid"]
db_busi_model = DB_BusiModel.EnterpriseBusiModelRepository()
busi_model = db_busi_model.get_busi_model_by_id(busi_model_id)
busi_model_suid = busi_model.suid
entity_suid_row = db_node.get_entity_suid_by_node_id(node_id)
if not entity_suid_row:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不存在")
db_device = DB_Device.EnterpriseDeviceRepository()
records = []
for device_id in device_ids.split(","):
device = db_device.get_device(int(device_id))
if not device:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备不存在")
records.append(
{
"suid": shortuuid.ShortUUID().random(length=10),
"entity_suid": entity_suid_row["entity_suid"],
"node_id": node_id,
"node_suid": node_suid,
"busi_model_id": busi_model_id,
"busi_model_suid": busi_model_suid,
"device_id": int(device_id),
"device_suid": device["suid"],
}
)
db_busi_model_node_device = (
DB_BusiModelNodeDevice.EnterpriseBusiModelNodeDeviceRepository()
)
db_busi_model_node_device.delete_by_busi_model_and_node(node_id, busi_model_id)
db_busi_model_node_device.batch_insert_record(records)
self.finish()
class AlertHandler(APIHandler):
"""
- 描述企业节点节点信息 -> 设备列表 -> 告警设置信息
- 请求方式post
- 请求参数
> - node_id, int, 节点id
- 返回值
```
{
"is_sms": 1, # 是否sms
"sms_to": "xxxx", # 短信联系人
"is_email": 1, # 是否email
"email_to": "xxx", # 邮件联系人
"freq": "T", # T/每次D/每天W/每周M/每月
}
```
"""
@authenticated
def post(self):
node_id = self.get_int_argument("node_id")
if not node_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
db_node = DB_Node.EnterpriseNodeRepository()
node = db_node.get_node_by_id(node_id)
if not node:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "节点不存在")
node_suid = node["suid"]
row = db_node.get_entity_suid_by_node_id(node_id)
entity_suid = row["entity_suid"]
db_alert = DB_NodeAlert.EnterpriseNodeAlertRepository()
data = db_alert.get_one(entity_suid, node_suid)
res = {
"is_sms": 0,
"sms_to": "",
"is_email": 0,
"email_to": "",
"freq": "T",
}
if data:
res.update({
"is_sms": data["is_sms"],
"sms_to": data["sms_to"],
"is_email": data["is_email"],
"email_to": data["email_to"],
"freq": data["freq"],
})
self.finish(res)
class AlertConfigHandler(APIHandler):
"""
- 描述企业节点节点信息 -> 设备列表 -> 告警设置更新配置
- 请求方式post
- 请求参数
> - node_id, int, 节点id
> - is_sms, int, 是否sms, 1/0/
> - sms_to, string, 短信联系人
> - is_email, int, 是否email1/0/
> - email_to, string, 邮件联系人
> - freq, string, 频次, T/每次D/每天W/每周M/每月
- 返回值
"""
@authenticated
def post(self):
node_id = self.get_int_argument("node_id")
is_sms = self.get_int_argument("is_sms", 0)
sms_to = self.get_escaped_argument("sms_to", "")
is_email = self.get_int_argument("is_email", 0)
email_to = self.get_escaped_argument("email_to", "")
freq = self.get_escaped_argument("freq", "T")
if not node_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
if freq not in ("T", "D", "W", "M"):
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数错误")
db_node = DB_Node.EnterpriseNodeRepository()
node = db_node.get_node_by_id(node_id)
if not node:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "节点不存在")
node_suid = node["suid"]
row = db_node.get_entity_suid_by_node_id(node_id)
entity_suid = row["entity_suid"]
insert_data = {
"entity_suid": entity_suid,
"node_id": node_id,
"node_suid": node_suid,
"is_sms": is_sms,
"sms_to": sms_to,
"is_email": is_email,
"email_to": email_to,
"freq": freq,
}
db_alert = DB_NodeAlert.EnterpriseNodeAlertRepository()
db_alert.update_or_inert(insert_data)
self.finish()

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from website.handlers.enterprise_node import handler
handlers = [
("/enterprise/entity/nodes/add", handler.AddHandler),
("/enterprise/entity/nodes/edit", handler.EditHandler),
("/enterprise/entity/nodes", handler.TreeHandler),
("/enterprise/entity/nodes/info", handler.InfoHandler),
("/enterprise/entity/nodes/delete", handler.DeleteHandler),
("/enterprise/entity/nodes/busimodel", handler.BusimodelHandler),
("/enterprise/entity/nodes/busimodel/info", handler.BusimodelInfoHandler),
("/enterprise/entity/nodes/busimodel/deploy", handler.BusimodelDeployHandler),
("/enterprise/entity/nodes/alert", handler.AlertHandler),
("/enterprise/entity/nodes/alert/config", handler.AlertConfigHandler),
]
page_handlers = []

@ -0,0 +1,260 @@
# -*- coding: utf-8 -*-
import logging
import random
from sqlalchemy import text
from website import errors
from website import settings
from website.db.enterprise_device.enterprise_device import EnterpriseEntityRepository as EntRepo
from website.db_mysql import to_json_list, to_json
from website.handler import APIHandler, authenticated, operation_log
from website.util import aes
from website.util import shortuuid
class AddHandler(APIHandler):
"""
- 描述新建服务器
- 请求方式post
- 请求参数
> - entity_id, int, 企业id
> - name, string, 服务器名称
> - ip, string,
> - port, int, 默认端口
> - username, string, 用户名
> - passwd, string,
- 返回值
"""
@authenticated
@operation_log("企业项目", "服务器管理", "新建", "新建服务器", "")
def post(self):
entity_id = self.get_int_argument('entity_id')
name = self.get_escaped_argument('name', '')
ip = self.get_escaped_argument('ip', '')
port = self.get_int_argument("port", 22)
username = self.get_escaped_argument('username', '')
passwd = self.get_escaped_argument('passwd', '')
if not entity_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "entity_id is required")
e = EntRepo()
entity_suid = e.get_entity_suid(entity_id)
if not entity_suid:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "entity is invalid")
with self.app_mysql.connect() as conn:
conn.execute(
text(
"insert into "
"enterprise_server(suid, entity_id, entity_suid, name, ip, port, username, passwd) "
"values(:suid, :entity_id, :entity_suid, :name, :ip, :port, :username, :passwd) "
),
{
"suid": shortuuid.ShortUUID().random(10),
"entity_id": entity_id,
"entity_suid": entity_suid,
"name": name,
"ip": ip,
"port": port,
"username": username,
"passwd": aes.encrypt(settings.enterprise_aes_key, passwd)
}
)
conn.commit()
self.finish()
class EditHandler(APIHandler):
@authenticated
@operation_log("企业项目", "服务器管理", "编辑", "编辑服务器", "")
def post(self):
server_id = self.get_int_argument("id")
name = self.get_escaped_argument('name', '')
ip = self.get_escaped_argument('ip', '')
port = self.get_int_argument("port", 22)
username = self.get_escaped_argument('username', '')
passwd = self.get_escaped_argument('passwd', '')
with self.app_mysql.connect() as conn:
conn.execute(
text(
"update enterprise_server "
"set name=:name, ip=:ip, port=:port, username=:username, passwd=:passwd where id=:id"
), {
"id": server_id,
"name": name,
"ip": ip,
"port": port,
"username": username,
"passwd": aes.encrypt(settings.enterprise_aes_key, passwd)
})
conn.commit()
self.finish()
class ListHandler(APIHandler):
"""
- 描述服务器列表
- 请求方式post
- 请求参数
> - entity_id, int, 企业id
> - status, int, 1000/all/默认, 1001/在线1002/离线
> - pageNo, int,
> - pageSize, int
- 返回值
```
{
"count": 123,
"data": [
{
"id": 123,
"name": "xx",
"status": 1001, # 状态, 1001/在线1002/离线
"cpu": 123,
"mem": 123,
"storage": 213,
"gpu": 123,
},
...
]
}
```
"""
@authenticated
@operation_log("企业项目", "服务器管理", "查询", "查询服务器列表", "")
def post(self):
entity_id = self.get_int_argument('entity_id')
status = self.get_int_argument("status")
pageNo = self.get_int_argument("pageNo", 1)
pageSize = self.get_int_argument("pageSize", 10)
if not entity_id:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "entity_id is required")
if status and status not in (1001, 1002):
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "status is invalid")
with self.app_mysql.connect() as conn:
p = {}
sql_count = "select count(1) from enterprise_server where entity_id=:entity_id"
sql_data = "select id, name, status from enterprise_server where entity_id=:entity_id"
p.update({"entity_id": entity_id})
if status:
sql_count += " and status=:status"
sql_data += " and status=:status"
p.update({"status": status})
count = conn.scalar(text(sql_count), p)
sql_data += " order by id desc limit :pageSize offset :offset"
p.update({"offset": (pageNo - 1) * pageSize, "pageSize": pageSize})
data = conn.execute(text(sql_data), p)
data = to_json_list(data)
status_dic = {1001: 0, 1002: 0}
sql_status = "select status, count(id) c from enterprise_server where entity_id=:entity_id group by status"
status_cur = conn.execute(text(sql_status), p)
status_list = to_json_list(status_cur)
for item in status_list:
status_dic[item["status"]] = item["c"]
logging.info("####################### ")
logging.info(data)
for item in data:
item.update(
{"cpu": random.randint(20, 30),
"mem": random.randint(20, 30),
"storage": random.randint(20, 30),
"gpu": random.randint(20, 30), })
self.finish({"count": count, "data": data, "status": status_dic})
class InfoHandler(APIHandler):
"""
- 描述服务器信息
- 请求方式post
- 请求参数
> - server_id, int, 服务器id
- 返回值
```
{
"name": "xx",
"ip": "xxx",
"port": "xxx",
"username": "xxx",
"cpu": 123,
"mem": 123,
"storage": 213,
"gpu": 123,
}
```
"""
@authenticated
@operation_log("企业项目", "服务器管理", "查询", "查询服务器信息", "")
def post(self):
server_id = self.get_int_argument('server_id')
with self.app_mysql.connect() as conn:
cur = conn.execute(
text("select name, ip, port, username from enterprise_server where id=:id"),
{"id": server_id})
data = to_json(cur)
if data:
data.update(
{"cpu": random.randint(20, 30),
"mem": random.randint(20, 30),
"storage": random.randint(20, 30),
"gpu": random.randint(20, 30), })
self.finish(data)
class DeleteHandler(APIHandler):
@authenticated
@operation_log("企业项目", "服务器管理", "删除", "删除服务器", "")
def post(self):
server_id = self.get_int_argument('server_id')
with self.app_mysql.connect() as conn:
conn.execute(
text("delete from enterprise_server where id=:id"), {"id": server_id}
)
conn.commit()
self.finish()
class LogHandler(APIHandler):
"""
- 描述服务器列表
- 请求方式post
- 请求参数
> - server_id, int, 服务器id
> - pageNo, int
> - pageSize, int
- 返回值
"""
@authenticated
def post(self):
entity_id = self.get_int_argument('entity_id')
pageNo = self.get_int_argument('pageNo', 1)
pageSize = self.get_int_argument('pageSize', 10)
with self.app_mysql.connect() as conn:
p = {}
sql_count = "select count(1) from enterprise_server where entity_id=:entity_id"
sql_data = "select id, name, ip, port from enterprise_server where entity_id=:entity_id"
p.update({"entity_id": entity_id})
count = conn.scalar(text(sql_count), p)
sql_data += " order by id desc limit :pageSize offset :offset"
p.update({"offset": (pageNo - 1) * pageSize, "pageSize": pageSize})
data = conn.execute(text(sql_data), p)
data = to_json_list(data)
self.finish({"count": count, "data": data})

@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from website.handlers.enterprise_server import handler
handlers = [
("/enterprise/server/add", handler.AddHandler),
("/enterprise/server/edit", handler.EditHandler),
("/enterprise/server/list", handler.ListHandler),
("/enterprise/server/info", handler.InfoHandler),
("/enterprise/server/log", handler.LogHandler),
("/enterprise/server/delete", handler.DeleteHandler)
]
page_handlers = []

@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
import hashlib
import logging
import os
import re
import aiofiles
from sqlalchemy import text
from website import db_mysql
from website import errors
from website import settings
from website.handler import APIHandler, authenticated
class UploadHandler(APIHandler):
@authenticated
async def post(self):
file_metas = self.request.files.get('file', None)
if not file_metas:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请选择文件")
filename = file_metas[0].filename
punctuation = """!"#$%&'()*+,/:;<=>?@[\]^`{|}~ """
regex = re.compile('[%s]' % re.escape(punctuation))
filename = regex.sub("", filename.replace('..', ''))
file_size = len(file_metas[0].body)
logging.info("file_size: %s", file_size)
if file_size > 300 * 1024 * 1024:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, 'Exceed 300M size limit')
filetype = filename.split(".") and filename.split(".")[-1] or ""
file_upload_dir = settings.file_upload_dir
os.makedirs(file_upload_dir, exist_ok=True)
md5_str = hashlib.md5(file_metas[0].body).hexdigest()
row = None
with self.app_mysql.connect() as conn:
sql = text("select id from files where md5_str=:md5_str")
cur = conn.execute(sql, {"md5_str": md5_str})
row = cur.fetchone()
if not row:
filepath = os.path.join(settings.file_upload_dir, md5_str + '_' + filename)
if not os.path.exists(filepath):
for meta in file_metas:
# filename = meta['filename']
async with aiofiles.open(filepath, 'wb') as f:
await f.write(meta['body'])
sql_insert = text(
"""insert into files(filename, filepath, md5_str, filesize, filetype, user)
values(:filename, :filepath, :md5_str, :file_size, :filetype, :user)"""
)
conn.execute(sql_insert, {"filename": filename, "filepath": filepath, "md5_str": md5_str,
"file_size": int(file_size / 1024 / 1024), "filetype": filetype,
"user": self.current_user.id})
conn.commit()
self.finish({"result": md5_str})
class DeleteHandler(APIHandler):
@authenticated
def post(self):
md5_str = self.get_escaped_argument("file_md5", "")
if not md5_str:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "file md5 is required")
logging.info("md5_str: %s", md5_str)
row = None
with self.app_mysql.connect() as conn:
sql = text("select filepath from files where md5_str=:md5_str")
cur = conn.execute(sql, {"md5_str": md5_str})
row = db_mysql.to_json(cur)
if not row:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "file not found")
filepath = row["filepath"]
if os.path.exists(filepath):
os.remove(filepath)
sql_del = text("delete from files where md5_str=:md5_str")
conn.execute(sql_del, {"md5_str": md5_str})
conn.commit()
self.finish()
class BigFileUploadHandler(APIHandler):
async def post(self):
file_metas = self.request.files.get('file', None)
if not file_metas:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请选择文件")
filename = file_metas[0].filename
punctuation = """!"#$%&'()*+,/:;<=>?@[\]^`{|}~ """
regex = re.compile('[%s]' % re.escape(punctuation))
filename = regex.sub("", filename.replace('..', ''))
file_size = len(file_metas[0].body)
logging.info("file_size: %s", file_size)
if file_size > 300 * 1024 * 1024:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, 'Exceed 300M size limit')
filetype = filename.split(".") and filename.split(".")[-1] or ""
file_upload_dir = settings.file_upload_dir
os.makedirs(file_upload_dir, exist_ok=True)
# md5_str = hashlib.md5(file_metas[0].body).hexdigest()
filepath = os.path.join(settings.file_upload_dir, filename)
if not os.path.exists(filepath):
for meta in file_metas:
# filename = meta['filename']
# with open(filepath, 'wb') as f:
# f.write(meta['body'])
async with aiofiles.open(filepath, 'wb') as f:
await f.write(meta['body'])
self.finish()

@ -0,0 +1,12 @@
from website.handlers.file import handler
handlers = [
# ("/", handler.Handler),
("/file/upload", handler.UploadHandler),
("/file/delete", handler.DeleteHandler),
("/bigfile/upload", handler.BigFileUploadHandler),
]
page_handlers = [
]

@ -0,0 +1,170 @@
# -*- coding: utf-8 -*-
"""系统信息"""
import logging
import uuid
import time
import re
import os
import json
import hashlib
import datetime
from website import errors
from website import settings
from website.handler import APIHandler, WebHandler, authenticated, operation_log, permission
from website.db_mysql import to_json_list
from website.util import sysinfo, rsa
from sqlalchemy import text
class VersionHandler(APIHandler):
@authenticated
# @permission([100014, 100016])
def post(self):
self.finish()
class IdentifycodeHandler(APIHandler):
@authenticated
# @permission([100014, 100015])
# @operation_log("资产管理中心", "系统激活", "查询", "查询本地识别码", "查询本地识别码")
def post(self):
code = sysinfo.get_identify_code()
self.finish({"result": code})
class LicenseUploadHandler(APIHandler):
@authenticated
# @permission([100014, 100015])
# @operation_log("资产管理中心", "系统激活", "导入", "上传license文件", "上传license文件")
def post(self):
file_metas = self.request.files.get('file', None)
if not file_metas:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请上传文件")
file = file_metas[0]
filename = file.filename
# punctuation = """!"#$%&'()*+,/:;<=>?@[\]^`{|}~ """
# punctuation = """!"#$%&'()*+,/:;<=>?@[\\]^`{|}~ """
punctuation = r"""!"#$%&'()*+,/:;<=>?@[\]^`{|}~ """
regex = re.compile('[%s]' % re.escape(punctuation))
filename = regex.sub("", filename.replace('..', ''))
file_size = len(file.body)
if file_size > 10 * 1024 * 1024:
raise errors.HTTPAPIError(errors.ERROR_METHOD_NOT_ALLOWED, 'Exceed 10M size limit')
md5_str = hashlib.md5(file.body).hexdigest()
filepath = settings.rsa_license_file
try:
body = file['body']
public_key = rsa.load_pub_key_string(open(settings.rsa_public_file).read().strip('\n').encode('utf-8'))
plaintext = rsa.decrypt(public_key, body)
plaintext_json = json.loads(self.tostr(plaintext))
syscode = plaintext_json["syscode"]
expireat = plaintext_json["expireat"]
current_syscode = sysinfo.get_identify_code()
if syscode != current_syscode:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "license激活失败请重新激活")
row = self.db_app.get("select id from license where syscode=%s", syscode)
if row:
self.db_app.update(
"update license set expireat=%s where syscode=%s", str(expireat), syscode
)
else:
self.db_app.insert(
"insert into license(syscode, expireat) values(%s, %s)",
syscode, expireat
)
self.r_app.set("system:license", json.dumps({"syscode":syscode, "expireat":expireat}))
with open(filepath, 'wb') as f:
f.write(file['body'])
logging.info(plaintext_json)
except Exception as e:
logging.info(e)
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "license激活失败请重新激活")
self.finish()
class ActivateInfoHandler(APIHandler):
@authenticated
# @permission([100014, 100015])
# @operation_log("资产管理中心", "系统激活", "查询", "查询系统激活信息", "查询系统激活信息")
def post(self):
license_str = ""
activate_at = ""
expire_at = ""
date_remain = 0
row = self.db_app.get(
"select create_time, expireat from license limit 1"
)
if row:
license_str = open(settings.rsa_license_file, 'r').read()
activate_at = str(row["create_time"])
expire_at = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(row["expireat"])))
now = datetime.datetime.now()
delta = (datetime.datetime.fromtimestamp(int(row["expireat"])).date() - now.date()).days
date_remain = delta if delta > 0 else 0
data = {
"system": settings.system_info[settings.system_type]["name"],
"license": license_str,
"activate_at": activate_at,
"expire_at": expire_at,
"date_remain": date_remain
}
self.finish(data)
class InfoHandler(APIHandler):
def post(self):
self.finish()
class LogHandler(APIHandler):
@authenticated
def post(self):
user = self.get_escaped_argument("user", "")
start = self.get_escaped_argument("startDate", "")
end = self.get_escaped_argument("endDate", "")
pageNo = self.get_int_argument("pageNo", 1)
pageSize = self.get_int_argument("pageSize", 20)
users = user.split(",")
with self.app_mysql.connect() as conn:
sql = "select user, ip, content, op_type, content from sys_log where 1=1"
sql_count = "select count(*) from sys_log where 1=1"
p = {}
if users:
sql += " and user in :users"
sql_count += " and user in :users"
p["users"] = users
if start:
sql += " and date_format(create_time, '%Y-%m-%d') >= :start"
sql_count += " and date_format(create_time, '%Y-%m-%d') >= :start"
p["start"] = start
if end:
sql += " and date_format(create_time, '%Y-%m-%d') <= :end"
sql_count += " and date_format(create_time, '%Y-%m-%d') <= :end"
p["end"] = end
count = conn.scalar(text(sql_count), p)
sql += " order by create_time desc limit :pageNo, :pageSize"
p["pageNo"] = (pageNo - 1) * pageSize
p["pageSize"] = pageSize
res = conn.execute(text(sql), p)
data = to_json_list(res)
self.finish({"count": count, "data": data})

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
from website.handlers.system import handler
handlers = [
# ("/", handler.Handler),
("/system/version", handler.VersionHandler),
("/system/identifycode", handler.IdentifycodeHandler),
("/system/license/upload", handler.LicenseUploadHandler),
("/system/activate/info", handler.ActivateInfoHandler),
("/system/info", handler.InfoHandler),
("/system/log", handler.LogHandler),
]
page_handlers = [
]

@ -0,0 +1,189 @@
# -*- coding: utf-8 -*-
import base64
import json
import logging
import uuid
from io import BytesIO
from sqlalchemy import text
from website import db_mysql
from website import errors
from website import settings
from website.handler import APIHandler, authenticated
from website.util import aes
from website.util import shortuuid
from website.util.captcha import create_validate_code
class CaptchaHandler(APIHandler):
def get(self):
self.set_header("Content-Type", "image/png")
image, image_str = create_validate_code()
c = uuid.uuid4().hex
token = self.create_signed_value("logc", c)
self.r_app.set("logincaptcha:%s" % c, image_str, ex=120)
buffered = BytesIO()
# 保存验证码图片
image.save(buffered, 'png')
img_b64 = base64.b64encode(buffered.getvalue())
# for line in buffered.getvalue():
# self.write(line)
# output.close()
self.finish({"token": self.tostr(token), "captcha": self.tostr(img_b64)})
class LogoutHandler(APIHandler):
def get(self):
if self.current_user:
# self.db_app.insert(
# "insert into system_log(user, ip, first_module, second_module, op_type, op_content, description) "
# "values(%s, %s, %s, %s, %s, %s, %s)",
# self.current_user.name, self.request.remote_ip, "平台管理中心", "账号管理", "登出", "系统登出", "系统登出"
# )
self.r_app.delete(settings.session_key_prefix % self.current_user.uuid)
self.finish()
class LoginHandler(APIHandler):
def post(self):
# suid = shortuuid.ShortUUID().random(10)
# logging.info(suid)
username = self.get_escaped_argument("username")
password = self.get_escaped_argument("pwd")
# captcha = self.get_escaped_argument("captcha", "")
# captcha_token = self.get_escaped_argument("captcha_token", "")
# wrong_time_lock = self.r_app.get("pwd:wrong:time:%s:lock" % self.tostr(username))
# if wrong_time_lock:
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "账号处于冷却期,请稍后再试")
# return
if not username or not password:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请输入用户名和密码")
# if not captcha:
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请输入验证码")
# if not captcha_token:
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "缺少参数")
# c = tornado.web.decode_signed_value(
# settings.cookie_secret,
# "logc",
# self.tostr(captcha_token)
# )
# code = self.r_app.get("logincaptcha:%s" % self.tostr(c))
# 清除校验码缓存
# self.r_app.delete("logincaptcha:%s" % c)
# if not code:
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "验证码已过期")
# 判断验证码与缓存是否一致
# if self.tostr(captcha).lower() != self.tostr(code).lower():
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "验证码错误")
username = self.tostr(username)
password = self.tostr(password)
pwd_enc = aes.encrypt(settings.pwd_aes_key, password)
row = {}
with self.app_mysql.connect() as conn:
cur = conn.execute(
text("select id, uid, available from sys_user where name=:name and pwd=:pwd"),
{"name": username, "pwd": pwd_enc}
)
# keys = list(cur.keys())
#
# one = cur.fetchone()
# row = dict(zip(keys, one))
# logging.info(db.Row(itertools.zip_longest(keys, one)))
row = db_mysql.to_json(cur)
cur.close()
# data = [dict(zip(keys, res)) for res in cur.fetchall()]
if not row:
# wrong_time = self.r_app.get("pwd:wrong:time:%s" % username)
# logging.info(wrong_time)
# logging.info(settings.pwd_error_limit - 1)
# if wrong_time and int(wrong_time) > settings.pwd_error_limit - 1:
# self.r_app.set("pwd:wrong:time:%s:lock" % username, 1, ex=3600)
# self.r_app.delete("pwd:wrong:time:%s" % username)
# else:
# self.r_app.incr("pwd:wrong:time:%s" % username)
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "用户名或者密码错误")
return
if row["available"] == 0:
raise errors.HTTPAPIError(errors.ERROR_FORBIDDEN, "当前用户被禁用")
return
# row_role = self.db_app.get("select role from user_role where userid=%s", row["id"])
# user_role = row_role["role"]
userId = row["id"]
jsessionid = row["uid"]
# create sign value admin_login_sign
secure_cookie = self.create_signed_value(settings.secure_cookie_name, str(jsessionid))
self.r_app.set(
settings.session_key_prefix % jsessionid,
json.dumps({
"id": userId,
"name": username,
"uuid": row["uid"],
# "role": user_role
}),
ex=settings.session_ttl
)
# self.db_app.insert(
# "insert into system_log(user, ip, first_module, second_module, op_type, op_content, description) "
# "values(%s, %s, %s, %s, %s, %s, %s)",
# username, self.request.remote_ip, "平台管理中心", "账号管理", "登录", "系统登录", "系统登录"
# )
# license_row = self.db_app.get(
# "select expireat from license limit 1"
# )
# system_status = get_license_status(license_row)
render_data = {
"token": str(secure_cookie, encoding="utf-8"),
# "role": user_role,
"username": username,
# "system_status": system_status, # 9000/未激活, 9001/已激活, 9002/过期可查看, 9003/完全过期
}
self.finish(render_data)
class UserInfoHandler(APIHandler):
def post(self):
# token = self.get_argument("token")
# user = self.get_current_user(token_body=self.tostr(token))
user = self.get_current_user()
if not user:
raise errors.HTTPAPIError(errors.ERROR_UNAUTHORIZED)
self.finish({"name": user.name, "avtar": ""})
class UserListHandler(APIHandler):
@authenticated
def post(self):
with self.app_mysql.connect() as conn:
cur = conn.execute(
text(
"select name from sys_user"
)
)
res = db_mysql.to_json_list(cur)
names = [row["name"] for row in res]
self.finish({"data": names})

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from website.handlers.user import handler
handlers = [
# ("/user/list", handler.UserListHandler),
# ("/captcha", handler.CaptchaHandler),
# ("/bodyargument", handler.BodyHandler),
# ("/user/info", handler.UserInfoHandler),
("/login", handler.LoginHandler),
("/logout", handler.LogoutHandler),
("/user/info", handler.UserInfoHandler),
("/users", handler.UserListHandler),
]
page_handlers = [
]

@ -0,0 +1,32 @@
import time
import datetime
import logging
from website import consts, settings
def get_license_status(license):
status = consts.system_status_not_active
# if not license:
# pass
if license:
now = datetime.datetime.now()
timestamp_now = int(now.timestamp())
expireat = int(license["expireat"])
expireat_datetime = datetime.datetime.fromtimestamp(expireat)
expireat_next30days = expireat_datetime + datetime.timedelta(days=30)
expireat_next30days_timestamp = int(expireat_next30days.timestamp())
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
# logging.info(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(expireat)))
# logging.info(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(expireat_next30days_timestamp)))
# logging.info(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timestamp_now)))
if timestamp_now >= expireat_next30days_timestamp:
status = consts.system_status_expire_atall
elif timestamp_now >= expireat and timestamp_now < expireat_next30days_timestamp:
status = consts.system_status_expire_but_ok
elif timestamp_now < expireat:
status = consts.system_status_activated
return status

@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
import os
# sqlalchemy
SQLALCHEMY_ECHO = 0
# db mysql
mysql_app = {
"host": "127.0.0.1:3306",
"database": "",
"user": "root",
"password": "",
"time_zone": "+8:00"
}
SQLALCHEMY_POOL_SIZE = 10 * os.cpu_count() # 连接池的大小
SQLALCHEMY_POOL_MAX_SIZE = 2 * SQLALCHEMY_POOL_SIZE # 连接池的最大大小
SQLALCHEMY_POOL_RECYCLE = 3600 # 连接池的回收时间
redis_app = ("127.0.0.1", 6382, 0, "")
redis_app_cluster_notes = [
{"host": "127.0.0.1", "port": "6379"},
{"host": "127.0.0.1", "port": "6380"}
]
redis_cluster_pwd = ""
redis_cluster = 0
# redis_sentinel = 1
# redis_sentinel_nodes = [
# ('192.168.0.1', 40500),
# ('192.168.0.2', 40500)
# ]
# redis_sentinel_master = ""
# redis_sentinel_pwd = ""
# session expire duration
session_ttl = 3600
session_key_prefix = "system:user:jid:%s"
# convert get to post
app_get_to_post = False
# system host
# host = "https://gap.sst.com"
# api root
# api_root = "https://gap.sst.com"
api_prefix = "/api/v1"
api_prefix_v2 = "/api/v2"
# api freq limit
api_count_in_ten_second = 20
login_url = "/login"
cookie_domain = ".sst.com"
# cookie_domain = "localhost"
# cookie_secret, generate method : base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
cookie_secret = "rn43LMFOQJu1w8lJXlN93Oc7GOqo3kiTvqOq4IrTDjk="
cookie_path = "/"
cookie_key = "system:jsessionid"
secure_cookie_name = "sst"
pwd_error_limit = 10
enable_curl_async_http_client = True
max_clients = 300
remote_request_timeout = 10.0 # 异步调用远程api超时时间 单位:Second
remote_connect_timeout = 10.0 # 异步调用远程api连接超时时间单位Second
# 系统密码加密秘钥
pwd_aes_key = "FquMBlcVoIkTAmL7"
enterprise_aes_key = "FquMBlcVoIkTAmL7"
file_upload_dir = "/data/fileupload"
rsa_public_file = "/home/app/public"
rsa_license_file = "/home/app/license"
# hashlib.sha256(base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)).hexdigest()
# 线上配置信息使用settings_local.py
try:
from settings_local import *
except:
pass

@ -0,0 +1,17 @@
import os
import sys
import importlib
handlers = []
# handlers_v2 = []
page_handlers = []
handlers_path = os.path.join(os.getcwd(), "handlers")
sys.path.append(handlers_path)
handlers_dir = os.listdir(handlers_path)
for item in handlers_dir:
if os.path.isdir(os.path.join(handlers_path, item)):
hu = importlib.import_module("{}.url".format(item))
handlers.extend(hu.handlers)
page_handlers.extend(hu.page_handlers)

@ -0,0 +1,145 @@
#!/usr/bin/env python
# -*- coding=utf-8 -*-
"""
AES加密解密工具类
@author jzx
@date 2018/10/24
此工具类加密解密结果与 http://tool.chacuo.net/cryptaes 结果一致
数据块128位
key 为16位
iv 为16位且与key相等
字符集utf-8
输出为base64
AES加密模式 为cbc
填充 pkcs7padding
"""
import base64
from Crypto.Cipher import AES
import random
def pkcs7padding(text):
"""
明文使用PKCS7填充
最终调用AES加密方法时传入的是一个byte数组要求是16的整数倍因此需要对明文进行处理
:param text: 待加密内容(明文)
:return:
"""
bs = AES.block_size # 16
length = len(text)
bytes_length = len(bytes(text, encoding='utf-8'))
# tipsutf-8编码时英文占1个byte而中文占3个byte
padding_size = length if (bytes_length == length) else bytes_length
padding = bs - padding_size % bs
# tipschr(padding)看与其它语言的约定,有的会使用'\0'
padding_text = chr(padding) * padding
return text + padding_text
def pkcs7unpadding(text):
"""
处理使用PKCS7填充过的数据
:param text: 解密后的字符串
:return:
"""
length = len(text)
unpadding = ord(text[length - 1])
return text[0:length - unpadding]
def encrypt(key, content):
"""
AES加密
key,iv使用同一个
模式cbc
填充pkcs7
:param key: 密钥
:param content: 加密内容
:return:
"""
key_bytes = bytes(key, encoding='utf-8')
iv = key_bytes
cipher = AES.new(key_bytes, AES.MODE_CBC, iv)
# 处理明文
content_padding = pkcs7padding(content)
# 加密
encrypt_bytes = cipher.encrypt(bytes(content_padding, encoding='utf-8'))
# 重新编码
result = str(base64.b64encode(encrypt_bytes), encoding='utf-8')
return result
def decrypt(key, content):
"""
AES解密
key,iv使用同一个
模式cbc
去填充pkcs7
:param key:
:param content:
:return:
"""
key_bytes = bytes(key, encoding='utf-8')
iv = key_bytes
cipher = AES.new(key_bytes, AES.MODE_CBC, iv)
# base64解码
encrypt_bytes = base64.b64decode(content)
# 解密
decrypt_bytes = cipher.decrypt(encrypt_bytes)
# 重新编码
result = str(decrypt_bytes, encoding='utf-8')
# 去除填充内容
result = pkcs7unpadding(result)
return result
def get_key(n):
"""
获取密钥 n 密钥长度
:return:
"""
c_length = int(n)
source = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
length = len(source) - 1
result = ''
for i in range(c_length):
result += source[random.randint(0, length)]
return result
if __name__ == "__main__":
# Test
# 非16字节的情况
aes_key = get_key(16)
print('aes_key:' + aes_key)
# 对英文加密
source_en = 'Hello!+world'
encrypt_en = encrypt(aes_key, source_en)
print(encrypt_en)
# 解密
decrypt_en = decrypt(aes_key, encrypt_en)
print(decrypt_en)
print(source_en == decrypt_en)
# 中英文混合加密
source_mixed = 'Hello, 韩- 梅 -梅'
encrypt_mixed = encrypt(aes_key, source_mixed)
print(encrypt_mixed)
decrypt_mixed = decrypt(aes_key, encrypt_mixed)
print(decrypt_mixed)
print(decrypt_mixed == source_mixed)
# 刚好16字节的情况
en_16 = 'abcdefgj10124567'
encrypt_en = encrypt(aes_key, en_16)
print(encrypt_en)
# 解密
decrypt_en = decrypt(aes_key, encrypt_en)
print(decrypt_en)
print(en_16 == decrypt_en)
mix_16 = 'abx张三丰12sa'
encrypt_mixed = encrypt(aes_key, mix_16)
print(encrypt_mixed)
decrypt_mixed = decrypt(aes_key, encrypt_mixed)
print(decrypt_mixed)
print(decrypt_mixed == mix_16)

@ -0,0 +1,171 @@
# -*- coding: utf-8 -*-
import logging
import os
import random
import settings
from PIL import Image, ImageDraw, ImageFont, ImageFilter
_letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,取出干扰
_upper_cases = _letter_cases.upper() # 大写字母
_numbers = ''.join(map(str, range(3, 10))) # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))
def create_validate_code(
size=(140, 30),
chars=init_chars,
img_type="GIF",
mode="RGB",
bg_color=(255, 255, 255),
fg_color=(0, 0, 255),
font_size=18,
font_type="ttf/%s.ttf" % random.randint(1, 6),
length=4,
draw_lines=True,
n_line=(3, 8),
draw_points=True,
point_chance=20):
'''
@todo: 生成验证码图片
@param size: 图片的大小格式默认为(120, 30)
@param chars: 允许的字符集合格式字符串
@param img_type: 图片保存的格式默认为GIF可选的为GIFJPEGTIFFPNG
@param mode: 图片模式默认为RGB
@param bg_color: 背景颜色默认为白色
@param fg_color: 前景色验证码字符颜色默认为蓝色#0000FF
@param font_size: 验证码字体大小
@param font_type: 验证码字体默认为 ae_AlArabiya.ttf
@param length: 验证码字符个数
@param draw_lines: 是否划干扰线
@param n_lines: 干扰线的条数范围格式元组默认为(1, 2)只有draw_lines为True时有效
@param draw_points: 是否画干扰点
@param point_chance: 干扰点出现的概率大小范围[0, 100]
@return: [0]: PIL Image实例
@return: [1]: 验证码图片中的字符串
'''
width, height = size # 宽, 高
img = Image.new(mode, size, bg_color) # 创建图形
draw = ImageDraw.Draw(img) # 创建画笔
def get_chars():
'''生成给定长度的字符串,返回列表格式'''
return random.sample(chars, length)
def rndColor():
return (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255))
# 随机颜色2:
def rndColor2():
return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127))
def create_lines():
'''绘制干扰线'''
line_num = random.randint(*n_line) # 干扰线条数
for i in range(line_num):
# 起始点
begin = (random.randint(0, size[0]), random.randint(0, size[1]))
# 结束点
end = (random.randint(0, size[0]), random.randint(0, size[1]))
draw.line([begin, end], fill=(0, 0, 0))
def create_points():
'''绘制干扰点'''
chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]
# 填充每个像素:
# for x in range(width):
# for y in range(height):
# draw.point((x, y), fill=rndColor())
for w in range(width):
for h in range(height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
# draw.point((w, h), fill=(0, 0, 0))
draw.point((w, h), fill=rndColor())
def create_strs():
'''绘制验证码字符'''
c_chars = get_chars()
strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开
logging.info(os.getcwd())
# font_type = settings.captcha_font_path + "%s.ttf" % random.randint(1, 4)
font_type = os.getcwd() + "/util/ttf/%s.ttf" % random.randint(1, 4)
logging.info("font type is : %s " % font_type)
font = ImageFont.truetype(font_type, font_size)
font_width, font_height = font.getsize(strs)
draw.text(((width - font_width) / 3, (height - font_height) / 3),
strs, font=font, fill=fg_color)
return ''.join(c_chars)
if draw_lines:
create_lines()
if draw_points:
create_points()
strs = create_strs()
# 图形扭曲参数
params = [1 - float(random.randint(1, 2)) / 100,
0,
0,
0,
1 - float(random.randint(1, 10)) / 100,
float(random.randint(1, 2)) / 500,
0.001,
float(random.randint(1, 2)) / 500
]
img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲
#img = img.filter(ImageFilter.BLUR) # 滤镜,边界加强(阈值更大)
#img = img.filter(ImageFilter.CONTOUR)
return img, strs
"""
#字体的位置,不同版本的系统会有不同
font_path = '/Library/Fonts/Arial.ttf'
#生成几位数的验证码
number = 4
#生成验证码图片的高度和宽度
size = (100,30)
#背景颜色,默认为白色
bgcolor = (255,255,255)
#字体颜色,默认为蓝色
fontcolor = (0,0,255)
#干扰线颜色。默认为红色
linecolor = (255,0,0)
#是否要加入干扰线
draw_line = True
#加入干扰线条数的上下限
line_number = (1,5)
#用来随机生成一个字符串
def gene_text():
source = list(string.letters)
for index in range(0,10):
source.append(str(index))
return ''.join(random.sample(source,number))#number是生成验证码的位数
#用来绘制干扰线
def gene_line(draw,width,height):
begin = (random.randint(0, width), random.randint(0, height))
end = (random.randint(0, width), random.randint(0, height))
draw.line([begin, end], fill = linecolor)
#生成验证码
def gene_code():
width,height = size #宽和高
image = Image.new('RGBA',(width,height),bgcolor) #创建图片
font = ImageFont.truetype(font_path,25) #验证码的字体
draw = ImageDraw.Draw(image) #创建画笔
text = gene_text() #生成字符串
font_width, font_height = font.getsize(text)
draw.text(((width - font_width) / number, (height - font_height) / number),text,
font= font,fill=fontcolor) #填充字符串
if draw_line:
gene_line(draw,width,height)
# image = image.transform((width+30,height+10), Image.AFFINE, (1,-0.3,0,-0.1,1,0),Image.BILINEAR) #创建扭曲
image = image.transform((width+20,height+10), Image.AFFINE, (1,-0.3,0,-0.1,1,0),Image.BILINEAR) #创建扭曲
image = image.filter(ImageFilter.EDGE_ENHANCE_MORE) #滤镜,边界加强
image.save('idencode.png') #保存验证码图片
"""

@ -0,0 +1,71 @@
from crontab import CronTab
from datetime import datetime
import json
import getpass
user = getpass.getuser()
class CronOpt:
#创建定时任务
def __init__(self,user=user):
self.initialize(user)
#初始化
def initialize(self,user=user):
self.cron = CronTab(user)
#查询列表
def select(self,reInit = False):
if reInit != False:
# 强制重新读取列表
self.initialize()
cronArray = []
for job in self.cron:
# print job.command
schedule = job.schedule(date_from=datetime.now())
cronDict = {
"task" : (job.command).replace(r'>/dev/null 2>&1', ''),
"next" : str(schedule.get_next()),
"prev" : str(schedule.get_prev()),
"comment": job.comment
}
cronArray.append(cronDict)
return cronArray
#新增定时任务
def add(self, command, timeStr, commentName):
# 创建任务
job = self.cron.new(command=command)
# 设置任务执行周期
job.setall(timeStr)
# 备注也是命令id
job.set_comment(commentName)
# 写入到定时任务
self.cron.write()
# 返回更新后的列表
return self.select(True)
#删除定时任务
def delCron(self,commentName):
for job in self.cron :
if job.comment == commentName:
self.cron.remove(job)
self.cron.write()
return self.select(True)
#更新定时任务
def update(self, command, timeStr, commentName):
# 先删除任务
self.delCron(commentName)
# 再创建任务,以达到更新的结果
return self.add(command, timeStr, commentName)
if __name__ == '__main__':
c = CronOpt()
#print(c.select())
# 新增定时任务
#print(c.add("ls /home","*/10 * * * *","测试"))
# 删除定时任务
print(c.delCron("测试"))

@ -0,0 +1,59 @@
#!/usr/bin/env python
import datetime
from datetime import datetime, timedelta
# 获取过去几天的日期
def get_last_days(delta):
days = []
now = datetime.datetime.now()
for i in range(1, delta):
last_date = now + datetime.timedelta(days=-i)
last_date_str = last_date.strftime('%Y-%m-%d') # 格式化输出
days.append(last_date_str)
days.sort()
return days
def time_diff(t1):
# 将字符串转换为 datetime 对象
t1 = datetime.strptime(t1, "%Y-%m-%d %H:%M:%S")
# 计算当前时间与 t1 的差值
diff = datetime.now() - t1
# 计算天数、小时数和分钟数
days = diff.days
hours, remainder = divmod(diff.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
# 返回格式化的时间差
return f"{days}:{hours}:{minutes}"
class DateNext(object):
def get_next_week_date(self, weekday):
# 获取下周几的日期
# weekday取值如下
# (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
today = datetime.date.today()
onday = datetime.timedelta(days=1)
# m1 = calendar.MONDAY
while today.weekday() != weekday:
today += onday
# logging.info(datetime.date(today.year, today.month, today.day))
nextWeekDay = today.strftime('%Y-%m-%d')
return nextWeekDay
def get_next_month_date(self, day_of_the_month):
# 获取下个月几号的日期
today_date = datetime.datetime.today()
today = today_date.day
if today == day_of_the_month:
return today_date.strftime('%Y-%m-%d')
if today < day_of_the_month:
return datetime.date(today_date.year, today_date.month, day_of_the_month).strftime('%Y-%m-%d')
if today > day_of_the_month:
if today_date.month == 12:
return datetime.date(today_date.year+1, 1, day_of_the_month).strftime('%Y-%m-%d')
return datetime.date(today_date.year, today_date.month+1, day_of_the_month).strftime('%Y-%m-%d')

@ -0,0 +1,10 @@
import re
def md5_validate(v: str) -> bool:
md5_pattern = re.compile(r'^[a-fA-F0-9]{32}$')
# 匹配字符串
if md5_pattern.match(v):
return True
return False

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from M2Crypto import RSA
from M2Crypto import BIO
from binascii import a2b_hex, b2a_hex
def load_pub_key_string(string):
bio = BIO.MemoryBuffer(string)
return RSA.load_pub_key_bio(bio)
def block_data(texts, block_size):
for i in range(0, len(texts), block_size):
yield texts[i:i + block_size]
def decrypt(publick_key, texts):
plaintext = b""
block_size = 256
for text in block_data(a2b_hex(texts), block_size):
current_text = publick_key.public_decrypt(text, RSA.pkcs1_padding)
plaintext += current_text
return plaintext

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64
"""
RSA 加密中,有两种常见的填充方式:PKCS1_v1_5 PKCS1_OAEP这两种填充方式在安全性和性能方面都有一些差异
PKCS1_v1_5 填充方式:
这是较早的 RSA 填充方式,相对简单且性能较好
但是它存在一些安全隐患,比如可能会受到选择密文攻击(Chosen Ciphertext Attack, CCA)
PKCS1_OAEP 填充方式:
PKCS1_OAEP 是一种更加安全的填充方式,它使用了随机填充来提高安全性
PKCS1_OAEP 可以抵御选择密文攻击(CCA)和其他一些攻击方式,因此被认为更加安全
但是,PKCS1_OAEP 的性能略低于 PKCS1_v1_5,因为它需要进行更多的计算
"""
# 生成密钥对
def generate_keys():
key = RSA.generate(2048)
private_key = key.export_key()
public_key = key.publickey().export_key()
return private_key, public_key
# 加密消息message为bytes类型
def encrypt_message(public_key, message):
cipher = PKCS1_OAEP.new(RSA.import_key(public_key))
encrypted_message = base64.b64encode(cipher.encrypt(message))
print("Encrypted message:", encrypted_message.decode())
return encrypted_message
# 解密消息
def decrypt_message(private_key, encrypted_message):
decipher = PKCS1_OAEP.new(RSA.import_key(private_key))
decrypted_message = decipher.decrypt(base64.b64decode(encrypted_message))
print("Decrypted message:", decrypted_message.decode())
return decrypted_message.decode()
# 主程序
if __name__ == "__main__":
# 生成密钥对
private_key, public_key = generate_keys()
print(private_key)
print(public_key)
# 序列化公钥和私钥
# private_pem = private_key.private_bytes(
# encoding=serialization.Encoding.PEM,
# format=serialization.PrivateFormat.PKCS8,
# encryption_algorithm=serialization.NoEncryption()
# )
# public_pem = public_key.public_bytes(
# encoding=serialization.Encoding.PEM,
# format=serialization.PublicFormat.SubjectPublicKeyInfo
# )
# 打印公钥和私钥
print("Private Key:")
print(private_key.decode())
print("Public Key:")
print(public_key.decode())
# 待加密消息
message = b"Hello, RSA!"
# 加密消息
encrypted_message = encrypt_message(public_key, message)
print("Encrypted Message:")
print(encrypted_message)
# 解密消息
decrypted_message = decrypt_message(private_key, encrypted_message)
print("Decrypted Message:")
print(decrypted_message)

@ -0,0 +1,137 @@
"""Concise UUID generation."""
import math
import secrets
import uuid as _uu
from typing import List
from typing import Optional
def int_to_string(
number: int, alphabet: List[str], padding: Optional[int] = None
) -> str:
"""
Convert a number to a string, using the given alphabet.
The output has the most significant digit first.
"""
output = ""
alpha_len = len(alphabet)
while number:
number, digit = divmod(number, alpha_len)
output += alphabet[digit]
if padding:
remainder = max(padding - len(output), 0)
output = output + alphabet[0] * remainder
return output[::-1]
def string_to_int(string: str, alphabet: List[str]) -> int:
"""
Convert a string to a number, using the given alphabet.
The input is assumed to have the most significant digit first.
"""
number = 0
alpha_len = len(alphabet)
for char in string:
number = number * alpha_len + alphabet.index(char)
return number
class ShortUUID(object):
def __init__(self, alphabet: Optional[str] = None) -> None:
if alphabet is None:
alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ" "abcdefghijkmnopqrstuvwxyz"
self.set_alphabet(alphabet)
@property
def _length(self) -> int:
"""Return the necessary length to fit the entire UUID given the current alphabet."""
return int(math.ceil(math.log(2**128, self._alpha_len)))
def encode(self, uuid: _uu.UUID, pad_length: Optional[int] = None) -> str:
"""
Encode a UUID into a string (LSB first) according to the alphabet.
If leftmost (MSB) bits are 0, the string might be shorter.
"""
if not isinstance(uuid, _uu.UUID):
raise ValueError("Input `uuid` must be a UUID object.")
if pad_length is None:
pad_length = self._length
return int_to_string(uuid.int, self._alphabet, padding=pad_length)
def decode(self, string: str, legacy: bool = False) -> _uu.UUID:
"""
Decode a string according to the current alphabet into a UUID.
Raises ValueError when encountering illegal characters or a too-long string.
If string too short, fills leftmost (MSB) bits with 0.
Pass `legacy=True` if your UUID was encoded with a ShortUUID version prior to
1.0.0.
"""
if not isinstance(string, str):
raise ValueError("Input `string` must be a str.")
if legacy:
string = string[::-1]
return _uu.UUID(int=string_to_int(string, self._alphabet))
def uuid(self, name: Optional[str] = None, pad_length: Optional[int] = None) -> str:
"""
Generate and return a UUID.
If the name parameter is provided, set the namespace to the provided
name and generate a UUID.
"""
if pad_length is None:
pad_length = self._length
# If no name is given, generate a random UUID.
if name is None:
u = _uu.uuid4()
elif name.lower().startswith(("http://", "https://")):
u = _uu.uuid5(_uu.NAMESPACE_URL, name)
else:
u = _uu.uuid5(_uu.NAMESPACE_DNS, name)
return self.encode(u, pad_length)
def random(self, length: Optional[int] = None) -> str:
"""Generate and return a cryptographically secure short random string of `length`."""
if length is None:
length = self._length
return "".join(secrets.choice(self._alphabet) for _ in range(length))
def get_alphabet(self) -> str:
"""Return the current alphabet used for new UUIDs."""
return "".join(self._alphabet)
def set_alphabet(self, alphabet: str) -> None:
"""Set the alphabet to be used for new UUIDs."""
# Turn the alphabet into a set and sort it to prevent duplicates
# and ensure reproducibility.
new_alphabet = list(sorted(set(alphabet)))
if len(new_alphabet) > 1:
self._alphabet = new_alphabet
self._alpha_len = len(self._alphabet)
else:
raise ValueError("Alphabet with more than " "one unique symbols required.")
def encoded_length(self, num_bytes: int = 16) -> int:
"""Return the string length of the shortened UUID."""
factor = math.log(256) / math.log(self._alpha_len)
return int(math.ceil(factor * num_bytes))
# For backwards compatibility
_global_instance = ShortUUID()
encode = _global_instance.encode
decode = _global_instance.decode
uuid = _global_instance.uuid
random = _global_instance.random
get_alphabet = _global_instance.get_alphabet
set_alphabet = _global_instance.set_alphabet

@ -0,0 +1,86 @@
import hashlib
import logging
import os
import socket
import subprocess
import uuid
def get_cpu_id():
p = subprocess.Popen(["dmidecode -t 4 | grep ID"],
shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
data = p.stdout
lines = []
while True:
line = str(data.readline(), encoding="utf-8")
if line == '\n':
break
if line:
d = dict([line.strip().split(': ')])
lines.append(d)
else:
break
return lines
def get_board_serialnumber():
p = subprocess.Popen(["dmidecode -t 2 | grep Serial"],
shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
data = p.stdout
lines = []
while True:
line = str(data.readline(), encoding="utf-8")
if line == '\n':
break
if line:
d = dict([line.strip().split(': ')])
lines.append(d)
else:
break
return lines
def get_identify_code():
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
mac_addr = ":".join([mac[e:e + 2] for e in range(0, 11, 2)])
host_name = socket.getfqdn(socket.gethostname())
cpu_ids = get_cpu_id()
serialnumbers = get_board_serialnumber()
s = ""
if mac_addr:
s += mac_addr
if host_name:
s += host_name
if cpu_ids:
for cpu in cpu_ids:
s += cpu["ID"]
if serialnumbers:
for number in serialnumbers:
s += number["Serial Number"]
logging.info(s)
code = hashlib.new('md5', s.encode("utf8")).hexdigest()
return code
def get_system_uuid():
# 获取系统uuid
# 这个 UUID 是与硬件相关的,因此即使在 Docker 容器中,它也应该是唯一的,可以用来标识宿主机,而不是容器本身。
with open("/sys/class/dmi/id/product_uuid", "r") as f:
host_uuid = f.read().strip()
return host_uuid
def get_docker_container_id():
# 获取当前 Docker 容器的 ID
cmd = "cat /proc/self/cgroup"
output = os.popen(cmd)
rests = output.readlines()
container_message = rests[-1]
if not container_message:
container_id = "abc"
else:
container_id = container_message.strip().split("docker/")[-1]
return container_id

@ -0,0 +1,103 @@
# -*- coding:utf8 -*-
import io
import pandas as pd
import tornado.gen as gen
import xlwt
class Excel(object):
@gen.coroutine
def generate_excel(self, head, rows):
"""
head is a dict, eg: [(0, u"编号"), (1, u"地址")]
rows is detail list, eg: [[0, "XXX"], ...]
"""
workbook = xlwt.Workbook(encoding='utf-8')
worksheet = workbook.add_sheet("sheet1")
row_num = 0
# col_num = 0
for item in head:
worksheet.write(row_num, head.index(item), item[1])
for row in rows:
row_num += 1
col_num = 0
for col in row:
worksheet.write(row_num, col_num, col)
col_num += 1
sio = io.BytesIO()
workbook.save(sio)
raise gen.Return(sio)
@gen.coroutine
# def generate_excel_pd(self, index, data, columns):
def generate_excel_pd(self, pd_data):
"""
pandas 构建图表
"""
sio = io.StringIO()
writer = pd.ExcelWriter(sio, engine='xlsxwriter')
for data in pd_data:
df = pd.DataFrame(data=data["data"], index=data["index"], columns=data["columns"])
sheet_name = data["sheet_name"]
df.to_excel(writer, sheet_name=sheet_name)
workbook = writer.book
worksheet = writer.sheets[sheet_name]
chart = workbook.add_chart({'type': 'line'})
max_row = len(df) + 1
for i in range(len(data['columns'])):
col = i + 1
chart.add_series({
# 'name': ['Sheet1', 0, col],
'name': [sheet_name, 0, col],
'categories': [sheet_name, 1, 0, max_row, 0],
'values': [sheet_name, 1, col, max_row, col],
'line': {'width': 1.00},
})
chart.set_x_axis({'name': 'Date', 'date_axis': True})
chart.set_y_axis({'name': 'Statistics', 'major_gridlines': {'visible': False}})
chart.set_legend({'position': 'top'})
worksheet.insert_chart('H2', chart)
# df = pd.DataFrame(data=data, index=index, columns=columns)
"""
# ================ anothor method =================
# workbook.save(sio)
io = StringIO.StringIO()
# Use a temp filename to keep pandas happy.
writer = pd.ExcelWriter('temp.xls', engine='xlsxwriter')
# Set the filename/file handle in the xlsxwriter.workbook object.
writer.book.filename = io
#
# Write the data frame to the StringIO object.
df.to_excel(writer, sheet_name='Sheet1')
writer.save()
xlsx_data = io.getvalue()
# ================ anothor method =================
"""
# sheet_name = 'Sheet1'
writer.save()
raise gen.Return(sio)
Loading…
Cancel
Save