Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbf4299a61 | ||
|
|
ad42be8fe8 | ||
|
|
d9c8923b6d | ||
|
|
aa913bce8d | ||
|
|
3e3af89939 | ||
|
|
783baaf8c8 | ||
|
|
a53565e06b | ||
|
|
061495eb13 | ||
|
|
6514363847 | ||
|
|
c3da882950 | ||
|
|
6deebedc75 | ||
|
|
2012f20a09 | ||
|
|
2377c50187 | ||
|
|
91b22297a2 | ||
|
|
5bbf46d141 | ||
|
|
3c7a0947ee | ||
|
|
83c64dddfb | ||
|
|
5d86c330fe | ||
|
|
6a13d3cb22 | ||
|
|
3a8addb4d2 | ||
|
|
90705781f2 | ||
|
|
d8818c3ec2 | ||
|
|
91fecad956 | ||
|
|
9635aa34a7 | ||
|
|
a2f7ab7f9c | ||
|
|
55d0dfd27d | ||
|
|
eb18142926 | ||
|
|
0649c4e3de | ||
|
|
f648e18557 | ||
|
|
9d3b46e9b3 | ||
|
|
14dfe457bf | ||
|
|
9651354317 | ||
|
|
8929aa2f74 | ||
|
|
ea98592012 | ||
|
|
b0be444fba | ||
|
|
b3daacbd8f | ||
|
|
37fc271741 | ||
|
|
bd76eb255d | ||
|
|
0810de23ea | ||
|
|
9f2e4582cc | ||
|
|
8e52631b46 | ||
|
|
7cd885a050 | ||
|
|
3045512320 | ||
|
|
55c2199c3f | ||
|
|
963cd0b227 | ||
|
|
1d5c579e64 | ||
|
|
773d95207f | ||
|
|
2103698417 | ||
|
|
25082b9ea1 | ||
|
|
cb20d56a7b | ||
|
|
d58d46ca8c | ||
|
|
29e6db75ca | ||
|
|
86abf4f634 | ||
|
|
876e763fcc | ||
|
|
32d60a4d43 | ||
|
|
6ffc5d27b5 | ||
|
|
ac412b0dde | ||
|
|
4bdb61870a | ||
|
|
24ad1f64df | ||
|
|
6774376418 | ||
|
|
364120c143 | ||
|
|
cb59390fed | ||
|
|
69bc5b859f | ||
|
|
c420747c6f | ||
|
|
1881086e98 | ||
|
|
eb8d618c2a | ||
|
|
8aa8cda677 | ||
|
|
e67ee60361 | ||
|
|
29fcdd311e | ||
|
|
bb243cf195 | ||
|
|
670e40f6f0 | ||
|
|
feb379f85f | ||
|
|
a8de5ab713 | ||
|
|
4b0c91f106 |
@@ -6,8 +6,12 @@ SPRING_PROFILES_ACTIVE=prod
|
||||
DEMO_MODE=false
|
||||
|
||||
API_CORS=true
|
||||
SECRET_KEY=uQeacXV8b3isvKLK
|
||||
API_IP_HEADERS=X-Forwarded-For,X-Real-IP
|
||||
API_EXPOSE_TOKEN=pmqeHOyZaumHm0Wt
|
||||
SECRET_KEY=uQeacXV8b3isvKLK
|
||||
|
||||
NGINX_SERVICE_HOST=service
|
||||
NGINX_SERVICE_PORT=9200
|
||||
|
||||
MYSQL_HOST=mysql
|
||||
MYSQL_PORT=3306
|
||||
|
||||
8
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
8
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -3,14 +3,6 @@ description: File a bug report.
|
||||
title: "[错误报告]: "
|
||||
labels: [ "" ]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
在提交前请确认:
|
||||
- 使用的是[最新版本](https://github.com/dromara/orion-visor/releases)
|
||||
- 参考了[安装文档](https://visor.orionsec.cn/quickstart/docker.html)
|
||||
- 查阅了[常见问题](https://visor.orionsec.cn/support/faq.html)
|
||||
- 搜索了[已有 issue](https://github.com/dromara/orion-visor/issues)
|
||||
- type: checkboxes
|
||||
id: confirm
|
||||
attributes:
|
||||
|
||||
7
.github/workflows/e2e.yaml
vendored
7
.github/workflows/e2e.yaml
vendored
@@ -1,9 +1,10 @@
|
||||
name: E2E
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
# pull_request:
|
||||
# branches:
|
||||
# - main
|
||||
|
||||
concurrency:
|
||||
group: ${{github.workflow}} - ${{github.ref}}
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,7 +16,8 @@ target/
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
**/.idea/*
|
||||
!**/.idea/icon.png
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
BIN
.idea/icon.png
generated
Normal file
BIN
.idea/icon.png
generated
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -113,7 +113,9 @@ docker compose up -d
|
||||
#### 主机监控
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
#### 批量执行
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ services:
|
||||
DEMO_MODE: false
|
||||
volumes:
|
||||
- /data/orion-visor-space/docker-volumes/service/root-orion:/root/orion
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "http://127.0.0.1:9200/orion-visor/api/server/bootstrap/health" ]
|
||||
interval: 15s
|
||||
@@ -87,4 +91,4 @@ services:
|
||||
|
||||
networks:
|
||||
orion-visor-net:
|
||||
driver: bridge
|
||||
driver: bridge
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
version: '3.3'
|
||||
|
||||
# latest = 2.5.0
|
||||
# latest = 2.5.5
|
||||
|
||||
# 支持以下源
|
||||
# lijiahangmax/*
|
||||
@@ -12,6 +12,9 @@ services:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-ui:latest
|
||||
ports:
|
||||
- ${SERVICE_PORT:-1081}:80
|
||||
environment:
|
||||
NGINX_SERVICE_HOST: ${NGINX_SERVICE_HOST:-service}
|
||||
NGINX_SERVICE_PORT: ${NGINX_SERVICE_PORT:-9200}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
service:
|
||||
@@ -44,12 +47,17 @@ services:
|
||||
GUACD_HOST: ${GUACD_HOST:-guacd}
|
||||
GUACD_PORT: ${GUACD_PORT:-4822}
|
||||
GUACD_DRIVE_PATH: ${GUACD_DRIVE_PATH:-/drive}
|
||||
SECRET_KEY: ${SECRET_KEY:-pmqeHOyZaumHm0Wt}
|
||||
API_EXPOSE_TOKEN: ${API_EXPOSE_TOKEN:-uQeacXV8b3isvKLK}
|
||||
SECRET_KEY: ${SECRET_KEY:-uQeacXV8b3isvKLK}
|
||||
API_EXPOSE_TOKEN: ${API_EXPOSE_TOKEN:-pmqeHOyZaumHm0Wt}
|
||||
API_IP_HEADERS: ${API_IP_HEADERS:-X-Forwarded-For,X-Real-IP}
|
||||
API_CORS: ${API_CORS:-true}
|
||||
DEMO_MODE: ${DEMO_MODE:-false}
|
||||
volumes:
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/service/root-orion:/root/orion
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "http://127.0.0.1:9200/orion-visor/api/server/bootstrap/health" ]
|
||||
@@ -171,4 +179,4 @@ services:
|
||||
|
||||
networks:
|
||||
orion-visor-net:
|
||||
driver: bridge
|
||||
driver: bridge
|
||||
|
||||
@@ -41,7 +41,7 @@ if [ "$DEMO_MODE" = true ]; then
|
||||
echo "Starting services for demo mode..."
|
||||
|
||||
# 启动指定的服务
|
||||
docker compose up -d --remove-orphans mysql redis ui service guacd adminer
|
||||
docker compose up -d --remove-orphans mysql redis ui service guacd influxdb adminer
|
||||
echo "Started services for demo mode..."
|
||||
else
|
||||
# 启动所有服务
|
||||
|
||||
@@ -7,7 +7,7 @@ set -e
|
||||
source ./project-build.sh "$@"
|
||||
|
||||
# 版本号
|
||||
version=2.5.0
|
||||
version=2.5.5
|
||||
# 是否推送镜像
|
||||
push_image=false
|
||||
# 是否构建 latest
|
||||
|
||||
@@ -4,7 +4,7 @@ set -e
|
||||
# DockerContext: orion-visor
|
||||
|
||||
# 版本号
|
||||
version=2.5.0
|
||||
version=2.5.5
|
||||
# 是否构建 service
|
||||
export build_service=false
|
||||
# 是否构建 ui
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM --platform=$TARGETPLATFORM openjdk:8-jdk-alpine
|
||||
FROM --platform=$TARGETPLATFORM openjdk:8u171-jdk-alpine3.7
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
@@ -11,13 +11,20 @@ RUN \
|
||||
ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \
|
||||
echo "${TZ}" > /etc/timezone && \
|
||||
rm -rf /var/cache/apk/* && \
|
||||
rm -rf /etc/nginx/nginx.conf && \
|
||||
rm -rf /etc/nginx/conf.d/*
|
||||
|
||||
# 复制包
|
||||
# 复制前端静态文件
|
||||
COPY ./ui/dist /usr/share/nginx/html
|
||||
|
||||
# 复制配置
|
||||
COPY ./ui/nginx.conf /etc/nginx/conf.d
|
||||
COPY ./ui/nginx.conf /etc/nginx
|
||||
COPY ./ui/service.conf /etc/nginx/conf.d
|
||||
|
||||
# 复制启动脚本
|
||||
COPY ./ui/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
# 启动
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
11
docker/ui/entrypoint.sh
Normal file
11
docker/ui/entrypoint.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 设置环境变量
|
||||
NGINX_SERVICE_HOST="${NGINX_SERVICE_HOST:-service}"
|
||||
NGINX_SERVICE_PORT="${NGINX_SERVICE_PORT:-9200}"
|
||||
|
||||
# 替换环境变量
|
||||
sed -i "s|\${NGINX_SERVICE_HOST}|${NGINX_SERVICE_HOST}|g" /etc/nginx/conf.d/service.conf
|
||||
sed -i "s|\${NGINX_SERVICE_PORT}|${NGINX_SERVICE_PORT}|g" /etc/nginx/conf.d/service.conf
|
||||
|
||||
exec "$@"
|
||||
@@ -1,56 +1,30 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
client_max_body_size 1024m;
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
# 是否启动 gzip 压缩
|
||||
gzip on;
|
||||
# 需要压缩的常见静态资源
|
||||
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
|
||||
# 如果文件大于 1k 就启动压缩
|
||||
gzip_min_length 1k;
|
||||
# 缓冲区
|
||||
gzip_buffers 4 16k;
|
||||
# 压缩的等级
|
||||
gzip_comp_level 2;
|
||||
# access_log /var/log/nginx/host.access.log main;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
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;
|
||||
# web history 模式 404
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /orion-visor/api {
|
||||
proxy_pass http://service:9200/orion-visor/api;
|
||||
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 /orion-visor/keep-alive {
|
||||
proxy_pass http://service:9200/orion-visor/keep-alive;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
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;
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
error_log /var/log/nginx/error.log notice;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
server_tokens off;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
|
||||
56
docker/ui/service.conf
Normal file
56
docker/ui/service.conf
Normal file
@@ -0,0 +1,56 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
client_max_body_size 1024m;
|
||||
|
||||
# 是否启动 gzip 压缩
|
||||
gzip on;
|
||||
# 需要压缩的常见静态资源
|
||||
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
|
||||
# 如果文件大于 1k 就启动压缩
|
||||
gzip_min_length 1k;
|
||||
# 缓冲区
|
||||
gzip_buffers 4 16k;
|
||||
# 压缩的等级
|
||||
gzip_comp_level 2;
|
||||
# access_log /var/log/nginx/host.access.log main;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
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;
|
||||
# web history 模式 404
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /orion-visor/api {
|
||||
proxy_pass http://${NGINX_SERVICE_HOST}:${NGINX_SERVICE_PORT}/orion-visor/api;
|
||||
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 /orion-visor/keep-alive {
|
||||
proxy_pass http://${NGINX_SERVICE_HOST}:${NGINX_SERVICE_PORT}/orion-visor/keep-alive;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
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;
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BIN
docs/assets/screenshot/monitor-alarm.png
Normal file
BIN
docs/assets/screenshot/monitor-alarm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 232 KiB |
BIN
docs/assets/screenshot/monitor-override.png
Normal file
BIN
docs/assets/screenshot/monitor-override.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 201 KiB |
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.configuration;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.utils.IpUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* 公共配置类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/20 10:34
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class CommonConfiguration {
|
||||
|
||||
@Value("${orion.api.ip-headers}")
|
||||
private String[] ipHeaders;
|
||||
|
||||
/**
|
||||
* 设置 IP 请求头
|
||||
*/
|
||||
@PostConstruct
|
||||
public void setIpHeader() {
|
||||
IpUtils.setIpHeader(ipHeaders);
|
||||
log.info("IpUtils.setIpHeader {}", String.join(",", ipHeaders));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public interface AppConst extends OrionConst {
|
||||
/**
|
||||
* 同 ${orion.version} 迭代时候需要手动更改
|
||||
*/
|
||||
String VERSION = "2.5.0";
|
||||
String VERSION = "2.5.5";
|
||||
|
||||
/**
|
||||
* 同 ${spring.application.name}
|
||||
|
||||
@@ -33,6 +33,8 @@ package org.dromara.visor.common.constant;
|
||||
*/
|
||||
public interface AutoConfigureOrderConst {
|
||||
|
||||
int FRAMEWORK_EXECUTOR = Integer.MIN_VALUE + 1000;
|
||||
|
||||
int FRAMEWORK_WEB = Integer.MIN_VALUE + 1100;
|
||||
|
||||
int FRAMEWORK_SECURITY = Integer.MIN_VALUE + 1200;
|
||||
@@ -63,13 +65,13 @@ public interface AutoConfigureOrderConst {
|
||||
|
||||
int FRAMEWORK_JOB = Integer.MIN_VALUE + 2600;
|
||||
|
||||
int FRAMEWORK_JOB_QUARTZ = Integer.MIN_VALUE + 2700;
|
||||
int FRAMEWORK_JOB_QUARTZ = Integer.MIN_VALUE + 2610;
|
||||
|
||||
int FRAMEWORK_JOB_ASYNC = Integer.MIN_VALUE + 2800;
|
||||
int FRAMEWORK_BIZ_PUSH = Integer.MIN_VALUE + 2700;
|
||||
|
||||
int FRAMEWORK_MONITOR = Integer.MIN_VALUE + 2900;
|
||||
int FRAMEWORK_BIZ_OPERATOR_LOG = Integer.MIN_VALUE + 7000;
|
||||
|
||||
int FRAMEWORK_BIZ_OPERATOR_LOG = Integer.MIN_VALUE + 3000;
|
||||
int FRAMEWORK_MONITOR = Integer.MIN_VALUE + 9000;
|
||||
|
||||
int FRAMEWORK_BANNER = Integer.MIN_VALUE + 10000;
|
||||
|
||||
|
||||
@@ -34,116 +34,116 @@ public interface ConfigKeys {
|
||||
/**
|
||||
* SFTP 文件预览大小
|
||||
*/
|
||||
String SFTP_PREVIEW_SIZE = "sftp_previewSize";
|
||||
String SFTP_PREVIEW_SIZE = "sftp.preview-size";
|
||||
|
||||
/**
|
||||
* SFTP 重复文件备份
|
||||
*/
|
||||
String SFTP_UPLOAD_PRESENT_BACKUP = "sftp_uploadPresentBackup";
|
||||
String SFTP_UPLOAD_PRESENT_BACKUP = "sftp.upload-present-backup";
|
||||
|
||||
/**
|
||||
* SFTP 备份文件名称
|
||||
*/
|
||||
String SFTP_UPLOAD_BACKUP_FILE_NAME = "sftp_uploadBackupFileName";
|
||||
String SFTP_UPLOAD_BACKUP_FILE_NAME = "sftp.upload-backup-file-name";
|
||||
|
||||
/**
|
||||
* 加密公钥
|
||||
*/
|
||||
String ENCRYPT_PUBLIC_KEY = "encrypt_publicKey";
|
||||
String ENCRYPT_PUBLIC_KEY = "encrypt.public-key";
|
||||
|
||||
/**
|
||||
* 加密私钥
|
||||
*/
|
||||
String ENCRYPT_PRIVATE_KEY = "encrypt_privateKey";
|
||||
String ENCRYPT_PRIVATE_KEY = "encrypt.private-key";
|
||||
|
||||
/**
|
||||
* 日志前端显示行数
|
||||
*/
|
||||
String LOG_WEB_SCROLL_LINES = "log_webScrollLines";
|
||||
String LOG_WEB_SCROLL_LINES = "log.web-scroll-lines";
|
||||
|
||||
/**
|
||||
* 日志加载偏移行
|
||||
*/
|
||||
String LOG_TRACKER_LOAD_LINES = "log_trackerLoadLines";
|
||||
String LOG_TRACKER_LOAD_LINES = "log.tracker-load-lines";
|
||||
|
||||
/**
|
||||
* 日志加载间隔毫秒
|
||||
*/
|
||||
String LOG_TRACKER_LOAD_INTERVAL = "log_trackerLoadInterval";
|
||||
String LOG_TRACKER_LOAD_INTERVAL = "log.tracker-load-interval";
|
||||
|
||||
/**
|
||||
* 是否生成详细的执行日志
|
||||
*/
|
||||
String LOG_EXEC_DETAIL_LOG = "log_execDetailLog";
|
||||
String LOG_EXEC_DETAIL_ENABLED = "log.exec-detail.enabled";
|
||||
|
||||
/**
|
||||
* 凭证有效期分
|
||||
* 凭证有效期 分
|
||||
*/
|
||||
String LOGIN_LOGIN_SESSION_TIME = "login_loginSessionTime";
|
||||
String LOGIN_LOGIN_SESSION_TIME = "login.login-session-time";
|
||||
|
||||
/**
|
||||
* 允许多端登录
|
||||
*/
|
||||
String LOGIN_ALLOW_MULTI_DEVICE = "login_allowMultiDevice";
|
||||
String LOGIN_ALLOW_MULTI_DEVICE = "login.allow-multi-device";
|
||||
|
||||
/**
|
||||
* 允许凭证续签
|
||||
*/
|
||||
String LOGIN_ALLOW_REFRESH = "login_allowRefresh";
|
||||
String LOGIN_ALLOW_REFRESH = "login.allow-refresh";
|
||||
|
||||
/**
|
||||
* 凭证续签最大次数
|
||||
*/
|
||||
String LOGIN_MAX_REFRESH_COUNT = "login_maxRefreshCount";
|
||||
String LOGIN_MAX_REFRESH_COUNT = "login.max-refresh-count";
|
||||
|
||||
/**
|
||||
* 凭证续签间隔分
|
||||
*/
|
||||
String LOGIN_REFRESH_INTERVAL = "login_refreshInterval";
|
||||
String LOGIN_REFRESH_INTERVAL = "login.refresh-interval";
|
||||
|
||||
/**
|
||||
* 登录失败锁定
|
||||
*/
|
||||
String LOGIN_LOGIN_FAILED_LOCK = "login_loginFailedLock";
|
||||
String LOGIN_LOGIN_FAILED_LOCK = "login.login-failed-lock";
|
||||
|
||||
/**
|
||||
* 登录失败锁定阈值分
|
||||
* 登录失败锁定阈值
|
||||
*/
|
||||
String LOGIN_LOGIN_FAILED_LOCK_THRESHOLD = "login_loginFailedLockThreshold";
|
||||
String LOGIN_LOGIN_FAILED_LOCK_THRESHOLD = "login.login-failed-lock-threshold";
|
||||
|
||||
/**
|
||||
* 登录失败锁定时间分
|
||||
* 登录失败锁定时间 分
|
||||
*/
|
||||
String LOGIN_LOGIN_FAILED_LOCK_TIME = "login_loginFailedLockTime";
|
||||
String LOGIN_LOGIN_FAILED_LOCK_TIME = "login.login-failed-lock-time";
|
||||
|
||||
/**
|
||||
* 登录失败发信
|
||||
*/
|
||||
String LOGIN_LOGIN_FAILED_SEND = "login_loginFailedSend";
|
||||
String LOGIN_LOGIN_FAILED_SEND = "login.login-failed-send";
|
||||
|
||||
/**
|
||||
* 登录失败发信阈值
|
||||
*/
|
||||
String LOGIN_LOGIN_FAILED_SEND_THRESHOLD = "login_loginFailedSendThreshold";
|
||||
String LOGIN_LOGIN_FAILED_SEND_THRESHOLD = "login.login-failed-send-threshold";
|
||||
|
||||
/**
|
||||
* 是否开启自动清理命令记录
|
||||
*/
|
||||
String AUTO_CLEAR_EXEC_LOG_ENABLED = "autoClear_execLogEnabled";
|
||||
String AUTO_CLEAR_EXEC_LOG_ENABLED = "auto-clear.exec-log.enabled";
|
||||
|
||||
/**
|
||||
* 自动清理命令记录保留天数
|
||||
*/
|
||||
String AUTO_CLEAR_EXEC_LOG_KEEP_DAYS = "autoClear_execLogKeepDays";
|
||||
String AUTO_CLEAR_EXEC_LOG_KEEP_DAYS = "auto-clear.exec-log.keep-days";
|
||||
|
||||
/**
|
||||
* 是否开启自动清理终端连接记录
|
||||
*/
|
||||
String AUTO_CLEAR_TERMINAL_LOG_ENABLED = "autoClear_terminalLogEnabled";
|
||||
String AUTO_CLEAR_TERMINAL_LOG_ENABLED = "auto-clear.terminal-log.enabled";
|
||||
|
||||
/**
|
||||
* 自动清理终端连接记录保留天数
|
||||
*/
|
||||
String AUTO_CLEAR_TERMINAL_LOG_KEEP_DAYS = "autoClear_terminalLogKeepDays";
|
||||
String AUTO_CLEAR_TERMINAL_LOG_KEEP_DAYS = "auto-clear.terminal-log.keep-days";
|
||||
|
||||
}
|
||||
|
||||
@@ -100,6 +100,12 @@ public interface ErrorMessage {
|
||||
|
||||
String GROUP_ABSENT = "分组不存在";
|
||||
|
||||
String METRICS_ABSENT = "指标不存在";
|
||||
|
||||
String RULE_ABSENT = "规则不存在";
|
||||
|
||||
String ALARM_POLICY_ABSENT = "告警策略不存在";
|
||||
|
||||
String HOST_TYPE_ERROR = "主机类型错误";
|
||||
|
||||
String HOST_NOT_ENABLED = "{} 主机未启用";
|
||||
@@ -132,6 +138,8 @@ public interface ErrorMessage {
|
||||
|
||||
String CURRENT_USER_UNSUPPORTED_OPT = "当前" + USER_UNSUPPORTED_OPT;
|
||||
|
||||
String PUSH_USER_NOT_EMPTY = "推送用户不能为空";
|
||||
|
||||
String PATH_NOT_NORMALIZE = "路径不合法";
|
||||
|
||||
String OPERATE_ERROR = "操作失败";
|
||||
@@ -140,6 +148,8 @@ public interface ErrorMessage {
|
||||
|
||||
String DECRYPT_ERROR = "数据解密失败";
|
||||
|
||||
String GET_REQUEST_URL_ERROR = "获取请求路径失败";
|
||||
|
||||
String UNKNOWN_TYPE = "未知类型";
|
||||
|
||||
String ERROR_TYPE = "错误的类型";
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.entity;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 推送用户
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 21:46
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "PushUser", description = "推送用户")
|
||||
public class PushUser implements Serializable {
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "用户id")
|
||||
private Long id;
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "花名")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String mobile;
|
||||
|
||||
}
|
||||
@@ -33,6 +33,20 @@ import java.io.Serializable;
|
||||
*/
|
||||
public interface RequestIdentity extends Serializable {
|
||||
|
||||
/**
|
||||
* 获取请求时间戳
|
||||
*
|
||||
* @return timestamp
|
||||
*/
|
||||
Long getTimestamp();
|
||||
|
||||
/**
|
||||
* 设置请求时间戳
|
||||
*
|
||||
* @param timestamp timestamp
|
||||
*/
|
||||
void setTimestamp(Long timestamp);
|
||||
|
||||
/**
|
||||
* 获取请求地址
|
||||
*
|
||||
|
||||
@@ -23,7 +23,10 @@
|
||||
package org.dromara.visor.common.entity;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 请求留痕模型
|
||||
@@ -33,9 +36,15 @@ import lombok.Data;
|
||||
* @since 2023/12/29 11:57
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "RequestIdentityModel", description = "请求留痕模型")
|
||||
public class RequestIdentityModel implements RequestIdentity {
|
||||
|
||||
@Schema(description = "时间戳")
|
||||
private Long timestamp;
|
||||
|
||||
@Schema(description = "请求地址")
|
||||
private String address;
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.mapstruct;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* date 转换器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/3/7 17:43
|
||||
*/
|
||||
public class DateConversion {
|
||||
|
||||
private DateConversion() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Long > Date
|
||||
*
|
||||
* @param timestamp timestamp
|
||||
* @return Date
|
||||
*/
|
||||
public static Date longToDate(Long timestamp) {
|
||||
return timestamp != null ? new Date(timestamp) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Date > Long
|
||||
*
|
||||
* @param date date
|
||||
* @return Long
|
||||
*/
|
||||
public static Long dateToLong(Date date) {
|
||||
return date != null ? date.getTime() : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,13 +37,13 @@ import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 验证器
|
||||
* 断言
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/18 11:23
|
||||
*/
|
||||
public class Valid extends cn.orionsec.kit.lang.utils.Valid {
|
||||
public class Assert extends cn.orionsec.kit.lang.utils.Assert {
|
||||
|
||||
private static final Validator validator = SpringHolder.getBean(Validator.class);
|
||||
|
||||
@@ -155,8 +155,8 @@ public class Valid extends cn.orionsec.kit.lang.utils.Valid {
|
||||
* @param path path
|
||||
*/
|
||||
public static String checkNormalize(String path) {
|
||||
Valid.notBlank(path);
|
||||
Valid.isTrue(Files1.isNormalize(path), ErrorMessage.PATH_NOT_NORMALIZE);
|
||||
Assert.notBlank(path);
|
||||
Assert.isTrue(Files1.isNormalize(path), ErrorMessage.PATH_NOT_NORMALIZE);
|
||||
return Files1.getPath(path);
|
||||
}
|
||||
|
||||
@@ -168,8 +168,8 @@ public class Valid extends cn.orionsec.kit.lang.utils.Valid {
|
||||
* @return file
|
||||
*/
|
||||
public static String checkSuffix(String file, String suffix) {
|
||||
Valid.notBlank(file);
|
||||
Valid.isTrue(file.toLowerCase().endsWith(Const.DOT + suffix), ErrorMessage.PLEASE_SELECT_SUFFIX_FILE, suffix);
|
||||
Assert.notBlank(file);
|
||||
Assert.isTrue(file.toLowerCase().endsWith(Const.DOT + suffix), ErrorMessage.PLEASE_SELECT_SUFFIX_FILE, suffix);
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ package org.dromara.visor.common.utils;
|
||||
|
||||
import cn.orionsec.kit.ext.location.Region;
|
||||
import cn.orionsec.kit.ext.location.region.LocationRegions;
|
||||
import cn.orionsec.kit.web.servlet.web.Servlets;
|
||||
import cn.orionsec.kit.lang.utils.net.IPs;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -40,6 +40,8 @@ import java.util.Map;
|
||||
*/
|
||||
public class IpUtils {
|
||||
|
||||
private static String[] IP_HEADER = new String[]{"X-Forwarded-For", "X-Real-IP"};
|
||||
|
||||
private static final Map<String, String> CACHE = new HashMap<>();
|
||||
|
||||
private IpUtils() {
|
||||
@@ -52,13 +54,17 @@ public class IpUtils {
|
||||
* @return addr
|
||||
*/
|
||||
public static String getRemoteAddr(HttpServletRequest request) {
|
||||
// 获取实际地址 X_REAL_IP 在多代理情况下会有问题
|
||||
// String realIp = request.getHeader(StandardHttpHeader.X_REAL_IP);
|
||||
// if (!Strings.isBlank(realIp)) {
|
||||
// return realIp;
|
||||
// }
|
||||
// 获取请求地址
|
||||
return Servlets.getRemoteAddr(request);
|
||||
if (request == null) {
|
||||
return null;
|
||||
} else {
|
||||
for (String remoteAddrHeader : IP_HEADER) {
|
||||
String addr = checkIpHeader(request.getHeader(remoteAddrHeader));
|
||||
if (addr != null) {
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
return checkIpHeader(request.getRemoteAddr());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,4 +118,23 @@ public class IpUtils {
|
||||
return Const.CN_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 ip 请求头
|
||||
*
|
||||
* @param headerValue headerValue
|
||||
* @return header
|
||||
*/
|
||||
private static String checkIpHeader(String headerValue) {
|
||||
if (headerValue == null) {
|
||||
return null;
|
||||
} else {
|
||||
headerValue = headerValue.split(",")[0];
|
||||
return IPs.checkIp(headerValue);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setIpHeader(String[] ipHeader) {
|
||||
IP_HEADER = ipHeader;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -64,10 +64,24 @@ public class Requests {
|
||||
.map(ServletRequestAttributes::getRequest)
|
||||
.ifPresent(request -> {
|
||||
String address = IpUtils.getRemoteAddr(request);
|
||||
identity.setTimestamp(System.currentTimeMillis());
|
||||
identity.setAddress(address);
|
||||
identity.setLocation(IpUtils.getLocation(address));
|
||||
identity.setUserAgent(Servlets.getUserAgent(request));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制留痕信息
|
||||
*
|
||||
* @param source source
|
||||
* @param target target
|
||||
*/
|
||||
public static void copyIdentity(RequestIdentityModel source, RequestIdentityModel target) {
|
||||
target.setTimestamp(source.getTimestamp());
|
||||
target.setAddress(source.getAddress());
|
||||
target.setLocation(source.getLocation());
|
||||
target.setUserAgent(source.getUserAgent());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
<url>https://github.com/dromara/orion-visor</url>
|
||||
|
||||
<properties>
|
||||
<revision>2.5.0</revision>
|
||||
<revision>2.5.5</revision>
|
||||
<spring.boot.version>2.7.17</spring.boot.version>
|
||||
<spring.boot.admin.version>2.7.15</spring.boot.admin.version>
|
||||
<flatten.maven.plugin.version>1.5.0</flatten.maven.plugin.version>
|
||||
<orion.kit.version>2.0.2</orion.kit.version>
|
||||
<orion.kit.version>2.0.5</orion.kit.version>
|
||||
<aspectj.version>1.9.7</aspectj.version>
|
||||
<lombok.version>1.18.26</lombok.version>
|
||||
<springdoc.version>1.6.15</springdoc.version>
|
||||
@@ -106,6 +106,11 @@
|
||||
<artifactId>orion-visor-spring-boot-starter-job</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-spring-boot-starter-executor</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-spring-boot-starter-websocket</artifactId>
|
||||
@@ -156,6 +161,11 @@
|
||||
<artifactId>orion-visor-spring-boot-starter-influxdb</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-spring-boot-starter-biz-push</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-spring-boot-starter-biz-operator-log</artifactId>
|
||||
|
||||
@@ -23,8 +23,11 @@
|
||||
package org.dromara.visor.framework.biz.operator.log.core.factory;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Arrays1;
|
||||
import cn.orionsec.kit.spring.SpringHolder;
|
||||
import org.dromara.visor.framework.biz.operator.log.core.annotation.Module;
|
||||
import org.dromara.visor.framework.biz.operator.log.core.model.OperatorType;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@@ -35,7 +38,14 @@ import javax.annotation.PostConstruct;
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/13 17:45
|
||||
*/
|
||||
public abstract class InitializingOperatorTypes implements OperatorTypeDefinition {
|
||||
public abstract class InitializingOperatorTypes implements OperatorTypeDefinition, BeanNameAware {
|
||||
|
||||
private String beanName;
|
||||
|
||||
@Override
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
@@ -55,6 +65,8 @@ public abstract class InitializingOperatorTypes implements OperatorTypeDefinitio
|
||||
type.setModule(module);
|
||||
OperatorTypeHolder.set(type);
|
||||
}
|
||||
// 自动销毁
|
||||
((BeanDefinitionRegistry) SpringHolder.getBeanFactory()).removeBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -123,8 +123,8 @@ public class OperatorLogModel implements RequestIdentity {
|
||||
private Date endTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
* 时间戳
|
||||
*/
|
||||
private Date createTime;
|
||||
private Long timestamp;
|
||||
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.SerializeFilter;
|
||||
import org.dromara.visor.common.entity.RequestIdentity;
|
||||
import org.dromara.visor.common.enums.BooleanBit;
|
||||
import org.dromara.visor.common.trace.TraceIdHolder;
|
||||
import org.dromara.visor.common.security.LoginUser;
|
||||
import org.dromara.visor.common.trace.TraceIdHolder;
|
||||
import org.dromara.visor.common.utils.Requests;
|
||||
import org.dromara.visor.framework.biz.operator.log.configuration.config.OperatorLogConfig;
|
||||
import org.dromara.visor.framework.biz.operator.log.core.enums.ReturnType;
|
||||
@@ -91,6 +91,7 @@ public class OperatorLogFiller implements Gettable<OperatorLogModel> {
|
||||
*/
|
||||
public OperatorLogFiller fillUsedTime(long start) {
|
||||
long end = System.currentTimeMillis();
|
||||
model.setTimestamp(start);
|
||||
model.setDuration((int) (end - start));
|
||||
model.setStartTime(new Date(start));
|
||||
model.setEndTime(new Date(end));
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>orion-visor-spring-boot-starter-biz-push</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<description>项目业务推送包</description>
|
||||
<url>https://github.com/dromara/orion-visor</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.configuration;
|
||||
|
||||
import cn.orionsec.kit.lang.function.Functions;
|
||||
import org.dromara.visor.common.constant.AutoConfigureOrderConst;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.framework.service.PushTemplateFrameworkService;
|
||||
import org.dromara.visor.framework.biz.push.core.framework.service.PushTemplateFrameworkServiceDelegate;
|
||||
import org.dromara.visor.framework.biz.push.core.framework.service.WebsiteMessageFrameworkService;
|
||||
import org.dromara.visor.framework.biz.push.core.listener.PushMessageEventListener;
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.service.*;
|
||||
import org.dromara.visor.framework.biz.push.core.utils.MessageChannelUtils;
|
||||
import org.dromara.visor.framework.biz.push.core.utils.PushUtils;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 推送自动配置类
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/17 23:04
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@AutoConfigureOrder(AutoConfigureOrderConst.FRAMEWORK_BIZ_PUSH)
|
||||
public class OrionPushAutoConfiguration {
|
||||
|
||||
/**
|
||||
* @return 钉钉推送服务
|
||||
*/
|
||||
@Bean
|
||||
public DingPushService dingPushService() {
|
||||
return new DingPushService();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 飞书推送服务
|
||||
*/
|
||||
@Bean
|
||||
public FeiShuPushService feiShuPushService() {
|
||||
return new FeiShuPushService();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 企业微信推送服务
|
||||
*/
|
||||
@Bean
|
||||
public WeComPushService weComPushService() {
|
||||
return new WeComPushService();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param websiteMessageFrameworkService websiteMessageFrameworkService
|
||||
* @return 站内信推送服务
|
||||
*/
|
||||
@Bean
|
||||
public WebsitePushService websitePushService(WebsiteMessageFrameworkService websiteMessageFrameworkService) {
|
||||
return new WebsitePushService(websiteMessageFrameworkService);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param impl impl
|
||||
* @return 推送模板服务
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
@ConditionalOnBean(PushTemplateFrameworkService.class)
|
||||
public PushTemplateFrameworkService pushTemplateFrameworkService(PushTemplateFrameworkService impl) {
|
||||
PushTemplateFrameworkServiceDelegate delegate = new PushTemplateFrameworkServiceDelegate(impl);
|
||||
// 设置到工具类
|
||||
PushUtils.setPushTemplateFrameworkService(delegate);
|
||||
return delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pushServices 推送服务
|
||||
* @return 消息推送事件监听器
|
||||
*/
|
||||
@Bean
|
||||
public PushMessageEventListener pushMessageEventListener(List<IPushService<? extends PushMessage>> pushServices) {
|
||||
// 服务列表
|
||||
Map<PushChannelEnum, IPushService<? extends PushMessage>> serviceMap = pushServices.stream()
|
||||
.collect(Collectors.toMap(
|
||||
MessageChannelUtils::getPushChannel,
|
||||
Function.identity(),
|
||||
Functions.right()));
|
||||
// 创建监听器
|
||||
return new PushMessageEventListener(serviceMap);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.annotation;
|
||||
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 消息渠道
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 21:58
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface MessageChannel {
|
||||
|
||||
/**
|
||||
* 消息渠道
|
||||
*
|
||||
* @return channel
|
||||
*/
|
||||
PushChannelEnum value();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.entity;
|
||||
|
||||
import cn.orionsec.kit.lang.able.IJsonObject;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 钉钉请求体
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:41
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DingRequestBody implements Serializable, IJsonObject {
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
@JSONField(name = "msgtype")
|
||||
private String msgType;
|
||||
|
||||
/**
|
||||
* markdown 内容
|
||||
*/
|
||||
private DingRequestBody.MarkdownPayload markdown;
|
||||
|
||||
/**
|
||||
* at 配置
|
||||
*/
|
||||
private DingRequestBody.AtPayload at;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class MarkdownPayload implements Serializable {
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private String text;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class AtPayload implements Serializable {
|
||||
|
||||
/**
|
||||
* 被 at 的手机号
|
||||
*/
|
||||
private List<String> atMobiles;
|
||||
|
||||
/**
|
||||
* 被 at 的 userId
|
||||
*/
|
||||
private List<String> atUserIds;
|
||||
|
||||
/**
|
||||
* 是否 at 所有人
|
||||
*/
|
||||
private Boolean isAtAll;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,43 +20,39 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.module.infra.entity.dto;
|
||||
package org.dromara.visor.framework.biz.push.core.entity;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.visor.common.entity.RequestIdentity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 身份信息
|
||||
* 钉钉响应体
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/11/1 1:01
|
||||
* @since 2025/9/18 18:41
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class LoginTokenIdentityDTO implements RequestIdentity {
|
||||
public class DingResponseBody implements Serializable {
|
||||
|
||||
/**
|
||||
* 原始登录时间
|
||||
* 错误码值
|
||||
*/
|
||||
private Long loginTime;
|
||||
@JSONField(name = "errcode")
|
||||
private Integer errCode;
|
||||
|
||||
/**
|
||||
* 当前设备登录地址
|
||||
* 错误码描述
|
||||
*/
|
||||
private String address;
|
||||
|
||||
/**
|
||||
* 当前设备登录地址
|
||||
*/
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 当前设备 userAgent
|
||||
*/
|
||||
private String userAgent;
|
||||
@JSONField(name = "errmsg")
|
||||
private String errMsg;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.entity;
|
||||
|
||||
import cn.orionsec.kit.lang.able.IJsonObject;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 飞书请求体
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:41
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FeiShuRequestBody implements Serializable, IJsonObject {
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 签名
|
||||
*/
|
||||
private String sign;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
@JSONField(name = "msg_type")
|
||||
private String msgType;
|
||||
|
||||
/**
|
||||
* text 内容
|
||||
*/
|
||||
private TextPayload content;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class TextPayload implements Serializable {
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private String text;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 飞书响应体
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:41
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FeiShuResponseBody implements Serializable {
|
||||
|
||||
/**
|
||||
* code
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* msg
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.entity;
|
||||
|
||||
import cn.orionsec.kit.lang.able.IJsonObject;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 企业微信请求体
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:41
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class WeComRequestBody implements Serializable, IJsonObject {
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
@JSONField(name = "msgtype")
|
||||
private String msgType;
|
||||
|
||||
/**
|
||||
* markdown 内容
|
||||
*/
|
||||
private MarkdownPayload markdown;
|
||||
|
||||
/**
|
||||
* 被 at 的 userId
|
||||
*/
|
||||
@JSONField(name = "mentioned_list")
|
||||
private List<String> mentionedList;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class MarkdownPayload implements Serializable {
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.entity;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 企业微信响应体
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:41
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class WeComResponseBody implements Serializable {
|
||||
|
||||
/**
|
||||
* 错误码值
|
||||
*/
|
||||
@JSONField(name = "errcode")
|
||||
private Integer errCode;
|
||||
|
||||
/**
|
||||
* 错误码描述
|
||||
*/
|
||||
@JSONField(name = "errmsg")
|
||||
private String errMsg;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.enums;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.dromara.visor.framework.biz.push.core.message.*;
|
||||
|
||||
/**
|
||||
* 通知模板类型枚举
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/15 23:20
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PushChannelEnum {
|
||||
|
||||
/**
|
||||
* 站内信
|
||||
*/
|
||||
WEBSITE(WebsiteMessage.class),
|
||||
|
||||
/**
|
||||
* 钉钉
|
||||
*/
|
||||
DING(DingPushMessage.class),
|
||||
|
||||
/**
|
||||
* 飞书
|
||||
*/
|
||||
FEI_SHU(FeiShuPushMessage.class),
|
||||
|
||||
/**
|
||||
* 企业微信
|
||||
*/
|
||||
WE_COM(WeComPushMessage.class),
|
||||
|
||||
;
|
||||
|
||||
public final Class<?> messageClass;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends PushMessage> T createMessage(String config) {
|
||||
try {
|
||||
return (T) JSON.parseObject(config, messageClass);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据渠道名称获取枚举
|
||||
*
|
||||
* @param channel 渠道名称
|
||||
* @return 枚举
|
||||
*/
|
||||
public static PushChannelEnum of(String channel) {
|
||||
for (PushChannelEnum value : values()) {
|
||||
if (value.name().equalsIgnoreCase(channel)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return WEBSITE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.event;
|
||||
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* 消息推送事件
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/15
|
||||
*/
|
||||
public class PushMessageEvent extends ApplicationEvent {
|
||||
|
||||
public PushMessageEvent(PushMessage message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.framework.service;
|
||||
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
|
||||
/**
|
||||
* 推送消息模板框架服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/21 16:26
|
||||
*/
|
||||
public interface PushTemplateFrameworkService {
|
||||
|
||||
/**
|
||||
* 根据 templateId 获取消息
|
||||
*
|
||||
* @param templateId templateId
|
||||
* @return message
|
||||
*/
|
||||
PushMessage getPushMessageByTemplateId(Long templateId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.framework.service;
|
||||
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
|
||||
/**
|
||||
* 推送消息模板框架服务委托类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/21 16:26
|
||||
*/
|
||||
public class PushTemplateFrameworkServiceDelegate implements PushTemplateFrameworkService {
|
||||
|
||||
private final PushTemplateFrameworkService delegate;
|
||||
|
||||
public PushTemplateFrameworkServiceDelegate(PushTemplateFrameworkService delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PushMessage getPushMessageByTemplateId(Long templateId) {
|
||||
return delegate.getPushMessageByTemplateId(templateId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.framework.service;
|
||||
|
||||
import org.dromara.visor.framework.biz.push.core.message.WebsiteMessage;
|
||||
|
||||
/**
|
||||
* 发送站内信框架服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 22:09
|
||||
*/
|
||||
public interface WebsiteMessageFrameworkService {
|
||||
|
||||
/**
|
||||
* 发送站内信
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
void push(WebsiteMessage message);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.event.PushMessageEvent;
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.service.IPushService;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 消息推送事件监听器
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/15
|
||||
*/
|
||||
@Slf4j
|
||||
public class PushMessageEventListener implements ApplicationListener<PushMessageEvent> {
|
||||
|
||||
private final Map<PushChannelEnum, IPushService<? extends PushMessage>> pushServiceMap;
|
||||
|
||||
public PushMessageEventListener(Map<PushChannelEnum, IPushService<? extends PushMessage>> pushServiceMap) {
|
||||
this.pushServiceMap = pushServiceMap;
|
||||
}
|
||||
|
||||
@Async("pushExecutor")
|
||||
@Override
|
||||
public void onApplicationEvent(PushMessageEvent event) {
|
||||
try {
|
||||
// 获取消息
|
||||
PushMessage message = (PushMessage) event.getSource();
|
||||
// 发送消息
|
||||
this.getPushService(message).push(message);
|
||||
log.info("PushMessageEventListener push success");
|
||||
} catch (Exception e) {
|
||||
log.error("PushMessageEventListener push error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends PushMessage> IPushService<T> getPushService(PushMessage message) {
|
||||
return (IPushService<T>) pushServiceMap.get(message.getChannel());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.visor.common.entity.PushUser;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 推送消息基类
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/15 23:28
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "BasePushMessage", description = "推送消息基类")
|
||||
public abstract class BasePushMessage implements PushMessage, Serializable {
|
||||
|
||||
@NotBlank
|
||||
@Schema(description = "消息模板")
|
||||
private String template;
|
||||
|
||||
@Schema(description = "消息参数")
|
||||
private Map<String, Object> params;
|
||||
|
||||
@Valid
|
||||
@Schema(description = "推送用户")
|
||||
private List<PushUser> pushUsers;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 钉钉推送消息
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/15 23:29
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "DingPushMessage", description = "钉钉推送消息")
|
||||
public class DingPushMessage extends BasePushMessage {
|
||||
|
||||
@NotBlank
|
||||
@Schema(description = "webhook")
|
||||
private String webhook;
|
||||
|
||||
@Schema(description = "密钥")
|
||||
private String secret;
|
||||
|
||||
@NotBlank
|
||||
@Schema(description = "推送标题")
|
||||
private String title;
|
||||
|
||||
@Override
|
||||
public PushChannelEnum getChannel() {
|
||||
return PushChannelEnum.DING;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 飞书推送消息
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/16 00:26
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "FeiShuPushMessage", description = "飞书推送消息")
|
||||
public class FeiShuPushMessage extends BasePushMessage {
|
||||
|
||||
@NotBlank
|
||||
@Schema(description = "webhook")
|
||||
private String webhook;
|
||||
|
||||
@Schema(description = "密钥")
|
||||
private String secret;
|
||||
|
||||
@Override
|
||||
public PushChannelEnum getChannel() {
|
||||
return PushChannelEnum.FEI_SHU;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.message;
|
||||
|
||||
import org.dromara.visor.common.entity.PushUser;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 推送消息接口
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:42
|
||||
*/
|
||||
public interface PushMessage extends Serializable {
|
||||
|
||||
/**
|
||||
* 获取模板
|
||||
*
|
||||
* @return 模板
|
||||
*/
|
||||
String getTemplate();
|
||||
|
||||
/**
|
||||
* 设置模板
|
||||
*
|
||||
* @param template template
|
||||
*/
|
||||
void setTemplate(String template);
|
||||
|
||||
/**
|
||||
* 获取参数
|
||||
*
|
||||
* @return 参数
|
||||
*/
|
||||
Map<String, Object> getParams();
|
||||
|
||||
/**
|
||||
* 设置参数
|
||||
*
|
||||
* @param params params
|
||||
*/
|
||||
void setParams(Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* 获取推送用户
|
||||
*
|
||||
* @return 推送用户
|
||||
*/
|
||||
List<PushUser> getPushUsers();
|
||||
|
||||
/**
|
||||
* 设置推送用户列表
|
||||
*
|
||||
* @param pushUsers pushUsers
|
||||
*/
|
||||
void setPushUsers(List<PushUser> pushUsers);
|
||||
|
||||
/**
|
||||
* 获取推送渠道
|
||||
*
|
||||
* @return 推送渠道
|
||||
*/
|
||||
PushChannelEnum getChannel();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 企业微信推送消息
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/15 23:30
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "WeComPushMessage", description = "企业微信推送消息")
|
||||
public class WeComPushMessage extends BasePushMessage {
|
||||
|
||||
@NotBlank
|
||||
@Schema(description = "webhook")
|
||||
private String webhook;
|
||||
|
||||
@Override
|
||||
public PushChannelEnum getChannel() {
|
||||
return PushChannelEnum.WE_COM;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 站内信推送
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 19:56
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "WebsiteMessage", description = "站内信消息")
|
||||
public class WebsiteMessage extends BasePushMessage {
|
||||
|
||||
@NotBlank
|
||||
@Size(max = 10)
|
||||
@Schema(description = "消息分类")
|
||||
private String messageClassify;
|
||||
|
||||
@NotBlank
|
||||
@Size(max = 32)
|
||||
@Schema(description = "消息类型")
|
||||
private String messageType;
|
||||
|
||||
@Schema(description = "消息关联")
|
||||
private String relKey;
|
||||
|
||||
@NotBlank
|
||||
@Size(max = 128)
|
||||
@Schema(description = "标题")
|
||||
private String title;
|
||||
|
||||
@Override
|
||||
public PushChannelEnum getChannel() {
|
||||
return PushChannelEnum.WEBSITE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.service;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
import org.dromara.visor.common.entity.PushUser;
|
||||
import org.dromara.visor.common.utils.Assert;
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 消息推送抽象服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:51
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class BasePushService<Message extends PushMessage> implements IPushService<Message> {
|
||||
|
||||
@Override
|
||||
public void push(Message message) {
|
||||
try {
|
||||
// 验证消息
|
||||
this.validateMessage(message);
|
||||
} catch (Exception e) {
|
||||
log.error("BasePushService validateMessage message: {}", JSON.toJSONString(message), e);
|
||||
return;
|
||||
}
|
||||
// 请求路径
|
||||
String url = this.buildRequestUrl(message);
|
||||
// 构建请求体
|
||||
String body = this.buildRequestBody(message);
|
||||
// 发送请求
|
||||
this.sendRequest(message, url, body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证消息
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
protected void validateMessage(Message message) {
|
||||
// 验证消息
|
||||
Assert.valid(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建请求 url
|
||||
*
|
||||
* @param message message
|
||||
* @return url
|
||||
*/
|
||||
protected abstract String buildRequestUrl(Message message);
|
||||
|
||||
/**
|
||||
* 构建请求体
|
||||
*
|
||||
* @param message message
|
||||
* @return body
|
||||
*/
|
||||
protected abstract String buildRequestBody(Message message);
|
||||
|
||||
/**
|
||||
* 发送请求
|
||||
*
|
||||
* @param message message
|
||||
* @param url url
|
||||
* @param body body
|
||||
*/
|
||||
protected abstract void sendRequest(Message message, String url, String body);
|
||||
|
||||
/**
|
||||
* 追加 at 用户
|
||||
*
|
||||
* @param message message
|
||||
* @param content content
|
||||
* @return content
|
||||
*/
|
||||
protected String appendAtUsers(Message message, String content) {
|
||||
if (Lists.isEmpty(message.getPushUsers())) {
|
||||
return content;
|
||||
}
|
||||
return content + Const.LF + this.getAtUsers(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 @ 的用户
|
||||
*
|
||||
* @param message message
|
||||
* @return users
|
||||
*/
|
||||
protected String getAtUsers(Message message) {
|
||||
return Lists.stream(message.getPushUsers())
|
||||
.map(PushUser::getNickname)
|
||||
.filter(Strings::isNotBlank)
|
||||
.map(s -> Const.AT + s)
|
||||
.collect(Collectors.joining(Const.SPACE));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.service;
|
||||
|
||||
import cn.orionsec.kit.http.ok.OkRequests;
|
||||
import cn.orionsec.kit.http.ok.OkResponse;
|
||||
import cn.orionsec.kit.lang.constant.StandardContentType;
|
||||
import cn.orionsec.kit.lang.utils.Assert;
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import cn.orionsec.kit.lang.utils.codec.Base64s;
|
||||
import cn.orionsec.kit.lang.utils.crypto.Signatures;
|
||||
import cn.orionsec.kit.lang.utils.math.Hex;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
|
||||
import org.dromara.visor.framework.biz.push.core.entity.DingRequestBody;
|
||||
import org.dromara.visor.framework.biz.push.core.entity.DingResponseBody;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.message.DingPushMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.utils.MessageUtils;
|
||||
|
||||
/**
|
||||
* 钉钉推送服务类
|
||||
* <p>
|
||||
* <a href="https://open.dingtalk.com/document/dingstart/obtain-the-webhook-address-of-a-custom-robot">doc</a>
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/17 23:13
|
||||
*/
|
||||
@Slf4j
|
||||
@MessageChannel(value = PushChannelEnum.DING)
|
||||
public class DingPushService extends BasePushService<DingPushMessage> {
|
||||
|
||||
private static final String MARKDOWN = "markdown";
|
||||
|
||||
private static final Integer SUCCESS_CODE = 0;
|
||||
|
||||
@Override
|
||||
protected String buildRequestUrl(DingPushMessage message) {
|
||||
String secret = message.getSecret();
|
||||
if (Strings.isBlank(secret)) {
|
||||
return message.getWebhook();
|
||||
}
|
||||
// 加签
|
||||
try {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String plainText = timestamp + "\n" + secret;
|
||||
String hexSign = Signatures.hmacSha256(plainText, secret);
|
||||
if (hexSign == null) {
|
||||
log.error("DingPushService-sign error plain: {}", plainText);
|
||||
throw Exceptions.argument(ErrorMessage.GET_REQUEST_URL_ERROR);
|
||||
}
|
||||
byte[] signData = Hex.hexToBytes(hexSign);
|
||||
String sign = Base64s.encodeToString(signData);
|
||||
// 实际请求地址
|
||||
return message.getWebhook() + "×tamp=" + timestamp + "&sign=" + sign;
|
||||
} catch (Exception e) {
|
||||
log.error("DingPushService-buildRequestUrl error", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildRequestBody(DingPushMessage message) {
|
||||
// 格式化内容
|
||||
String formattedContent = MessageUtils.format(message.getTemplate(), message.getParams());
|
||||
String formattedTitle = MessageUtils.format(message.getTitle(), message.getParams());
|
||||
// at 用户
|
||||
formattedContent = this.appendAtUsers(message, formattedContent);
|
||||
|
||||
// 构建请求体
|
||||
return DingRequestBody.builder()
|
||||
.msgType(MARKDOWN)
|
||||
.markdown(DingRequestBody.MarkdownPayload.builder()
|
||||
.title(formattedTitle)
|
||||
.text(formattedContent)
|
||||
.build())
|
||||
.build()
|
||||
.toJsonString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendRequest(DingPushMessage message, String url, String body) {
|
||||
// 发送请求
|
||||
OkResponse response = OkRequests.post(url, StandardContentType.APPLICATION_JSON_UTF8, body);
|
||||
DingResponseBody responseBody = JSON.parseObject(response.getBodyString(), DingResponseBody.class);
|
||||
// 验证发送结果
|
||||
Assert.eq(responseBody.getErrCode(), SUCCESS_CODE, responseBody.getErrMsg());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.service;
|
||||
|
||||
import cn.orionsec.kit.http.ok.OkRequests;
|
||||
import cn.orionsec.kit.http.ok.OkResponse;
|
||||
import cn.orionsec.kit.lang.constant.Const;
|
||||
import cn.orionsec.kit.lang.constant.StandardContentType;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import cn.orionsec.kit.lang.utils.codec.Base64s;
|
||||
import cn.orionsec.kit.lang.utils.crypto.Signatures;
|
||||
import cn.orionsec.kit.lang.utils.crypto.enums.SecretKeySpecMode;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.utils.Assert;
|
||||
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
|
||||
import org.dromara.visor.framework.biz.push.core.entity.FeiShuRequestBody;
|
||||
import org.dromara.visor.framework.biz.push.core.entity.FeiShuResponseBody;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.message.FeiShuPushMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.utils.MessageUtils;
|
||||
|
||||
/**
|
||||
* 飞书推送服务类
|
||||
* <p>
|
||||
* <a href="https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot?lang=zh-CN">doc</a>
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/17 23:13
|
||||
*/
|
||||
@Slf4j
|
||||
@MessageChannel(value = PushChannelEnum.FEI_SHU)
|
||||
public class FeiShuPushService extends BasePushService<FeiShuPushMessage> {
|
||||
|
||||
private static final String TEXT = "text";
|
||||
|
||||
private static final Integer SUCCESS_CODE = 0;
|
||||
|
||||
@Override
|
||||
protected String buildRequestUrl(FeiShuPushMessage message) {
|
||||
return message.getWebhook();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
*
|
||||
* @param message message
|
||||
* @param timestamp timestamp
|
||||
* @return sign
|
||||
*/
|
||||
protected String buildSign(FeiShuPushMessage message, long timestamp) {
|
||||
String secret = message.getSecret();
|
||||
if (Strings.isBlank(secret)) {
|
||||
return null;
|
||||
}
|
||||
// 加签
|
||||
try {
|
||||
String stringToSign = timestamp + "\n" + secret;
|
||||
byte[] signData = Signatures.hmacHashSignBytes(new byte[]{}, Strings.bytes(stringToSign), SecretKeySpecMode.HMAC_SHA256);
|
||||
return Base64s.encodeToString(signData);
|
||||
} catch (Exception e) {
|
||||
log.error("FeiShuPushService-buildSign error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildRequestBody(FeiShuPushMessage message) {
|
||||
long timestamp = System.currentTimeMillis() / Const.MS_S_1;
|
||||
// 加签
|
||||
String sign = this.buildSign(message, timestamp);
|
||||
// 格式化内容
|
||||
String formattedContent = MessageUtils.format(message.getTemplate(), message.getParams());
|
||||
// at 的用户
|
||||
formattedContent = this.appendAtUsers(message, formattedContent);
|
||||
// 构建请求体
|
||||
return FeiShuRequestBody.builder()
|
||||
.timestamp(timestamp)
|
||||
.sign(sign)
|
||||
.msgType(TEXT)
|
||||
.content(FeiShuRequestBody.TextPayload.builder()
|
||||
.text(formattedContent)
|
||||
.build())
|
||||
.build()
|
||||
.toJsonString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendRequest(FeiShuPushMessage message, String url, String body) {
|
||||
// 发送请求
|
||||
OkResponse response = OkRequests.post(url, StandardContentType.APPLICATION_JSON_UTF8, body);
|
||||
FeiShuResponseBody responseBody = JSON.parseObject(response.getBodyString(), FeiShuResponseBody.class);
|
||||
// 验证发送结果
|
||||
Assert.eq(responseBody.getCode(), SUCCESS_CODE, responseBody.getMsg());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.service;
|
||||
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
|
||||
/**
|
||||
* 消息推送服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/17 17:17
|
||||
*/
|
||||
public interface IPushService<Message extends PushMessage> {
|
||||
|
||||
/**
|
||||
* 推送消息
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
void push(Message message);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.service;
|
||||
|
||||
import cn.orionsec.kit.http.ok.OkRequests;
|
||||
import cn.orionsec.kit.http.ok.OkResponse;
|
||||
import cn.orionsec.kit.lang.constant.StandardContentType;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.utils.Assert;
|
||||
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
|
||||
import org.dromara.visor.framework.biz.push.core.entity.WeComRequestBody;
|
||||
import org.dromara.visor.framework.biz.push.core.entity.WeComResponseBody;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.message.WeComPushMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.utils.MessageUtils;
|
||||
|
||||
/**
|
||||
* 企业微信推送服务
|
||||
* <p>
|
||||
* <a href="https://developer.work.weixin.qq.com/document/path/99110">docs</a>
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/17 23:13
|
||||
*/
|
||||
@Slf4j
|
||||
@MessageChannel(value = PushChannelEnum.WE_COM)
|
||||
public class WeComPushService extends BasePushService<WeComPushMessage> {
|
||||
|
||||
private static final String MAKRDOWN = "markdown";
|
||||
|
||||
private static final Integer SUCCESS_CODE = 0;
|
||||
|
||||
@Override
|
||||
protected String buildRequestUrl(WeComPushMessage message) {
|
||||
return message.getWebhook();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildRequestBody(WeComPushMessage message) {
|
||||
// 格式化内容
|
||||
String formattedContent = MessageUtils.format(message.getTemplate(), message.getParams());
|
||||
// at 用户
|
||||
formattedContent = this.appendAtUsers(message, formattedContent);
|
||||
// 构建请求体
|
||||
WeComRequestBody.WeComRequestBodyBuilder builder = WeComRequestBody.builder()
|
||||
.msgType(MAKRDOWN)
|
||||
.markdown(WeComRequestBody.MarkdownPayload.builder()
|
||||
.content(formattedContent)
|
||||
.build());
|
||||
|
||||
return builder.build().toJsonString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendRequest(WeComPushMessage message, String url, String body) {
|
||||
// 发送请求
|
||||
OkResponse response = OkRequests.post(url, StandardContentType.APPLICATION_JSON_UTF8, body);
|
||||
WeComResponseBody responseBody = JSON.parseObject(response.getBodyString(), WeComResponseBody.class);
|
||||
// 验证发送结果
|
||||
Assert.eq(responseBody.getErrCode(), SUCCESS_CODE, responseBody.getErrMsg());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.framework.service.WebsiteMessageFrameworkService;
|
||||
import org.dromara.visor.framework.biz.push.core.message.WebsiteMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.utils.MessageUtils;
|
||||
|
||||
/**
|
||||
* 站内信推送服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 22:13
|
||||
*/
|
||||
@Slf4j
|
||||
@MessageChannel(value = PushChannelEnum.WEBSITE)
|
||||
public class WebsitePushService extends BasePushService<WebsiteMessage> {
|
||||
|
||||
private final WebsiteMessageFrameworkService websiteMessageFrameworkService;
|
||||
|
||||
public WebsitePushService(WebsiteMessageFrameworkService websiteMessageFrameworkService) {
|
||||
this.websiteMessageFrameworkService = websiteMessageFrameworkService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildRequestUrl(WebsiteMessage message) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildRequestBody(WebsiteMessage message) {
|
||||
// 格式化内容
|
||||
message.setTemplate(MessageUtils.format(message.getTemplate(), message.getParams()));
|
||||
// 格式化标题
|
||||
message.setTitle(MessageUtils.format(message.getTitle(), message.getParams()));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendRequest(WebsiteMessage message, String url, String body) {
|
||||
websiteMessageFrameworkService.push(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.utils;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.service.IPushService;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
/**
|
||||
* 消息渠道工具类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 22:05
|
||||
*/
|
||||
public class MessageChannelUtils {
|
||||
|
||||
private MessageChannelUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取推送渠道
|
||||
*
|
||||
* @param service service
|
||||
* @return channel
|
||||
*/
|
||||
public static PushChannelEnum getPushChannel(IPushService<? extends PushMessage> service) {
|
||||
// 获取类型注解
|
||||
MessageChannel messageChannel = AnnotationUtils.findAnnotation(service.getClass(), MessageChannel.class);
|
||||
if (messageChannel == null) {
|
||||
throw new BeanInitializationException(Strings.format("Push service [{}] not found @MessageChannel annotation", service.getClass().getName()));
|
||||
}
|
||||
return messageChannel.value();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.utils;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.json.matcher.NoMatchStrategy;
|
||||
import cn.orionsec.kit.lang.utils.json.matcher.ReplacementFormatter;
|
||||
import cn.orionsec.kit.lang.utils.json.matcher.ReplacementFormatters;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 消息工具类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/16 17:28
|
||||
*/
|
||||
public class MessageUtils {
|
||||
|
||||
private static final ReplacementFormatter FORMATTER = ReplacementFormatters.create("@{{ ", " }}")
|
||||
.noMatchStrategy(NoMatchStrategy.KEEP);
|
||||
|
||||
private MessageUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换模板
|
||||
*
|
||||
* @param template template
|
||||
* @param params params
|
||||
* @return template
|
||||
*/
|
||||
public static String format(String template, Map<String, Object> params) {
|
||||
if (params == null) {
|
||||
return template;
|
||||
}
|
||||
return FORMATTER.format(template, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换模板
|
||||
*
|
||||
* @param template template
|
||||
* @param json json
|
||||
* @return template
|
||||
*/
|
||||
public static String format(String template, String json) {
|
||||
return FORMATTER.format(template, json);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.utils;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.spring.SpringHolder;
|
||||
import org.dromara.visor.common.entity.PushUser;
|
||||
import org.dromara.visor.framework.biz.push.core.event.PushMessageEvent;
|
||||
import org.dromara.visor.framework.biz.push.core.framework.service.PushTemplateFrameworkService;
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 消息推送工具类
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/17 23:13
|
||||
*/
|
||||
public class PushUtils {
|
||||
|
||||
private static PushTemplateFrameworkService pushTemplateFrameworkService;
|
||||
|
||||
/**
|
||||
* 发布消息推送事件
|
||||
*
|
||||
* @param pushMessage 推送消息配置
|
||||
*/
|
||||
public static void push(PushMessage pushMessage) {
|
||||
SpringHolder.publishEvent(new PushMessageEvent(pushMessage));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模板推送消息
|
||||
*
|
||||
* @param templateId 模板ID
|
||||
* @param params 模板参数
|
||||
*/
|
||||
public static void pushTemplate(Long templateId, Map<String, Object> params) {
|
||||
pushTemplate(templateId, params, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模板推送消息
|
||||
*
|
||||
* @param templateId 模板ID
|
||||
* @param params 模板参数
|
||||
* @param pushUsers 推送用户
|
||||
*/
|
||||
public static void pushTemplate(Long templateId, Map<String, Object> params, List<PushUser> pushUsers) {
|
||||
PushMessage pushMessage = pushTemplateFrameworkService.getPushMessageByTemplateId(templateId);
|
||||
if (pushMessage == null) {
|
||||
return;
|
||||
}
|
||||
// 设置模板参数
|
||||
pushMessage.setParams(params);
|
||||
pushMessage.setPushUsers(pushUsers);
|
||||
// 发布消息
|
||||
push(pushMessage);
|
||||
}
|
||||
|
||||
public static void setPushTemplateFrameworkService(PushTemplateFrameworkService pushTemplateFrameworkService) {
|
||||
if (PushUtils.pushTemplateFrameworkService != null) {
|
||||
// unmodified
|
||||
throw Exceptions.state();
|
||||
}
|
||||
PushUtils.pushTemplateFrameworkService = pushTemplateFrameworkService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.dromara.visor.framework.biz.push.configuration.OrionPushAutoConfiguration
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>orion-visor-spring-boot-starter-executor</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<description>项目线程池配置包</description>
|
||||
<url>https://github.com/dromara/orion-visor</url>
|
||||
|
||||
<dependencies>
|
||||
<!-- common -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-common</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.executor.configuration;
|
||||
|
||||
import org.dromara.visor.common.constant.AutoConfigureOrderConst;
|
||||
import org.dromara.visor.framework.executor.configuration.config.ExecutorsConfig;
|
||||
import org.dromara.visor.framework.executor.core.context.ExecutorContext;
|
||||
import org.dromara.visor.framework.executor.core.utils.ExecutorUtils;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 项目线程池配置
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/16 14:34
|
||||
*/
|
||||
@EnableAsync
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties({ExecutorsConfig.class})
|
||||
@AutoConfigureOrder(AutoConfigureOrderConst.FRAMEWORK_EXECUTOR)
|
||||
public class OrionExecutorAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 创建线程池上下文
|
||||
*
|
||||
* @param executorsConfig executorsConfig
|
||||
* @param beanFactory beanFactory
|
||||
* @return 线程池上下文
|
||||
*/
|
||||
@Bean
|
||||
public ExecutorContext executorContext(ExecutorsConfig executorsConfig,
|
||||
ConfigurableBeanFactory beanFactory) {
|
||||
// 创建上下文
|
||||
ExecutorContext context = new ExecutorContext(executorsConfig, beanFactory);
|
||||
Map<String, ThreadPoolTaskExecutor> executorMap = context.init();
|
||||
// 获取线程池列表
|
||||
ExecutorUtils.setExecutors(executorMap);
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.executor.configuration.config;
|
||||
|
||||
import cn.orionsec.kit.lang.define.thread.RejectPolicy;
|
||||
import cn.orionsec.kit.lang.utils.Systems;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 线程池配置
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/16 10:51
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class ExecutorConfig {
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled;
|
||||
|
||||
/**
|
||||
* 是否使用 MDC
|
||||
*/
|
||||
private boolean mdc;
|
||||
|
||||
/**
|
||||
* 核心线程数
|
||||
*/
|
||||
private int corePoolSize;
|
||||
|
||||
/**
|
||||
* 最大线程数
|
||||
*/
|
||||
private int maxPoolSize;
|
||||
|
||||
/**
|
||||
* 队列容量
|
||||
*/
|
||||
private int queueCapacity;
|
||||
|
||||
/**
|
||||
* 空闲线程存活时间
|
||||
*/
|
||||
private int keepAliveSeconds;
|
||||
|
||||
/**
|
||||
* 是否允许核心线程超时
|
||||
*/
|
||||
private boolean allowCoreTimeout;
|
||||
|
||||
/**
|
||||
* 是否使用同步队列
|
||||
*/
|
||||
private boolean synchronousQueue;
|
||||
|
||||
/**
|
||||
* 线程名称前缀
|
||||
*/
|
||||
private String threadNamePrefix;
|
||||
|
||||
/**
|
||||
* 拒绝策略
|
||||
*/
|
||||
private RejectPolicy rejectPolicy;
|
||||
|
||||
public ExecutorConfig() {
|
||||
this.enabled = true;
|
||||
this.corePoolSize = Systems.PROCESS_NUM;
|
||||
this.maxPoolSize = Systems.PROCESS_NUM;
|
||||
this.queueCapacity = 100;
|
||||
this.keepAliveSeconds = 300;
|
||||
this.rejectPolicy = RejectPolicy.CALLER_RUNS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,47 +20,27 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.job.configuration.config;
|
||||
package org.dromara.visor.framework.executor.configuration.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 线程池配置类
|
||||
* 线程池配置
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/10 15:49
|
||||
* @since 2025/9/16 16:21
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "orion.async.executor")
|
||||
public class AsyncExecutorConfig {
|
||||
@ConfigurationProperties(prefix = "app")
|
||||
public class ExecutorsConfig {
|
||||
|
||||
/**
|
||||
* 核心线程数量
|
||||
* 线程池配置
|
||||
*/
|
||||
private int corePoolSize;
|
||||
|
||||
/**
|
||||
* 最大线程数量
|
||||
*/
|
||||
private int maxPoolSize;
|
||||
|
||||
/**
|
||||
* 队列容量
|
||||
*/
|
||||
private int queueCapacity;
|
||||
|
||||
/**
|
||||
* 活跃时间
|
||||
*/
|
||||
private int keepAliveSeconds;
|
||||
|
||||
public AsyncExecutorConfig() {
|
||||
this.corePoolSize = 8;
|
||||
this.maxPoolSize = 16;
|
||||
this.queueCapacity = 200;
|
||||
this.keepAliveSeconds = 300;
|
||||
}
|
||||
private Map<String, ExecutorConfig> executors;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.executor.core.context;
|
||||
|
||||
import cn.orionsec.kit.lang.define.thread.RejectPolicy;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.thread.ThreadPoolMdcTaskExecutor;
|
||||
import org.dromara.visor.framework.executor.configuration.config.ExecutorConfig;
|
||||
import org.dromara.visor.framework.executor.configuration.config.ExecutorsConfig;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 执行器上下文
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/16 16:28
|
||||
*/
|
||||
@Slf4j
|
||||
public class ExecutorContext {
|
||||
|
||||
private final ExecutorsConfig executorsConfig;
|
||||
|
||||
private final ConfigurableBeanFactory beanFactory;
|
||||
|
||||
public ExecutorContext(ExecutorsConfig executorsConfig, ConfigurableBeanFactory beanFactory) {
|
||||
this.executorsConfig = executorsConfig;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @return executorMap
|
||||
*/
|
||||
public Map<String, ThreadPoolTaskExecutor> init() {
|
||||
log.info("--------- Executors init start --------- ");
|
||||
Map<String, ThreadPoolTaskExecutor> executorMap = new HashMap<>();
|
||||
executorsConfig.getExecutors().forEach((beanName, config) -> {
|
||||
String executorName = Strings.leftPad(beanName, 30);
|
||||
// 是否启用
|
||||
if (!config.isEnabled()) {
|
||||
log.info("Executor [{}] is disabled.", executorName);
|
||||
return;
|
||||
}
|
||||
// 创建线程池
|
||||
ThreadPoolTaskExecutor executor = this.createTaskExecutor(config);
|
||||
// 注册到容器中
|
||||
beanFactory.registerSingleton(beanName, executor);
|
||||
executorMap.put(beanName, executor);
|
||||
log.info("Executor [{}] init success. {}", executorName, JSON.toJSONString(config));
|
||||
});
|
||||
log.info("--------- Executors init end --------- ");
|
||||
return executorMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建线程池
|
||||
*
|
||||
* @param config config
|
||||
*/
|
||||
private ThreadPoolTaskExecutor createTaskExecutor(ExecutorConfig config) {
|
||||
// 创建线程池
|
||||
ThreadPoolTaskExecutor executor;
|
||||
if (config.isMdc()) {
|
||||
executor = new ThreadPoolMdcTaskExecutor();
|
||||
} else {
|
||||
executor = new ThreadPoolTaskExecutor();
|
||||
}
|
||||
// 同步队列
|
||||
if (config.isSynchronousQueue()) {
|
||||
config.setCorePoolSize(0);
|
||||
config.setMaxPoolSize(Integer.MAX_VALUE);
|
||||
config.setQueueCapacity(0);
|
||||
config.setRejectPolicy(RejectPolicy.ABORT);
|
||||
}
|
||||
// 设置参数
|
||||
executor.setCorePoolSize(config.getCorePoolSize());
|
||||
executor.setMaxPoolSize(config.getMaxPoolSize());
|
||||
executor.setQueueCapacity(config.getQueueCapacity());
|
||||
executor.setThreadNamePrefix(config.getThreadNamePrefix());
|
||||
executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
|
||||
executor.setAllowCoreThreadTimeOut(config.isAllowCoreTimeout());
|
||||
executor.setRejectedExecutionHandler(config.getRejectPolicy().getHandler());
|
||||
// 设置等待所有任务执行结束再关闭线程池
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
executor.setAwaitTerminationSeconds(60);
|
||||
// 初始化
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.executor.core.utils;
|
||||
|
||||
import cn.orionsec.kit.lang.able.Executable;
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 线程池工具类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/16 16:34
|
||||
*/
|
||||
public class ExecutorUtils {
|
||||
|
||||
private static Map<String, ThreadPoolTaskExecutor> executorMap;
|
||||
|
||||
private ExecutorUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取线程池
|
||||
*
|
||||
* @param name name
|
||||
* @return executor
|
||||
*/
|
||||
public static ThreadPoolTaskExecutor getExecutor(String name) {
|
||||
return executorMap.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行
|
||||
*
|
||||
* @param name name
|
||||
* @param runnable runnable
|
||||
*/
|
||||
public static void execute(String name, Runnable runnable) {
|
||||
getExecutor(name).execute(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行
|
||||
*
|
||||
* @param name name
|
||||
* @param executable executable
|
||||
*/
|
||||
public static void execute(String name, Executable executable) {
|
||||
getExecutor(name).execute(executable::exec);
|
||||
}
|
||||
|
||||
public static void setExecutors(Map<String, ThreadPoolTaskExecutor> executorMap) {
|
||||
if (ExecutorUtils.executorMap != null) {
|
||||
// unmodified
|
||||
throw Exceptions.state();
|
||||
}
|
||||
ExecutorUtils.executorMap = Maps.unmodified(executorMap);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "orion.executors.*",
|
||||
"type": "org.dromara.visor.framework.executor.configuration.config.ExecutorConfig",
|
||||
"sourceType": "org.dromara.visor.framework.executor.configuration.config.ExecutorConfig",
|
||||
"description": "线程池配置项."
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "orion.executors",
|
||||
"type": "java.util.Map<java.lang.String,org.dromara.visor.framework.executor.configuration.config.ExecutorConfig>",
|
||||
"description": "线程池配置.",
|
||||
"defaultValue": "{}"
|
||||
},
|
||||
{
|
||||
"name": "orion.executors.*.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否启用.",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "orion.executors.*.corePoolSize",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "核心线程数.",
|
||||
"defaultValue": "CPU_NUM"
|
||||
},
|
||||
{
|
||||
"name": "orion.executors.*.maxPoolSize",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "最大线程数.",
|
||||
"defaultValue": "CPU_NUM"
|
||||
},
|
||||
{
|
||||
"name": "orion.executors.*.queueCapacity",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "任务队列容量.",
|
||||
"defaultValue": 100
|
||||
},
|
||||
{
|
||||
"name": "orion.executors.*.keepAliveSeconds",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "空闲线程存活时间.",
|
||||
"defaultValue": 60
|
||||
},
|
||||
{
|
||||
"name": "orion.executors.*.allowCoreTimeout",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否允许核心线程超时回收.",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "orion.executors.*.synchronousQueue",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否启用同步队列模式.",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "orion.executors.*.mdc",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否启用 MDC 上下文传递.",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "orion.executors.*.threadNamePrefix",
|
||||
"type": "java.lang.String",
|
||||
"description": "线程名称前缀."
|
||||
},
|
||||
{
|
||||
"name": "orion.executors.*.rejectPolicy",
|
||||
"type": "cn.orionsec.kit.lang.define.thread.RejectPolicy",
|
||||
"description": "拒绝策略.",
|
||||
"defaultValue": "CALLER_RUNS"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.dromara.visor.framework.executor.configuration.OrionExecutorAutoConfiguration
|
||||
@@ -54,8 +54,6 @@ import java.net.ConnectException;
|
||||
public class OrionInfluxdbAutoConfiguration {
|
||||
|
||||
/**
|
||||
* TODO 重连
|
||||
*
|
||||
* @param config config
|
||||
* @return influxdb 客户端
|
||||
*/
|
||||
|
||||
@@ -24,6 +24,7 @@ package org.dromara.visor.framework.influxdb.core.query;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.collect.Collections;
|
||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
|
||||
import java.time.Instant;
|
||||
@@ -194,7 +195,7 @@ public class FluxQueryBuilder {
|
||||
if (values.size() == 1) {
|
||||
return this.tag(key, Collections.first(values));
|
||||
}
|
||||
//
|
||||
// 使用 or 拼接
|
||||
Collection<String> conditions = values.stream()
|
||||
.map(value -> String.format("r[\"%s\"] == \"%s\"", key, value))
|
||||
.collect(Collectors.toList());
|
||||
@@ -204,33 +205,18 @@ public class FluxQueryBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤多个 tag
|
||||
* tag 使用 and
|
||||
* value 使用 or
|
||||
* 过滤 tag
|
||||
*
|
||||
* @param tags tags
|
||||
* @return this
|
||||
*/
|
||||
public FluxQueryBuilder tags(Map<String, ? extends Collection<String>> tags) {
|
||||
for (Map.Entry<String, ? extends Collection<String>> entry : tags.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Collection<String> values = entry.getValue();
|
||||
if (Collections.isEmpty(values)) {
|
||||
continue;
|
||||
}
|
||||
if (values.size() == 1) {
|
||||
// 单值直接用等号
|
||||
String singleValue = values.iterator().next();
|
||||
this.appendFilter(String.format("r[\"%s\"] == \"%s\"", key, singleValue));
|
||||
} else {
|
||||
// 多值用 OR
|
||||
Collection<String> conditions = values.stream()
|
||||
.map(v -> String.format("r[\"%s\"] == \"%s\"", key, v))
|
||||
.collect(Collectors.toList());
|
||||
this.appendFilter("(" + String.join(" or ", conditions) + ")");
|
||||
}
|
||||
if (Maps.isEmpty(tags)) {
|
||||
return this;
|
||||
}
|
||||
for (Map.Entry<String, ? extends Collection<String>> entry : tags.entrySet()) {
|
||||
this.tag(entry.getKey(), entry.getValue());
|
||||
}
|
||||
this.closeFilter();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.job.configuration;
|
||||
|
||||
import org.dromara.visor.common.constant.AutoConfigureOrderConst;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
import org.dromara.visor.common.thread.ThreadPoolMdcTaskExecutor;
|
||||
import org.dromara.visor.framework.job.configuration.config.AsyncExecutorConfig;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* async 异步任务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/20 10:34
|
||||
*/
|
||||
@EnableAsync
|
||||
@AutoConfiguration
|
||||
@AutoConfigureOrder(AutoConfigureOrderConst.FRAMEWORK_JOB_ASYNC)
|
||||
@EnableConfigurationProperties(AsyncExecutorConfig.class)
|
||||
public class OrionAsyncAutoConfiguration {
|
||||
|
||||
/**
|
||||
* {@code @Async("asyncExecutor")}
|
||||
*
|
||||
* @return 支持 MDC 的异步线程池
|
||||
*/
|
||||
@Bean(name = "asyncExecutor")
|
||||
public TaskExecutor asyncExecutor(AsyncExecutorConfig config) {
|
||||
ThreadPoolMdcTaskExecutor executor = new ThreadPoolMdcTaskExecutor();
|
||||
executor.setCorePoolSize(config.getCorePoolSize());
|
||||
executor.setMaxPoolSize(config.getMaxPoolSize());
|
||||
executor.setQueueCapacity(config.getQueueCapacity());
|
||||
executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
|
||||
executor.setAllowCoreThreadTimeOut(true);
|
||||
executor.setThreadNamePrefix("async-task-");
|
||||
// 设置等待所有任务执行结束再关闭线程池
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
// 以确保应用最后能够被关闭
|
||||
executor.setAwaitTerminationSeconds(60);
|
||||
// 调用者调用拒绝策略
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code @Async("metricsExecutor")}
|
||||
*
|
||||
* @return 指标线程池
|
||||
*/
|
||||
@Bean(name = "metricsExecutor")
|
||||
public TaskExecutor metricsExecutor() {
|
||||
ThreadPoolMdcTaskExecutor executor = new ThreadPoolMdcTaskExecutor();
|
||||
executor.setCorePoolSize(4);
|
||||
executor.setMaxPoolSize(8);
|
||||
executor.setQueueCapacity(1000);
|
||||
executor.setKeepAliveSeconds(Const.MS_S_60);
|
||||
executor.setAllowCoreThreadTimeOut(true);
|
||||
executor.setThreadNamePrefix("metrics-task-");
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
executor.setAwaitTerminationSeconds(60);
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,8 @@ package org.dromara.visor.framework.job.configuration;
|
||||
import org.dromara.visor.common.constant.AutoConfigureOrderConst;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.task.TaskSchedulingProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
@@ -40,17 +42,18 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
@EnableScheduling
|
||||
@AutoConfiguration
|
||||
@AutoConfigureOrder(AutoConfigureOrderConst.FRAMEWORK_JOB)
|
||||
@EnableConfigurationProperties(TaskSchedulingProperties.class)
|
||||
public class OrionSchedulerAutoConfiguration {
|
||||
|
||||
/**
|
||||
* @return 任务调度器
|
||||
*/
|
||||
@Bean
|
||||
public TaskScheduler taskScheduler() {
|
||||
public TaskScheduler taskScheduler(TaskSchedulingProperties properties) {
|
||||
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||
scheduler.setPoolSize(4);
|
||||
scheduler.setRemoveOnCancelPolicy(true);
|
||||
scheduler.setThreadNamePrefix("scheduling-task-");
|
||||
scheduler.setPoolSize(properties.getPool().getSize());
|
||||
scheduler.setThreadNamePrefix(properties.getThreadNamePrefix());
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "orion.async.executor",
|
||||
"type": "org.dromara.visor.framework.job.configuration.config.AsyncExecutorConfig",
|
||||
"sourceType": "org.dromara.visor.framework.job.configuration.config.AsyncExecutorConfig"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "orion.async.executor.core-pool-size",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "核心线程数量.",
|
||||
"defaultValue": "8"
|
||||
},
|
||||
{
|
||||
"name": "orion.async.executor.max-pool-size",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "最大线程数量.",
|
||||
"defaultValue": "16"
|
||||
},
|
||||
{
|
||||
"name": "orion.async.executor.queue-capacity",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "队列容量.",
|
||||
"defaultValue": "200"
|
||||
},
|
||||
{
|
||||
"name": "orion.async.executor.keep-alive-seconds",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "活跃时间.",
|
||||
"defaultValue": "300"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
org.dromara.visor.framework.job.configuration.OrionSchedulerAutoConfiguration
|
||||
org.dromara.visor.framework.job.configuration.OrionQuartzAutoConfiguration
|
||||
org.dromara.visor.framework.job.configuration.OrionAsyncAutoConfiguration
|
||||
@@ -54,19 +54,19 @@ public class BaseDO implements Serializable {
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@TableField(value = "create_time", fill = FieldFill.INSERT)
|
||||
private Date createTime;
|
||||
|
||||
@Schema(description = "修改时间")
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
|
||||
private Date updateTime;
|
||||
|
||||
@Schema(description = "创建人")
|
||||
@TableField(fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)
|
||||
@TableField(value = "creator", fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)
|
||||
private String creator;
|
||||
|
||||
@Schema(description = "修改人")
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE, update = "IFNULL(#{et.updater}, updater)", jdbcType = JdbcType.VARCHAR)
|
||||
@TableField(value = "updater", fill = FieldFill.INSERT_UPDATE, update = "IFNULL(#{et.updater}, updater)", jdbcType = JdbcType.VARCHAR)
|
||||
private String updater;
|
||||
|
||||
/**
|
||||
@@ -75,7 +75,7 @@ public class BaseDO implements Serializable {
|
||||
*/
|
||||
@Schema(description = "是否删除 0未删除 1已删除")
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT, jdbcType = JdbcType.TINYINT)
|
||||
@TableField(value = "deleted", fill = FieldFill.INSERT, jdbcType = JdbcType.TINYINT)
|
||||
private Boolean deleted;
|
||||
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.dromara.visor.common.utils.Valid;
|
||||
import org.dromara.visor.common.utils.Assert;
|
||||
import org.dromara.visor.framework.mybatis.core.domain.BaseDO;
|
||||
import org.dromara.visor.framework.mybatis.core.generator.template.Table;
|
||||
import org.dromara.visor.framework.mybatis.core.mapper.IMapper;
|
||||
@@ -101,7 +101,7 @@ public class CodeGenerator implements Executable {
|
||||
*/
|
||||
@Override
|
||||
public void exec() {
|
||||
Valid.notEmpty(tables, "请先配置需要生成的表");
|
||||
Assert.notEmpty(tables, "请先配置需要生成的表");
|
||||
|
||||
// 创建引擎
|
||||
VelocityTemplateEngine engine = this.getEngine(tables);
|
||||
|
||||
@@ -25,7 +25,7 @@ package org.dromara.visor.framework.mybatis.core.query;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.common.utils.Valid;
|
||||
import org.dromara.visor.common.utils.Assert;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -81,7 +81,7 @@ public class Conditions {
|
||||
* @return wrapper
|
||||
*/
|
||||
public static <T, E> LambdaQueryWrapper<T> eq(SFunction<T, E> mapping, E e) {
|
||||
Valid.notNull(e, ErrorMessage.INVALID_PARAM);
|
||||
Assert.notNull(e, ErrorMessage.INVALID_PARAM);
|
||||
return new LambdaQueryWrapper<T>().eq(mapping, e);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public class Conditions {
|
||||
* @return wrapper
|
||||
*/
|
||||
public static <T, E> LambdaQueryWrapper<T> in(SFunction<T, E> mapping, Collection<E> es) {
|
||||
Valid.notEmpty(es, ErrorMessage.INVALID_PARAM);
|
||||
Assert.notEmpty(es, ErrorMessage.INVALID_PARAM);
|
||||
return new LambdaQueryWrapper<T>().in(mapping, es);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import cn.orionsec.kit.lang.define.wrapper.PageRequest;
|
||||
import cn.orionsec.kit.lang.define.wrapper.Pager;
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.lang.utils.Objects1;
|
||||
import cn.orionsec.kit.lang.utils.Valid;
|
||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||
import cn.orionsec.kit.lang.utils.reflect.Classes;
|
||||
import cn.orionsec.kit.spring.SpringHolder;
|
||||
@@ -42,6 +41,7 @@ import org.dromara.visor.common.constant.Const;
|
||||
import org.dromara.visor.common.entity.IOrderRequest;
|
||||
import org.dromara.visor.common.entity.IPageRequest;
|
||||
import org.dromara.visor.common.enums.BooleanBit;
|
||||
import org.dromara.visor.common.utils.Assert;
|
||||
import org.dromara.visor.common.utils.SqlUtils;
|
||||
import org.dromara.visor.framework.mybatis.core.domain.BaseDO;
|
||||
|
||||
@@ -85,23 +85,23 @@ public class DataQuery<T> {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends BaseDO> DataQuery<T> create(Class<T> entityClass) {
|
||||
TableInfo table = Valid.notNull(TableInfoHelper.getTableInfo(entityClass), "notfound mapper class");
|
||||
TableInfo table = Assert.notNull(TableInfoHelper.getTableInfo(entityClass), "notfound mapper class");
|
||||
Class<BaseMapper<T>> mapperClass = (Class<BaseMapper<T>>) Classes.loadClass(table.getCurrentNamespace());
|
||||
return new DataQuery<T>(SpringHolder.getBean(mapperClass));
|
||||
}
|
||||
|
||||
public static <T> DataQuery<T> of(BaseMapper<T> dao) {
|
||||
Valid.notNull(dao, "dao is null");
|
||||
Assert.notNull(dao, "dao is null");
|
||||
return new DataQuery<>(dao);
|
||||
}
|
||||
|
||||
public static <T> DataQuery<T> of(BaseMapper<T> dao, Wrapper<T> wrapper) {
|
||||
Valid.notNull(dao, "dao is null");
|
||||
Assert.notNull(dao, "dao is null");
|
||||
return new DataQuery<>(dao, wrapper);
|
||||
}
|
||||
|
||||
public DataQuery<T> page(IPageRequest request) {
|
||||
Valid.notNull(request, "page is null");
|
||||
Assert.notNull(request, "page is null");
|
||||
this.page = new PageRequest(request.getPage(), request.getLimit());
|
||||
return this;
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public class DataQuery<T> {
|
||||
}
|
||||
|
||||
public DataQuery<T> wrapper(Wrapper<T> wrapper) {
|
||||
this.wrapper = Valid.notNull(wrapper, "wrapper is null");
|
||||
this.wrapper = Assert.notNull(wrapper, "wrapper is null");
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -177,18 +177,18 @@ public class DataQuery<T> {
|
||||
// -------------------- id --------------------
|
||||
|
||||
public T get(Serializable id) {
|
||||
Valid.notNull(id, "id is null");
|
||||
Assert.notNull(id, "id is null");
|
||||
return dao.selectById(id);
|
||||
}
|
||||
|
||||
public <R> R get(Serializable id, Function<T, R> mapper) {
|
||||
Valid.notNull(id, "id is null");
|
||||
Valid.notNull(mapper, "convert function is null");
|
||||
Assert.notNull(id, "id is null");
|
||||
Assert.notNull(mapper, "convert function is null");
|
||||
return Objects1.map(dao.selectById(id), mapper);
|
||||
}
|
||||
|
||||
public Optional<T> optional(Serializable id) {
|
||||
Valid.notNull(id, "id is null");
|
||||
Assert.notNull(id, "id is null");
|
||||
return Optional.ofNullable(dao.selectById(id));
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ public class DataQuery<T> {
|
||||
}
|
||||
|
||||
public <R> R get(Function<T, R> mapper) {
|
||||
Valid.notNull(mapper, "convert function is null");
|
||||
Assert.notNull(mapper, "convert function is null");
|
||||
return Objects1.map(dao.selectOne(wrapper), mapper);
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ public class DataQuery<T> {
|
||||
}
|
||||
|
||||
public <R> List<R> list(Function<T, R> mapper) {
|
||||
Valid.notNull(mapper, "convert function is null");
|
||||
Assert.notNull(mapper, "convert function is null");
|
||||
return Lists.map(dao.selectList(wrapper), mapper);
|
||||
}
|
||||
|
||||
@@ -277,10 +277,10 @@ public class DataQuery<T> {
|
||||
}
|
||||
|
||||
public <R> DataGrid<R> dataGrid(Wrapper<T> countWrapper, Function<T, R> mapper) {
|
||||
Valid.notNull(page, "page is null");
|
||||
Valid.notNull(wrapper, "wrapper is null");
|
||||
Valid.notNull(countWrapper, "count wrapper is null");
|
||||
Valid.notNull(mapper, "convert function is null");
|
||||
Assert.notNull(page, "page is null");
|
||||
Assert.notNull(wrapper, "wrapper is null");
|
||||
Assert.notNull(countWrapper, "count wrapper is null");
|
||||
Assert.notNull(mapper, "convert function is null");
|
||||
Long count = dao.selectCount(countWrapper);
|
||||
Pager<R> pager = new Pager<>(page);
|
||||
pager.setTotal(count.intValue());
|
||||
|
||||
@@ -90,6 +90,20 @@ public class DomainFillUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空基础字段
|
||||
*
|
||||
* @param baseDO baseDO
|
||||
*/
|
||||
public static void clearBaseFields(BaseDO baseDO) {
|
||||
baseDO.setId(null);
|
||||
baseDO.setCreateTime(null);
|
||||
baseDO.setUpdateTime(null);
|
||||
baseDO.setCreator(null);
|
||||
baseDO.setUpdater(null);
|
||||
baseDO.setDeleted(null);
|
||||
}
|
||||
|
||||
public static void setSecurityHolder(SecurityHolder securityHolder) {
|
||||
if (DomainFillUtils.securityHolder != null) {
|
||||
// unmodified
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.common.utils.Valid;
|
||||
import org.dromara.visor.common.utils.Assert;
|
||||
#if($meta.enableCache)
|
||||
import org.dromara.visor.framework.redis.core.utils.RedisMaps;
|
||||
import org.dromara.visor.framework.redis.core.utils.barrier.CacheBarriers;
|
||||
@@ -84,11 +84,11 @@ public class ${table.serviceImplName} implements ${table.serviceName} {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Integer update${type}ById(${type}UpdateRequest request) {
|
||||
Long id = Valid.notNull(request.getId(), ErrorMessage.ID_MISSING);
|
||||
Long id = Assert.notNull(request.getId(), ErrorMessage.ID_MISSING);
|
||||
log.info("${type}Service-update${type}ById id: {}, request: {}", id, JSON.toJSONString(request));
|
||||
// 查询
|
||||
${type}DO record = ${typeLower}DAO.selectById(id);
|
||||
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
|
||||
Assert.notNull(record, ErrorMessage.DATA_ABSENT);
|
||||
// 转换
|
||||
${type}DO updateRecord = ${type}Convert.MAPPER.to(request);
|
||||
// 查询数据是否冲突
|
||||
@@ -125,7 +125,7 @@ public class ${table.serviceImplName} implements ${table.serviceName} {
|
||||
public ${type}VO get${type}ById(Long id) {
|
||||
// 查询
|
||||
${type}DO record = ${typeLower}DAO.selectById(id);
|
||||
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
|
||||
Assert.notNull(record, ErrorMessage.DATA_ABSENT);
|
||||
// 转换
|
||||
return ${type}Convert.MAPPER.to(record);
|
||||
}
|
||||
@@ -202,7 +202,7 @@ public class ${table.serviceImplName} implements ${table.serviceName} {
|
||||
log.info("${type}Service-delete${type}ById id: {}", id);
|
||||
// 检查数据是否存在
|
||||
${type}DO record = ${typeLower}DAO.selectById(id);
|
||||
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
|
||||
Assert.notNull(record, ErrorMessage.DATA_ABSENT);
|
||||
// 删除
|
||||
int effect = ${typeLower}DAO.deleteById(id);
|
||||
log.info("${type}Service-delete${type}ById id: {}, effect: {}", id, effect);
|
||||
@@ -260,7 +260,7 @@ public class ${table.serviceImplName} implements ${table.serviceName} {
|
||||
#end
|
||||
// 检查是否存在
|
||||
boolean present = ${typeLower}DAO.of(wrapper).present();
|
||||
Valid.isFalse(present, ErrorMessage.DATA_PRESENT);
|
||||
Assert.isFalse(present, ErrorMessage.DATA_PRESENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,7 +27,7 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.common.utils.Valid;
|
||||
import org.dromara.visor.common.utils.Assert;
|
||||
#foreach($pkg in ${customModuleFilePackages})
|
||||
import ${pkg}.*;
|
||||
#end
|
||||
@@ -64,7 +64,7 @@ public class ${type}ApiImpl implements ${type}Api {
|
||||
@Override
|
||||
public Long create${type}(${type}CreateDTO dto) {
|
||||
log.info("${type}Api.create${type} dto: {}", JSON.toJSONString(dto));
|
||||
Valid.valid(dto);
|
||||
Assert.valid(dto);
|
||||
// 转换
|
||||
${type}CreateRequest request = ${type}ProviderConvert.MAPPER.toRequest(dto);
|
||||
// 创建
|
||||
@@ -74,7 +74,7 @@ public class ${type}ApiImpl implements ${type}Api {
|
||||
@Override
|
||||
public Integer update${type}ById(${type}UpdateDTO dto) {
|
||||
log.info("${type}Api.update${type}ById dto: {}", JSON.toJSONString(dto));
|
||||
Valid.valid(dto);
|
||||
Assert.valid(dto);
|
||||
// 转换
|
||||
${type}UpdateRequest request = ${type}ProviderConvert.MAPPER.toRequest(dto);
|
||||
// 修改
|
||||
@@ -84,8 +84,8 @@ public class ${type}ApiImpl implements ${type}Api {
|
||||
@Override
|
||||
public Integer update${type}(${type}QueryDTO query, ${type}UpdateDTO update) {
|
||||
log.info("${type}Api.update${type} query: {}, update: {}", JSON.toJSONString(query), JSON.toJSONString(update));
|
||||
Valid.valid(query);
|
||||
Valid.valid(update);
|
||||
Assert.valid(query);
|
||||
Assert.valid(update);
|
||||
// 更新
|
||||
int effect = ${typeLower}Service.update${type}(${type}ProviderConvert.MAPPER.toRequest(query),
|
||||
${type}ProviderConvert.MAPPER.toRequest(update));
|
||||
@@ -96,7 +96,7 @@ public class ${type}ApiImpl implements ${type}Api {
|
||||
@Override
|
||||
public ${type}DTO get${type}ById(Long id) {
|
||||
log.info("${type}Api.get${type}ById id: {}", id);
|
||||
Valid.notNull(id, ErrorMessage.ID_MISSING);
|
||||
Assert.notNull(id, ErrorMessage.ID_MISSING);
|
||||
// 修改
|
||||
${type}DO record = ${typeLower}DAO.selectById(id);
|
||||
if (record == null) {
|
||||
@@ -121,7 +121,7 @@ public class ${type}ApiImpl implements ${type}Api {
|
||||
@Override
|
||||
public List<${type}DTO> get${type}List(${type}QueryDTO dto) {
|
||||
log.info("${type}Api.get${type}List dto: {}", JSON.toJSONString(dto));
|
||||
Valid.valid(dto);
|
||||
Assert.valid(dto);
|
||||
// 条件
|
||||
LambdaQueryWrapper<${type}DO> wrapper = this.buildQueryWrapper(dto);
|
||||
// 查询
|
||||
@@ -142,7 +142,7 @@ public class ${type}ApiImpl implements ${type}Api {
|
||||
@Override
|
||||
public Long get${type}Count(${type}QueryDTO dto) {
|
||||
log.info("${type}Api.get${type}Count dto: {}", JSON.toJSONString(dto));
|
||||
Valid.valid(dto);
|
||||
Assert.valid(dto);
|
||||
// 条件
|
||||
LambdaQueryWrapper<${type}DO> wrapper = this.buildQueryWrapper(dto);
|
||||
// 查询
|
||||
@@ -152,7 +152,7 @@ public class ${type}ApiImpl implements ${type}Api {
|
||||
@Override
|
||||
public Integer delete${type}ById(Long id) {
|
||||
log.info("${type}Api.delete${type}ById id: {}", id);
|
||||
Valid.notNull(id, ErrorMessage.ID_MISSING);
|
||||
Assert.notNull(id, ErrorMessage.ID_MISSING);
|
||||
// 删除
|
||||
Integer effect = ${typeLower}Service.delete${type}ById(id);
|
||||
log.info("${type}Api.delete${type}ById id: {}, effect: {}", id, effect);
|
||||
@@ -162,7 +162,7 @@ public class ${type}ApiImpl implements ${type}Api {
|
||||
@Override
|
||||
public Integer delete${type}ByIdList(List<Long> idList) {
|
||||
log.info("${type}Api.delete${type}ByIdList idList: {}", idList);
|
||||
Valid.notEmpty(idList, ErrorMessage.ID_MISSING);
|
||||
Assert.notEmpty(idList, ErrorMessage.ID_MISSING);
|
||||
// 删除
|
||||
Integer effect = ${typeLower}Service.delete${type}ByIdList(idList);
|
||||
log.info("${type}Api.delete${type}ByIdList effect: {}", effect);
|
||||
@@ -172,7 +172,7 @@ public class ${type}ApiImpl implements ${type}Api {
|
||||
@Override
|
||||
public Integer delete${type}(${type}QueryDTO dto) {
|
||||
log.info("${type}Api.delete${type} dto: {}", JSON.toJSONString(dto));
|
||||
Valid.valid(dto);
|
||||
Assert.valid(dto);
|
||||
// 删除
|
||||
Integer effect = ${typeLower}Service.delete${type}(${type}ProviderConvert.MAPPER.toRequest(dto));
|
||||
log.info("${type}Api.delete${type} effect: {}", effect);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
:create-card-position="false"
|
||||
:loading="loading"
|
||||
:field-config="cardFieldConfig"
|
||||
:list="list"
|
||||
:list="renderList"
|
||||
:pagination="pagination"
|
||||
:card-layout-cols="cardColLayout"
|
||||
:filter-count="filterCount"
|
||||
@@ -140,7 +140,7 @@
|
||||
const { toOptions, getDictValue } = useDictStore();
|
||||
#end
|
||||
|
||||
const list = ref<Array<${vue.featureEntity}QueryResponse>>([]);
|
||||
const renderList = ref<Array<${vue.featureEntity}QueryResponse>>([]);
|
||||
const formRef = ref();
|
||||
const formModel = reactive<${vue.featureEntity}QueryRequest>({
|
||||
searchValue: undefined,
|
||||
@@ -196,7 +196,7 @@
|
||||
try {
|
||||
setLoading(true);
|
||||
const { data } = await get${vue.featureEntity}Page(queryOrder.markOrderly(request));
|
||||
list.value = data.rows;
|
||||
renderList.value = data.rows;
|
||||
pagination.total = data.total;
|
||||
pagination.current = request.page;
|
||||
pagination.pageSize = request.limit;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:unmount-on-close="true"
|
||||
:ok-button-props="{ disabled: loading }"
|
||||
:cancel-button-props="{ disabled: loading }"
|
||||
:on-before-ok="handlerOk"
|
||||
:on-before-ok="handleOk"
|
||||
@cancel="handleClose">
|
||||
<a-spin class="full drawer-form-small" :loading="loading">
|
||||
<a-form :model="formModel"
|
||||
@@ -57,10 +57,12 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ${vue.featureEntity}UpdateRequest } from '@/api/${vue.module}/${vue.feature}';
|
||||
import type { FormHandle } from '@/types/form';
|
||||
import { ref } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import formRules from '../types/form.rules';
|
||||
import { assignOmitRecord } from '@/utils';
|
||||
#if($dictMap.entrySet().size() > 0)
|
||||
import { #foreach($entry in ${dictMap.entrySet()})${entry.value.keyField}#if($foreach.hasNext), #end#end } from '../types/const';
|
||||
#else
|
||||
@@ -81,14 +83,14 @@
|
||||
#end
|
||||
|
||||
const title = ref<string>();
|
||||
const isAddHandle = ref<boolean>(true);
|
||||
const formHandle = ref<FormHandle>('add');
|
||||
const formRef = ref<any>();
|
||||
const formModel = ref<${vue.featureEntity}UpdateRequest>({});
|
||||
|
||||
const defaultForm = (): ${vue.featureEntity}UpdateRequest => {
|
||||
return {
|
||||
#foreach($field in ${table.fields})
|
||||
${field.propertyName}: undefined,
|
||||
${field.propertyName}: undefined,
|
||||
#end
|
||||
};
|
||||
};
|
||||
@@ -96,28 +98,23 @@
|
||||
// 打开新增
|
||||
const openAdd = () => {
|
||||
title.value = '添加${table.comment}';
|
||||
isAddHandle.value = true;
|
||||
renderForm({ ...defaultForm() });
|
||||
formHandle.value = 'add';
|
||||
formModel.value = assignOmitRecord({ ...defaultForm() });
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
// 打开修改
|
||||
const openUpdate = (record: any) => {
|
||||
title.value = '修改${table.comment}';
|
||||
isAddHandle.value = false;
|
||||
renderForm({ ...defaultForm(), ...record });
|
||||
formHandle.value = 'update';
|
||||
formModel.value = assignOmitRecord({ ...defaultForm(), ...record });
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
// 渲染表单
|
||||
const renderForm = (record: any) => {
|
||||
formModel.value = Object.assign({}, record);
|
||||
};
|
||||
|
||||
defineExpose({ openAdd, openUpdate });
|
||||
|
||||
// 确定
|
||||
const handlerOk = async () => {
|
||||
const handleOk = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 验证参数
|
||||
@@ -125,7 +122,7 @@
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
if (isAddHandle.value) {
|
||||
if (formHandle.value === 'add') {
|
||||
// 新增
|
||||
await create${vue.featureEntity}(formModel.value);
|
||||
Message.success('创建成功');
|
||||
@@ -136,8 +133,8 @@
|
||||
Message.success('修改成功');
|
||||
emits('updated');
|
||||
}
|
||||
// 清空
|
||||
handlerClear();
|
||||
handleClose();
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
} finally {
|
||||
@@ -147,11 +144,12 @@
|
||||
|
||||
// 关闭
|
||||
const handleClose = () => {
|
||||
handlerClear();
|
||||
handleClear();
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
// 清空
|
||||
const handlerClear = () => {
|
||||
const handleClear = () => {
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
:unmount-on-close="true"
|
||||
:ok-button-props="{ disabled: loading }"
|
||||
:cancel-button-props="{ disabled: loading }"
|
||||
:on-before-ok="handlerOk"
|
||||
:on-before-ok="handleOk"
|
||||
@close="handleClose">
|
||||
<a-spin class="full" :loading="loading">
|
||||
<a-form :model="formModel"
|
||||
@@ -61,10 +61,12 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ${vue.featureEntity}UpdateRequest } from '@/api/${vue.module}/${vue.feature}';
|
||||
import type { FormHandle } from '@/types/form';
|
||||
import { ref } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import formRules from '../types/form.rules';
|
||||
import { assignOmitRecord } from '@/utils';
|
||||
#if($dictMap.entrySet().size() > 0)
|
||||
import { #foreach($entry in ${dictMap.entrySet()})${entry.value.keyField}#if($foreach.hasNext), #end#end } from '../types/const';
|
||||
#else
|
||||
@@ -85,7 +87,7 @@
|
||||
#end
|
||||
|
||||
const title = ref<string>();
|
||||
const isAddHandle = ref<boolean>(true);
|
||||
const formHandle = ref<FormHandle>('add');
|
||||
const formRef = ref<any>();
|
||||
const formModel = ref<${vue.featureEntity}UpdateRequest>({});
|
||||
|
||||
@@ -100,28 +102,23 @@
|
||||
// 打开新增
|
||||
const openAdd = () => {
|
||||
title.value = '添加${table.comment}';
|
||||
isAddHandle.value = true;
|
||||
renderForm({ ...defaultForm() });
|
||||
formHandle.value = 'add';
|
||||
formModel.value = assignOmitRecord({ ...defaultForm() });
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
// 打开修改
|
||||
const openUpdate = (record: any) => {
|
||||
title.value = '修改${table.comment}';
|
||||
isAddHandle.value = false;
|
||||
renderForm({ ...defaultForm(), ...record });
|
||||
formHandle.value = 'update';
|
||||
formModel.value = assignOmitRecord({ ...defaultForm(), ...record });
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
// 渲染表单
|
||||
const renderForm = (record: any) => {
|
||||
formModel.value = Object.assign({}, record);
|
||||
};
|
||||
|
||||
defineExpose({ openAdd, openUpdate });
|
||||
|
||||
// 确定
|
||||
const handlerOk = async () => {
|
||||
const handleOk = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 验证参数
|
||||
@@ -129,7 +126,7 @@
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
if (isAddHandle.value) {
|
||||
if (formHandle.value === 'add') {
|
||||
// 新增
|
||||
await create${vue.featureEntity}(formModel.value);
|
||||
Message.success('创建成功');
|
||||
@@ -140,8 +137,8 @@
|
||||
Message.success('修改成功');
|
||||
emits('updated');
|
||||
}
|
||||
// 清空
|
||||
handlerClear();
|
||||
handleClose();
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
} finally {
|
||||
@@ -151,11 +148,12 @@
|
||||
|
||||
// 关闭
|
||||
const handleClose = () => {
|
||||
handlerClear();
|
||||
handleClear();
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
// 清空
|
||||
const handlerClear = () => {
|
||||
const handleClear = () => {
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
<a-table row-key="id"
|
||||
#end
|
||||
ref="tableRef"
|
||||
class="table-resize"
|
||||
:loading="loading"
|
||||
:columns="tableColumns"
|
||||
#if($vue.enableRowSelection)
|
||||
@@ -99,6 +100,7 @@
|
||||
:data="tableRenderData"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
:column-resizable="true"
|
||||
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
|
||||
@page-size-change="(size: number) => fetchTableData(1, size)">
|
||||
#foreach($field in ${table.fields})
|
||||
@@ -146,7 +148,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { ${vue.featureEntity}QueryRequest, ${vue.featureEntity}QueryResponse } from '@/api/${vue.module}/${vue.feature}';
|
||||
import { reactive, ref, onMounted } from 'vue';
|
||||
import { batchDelete${vue.featureEntity}, delete${vue.featureEntity}, get${vue.featureEntity}Page } from '@/api/${vue.module}/${vue.feature}';
|
||||
import {#if($vue.enableRowSelection) batchDelete${vue.featureEntity},#end delete${vue.featureEntity}, get${vue.featureEntity}Page } from '@/api/${vue.module}/${vue.feature}';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import columns from '../types/table.columns';
|
||||
|
||||
@@ -5,17 +5,17 @@
|
||||
<${vue.feature}-table v-if="renderTable"
|
||||
ref="table"
|
||||
@open-add="() =>#if($vue.enableDrawerForm) drawer#else modal#end.openAdd()"
|
||||
@open-update="(e) =>#if($vue.enableDrawerForm) drawer#else modal#end.openUpdate(e)" />
|
||||
@open-update="(e: any) =>#if($vue.enableDrawerForm) drawer#else modal#end.openUpdate(e)" />
|
||||
<!-- 列表-卡片 -->
|
||||
<${vue.feature}-card-list v-else
|
||||
ref="card"
|
||||
@open-add="() =>#if($vue.enableDrawerForm) drawer#else modal#end.openAdd()"
|
||||
@open-update="(e) =>#if($vue.enableDrawerForm) drawer#else modal#end.openUpdate(e)" />
|
||||
@open-update="(e: any) =>#if($vue.enableDrawerForm) drawer#else modal#end.openUpdate(e)" />
|
||||
#else
|
||||
<!-- 列表-表格 -->
|
||||
<${vue.feature}-table ref="table"
|
||||
@open-add="() =>#if($vue.enableDrawerForm) drawer#else modal#end.openAdd()"
|
||||
@open-update="(e) =>#if($vue.enableDrawerForm) drawer#else modal#end.openUpdate(e)" />
|
||||
@open-update="(e: any) =>#if($vue.enableDrawerForm) drawer#else modal#end.openUpdate(e)" />
|
||||
#end
|
||||
#if($vue.enableDrawerForm)
|
||||
<!-- 添加修改抽屉 -->
|
||||
|
||||
@@ -30,7 +30,6 @@ const fieldConfig = {
|
||||
render: ({ record }) => {
|
||||
return dateFormat(new Date(record.createTime));
|
||||
},
|
||||
default: true,
|
||||
}, {
|
||||
label: '修改时间',
|
||||
dataIndex: 'updateTime',
|
||||
@@ -38,6 +37,7 @@ const fieldConfig = {
|
||||
render: ({ record }) => {
|
||||
return dateFormat(new Date(record.updateTime));
|
||||
},
|
||||
default: true,
|
||||
}, {
|
||||
label: '创建人',
|
||||
dataIndex: 'creator',
|
||||
@@ -45,7 +45,6 @@ const fieldConfig = {
|
||||
width: 148,
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
default: true,
|
||||
}, {
|
||||
label: '修改人',
|
||||
dataIndex: 'updater',
|
||||
@@ -53,6 +52,7 @@ const fieldConfig = {
|
||||
width: 148,
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
default: true,
|
||||
}
|
||||
] as CardField[]
|
||||
} as CardFieldConfig;
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
import type { FieldRule } from '@arco-design/web-vue';
|
||||
|
||||
const rules = {
|
||||
#foreach($field in ${table.fields})
|
||||
#if("$!field.propertyName" != "id")
|
||||
#if(${field.propertyType} == 'String' && "$field.metaInfo.jdbcType" != "LONGVARCHAR")
|
||||
export const ${field.propertyName} = [{
|
||||
required: true,
|
||||
message: '请输入${field.comment}'
|
||||
}, {
|
||||
maxLength: $field.metaInfo.length,
|
||||
message: '${field.comment}长度不能大于$field.metaInfo.length位'
|
||||
}] as FieldRule[];
|
||||
#else
|
||||
export const ${field.propertyName} = [{
|
||||
required: true,
|
||||
message: '请输入${field.comment}'
|
||||
}] as FieldRule[];
|
||||
#end
|
||||
#end
|
||||
|
||||
#end
|
||||
export default {
|
||||
#foreach($field in ${table.fields})
|
||||
#if("$!field.propertyName" != "id")
|
||||
${field.propertyName},
|
||||
${field.propertyName}: [{
|
||||
required: true,
|
||||
message: '请输入${field.comment}'
|
||||
}#if(${field.propertyType} == 'String' && "$field.metaInfo.jdbcType" != "LONGVARCHAR"), {
|
||||
maxLength: $field.metaInfo.length,
|
||||
message: '${field.comment}长度不能大于$field.metaInfo.length位'
|
||||
}#end],
|
||||
#end
|
||||
#end
|
||||
} as Record<string, FieldRule | FieldRule[]>;
|
||||
|
||||
export default rules;
|
||||
|
||||
@@ -37,7 +37,6 @@ const columns = [
|
||||
render: ({ record }) => {
|
||||
return dateFormat(new Date(record.createTime));
|
||||
},
|
||||
default: true,
|
||||
}, {
|
||||
title: '修改时间',
|
||||
dataIndex: 'updateTime',
|
||||
@@ -47,15 +46,18 @@ const columns = [
|
||||
render: ({ record }) => {
|
||||
return dateFormat(new Date(record.updateTime));
|
||||
},
|
||||
default: true,
|
||||
}, {
|
||||
title: '创建人',
|
||||
dataIndex: 'creator',
|
||||
slotName: 'creator',
|
||||
default: true,
|
||||
width: 148,
|
||||
}, {
|
||||
title: '修改人',
|
||||
dataIndex: 'updater',
|
||||
slotName: 'updater',
|
||||
width: 148,
|
||||
default: true,
|
||||
}, {
|
||||
title: '操作',
|
||||
slotName: 'handle',
|
||||
|
||||
@@ -312,6 +312,19 @@ public class RedisMaps extends RedisUtils {
|
||||
return getJson(key.getKey(), hashKey, (Class<V>) key.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取值 json
|
||||
*
|
||||
* @param define define
|
||||
* @param key key
|
||||
* @param hashKey hashKey
|
||||
* @param <V> V
|
||||
* @return value
|
||||
*/
|
||||
public static <V> V getJson(String key, CacheKeyDefine define, Object hashKey) {
|
||||
return getJson(key, hashKey, (Class<V>) define.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取值 json
|
||||
*
|
||||
@@ -355,11 +368,9 @@ public class RedisMaps extends RedisUtils {
|
||||
/**
|
||||
* 获取值 json
|
||||
*
|
||||
* @param key key
|
||||
* @param hashKeys hashKeys
|
||||
* @param keyMapper keyMapper
|
||||
* @param <K> K
|
||||
* @param <V> V
|
||||
* @param key key
|
||||
* @param hashKeys hashKeys
|
||||
* @param <V> V
|
||||
* @return values
|
||||
*/
|
||||
public static <V> List<V> multiGetJson(CacheKeyDefine key, List<?> hashKeys) {
|
||||
@@ -369,12 +380,10 @@ public class RedisMaps extends RedisUtils {
|
||||
/**
|
||||
* 获取值 json
|
||||
*
|
||||
* @param key key
|
||||
* @param hashKeys hashKeys
|
||||
* @param keyMapper keyMapper
|
||||
* @param clazz clazz
|
||||
* @param <K> K
|
||||
* @param <V> V
|
||||
* @param key key
|
||||
* @param hashKeys hashKeys
|
||||
* @param clazz clazz
|
||||
* @param <V> V
|
||||
* @return values
|
||||
*/
|
||||
public static <V> List<V> multiGetJson(String key, List<?> hashKeys, Class<V> clazz) {
|
||||
|
||||
@@ -272,6 +272,16 @@ public class RedisStrings extends RedisUtils {
|
||||
return getList(keys, s -> JSON.parseArray(s, type));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置值
|
||||
*
|
||||
* @param key key
|
||||
* @param value value
|
||||
*/
|
||||
public static void set(String key, Object value) {
|
||||
set(key, null, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置值
|
||||
*
|
||||
|
||||
@@ -49,6 +49,26 @@ public class RedisUtils {
|
||||
protected RedisUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含 key
|
||||
*
|
||||
* @param define define
|
||||
* @return has
|
||||
*/
|
||||
public static boolean hasKey(CacheKeyDefine define) {
|
||||
return hasKey(define.getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含 key
|
||||
*
|
||||
* @param key key
|
||||
* @return has
|
||||
*/
|
||||
public static boolean hasKey(String key) {
|
||||
return redisTemplate.hasKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描 key
|
||||
*
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
*/
|
||||
package org.dromara.visor.framework.security.core.context;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Valid;
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import org.dromara.visor.common.utils.Assert;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
@@ -60,7 +60,7 @@ public class TransmittableThreadLocalSecurityContextHolderStrategy implements Se
|
||||
|
||||
@Override
|
||||
public void setContext(SecurityContext context) {
|
||||
Valid.notNull(context, "only non-null SecurityContext instances are permitted");
|
||||
Assert.notNull(context, "only non-null SecurityContext instances are permitted");
|
||||
CONTEXT_HOLDER.set(context);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ package org.dromara.visor.framework.security.core.utils;
|
||||
import cn.orionsec.kit.lang.constant.StandardHttpHeader;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
import org.dromara.visor.common.constant.ErrorCode;
|
||||
import org.dromara.visor.common.security.LoginUser;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -90,6 +91,19 @@ public class SecurityUtils {
|
||||
return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户 不为空
|
||||
*
|
||||
* @return 当前用户
|
||||
*/
|
||||
public static LoginUser getLoginUserNotNull() {
|
||||
LoginUser user = getLoginUser();
|
||||
if (user == null) {
|
||||
throw ErrorCode.UNAUTHORIZED.exception();
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 userId
|
||||
*
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user