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..0b369748 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/* 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/docs/assets/screenshot/alarm-webhook.jpg b/docs/assets/screenshot/alarm-webhook.jpg deleted file mode 100644 index 97aa9b42..00000000 Binary files a/docs/assets/screenshot/alarm-webhook.jpg and /dev/null differ 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 d34af72c..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 @@ -67,7 +67,7 @@ public interface AutoConfigureOrderConst { int FRAMEWORK_JOB_ASYNC = Integer.MIN_VALUE + 2620; - int FRAMEWORK_PUSH = Integer.MIN_VALUE + 2700; + int FRAMEWORK_BIZ_PUSH = Integer.MIN_VALUE + 2700; int FRAMEWORK_BIZ_OPERATOR_LOG = Integer.MIN_VALUE + 7000; 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 index 92ac3c33..c85d43e5 100644 --- 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 @@ -1,3 +1,25 @@ +/* + * 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; @@ -37,7 +59,4 @@ public class PushUser implements Serializable { @Schema(description = "手机号") private String mobile; - @Schema(description = "邮箱") - private String email; - } diff --git a/orion-visor-dependencies/pom.xml b/orion-visor-dependencies/pom.xml index 960fb32e..af86d80a 100644 --- a/orion-visor-dependencies/pom.xml +++ b/orion-visor-dependencies/pom.xml @@ -14,7 +14,7 @@ https://github.com/dromara/orion-visor - 2.5.0 + 2.5.1 2.7.17 2.7.15 1.5.0 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 @@