diff --git a/.env.example b/.env.example index c6400a8c..e3b1e210 100644 --- a/.env.example +++ b/.env.example @@ -6,8 +6,11 @@ SPRING_PROFILES_ACTIVE=prod DEMO_MODE=false API_CORS=true -SECRET_KEY=uQeacXV8b3isvKLK API_EXPOSE_TOKEN=pmqeHOyZaumHm0Wt +SECRET_KEY=uQeacXV8b3isvKLK + +NGINX_SERVICE_HOST=service +NGINX_SERVICE_PORT=9200 MYSQL_HOST=mysql MYSQL_PORT=3306 diff --git a/.gitignore b/.gitignore index 35246529..8371d8e3 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,8 @@ target/ .sts4-cache ### IntelliJ IDEA ### -.idea +**/.idea/* +!**/.idea/icon.png *.iws *.iml *.ipr diff --git a/.idea/icon.png b/.idea/icon.png new file mode 100644 index 00000000..82215dc5 Binary files /dev/null and b/.idea/icon.png differ diff --git a/README.md b/README.md index 398cbea4..b35f1dbf 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,9 @@ docker compose up -d #### 主机监控 ![主机监控](docs/assets/screenshot/monitor-list.png?time=20250627 "主机监控") -![监控详情](docs/assets/screenshot/monitor-detail.png?time=20250627 "监控详情") +![监控概览](docs/assets/screenshot/monitor-override.png?time=20250627 "监控概览") +![监控详情](docs/assets/screenshot/monitor-chart.png?time=20250627 "监控表格") +![告警通知](docs/assets/screenshot/monitor-alarm.png?time=20250627 "告警通知") #### 批量执行 diff --git a/docker-compose.yaml b/docker-compose.yaml index 5bae4235..22216eda 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,6 +1,6 @@ version: '3.3' -# latest = 2.5.0 +# latest = 2.5.1 # 支持以下源 # 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: diff --git a/docker/docker-build.sh b/docker/docker-build.sh index e9797cae..df4653dc 100644 --- a/docker/docker-build.sh +++ b/docker/docker-build.sh @@ -7,7 +7,7 @@ set -e source ./project-build.sh "$@" # 版本号 -version=2.5.0 +version=2.5.1 # 是否推送镜像 push_image=false # 是否构建 latest diff --git a/docker/project-build.sh b/docker/project-build.sh index e4e5c237..9db5d336 100644 --- a/docker/project-build.sh +++ b/docker/project-build.sh @@ -4,7 +4,7 @@ set -e # DockerContext: orion-visor # 版本号 -version=2.5.0 +version=2.5.1 # 是否构建 service export build_service=false # 是否构建 ui diff --git a/docker/ui/Dockerfile b/docker/ui/Dockerfile index 6abe6e8e..37436173 100644 --- a/docker/ui/Dockerfile +++ b/docker/ui/Dockerfile @@ -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;"] \ No newline at end of file diff --git a/docker/ui/entrypoint.sh b/docker/ui/entrypoint.sh new file mode 100644 index 00000000..fc3cd7bc --- /dev/null +++ b/docker/ui/entrypoint.sh @@ -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 "$@" \ No newline at end of file diff --git a/docker/ui/nginx.conf b/docker/ui/nginx.conf index ec9925bc..634b7c46 100644 --- a/docker/ui/nginx.conf +++ b/docker/ui/nginx.conf @@ -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; +} diff --git a/docker/ui/service.conf b/docker/ui/service.conf new file mode 100644 index 00000000..a940f896 --- /dev/null +++ b/docker/ui/service.conf @@ -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; + } + +} + diff --git a/docs/assets/screenshot/monitor-alarm.png b/docs/assets/screenshot/monitor-alarm.png new file mode 100644 index 00000000..274cb01b Binary files /dev/null and b/docs/assets/screenshot/monitor-alarm.png differ diff --git a/docs/assets/screenshot/monitor-detail.png b/docs/assets/screenshot/monitor-chart.png similarity index 100% rename from docs/assets/screenshot/monitor-detail.png rename to docs/assets/screenshot/monitor-chart.png diff --git a/docs/assets/screenshot/monitor-override.png b/docs/assets/screenshot/monitor-override.png new file mode 100644 index 00000000..fb042dab Binary files /dev/null and b/docs/assets/screenshot/monitor-override.png differ diff --git a/orion-visor-common/src/main/java/org/dromara/visor/common/constant/AppConst.java b/orion-visor-common/src/main/java/org/dromara/visor/common/constant/AppConst.java index caa4bc74..e5835c82 100644 --- a/orion-visor-common/src/main/java/org/dromara/visor/common/constant/AppConst.java +++ b/orion-visor-common/src/main/java/org/dromara/visor/common/constant/AppConst.java @@ -36,7 +36,7 @@ public interface AppConst extends OrionConst { /** * 同 ${orion.version} 迭代时候需要手动更改 */ - String VERSION = "2.5.0"; + String VERSION = "2.5.1"; /** * 同 ${spring.application.name} diff --git a/orion-visor-common/src/main/java/org/dromara/visor/common/constant/AutoConfigureOrderConst.java b/orion-visor-common/src/main/java/org/dromara/visor/common/constant/AutoConfigureOrderConst.java index 2b089ba3..c11c7a29 100644 --- a/orion-visor-common/src/main/java/org/dromara/visor/common/constant/AutoConfigureOrderConst.java +++ b/orion-visor-common/src/main/java/org/dromara/visor/common/constant/AutoConfigureOrderConst.java @@ -63,13 +63,15 @@ 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_JOB_ASYNC = Integer.MIN_VALUE + 2620; - int FRAMEWORK_MONITOR = Integer.MIN_VALUE + 2900; + int FRAMEWORK_BIZ_PUSH = Integer.MIN_VALUE + 2700; - int FRAMEWORK_BIZ_OPERATOR_LOG = Integer.MIN_VALUE + 3000; + int FRAMEWORK_BIZ_OPERATOR_LOG = Integer.MIN_VALUE + 7000; + + int FRAMEWORK_MONITOR = Integer.MIN_VALUE + 9000; int FRAMEWORK_BANNER = Integer.MIN_VALUE + 10000; diff --git a/orion-visor-common/src/main/java/org/dromara/visor/common/constant/ErrorMessage.java b/orion-visor-common/src/main/java/org/dromara/visor/common/constant/ErrorMessage.java index c1463e93..3af48398 100644 --- a/orion-visor-common/src/main/java/org/dromara/visor/common/constant/ErrorMessage.java +++ b/orion-visor-common/src/main/java/org/dromara/visor/common/constant/ErrorMessage.java @@ -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 = "错误的类型"; diff --git a/orion-visor-common/src/main/java/org/dromara/visor/common/entity/PushUser.java b/orion-visor-common/src/main/java/org/dromara/visor/common/entity/PushUser.java new file mode 100644 index 00000000..c85d43e5 --- /dev/null +++ b/orion-visor-common/src/main/java/org/dromara/visor/common/entity/PushUser.java @@ -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; + +} diff --git a/orion-visor-dependencies/pom.xml b/orion-visor-dependencies/pom.xml index 86ff5680..af86d80a 100644 --- a/orion-visor-dependencies/pom.xml +++ b/orion-visor-dependencies/pom.xml @@ -14,11 +14,11 @@ https://github.com/dromara/orion-visor - 2.5.0 + 2.5.1 2.7.17 2.7.15 1.5.0 - 2.0.2 + 2.0.3 1.9.7 1.18.26 1.6.15 @@ -156,6 +156,11 @@ orion-visor-spring-boot-starter-influxdb ${revision} + + org.dromara.visor + orion-visor-spring-boot-starter-biz-push + ${revision} + org.dromara.visor orion-visor-spring-boot-starter-biz-operator-log diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-operator-log/src/main/java/org/dromara/visor/framework/biz/operator/log/core/factory/InitializingOperatorTypes.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-operator-log/src/main/java/org/dromara/visor/framework/biz/operator/log/core/factory/InitializingOperatorTypes.java index e78d9179..20fdc1ec 100644 --- a/orion-visor-framework/orion-visor-spring-boot-starter-biz-operator-log/src/main/java/org/dromara/visor/framework/biz/operator/log/core/factory/InitializingOperatorTypes.java +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-operator-log/src/main/java/org/dromara/visor/framework/biz/operator/log/core/factory/InitializingOperatorTypes.java @@ -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); } } diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/pom.xml b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/pom.xml new file mode 100644 index 00000000..c7fd1482 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/pom.xml @@ -0,0 +1,37 @@ + + + + org.dromara.visor + orion-visor-framework + ${revision} + + + 4.0.0 + orion-visor-spring-boot-starter-biz-push + ${project.artifactId} + jar + + 项目业务推送包 + https://github.com/dromara/orion-visor + + + + org.dromara.visor + orion-visor-common + + + + org.springframework.boot + spring-boot-starter + + + + + org.springframework.boot + spring-boot-starter-web + provided + + + diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/configuration/OrionPushAutoConfiguration.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/configuration/OrionPushAutoConfiguration.java new file mode 100644 index 00000000..efd37876 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/configuration/OrionPushAutoConfiguration.java @@ -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> pushServices) { + // 服务列表 + Map> serviceMap = pushServices.stream() + .collect(Collectors.toMap( + MessageChannelUtils::getPushChannel, + Function.identity(), + Functions.right())); + // 创建监听器 + return new PushMessageEventListener(serviceMap); + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/annotation/MessageChannel.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/annotation/MessageChannel.java new file mode 100644 index 00000000..4d5b3270 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/annotation/MessageChannel.java @@ -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(); + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/DingRequestBody.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/DingRequestBody.java new file mode 100644 index 00000000..495757a2 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/DingRequestBody.java @@ -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 atMobiles; + + /** + * 被 at 的 userId + */ + private List atUserIds; + + /** + * 是否 at 所有人 + */ + private Boolean isAtAll; + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/DingResponseBody.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/DingResponseBody.java new file mode 100644 index 00000000..be9a318a --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/DingResponseBody.java @@ -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 DingResponseBody implements Serializable { + + /** + * 错误码值 + */ + @JSONField(name = "errcode") + private Integer errCode; + + /** + * 错误码描述 + */ + @JSONField(name = "errmsg") + private String errMsg; + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/FeiShuRequestBody.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/FeiShuRequestBody.java new file mode 100644 index 00000000..361d22dc --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/FeiShuRequestBody.java @@ -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; + + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/FeiShuResponseBody.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/FeiShuResponseBody.java new file mode 100644 index 00000000..68d003a2 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/FeiShuResponseBody.java @@ -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; + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/WeComRequestBody.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/WeComRequestBody.java new file mode 100644 index 00000000..edcec1e6 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/WeComRequestBody.java @@ -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 mentionedList; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class MarkdownPayload implements Serializable { + + /** + * 内容 + */ + private String content; + + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/WeComResponseBody.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/WeComResponseBody.java new file mode 100644 index 00000000..039b860d --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/entity/WeComResponseBody.java @@ -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; + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/enums/PushChannelEnum.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/enums/PushChannelEnum.java new file mode 100644 index 00000000..7968994c --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/enums/PushChannelEnum.java @@ -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 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; + } + +} \ No newline at end of file diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/event/PushMessageEvent.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/event/PushMessageEvent.java new file mode 100644 index 00000000..a02cf39a --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/event/PushMessageEvent.java @@ -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); + } + +} \ No newline at end of file diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/framework/service/PushTemplateFrameworkService.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/framework/service/PushTemplateFrameworkService.java new file mode 100644 index 00000000..af80d761 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/framework/service/PushTemplateFrameworkService.java @@ -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); + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/framework/service/PushTemplateFrameworkServiceDelegate.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/framework/service/PushTemplateFrameworkServiceDelegate.java new file mode 100644 index 00000000..97fa760a --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/framework/service/PushTemplateFrameworkServiceDelegate.java @@ -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); + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/framework/service/WebsiteMessageFrameworkService.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/framework/service/WebsiteMessageFrameworkService.java new file mode 100644 index 00000000..7483255c --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/framework/service/WebsiteMessageFrameworkService.java @@ -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); + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/listener/PushMessageEventListener.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/listener/PushMessageEventListener.java new file mode 100644 index 00000000..6df72da8 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/listener/PushMessageEventListener.java @@ -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.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 { + + private final Map> pushServiceMap; + + public PushMessageEventListener(Map> pushServiceMap) { + this.pushServiceMap = pushServiceMap; + } + + // FIXME + @Async("asyncExecutor") + @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 IPushService getPushService(PushMessage message) { + return (IPushService) pushServiceMap.get(message.getChannel()); + } + +} \ No newline at end of file diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/BasePushMessage.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/BasePushMessage.java new file mode 100644 index 00000000..ce868353 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/BasePushMessage.java @@ -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 params; + + @Valid + @Schema(description = "推送用户") + private List pushUsers; + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/DingPushMessage.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/DingPushMessage.java new file mode 100644 index 00000000..1d100a8b --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/DingPushMessage.java @@ -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; + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/FeiShuPushMessage.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/FeiShuPushMessage.java new file mode 100644 index 00000000..98c9f7e0 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/FeiShuPushMessage.java @@ -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; + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/PushMessage.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/PushMessage.java new file mode 100644 index 00000000..644db443 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/PushMessage.java @@ -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 getParams(); + + /** + * 设置参数 + * + * @param params params + */ + void setParams(Map params); + + /** + * 获取推送用户 + * + * @return 推送用户 + */ + List getPushUsers(); + + /** + * 设置推送用户列表 + * + * @param pushUsers pushUsers + */ + void setPushUsers(List pushUsers); + + /** + * 获取推送渠道 + * + * @return 推送渠道 + */ + PushChannelEnum getChannel(); + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/WeComPushMessage.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/WeComPushMessage.java new file mode 100644 index 00000000..c8370286 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/WeComPushMessage.java @@ -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; + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/WebsiteMessage.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/WebsiteMessage.java new file mode 100644 index 00000000..cc71d513 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/message/WebsiteMessage.java @@ -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; + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/BasePushService.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/BasePushService.java new file mode 100644 index 00000000..aef79c7e --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/BasePushService.java @@ -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.Valid; +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 implements IPushService { + + @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) { + // 验证消息 + Valid.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)); + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/DingPushService.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/DingPushService.java new file mode 100644 index 00000000..141c8f8e --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/DingPushService.java @@ -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.Exceptions; +import cn.orionsec.kit.lang.utils.Strings; +import cn.orionsec.kit.lang.utils.Valid; +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; + +/** + * 钉钉推送服务类 + *

+ * doc + * + * @author Shihao Lv + * @version 1.0.0 + * @since 2025/9/17 23:13 + */ +@Slf4j +@MessageChannel(value = PushChannelEnum.DING) +public class DingPushService extends BasePushService { + + 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); + // 验证发送结果 + Valid.eq(responseBody.getErrCode(), SUCCESS_CODE, responseBody.getErrMsg()); + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/FeiShuPushService.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/FeiShuPushService.java new file mode 100644 index 00000000..5fa441a6 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/FeiShuPushService.java @@ -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.Valid; +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.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; + +/** + * 飞书推送服务类 + *

+ * doc + * + * @author Shihao Lv + * @version 1.0.0 + * @since 2025/9/17 23:13 + */ +@Slf4j +@MessageChannel(value = PushChannelEnum.FEI_SHU) +public class FeiShuPushService extends BasePushService { + + 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); + // 验证发送结果 + Valid.eq(responseBody.getCode(), SUCCESS_CODE, responseBody.getMsg()); + } + +} \ No newline at end of file diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/IPushService.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/IPushService.java new file mode 100644 index 00000000..0b76f19d --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/IPushService.java @@ -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 { + + /** + * 推送消息 + * + * @param message message + */ + void push(Message message); + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/WeComPushService.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/WeComPushService.java new file mode 100644 index 00000000..110c74cb --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/WeComPushService.java @@ -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.Valid; +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; + +/** + * 企业微信推送服务 + *

+ * docs + * + * @author Shihao Lv + * @version 1.0.0 + * @since 2025/9/17 23:13 + */ +@Slf4j +@MessageChannel(value = PushChannelEnum.WE_COM) +public class WeComPushService extends BasePushService { + + 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); + // 验证发送结果 + Valid.eq(responseBody.getErrCode(), SUCCESS_CODE, responseBody.getErrMsg()); + } + +} \ No newline at end of file diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/WebsitePushService.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/WebsitePushService.java new file mode 100644 index 00000000..c1cc0ae5 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/service/WebsitePushService.java @@ -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 { + + 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); + } + +} \ No newline at end of file diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/utils/MessageChannelUtils.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/utils/MessageChannelUtils.java new file mode 100644 index 00000000..16426efb --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/utils/MessageChannelUtils.java @@ -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 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(); + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/utils/MessageUtils.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/utils/MessageUtils.java new file mode 100644 index 00000000..211fe91d --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/utils/MessageUtils.java @@ -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 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); + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/utils/PushUtils.java b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/utils/PushUtils.java new file mode 100644 index 00000000..df29758f --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/java/org/dromara/visor/framework/biz/push/core/utils/PushUtils.java @@ -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 params) { + pushTemplate(templateId, params, null); + } + + /** + * 根据模板推送消息 + * + * @param templateId 模板ID + * @param params 模板参数 + * @param pushUsers 推送用户 + */ + public static void pushTemplate(Long templateId, Map params, List 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; + } + +} diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..01f695c5 --- /dev/null +++ b/orion-visor-framework/orion-visor-spring-boot-starter-biz-push/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.dromara.visor.framework.biz.push.configuration.OrionPushAutoConfiguration \ No newline at end of file diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-influxdb/src/main/java/org/dromara/visor/framework/influxdb/configuration/OrionInfluxdbAutoConfiguration.java b/orion-visor-framework/orion-visor-spring-boot-starter-influxdb/src/main/java/org/dromara/visor/framework/influxdb/configuration/OrionInfluxdbAutoConfiguration.java index e6982f35..4e969223 100644 --- a/orion-visor-framework/orion-visor-spring-boot-starter-influxdb/src/main/java/org/dromara/visor/framework/influxdb/configuration/OrionInfluxdbAutoConfiguration.java +++ b/orion-visor-framework/orion-visor-spring-boot-starter-influxdb/src/main/java/org/dromara/visor/framework/influxdb/configuration/OrionInfluxdbAutoConfiguration.java @@ -54,8 +54,6 @@ import java.net.ConnectException; public class OrionInfluxdbAutoConfiguration { /** - * TODO 重连 - * * @param config config * @return influxdb 客户端 */ diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/java/org/dromara/visor/framework/mybatis/core/domain/BaseDO.java b/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/java/org/dromara/visor/framework/mybatis/core/domain/BaseDO.java index 03feba02..31d30798 100644 --- a/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/java/org/dromara/visor/framework/mybatis/core/domain/BaseDO.java +++ b/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/java/org/dromara/visor/framework/mybatis/core/domain/BaseDO.java @@ -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; } diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/java/org/dromara/visor/framework/mybatis/core/utils/DomainFillUtils.java b/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/java/org/dromara/visor/framework/mybatis/core/utils/DomainFillUtils.java index e02312b0..491f2dcf 100644 --- a/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/java/org/dromara/visor/framework/mybatis/core/utils/DomainFillUtils.java +++ b/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/java/org/dromara/visor/framework/mybatis/core/utils/DomainFillUtils.java @@ -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 diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/resources/templates/orion-vue-views-components-card-list.vue.vm b/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/resources/templates/orion-vue-views-components-card-list.vue.vm index 81142dbf..ee58fbe0 100644 --- a/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/resources/templates/orion-vue-views-components-card-list.vue.vm +++ b/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/resources/templates/orion-vue-views-components-card-list.vue.vm @@ -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>([]); + const renderList = ref>([]); 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; diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/resources/templates/orion-vue-views-components-form-drawer.vue.vm b/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/resources/templates/orion-vue-views-components-form-drawer.vue.vm index 35db9b00..8d290b1d 100644 --- a/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/resources/templates/orion-vue-views-components-form-drawer.vue.vm +++ b/orion-visor-framework/orion-visor-spring-boot-starter-mybatis/src/main/resources/templates/orion-vue-views-components-form-drawer.vue.vm @@ -57,10 +57,12 @@ + + + + diff --git a/orion-visor-ui/src/components/monitor/metrics/selector/index.vue b/orion-visor-ui/src/components/monitor/metrics/selector/index.vue new file mode 100644 index 00000000..d63807f0 --- /dev/null +++ b/orion-visor-ui/src/components/monitor/metrics/selector/index.vue @@ -0,0 +1,54 @@ + + + + + + + diff --git a/orion-visor-ui/src/components/system/message-box/index.vue b/orion-visor-ui/src/components/system/message-box/index.vue index 8fbed43d..726bb535 100644 --- a/orion-visor-ui/src/components/system/message-box/index.vue +++ b/orion-visor-ui/src/components/system/message-box/index.vue @@ -22,25 +22,25 @@ - - - 清空 - - - - 全部已读 - + + + + + @@ -263,6 +263,10 @@ .header-button { padding: 0 6px; } + + :deep(.arco-tabs-tab) { + margin: 0 6px 0 0 !important; + } } diff --git a/orion-visor-ui/src/components/system/notify-template/selector/index.vue b/orion-visor-ui/src/components/system/notify-template/selector/index.vue new file mode 100644 index 00000000..cd82ad5b --- /dev/null +++ b/orion-visor-ui/src/components/system/notify-template/selector/index.vue @@ -0,0 +1,62 @@ + + + + + + + diff --git a/orion-visor-ui/src/router/routes/modules/monitor.ts b/orion-visor-ui/src/router/routes/modules/monitor.ts index b3075cbe..fa8c22c2 100644 --- a/orion-visor-ui/src/router/routes/modules/monitor.ts +++ b/orion-visor-ui/src/router/routes/modules/monitor.ts @@ -17,6 +17,16 @@ const MONITOR: AppRouteRecordRaw = { path: '/monitor/monitor-host', component: () => import('@/views/monitor/monitor-host/index.vue'), }, + { + name: 'alarmPolicy', + path: '/monitor/alarm-policy', + component: () => import('@/views/monitor/alarm-policy/index.vue'), + }, + { + name: 'alarmEvent', + path: '/monitor/alarm-event', + component: () => import('@/views/monitor/alarm-event/index.vue'), + }, { name: 'monitorDetail', path: '/monitor/detail', @@ -32,6 +42,21 @@ const MONITOR: AppRouteRecordRaw = { }, component: () => import('@/views/monitor/monitor-detail/index.vue'), }, + { + name: 'alarmRule', + path: '/monitor/alarm-rule', + meta: { + // 固定到 tab + noAffix: false, + // 是否允许打开多个 tab + multipleTab: true, + // 名称模板 + localeTemplate: (route: RouteLocationNormalized) => { + return `${route.meta.locale} - ${route.query.name || ''}`; + }, + }, + component: () => import('@/views/monitor/alarm-rule/index.vue'), + }, ], }; diff --git a/orion-visor-ui/src/router/routes/modules/system.ts b/orion-visor-ui/src/router/routes/modules/system.ts index 0237f002..a2ca2145 100644 --- a/orion-visor-ui/src/router/routes/modules/system.ts +++ b/orion-visor-ui/src/router/routes/modules/system.ts @@ -21,6 +21,11 @@ const SYSTEM: AppRouteRecordRaw = { path: '/system/dict-value', component: () => import('@/views/system/dict-value/index.vue'), }, + { + name: 'notifyTemplate', + path: '/system/notify-template', + component: () => import('@/views/system/notify-template/index.vue'), + }, { name: 'systemSetting', path: '/system/setting', diff --git a/orion-visor-ui/src/store/modules/cache/index.ts b/orion-visor-ui/src/store/modules/cache/index.ts index bc78ccd7..73e460af 100644 --- a/orion-visor-ui/src/store/modules/cache/index.ts +++ b/orion-visor-ui/src/store/modules/cache/index.ts @@ -24,6 +24,9 @@ import { getExecJobList } from '@/api/exec/exec-job'; import { getPathBookmarkGroupList } from '@/api/terminal/path-bookmark-group'; import { getCommandSnippetList } from '@/api/terminal/command-snippet'; import { getPathBookmarkList } from '@/api/terminal/path-bookmark'; +import { getNotifyTemplateList } from '@/api/system/notify-template'; +import { getAlarmPolicyList } from '@/api/monitor/alarm-policy'; +import { getMetricsList } from '@/api/monitor/metrics'; export default defineStore('cache', { state: (): CacheState => ({}), @@ -173,6 +176,21 @@ export default defineStore('cache', { return await this.load('execJob', getExecJobList, ['exec:exec-job:query'], force); }, + // 查询监控告警策略列表 + async loadMonitorAlarmPolicy(force = false) { + return await this.load('alarmPolicy', getAlarmPolicyList, ['monitor:alarm-policy:query'], force); + }, + + // 查询监控指标列表 + async loadMonitorMetricsList(force = false) { + return await this.load('monitorMetrics', getMetricsList, ['monitor:monitor-metrics:query'], force); + }, + + // 查询通知模板列表 + async loadNotifyTemplate(bizType: string, force = false) { + return await this.load(`notifyTemplate_${bizType}`, () => getNotifyTemplateList(bizType), ['infra:notify-template:query'], force); + }, + // 加载偏好 async loadPreference(type: PreferenceType, force = false) { return await this.load(`preference_${type}`, () => getPreference(type), undefined, force, {}); @@ -185,8 +203,8 @@ export default defineStore('cache', { // 加载系统设置 async loadSystemSetting(force = false) { - return await this.load(`system_setting`, getSystemAggregateSetting, undefined, force, {}); - }, + return await this.load('systemSetting', getSystemAggregateSetting, undefined, force, {}); + } } }); diff --git a/orion-visor-ui/src/store/modules/cache/types.ts b/orion-visor-ui/src/store/modules/cache/types.ts index ec83dc71..2d0feb71 100644 --- a/orion-visor-ui/src/store/modules/cache/types.ts +++ b/orion-visor-ui/src/store/modules/cache/types.ts @@ -7,10 +7,11 @@ export type CacheType = 'users' | 'menus' | 'roles' | 'authorizedHostKeys' | 'authorizedHostIdentities' | 'commandSnippetGroups' | 'pathBookmarkGroups' | 'commandSnippets' | 'pathBookmarks' - | 'system_setting' + | 'alarmPolicy' | 'monitorMetrics' + | 'systemSetting' | 'notifyTemplate*' | '*_Tags' | 'preference_*' | string export interface CacheState { - [key: CacheType]: unknown; + [key: CacheType]: any; } diff --git a/orion-visor-ui/src/types/form.ts b/orion-visor-ui/src/types/form.ts index e1395a2e..0185ad7b 100644 --- a/orion-visor-ui/src/types/form.ts +++ b/orion-visor-ui/src/types/form.ts @@ -1,5 +1,8 @@ import type { SelectOptionData, TreeNodeData } from '@arco-design/web-vue'; +// 表单操作 +export type FormHandle = 'add' | 'update' | 'copy' | 'view'; + // 通过 label 进行过滤 export const labelFilter = (searchValue: string, option: { label: string }) => { return option.label.toLowerCase().includes(searchValue.toLowerCase()); diff --git a/orion-visor-ui/src/utils/charts.ts b/orion-visor-ui/src/utils/charts.ts index 6626d1a4..915011c7 100644 --- a/orion-visor-ui/src/utils/charts.ts +++ b/orion-visor-ui/src/utils/charts.ts @@ -1,10 +1,14 @@ -// 获取百分比进度状态 -export const getPercentProgressColor = (percent: number) => { - if (percent < 0.6) { - return 'rgb(var(--green-6))'; - } else if (percent < 0.8) { - return 'rgb(var(--orange-6))'; - } else { - return 'rgb(var(--red-6))'; +// 获取百分比进度颜色 +export const getPercentProgressColor = (percent: number, defaultColor = 'rgb(var(--green-6))') => { + try { + if (percent < 0.6) { + return defaultColor; + } else if (percent < 0.8) { + return 'rgb(var(--orange-6))'; + } else { + return 'rgb(var(--red-6))'; + } + } catch (e) { + return defaultColor; } }; diff --git a/orion-visor-ui/src/utils/index.ts b/orion-visor-ui/src/utils/index.ts index 7b376dfd..3299398f 100644 --- a/orion-visor-ui/src/utils/index.ts +++ b/orion-visor-ui/src/utils/index.ts @@ -239,6 +239,10 @@ export function replaceHtmlTag(message: string) { .replaceAll('<sr 2>', '') .replaceAll('<sr 4>', '') .replaceAll('</sr>', '') + .replaceAll('<sg>', '') + .replaceAll('<sg 2>', '') + .replaceAll('<sg 4>', '') + .replaceAll('</sg>', '') .replaceAll('<b>', '') .replaceAll('</b>', ''); } @@ -256,9 +260,24 @@ export function clearHtmlTag(message: string) { .replaceAll('<sr 2>', '') .replaceAll('<sr>', '') .replaceAll('</sr>', '') + .replaceAll('<sg 0>', '') + .replaceAll('<sg 2>', '') + .replaceAll('<sg>', '') + .replaceAll('</sg>', '') .replaceAll('<b>', '') .replaceAll('</b>', '') .replaceAll('
', '\n'); } +/** + * 分配记录 (忽略基础信息) + */ +export const assignOmitRecord = (record: any, ...omits: Array) => { + const model = Object.assign({}, record); + for (const omitKey of [...omits, 'creator', 'updater', 'createTime', 'updateTime']) { + delete model[omitKey]; + } + return model; +}; + export default null; diff --git a/orion-visor-ui/src/utils/metrics.ts b/orion-visor-ui/src/utils/metrics.ts index 4ef0ceac..857593ed 100644 --- a/orion-visor-ui/src/utils/metrics.ts +++ b/orion-visor-ui/src/utils/metrics.ts @@ -27,7 +27,7 @@ export type WindowUnit = // 指标单位格式化选项 export interface MetricUnitFormatOptions { // 小数位 - digit?: number; + precision?: number; // 后缀 suffix?: string; // 空转0 @@ -37,7 +37,12 @@ export interface MetricUnitFormatOptions { } // 指标单位格式化函数 -type MetricUnitFormatterFn = (value: number, option?: MetricUnitFormatOptions) => string; +type MetricUnitFormatterOption = { + // 格式化单位 + format: (value: number, option?: MetricUnitFormatOptions) => string; + // 获取阈值原始值 + getThresholdOriginalValue: (value: number) => number; +}; // 指标单位格式化配置 type WindowTimeFormatterOption = { @@ -54,27 +59,57 @@ type WindowTimeFormatterOption = { }; // 指标单位格式化 -export const MetricUnitFormatter: Record = { +export const MetricUnitFormatter: Record = { // 字节 - BYTES: formatBytes, + BYTES: { + format: formatBytes, + getThresholdOriginalValue: getByteThresholdOriginalValue, + }, // 比特 - BITS: formatBits, + BITS: { + format: formatBits, + getThresholdOriginalValue: getBitThresholdOriginalValue, + }, // 次数 - COUNT: formatCount, + COUNT: { + format: formatCount, + getThresholdOriginalValue: identity, + }, // 秒 - SECONDS: formatSeconds, + SECONDS: { + format: formatSeconds, + getThresholdOriginalValue: identity, + }, // 百分比 - PER: formatPer, + PER: { + format: formatPer, + getThresholdOriginalValue: identity, + }, // 字节/秒 - BYTES_S: (value, option) => formatBytes(value, option) + '/s', + BYTES_S: { + format: (value, option) => formatBytes(value, option) + '/s', + getThresholdOriginalValue: getByteThresholdOriginalValue, + }, // 比特/秒 - BITS_S: (value, option) => formatBits(value, option) + 'ps', + BITS_S: { + format: (value, option) => formatBits(value, option) + 'ps', + getThresholdOriginalValue: getBitThresholdOriginalValue, + }, // 次数/秒 - COUNT_S: (value, option) => formatCount(value, option) + '/s', + COUNT_S: { + format: (value, option) => formatCount(value, option) + '/s', + getThresholdOriginalValue: identity, + }, // 文本 - TEXT: formatText, + TEXT: { + format: formatText, + getThresholdOriginalValue: identity, + }, // 无单位 - NONE: (value, option) => formatNumber(value, option), + NONE: { + format: formatText, + getThresholdOriginalValue: identity, + }, }; // 窗口单位格式化 @@ -124,39 +159,26 @@ export const parseWindowUnit = (windowValue: string): [number, WindowUnit] => { } }; -// 安全取小数位 -function getFixed(option?: MetricUnitFormatOptions, defaultValue = 2): number { - return typeof option?.digit === 'number' ? option.digit : defaultValue; +// 提取单位 +export function extractUnit(str: string): string { + const match = str.match(/[^\d.]+$/); + return match ? match[0] : ''; } -// 格式化数字 -function formatNumber(value: number, option?: MetricUnitFormatOptions): string { - const fixed = getFixed(option, 2); - const abs = Math.abs(value); - let result: string; - - if (abs >= 1e9) { - result = (value / 1e9).toFixed(fixed); - } else if (abs >= 1_000_000) { - result = (value / 1_000_000).toFixed(fixed); - } else if (abs >= 1_000) { - result = (value / 1_000).toFixed(fixed); - } else { - result = value.toFixed(fixed); - } - - return parseFloat(result).toString(); +// 安全取小数位 +function getPrecision(option?: MetricUnitFormatOptions, defaultValue = 2): number { + return typeof option?.precision === 'number' ? option.precision : defaultValue; } // 格式化百分比 function formatPer(value: number, option?: MetricUnitFormatOptions): string { - const fixed = getFixed(option, 2); + const fixed = getPrecision(option, 2); return parseFloat((value).toFixed(fixed)) + '%'; } // 格式化字节 -function formatBytes(value: number, option?: MetricUnitFormatOptions): string { - const fixed = getFixed(option, 2); +export function formatBytes(value: number, option?: MetricUnitFormatOptions): string { + const fixed = getPrecision(option, 2); const units = ['B', 'KB', 'MB', 'GB', 'TB']; let v = Math.abs(value); let i = 0; @@ -170,10 +192,10 @@ function formatBytes(value: number, option?: MetricUnitFormatOptions): string { } // 格式化比特 -function formatBits(value: number, option?: MetricUnitFormatOptions): string { - const fixed = getFixed(option, 2); +export function formatBits(value: number, option?: MetricUnitFormatOptions): string { + const fixed = getPrecision(option, 2); const units = ['b', 'Kb', 'Mb', 'Gb']; - let v = Math.abs(value); + let v = Math.abs(value * 8); let i = 0; while (v >= 1000 && i < units.length - 1) { v /= 1000; @@ -186,7 +208,7 @@ function formatBits(value: number, option?: MetricUnitFormatOptions): string { // 格式化次数 function formatCount(value: number, option?: MetricUnitFormatOptions): string { - const fixed = getFixed(option, 2); + const fixed = getPrecision(option, 2); const abs = Math.abs(value); if (abs >= 1_000_000) { return parseFloat((value / 1_000_000).toFixed(fixed)) + 'M'; @@ -198,7 +220,7 @@ function formatCount(value: number, option?: MetricUnitFormatOptions): string { // 格式化时间 function formatSeconds(value: number, option?: MetricUnitFormatOptions): string { - const fixed = getFixed(option, 2); + const fixed = getPrecision(option, 2); if (value >= 3600) { return parseFloat((value / 3600).toFixed(fixed)) + 'h'; } else if (value >= 60) { @@ -209,8 +231,23 @@ function formatSeconds(value: number, option?: MetricUnitFormatOptions): string // 格式化文本 function formatText(value: number, option?: MetricUnitFormatOptions): string { - const fixed = getFixed(option, 2); + const fixed = getPrecision(option, 2); const unitText = option?.suffix || ''; const numStr = value.toFixed(fixed); return unitText ? `${numStr} ${unitText}` : numStr; } + +// 获取 byte 的阈值原值 MB > b +function getByteThresholdOriginalValue(value: number) { + return value * 1024 * 1024; +} + +// 获取 bit 的阈值原值 Mb > bit +function getBitThresholdOriginalValue(value: number) { + return value / 8 * 1000 * 1000; +} + +// 返回原值 +function identity(value: number): number { + return value; +} diff --git a/orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-clear-modal.vue b/orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-clear-modal.vue index ff304ebc..7d1ca626 100644 --- a/orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-clear-modal.vue +++ b/orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-clear-modal.vue @@ -2,12 +2,12 @@ { Modal.confirm({ - title: '删除清空', + title: '清理确认', content: `确定要删除 ${count} 条数据吗? 确定后将立即删除且无法恢复!`, onOk: async () => { setLoading(true); try { - // 调用清空 + // 调用清理 const { data } = await clearTerminalConnectLog(formModel.value); - Message.success(`已成功清空 ${data} 条数据`); + Message.success(`已成功清理 ${data} 条数据`); emits('clear'); // 清空 setVisible(false); diff --git a/orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-table.vue b/orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-table.vue index 0ed053a4..8cd7619e 100644 --- a/orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-table.vue +++ b/orion-visor-ui/src/views/asset-audit/terminal-connect-log/components/connect-log-table.vue @@ -63,11 +63,11 @@

- + - 清空 + 清理 diff --git a/orion-visor-ui/src/views/exec/exec-command-log/components/exec-command-log-clear-modal.vue b/orion-visor-ui/src/views/exec/exec-command-log/components/exec-command-log-clear-modal.vue index 47579815..fbcf7248 100644 --- a/orion-visor-ui/src/views/exec/exec-command-log/components/exec-command-log-clear-modal.vue +++ b/orion-visor-ui/src/views/exec/exec-command-log/components/exec-command-log-clear-modal.vue @@ -2,12 +2,12 @@ { Modal.confirm({ - title: '删除清空', + title: '清理确认', content: `确定要删除 ${count} 条数据吗? 确定后将立即删除且无法恢复!`, onOk: async () => { setLoading(true); try { - // 调用清空 + // 调用清理 const { data } = await clearExecCommandLog(formModel.value); - Message.success(`已成功清空 ${data} 条数据`); + Message.success(`已成功清理 ${data} 条数据`); emits('clear'); // 清空 setVisible(false); diff --git a/orion-visor-ui/src/views/exec/exec-command-log/components/exec-command-log-table.vue b/orion-visor-ui/src/views/exec/exec-command-log/components/exec-command-log-table.vue index d1bd6bc7..7d726d85 100644 --- a/orion-visor-ui/src/views/exec/exec-command-log/components/exec-command-log-table.vue +++ b/orion-visor-ui/src/views/exec/exec-command-log/components/exec-command-log-table.vue @@ -70,11 +70,11 @@ - + - 清空 + 清理 diff --git a/orion-visor-ui/src/views/exec/exec-job-log/components/exec-job-log-clear-modal.vue b/orion-visor-ui/src/views/exec/exec-job-log/components/exec-job-log-clear-modal.vue index a53b27f3..a63233cf 100644 --- a/orion-visor-ui/src/views/exec/exec-job-log/components/exec-job-log-clear-modal.vue +++ b/orion-visor-ui/src/views/exec/exec-job-log/components/exec-job-log-clear-modal.vue @@ -2,12 +2,12 @@ { Modal.confirm({ - title: '删除清空', + title: '清理确认', content: `确定要删除 ${count} 条数据吗? 确定后将立即删除且无法恢复!`, onOk: async () => { setLoading(true); try { - // 调用清空 + // 调用清理 const { data } = await clearExecJobLog(formModel.value); - Message.success(`已成功清空 ${data} 条数据`); + Message.success(`已成功清理 ${data} 条数据`); emits('clear'); // 清空 setVisible(false); diff --git a/orion-visor-ui/src/views/exec/exec-job-log/components/exec-job-log-table.vue b/orion-visor-ui/src/views/exec/exec-job-log/components/exec-job-log-table.vue index 460f37f7..790c95d3 100644 --- a/orion-visor-ui/src/views/exec/exec-job-log/components/exec-job-log-table.vue +++ b/orion-visor-ui/src/views/exec/exec-job-log/components/exec-job-log-table.vue @@ -61,11 +61,11 @@
- + - 清空 + 清理 diff --git a/orion-visor-ui/src/views/exec/exec-template/types/table.columns.ts b/orion-visor-ui/src/views/exec/exec-template/types/table.columns.ts index 839406a8..a6bbd8cd 100644 --- a/orion-visor-ui/src/views/exec/exec-template/types/table.columns.ts +++ b/orion-visor-ui/src/views/exec/exec-template/types/table.columns.ts @@ -22,6 +22,7 @@ const columns = [ title: '模板命令', dataIndex: 'command', slotName: 'command', + minWidth: 380, align: 'left', ellipsis: true, default: true, diff --git a/orion-visor-ui/src/views/exec/upload-task/components/upload-task-clear-modal.vue b/orion-visor-ui/src/views/exec/upload-task/components/upload-task-clear-modal.vue index ffbbea1e..b28aec9e 100644 --- a/orion-visor-ui/src/views/exec/upload-task/components/upload-task-clear-modal.vue +++ b/orion-visor-ui/src/views/exec/upload-task/components/upload-task-clear-modal.vue @@ -122,7 +122,7 @@ // 获取总数量 const { data } = await getUploadTaskCount(formModel.value); if (data) { - // 清空 + // 清理 doClear(data); } else { // 无数据 @@ -138,14 +138,14 @@ // 执行删除 const doClear = (count: number) => { Modal.confirm({ - title: '删除清空', + title: '清理确认', content: `确定要删除 ${count} 条数据吗? 确定后将立即删除且无法恢复!`, onOk: async () => { setLoading(true); try { - // 调用清空 + // 调用清理 const { data } = await clearUploadTask(formModel.value); - Message.success(`已成功清空 ${data} 条数据`); + Message.success(`已成功清理 ${data} 条数据`); emits('clear'); // 清空 setVisible(false); diff --git a/orion-visor-ui/src/views/exec/upload-task/components/upload-task-table.vue b/orion-visor-ui/src/views/exec/upload-task/components/upload-task-table.vue index 7a154336..bdca0bb3 100644 --- a/orion-visor-ui/src/views/exec/upload-task/components/upload-task-table.vue +++ b/orion-visor-ui/src/views/exec/upload-task/components/upload-task-table.vue @@ -70,11 +70,11 @@ - + - 清空 + 清理 diff --git a/orion-visor-ui/src/views/monitor/alarm-event/components/alarm-event-clear-modal.vue b/orion-visor-ui/src/views/monitor/alarm-event/components/alarm-event-clear-modal.vue new file mode 100644 index 00000000..938e4c2a --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-event/components/alarm-event-clear-modal.vue @@ -0,0 +1,223 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/monitor/alarm-event/components/alarm-event-handle-modal.vue b/orion-visor-ui/src/views/monitor/alarm-event/components/alarm-event-handle-modal.vue new file mode 100644 index 00000000..b6128fc0 --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-event/components/alarm-event-handle-modal.vue @@ -0,0 +1,130 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/monitor/alarm-event/components/alarm-event-table-base.vue b/orion-visor-ui/src/views/monitor/alarm-event/components/alarm-event-table-base.vue new file mode 100644 index 00000000..81d5f07f --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-event/components/alarm-event-table-base.vue @@ -0,0 +1,348 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/monitor/alarm-event/components/alarm-event-table.vue b/orion-visor-ui/src/views/monitor/alarm-event/components/alarm-event-table.vue new file mode 100644 index 00000000..d5facd64 --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-event/components/alarm-event-table.vue @@ -0,0 +1,199 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/monitor/alarm-event/index.vue b/orion-visor-ui/src/views/monitor/alarm-event/index.vue new file mode 100644 index 00000000..b800870f --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-event/index.vue @@ -0,0 +1,48 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/monitor/alarm-event/types/const.ts b/orion-visor-ui/src/views/monitor/alarm-event/types/const.ts new file mode 100644 index 00000000..8fdb1971 --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-event/types/const.ts @@ -0,0 +1,30 @@ +export const TableName = 'alarm_event'; + +// 最大清理数量 +export const maxClearLimit = 2000; + +// 是否为误报 +export const FalseAlarm = { + // 误报 + TRUE: 1, + // 非误报 + FALSE: 0, +}; + +// 告警条件 字典项 +export const TriggerConditionKey = 'alarmTriggerCondition'; + +// 告警记录处理状态 字典项 +export const HandleStatusKey = 'alarmEventHandleStatus'; + +// 是否为误报 字典项 +export const FalseAlarmKey = 'falseAlarm'; + +// 指标数据集 字典项 +export const MetricsMeasurementKey = 'metricsMeasurement'; + +// 告警等级 字典项 +export const AlarmLevelKey = 'alarmLevel'; + +// 加载的字典值 +export const dictKeys = [TriggerConditionKey, HandleStatusKey, FalseAlarmKey, MetricsMeasurementKey, AlarmLevelKey]; diff --git a/orion-visor-ui/src/views/monitor/alarm-event/types/form.rules.ts b/orion-visor-ui/src/views/monitor/alarm-event/types/form.rules.ts new file mode 100644 index 00000000..112f624b --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-event/types/form.rules.ts @@ -0,0 +1,22 @@ +import type { FieldRule } from '@arco-design/web-vue'; + +export const handleRules = { + handleStatus: [{ + required: true, + message: '请输入处理状态' + }, { + maxLength: 16, + message: '处理状态长度不能大于16位' + }], + handleTime: [{ + required: true, + message: '请输入处理时间' + }], + handleRemark: [{ + required: true, + message: '请输入处理备注' + }, { + maxLength: 512, + message: '处理备注长度不能大于512位' + }], +} as Record; diff --git a/orion-visor-ui/src/views/monitor/alarm-event/types/table.columns.ts b/orion-visor-ui/src/views/monitor/alarm-event/types/table.columns.ts new file mode 100644 index 00000000..bdbbe86f --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-event/types/table.columns.ts @@ -0,0 +1,146 @@ +import type { TableColumnData } from '@arco-design/web-vue'; +import { dateFormat } from '@/utils'; + +const columns = [ + { + title: 'id', + dataIndex: 'id', + slotName: 'id', + width: 98, + align: 'left', + fixed: 'left', + default: true, + }, { + title: '主机信息', + dataIndex: 'hostInfo', + slotName: 'hostInfo', + width: 248, + align: 'left', + fixed: 'left', + default: true, + }, { + title: '处理状态', + dataIndex: 'handleStatus', + slotName: 'handleStatus', + width: 108, + align: 'left', + fixed: 'left', + default: true, + }, { + title: '告警级别', + dataIndex: 'alarmLevel', + slotName: 'alarmLevel', + align: 'left', + width: 108, + default: true, + }, { + title: '告警策略', + dataIndex: 'policyId', + slotName: 'policyId', + align: 'left', + width: 120, + default: false, + }, { + title: '指标数据集', + dataIndex: 'metricsMeasurement', + slotName: 'metricsMeasurement', + align: 'left', + width: 108, + ellipsis: true, + tooltip: true, + default: false, + }, { + title: '告警指标', + dataIndex: 'metricsId', + slotName: 'metricsId', + align: 'left', + width: 184, + default: false, + }, { + title: '告警标签', + dataIndex: 'alarmTags', + slotName: 'alarmTags', + align: 'left', + minWidth: 168, + ellipsis: true, + tooltip: true, + default: true, + }, { + title: '告警值', + dataIndex: 'alarmValue', + slotName: 'alarmValue', + width: 148, + align: 'left', + default: true, + }, { + title: '告警阈值', + dataIndex: 'alarmThreshold', + slotName: 'alarmThreshold', + width: 148, + align: 'left', + default: false, + }, { + title: '告警摘要', + dataIndex: 'alarmInfo', + slotName: 'alarmInfo', + align: 'left', + width: 248, + tooltip: true, + default: true, + }, { + title: '连续触发次数', + dataIndex: 'consecutiveCount', + slotName: 'consecutiveCount', + align: 'left', + width: 116, + default: true, + }, { + title: '处理人', + dataIndex: 'handleUsername', + slotName: 'handleUsername', + align: 'left', + width: 138, + ellipsis: true, + tooltip: true, + default: true, + }, { + title: '处理备注', + dataIndex: 'handleRemark', + slotName: 'handleRemark', + align: 'left', + minWidth: 128, + ellipsis: true, + tooltip: true, + default: true, + }, { + title: '处理时间', + dataIndex: 'handleTime', + slotName: 'handleTime', + align: 'left', + width: 180, + render: ({ record }) => { + return record.handleTime && dateFormat(new Date(record.handleTime)); + }, + default: true, + }, { + title: '告警时间', + dataIndex: 'createTime', + slotName: 'createTime', + align: 'center', + width: 180, + render: ({ record }) => { + return dateFormat(new Date(record.createTime)); + }, + fixed: 'right', + default: true, + }, { + title: '操作', + slotName: 'handle', + width: 130, + align: 'center', + fixed: 'right', + default: true, + }, +] as TableColumnData[]; + +export default columns; diff --git a/orion-visor-ui/src/views/monitor/alarm-policy/components/alarm-policy-form-modal.vue b/orion-visor-ui/src/views/monitor/alarm-policy/components/alarm-policy-form-modal.vue new file mode 100644 index 00000000..88026878 --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-policy/components/alarm-policy-form-modal.vue @@ -0,0 +1,173 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/monitor/alarm-policy/components/alarm-policy-table.vue b/orion-visor-ui/src/views/monitor/alarm-policy/components/alarm-policy-table.vue new file mode 100644 index 00000000..91a93dcb --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-policy/components/alarm-policy-table.vue @@ -0,0 +1,230 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/monitor/alarm-policy/index.vue b/orion-visor-ui/src/views/monitor/alarm-policy/index.vue new file mode 100644 index 00000000..0702c54b --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-policy/index.vue @@ -0,0 +1,43 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/monitor/alarm-policy/types/const.ts b/orion-visor-ui/src/views/monitor/alarm-policy/types/const.ts new file mode 100644 index 00000000..3f6075fa --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-policy/types/const.ts @@ -0,0 +1 @@ +export const TableName = 'monitor_alarm_policy'; diff --git a/orion-visor-ui/src/views/monitor/alarm-policy/types/form.rules.ts b/orion-visor-ui/src/views/monitor/alarm-policy/types/form.rules.ts new file mode 100644 index 00000000..ac59a73b --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-policy/types/form.rules.ts @@ -0,0 +1,20 @@ +import type { FieldRule } from '@arco-design/web-vue'; + +const rules = { + name: [{ + required: true, + message: '请输入策略名称' + }, { + maxLength: 64, + message: '策略名称长度不能大于64位' + }], + description: [{ + required: true, + message: '请输入策略描述' + }, { + maxLength: 255, + message: '策略描述长度不能大于255位' + }], +} as Record; + +export default rules; diff --git a/orion-visor-ui/src/views/monitor/alarm-policy/types/table.columns.ts b/orion-visor-ui/src/views/monitor/alarm-policy/types/table.columns.ts new file mode 100644 index 00000000..3a1fdbaa --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-policy/types/table.columns.ts @@ -0,0 +1,99 @@ +import type { TableColumnData } from '@arco-design/web-vue'; +import { dateFormat } from '@/utils'; + +const columns = [ + { + title: 'id', + dataIndex: 'id', + slotName: 'id', + width: 68, + align: 'left', + fixed: 'left', + default: true, + }, { + title: '策略名称', + dataIndex: 'name', + slotName: 'name', + align: 'left', + minWidth: 218, + ellipsis: true, + tooltip: true, + default: true, + }, { + title: '规则数量', + dataIndex: 'ruleCount', + slotName: 'ruleCount', + align: 'left', + width: 128, + default: true, + }, { + title: '主机数量', + dataIndex: 'hostCount', + slotName: 'hostCount', + align: 'left', + width: 128, + default: true, + }, { + title: '今日触发次数', + dataIndex: 'todayTriggerCount', + slotName: 'todayTriggerCount', + align: 'left', + width: 128, + default: true, + }, { + title: '7日触发次数', + dataIndex: 'weekTriggerCount', + slotName: 'weekTriggerCount', + align: 'left', + width: 128, + default: true, + }, { + title: '策略描述', + dataIndex: 'description', + slotName: 'description', + align: 'left', + minWidth: 238, + ellipsis: true, + tooltip: true, + default: true, + }, { + title: '创建时间', + dataIndex: 'createTime', + slotName: 'createTime', + align: 'center', + width: 180, + render: ({ record }) => { + return dateFormat(new Date(record.createTime)); + }, + }, { + title: '修改时间', + dataIndex: 'updateTime', + slotName: 'updateTime', + align: 'center', + width: 180, + render: ({ record }) => { + return dateFormat(new Date(record.updateTime)); + }, + default: true, + }, { + title: '创建人', + width: 148, + dataIndex: 'creator', + slotName: 'creator', + }, { + title: '修改人', + width: 148, + dataIndex: 'updater', + slotName: 'updater', + default: true, + }, { + title: '操作', + slotName: 'handle', + width: 248, + align: 'center', + fixed: 'right', + default: true, + }, +] as TableColumnData[]; + +export default columns; diff --git a/orion-visor-ui/src/views/monitor/alarm-rule/components/alarm-rule-form-drawer.vue b/orion-visor-ui/src/views/monitor/alarm-rule/components/alarm-rule-form-drawer.vue new file mode 100644 index 00000000..54029640 --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-rule/components/alarm-rule-form-drawer.vue @@ -0,0 +1,368 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/monitor/alarm-rule/components/alarm-rule-table.vue b/orion-visor-ui/src/views/monitor/alarm-rule/components/alarm-rule-table.vue new file mode 100644 index 00000000..3a64350e --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-rule/components/alarm-rule-table.vue @@ -0,0 +1,273 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/monitor/alarm-rule/index.vue b/orion-visor-ui/src/views/monitor/alarm-rule/index.vue new file mode 100644 index 00000000..9d636f9f --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-rule/index.vue @@ -0,0 +1,48 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/monitor/alarm-rule/types/const.ts b/orion-visor-ui/src/views/monitor/alarm-rule/types/const.ts new file mode 100644 index 00000000..2a50ff25 --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-rule/types/const.ts @@ -0,0 +1,32 @@ +// 告警规则标签 +export interface RuleTag { + key: string; + value: string[]; +} + +// 表格名称 +export const TableName = 'alarm-rule'; + +// 默认告警条件 +export const DefaultCondition = 'GE'; + +// 默认告警等级 +export const DefaultLevel = 0; + +// 指标度量 字典项 +export const MeasurementKey = 'metricsMeasurement'; + +// 监控指标单位 字典项 +export const MetricsUnitKey = 'metricsUnit'; + +// 规则开关 字典项 +export const RuleSwitchKey = 'monitorAlarmSwitch'; + +// 告警条件 字典项 +export const TriggerConditionKey = 'alarmTriggerCondition'; + +// 告警等级 字典项 +export const LevelKey = 'alarmLevel'; + +// 加载的字典值 +export const dictKeys = [MetricsUnitKey, MeasurementKey, TriggerConditionKey, RuleSwitchKey, LevelKey]; diff --git a/orion-visor-ui/src/views/monitor/alarm-rule/types/form.rules.ts b/orion-visor-ui/src/views/monitor/alarm-rule/types/form.rules.ts new file mode 100644 index 00000000..0207e96a --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-rule/types/form.rules.ts @@ -0,0 +1,45 @@ +import type { FieldRule } from '@arco-design/web-vue'; + +const rules = { + policyId: [{ + required: true, + message: '请输入策略id' + }], + metricsId: [{ + required: true, + message: '请输入指标id' + }], + ruleSwitch: [{ + required: true, + message: '请输入规则开关' + }], + level: [{ + required: true, + message: '请输入告警级别' + }], + triggerCondition: [{ + required: true, + message: '请输入告警条件' + }, { + maxLength: 8, + message: '告警条件长度不能大于8位' + }], + threshold: [{ + required: true, + message: '请输入触发阈值' + }], + silencePeriod: [{ + required: true, + message: '请输入静默时间' + }], + consecutiveCount: [{ + required: true, + message: '请输入持续数据点' + }], + description: [{ + maxLength: 255, + message: '规则描述长度不能大于255位' + }], +} as Record; + +export default rules; diff --git a/orion-visor-ui/src/views/monitor/alarm-rule/types/table.columns.ts b/orion-visor-ui/src/views/monitor/alarm-rule/types/table.columns.ts new file mode 100644 index 00000000..af61e2f6 --- /dev/null +++ b/orion-visor-ui/src/views/monitor/alarm-rule/types/table.columns.ts @@ -0,0 +1,107 @@ +import type { TableColumnData } from '@arco-design/web-vue'; +import { dateFormat } from '@/utils'; + +const columns = [ + { + title: 'id', + dataIndex: 'id', + slotName: 'id', + width: 68, + align: 'left', + fixed: 'left', + default: true, + }, { + title: '告警条件', + dataIndex: 'triggerCondition', + slotName: 'triggerCondition', + align: 'left', + minWidth: 348, + ellipsis: true, + tooltip: true, + default: true, + }, { + title: '指标标签', + dataIndex: 'tags', + slotName: 'tags', + align: 'left', + width: 168, + ellipsis: true, + tooltip: true, + default: true, + }, { + title: '告警级别', + dataIndex: 'level', + slotName: 'level', + align: 'left', + width: 120, + default: true, + }, { + title: '持续数据点', + dataIndex: 'consecutiveCount', + slotName: 'consecutiveCount', + align: 'left', + width: 108, + default: true, + }, { + title: '静默时间', + dataIndex: 'silencePeriod', + slotName: 'silencePeriod', + align: 'left', + width: 108, + default: true, + }, { + title: '规则开关', + dataIndex: 'ruleSwitch', + slotName: 'ruleSwitch', + align: 'left', + width: 118, + default: true, + }, { + title: '规则描述', + dataIndex: 'description', + slotName: 'description', + align: 'left', + minWidth: 128, + ellipsis: true, + tooltip: true, + default: true, + }, { + title: '创建时间', + dataIndex: 'createTime', + slotName: 'createTime', + align: 'center', + width: 180, + render: ({ record }) => { + return dateFormat(new Date(record.createTime)); + }, + }, { + title: '修改时间', + dataIndex: 'updateTime', + slotName: 'updateTime', + align: 'center', + width: 180, + render: ({ record }) => { + return dateFormat(new Date(record.updateTime)); + }, + default: true, + }, { + title: '创建人', + width: 148, + dataIndex: 'creator', + slotName: 'creator', + }, { + title: '修改人', + width: 148, + dataIndex: 'updater', + slotName: 'updater', + }, { + title: '操作', + slotName: 'handle', + width: 168, + align: 'center', + fixed: 'right', + default: true, + }, +] as TableColumnData[]; + +export default columns; diff --git a/orion-visor-ui/src/views/monitor/metrics/components/metrics-form-modal.vue b/orion-visor-ui/src/views/monitor/metrics/components/metrics-form-modal.vue index cf0061ee..4df5ae30 100644 --- a/orion-visor-ui/src/views/monitor/metrics/components/metrics-form-modal.vue +++ b/orion-visor-ui/src/views/monitor/metrics/components/metrics-form-modal.vue @@ -28,6 +28,7 @@ @@ -71,6 +72,7 @@ + + + + diff --git a/orion-visor-ui/src/views/monitor/monitor-detail/compoments/detail-header.vue b/orion-visor-ui/src/views/monitor/monitor-detail/compoments/detail-header.vue index 47195094..0f2e0236 100644 --- a/orion-visor-ui/src/views/monitor/monitor-detail/compoments/detail-header.vue +++ b/orion-visor-ui/src/views/monitor/monitor-detail/compoments/detail-header.vue @@ -7,7 +7,9 @@ + +
+ +
+ 更新时间: {{ dateFormat(new Date(overrideTimestamp)) }} +
-
+
(); const emits = defineEmits(['reloadChart']); const activeKey = defineModel('activeKey', { type: String }); @@ -199,7 +207,7 @@ .header-right { padding-right: 16px; - .chart-handle { + .handle-wrapper { display: flex; } } diff --git a/orion-visor-ui/src/views/monitor/monitor-detail/compoments/host-overview-tab.vue b/orion-visor-ui/src/views/monitor/monitor-detail/compoments/host-overview-tab.vue new file mode 100644 index 00000000..567c5239 --- /dev/null +++ b/orion-visor-ui/src/views/monitor/monitor-detail/compoments/host-overview-tab.vue @@ -0,0 +1,522 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/monitor/monitor-detail/compoments/metrics-chart-tab.vue b/orion-visor-ui/src/views/monitor/monitor-detail/compoments/metrics-chart-tab.vue index bcc8608f..cfe480f1 100644 --- a/orion-visor-ui/src/views/monitor/monitor-detail/compoments/metrics-chart-tab.vue +++ b/orion-visor-ui/src/views/monitor/monitor-detail/compoments/metrics-chart-tab.vue @@ -18,7 +18,7 @@ diff --git a/orion-visor-ui/src/views/monitor/monitor-detail/types/const.ts b/orion-visor-ui/src/views/monitor/monitor-detail/types/const.ts index b83e96af..2a2a68c5 100644 --- a/orion-visor-ui/src/views/monitor/monitor-detail/types/const.ts +++ b/orion-visor-ui/src/views/monitor/monitor-detail/types/const.ts @@ -1,4 +1,5 @@ import type { WindowUnit, MetricUnitType, MetricUnitFormatOptions } from '@/utils/metrics'; +import { dictKeys as alarmDictKeys } from '@/views/monitor/alarm-event/types/const'; // 图表组件配置 export interface MetricsChartProps { @@ -26,9 +27,13 @@ export interface MetricsChartOption { // tab export const TabKeys = { - CHART: 'chart' + OVERVIEW: 'overview', + CHART: 'chart', + ALARM: 'alarm', }; +export const TableName = 'host_alarm_event'; + // 探针在线状态 字典项 export const OnlineStatusKey = 'agentOnlineStatus'; @@ -42,4 +47,4 @@ export const ChartRangeKey = 'metricsChartRange'; export const MetricsAggregateKey = 'metricsAggregate'; // 加载的字典值 -export const dictKeys = [AlarmSwitchKey, OnlineStatusKey, ChartRangeKey, MetricsAggregateKey]; +export const dictKeys = [AlarmSwitchKey, OnlineStatusKey, ChartRangeKey, MetricsAggregateKey, ...alarmDictKeys]; diff --git a/orion-visor-ui/src/views/monitor/monitor-host/components/monitor-host-card-list.vue b/orion-visor-ui/src/views/monitor/monitor-host/components/monitor-host-card-list.vue index 7fd152a5..d2f9bb47 100644 --- a/orion-visor-ui/src/views/monitor/monitor-host/components/monitor-host-card-list.vue +++ b/orion-visor-ui/src/views/monitor/monitor-host/components/monitor-host-card-list.vue @@ -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" @@ -101,7 +101,7 @@