diff --git a/ballcat-common/ballcat-common-conf/pom.xml b/ballcat-common/ballcat-common-conf/pom.xml index 81aedf03..3b9a0c01 100644 --- a/ballcat-common/ballcat-common-conf/pom.xml +++ b/ballcat-common/ballcat-common-conf/pom.xml @@ -1,38 +1,53 @@ - - ballcat-common - com.hccake - ${revision} - - 4.0.0 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + ballcat-common + com.hccake + ${revision} + + 4.0.0 - ballcat-common-conf + ballcat-common-conf - + com.hccake ballcat-common-core - - - com.baomidou - mybatis-plus-extension - - - javax.validation - validation-api - - - org.springframework - spring-webmvc - + + com.hccake + ballcat-extend-ding-talk + true + + + com.hccake + ballcat-spring-boot-starter-mail + true + + + + com.baomidou + mybatis-plus-extension + + + javax.validation + validation-api + + + org.springframework + spring-webmvc + org.springframework.boot spring-boot-autoconfigure + + org.springframework.boot + spring-boot-configuration-processor + true + diff --git a/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/config/ExceptionHandleConfig.java b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/config/ExceptionHandleConfig.java new file mode 100644 index 00000000..7edf12cd --- /dev/null +++ b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/config/ExceptionHandleConfig.java @@ -0,0 +1,39 @@ +package com.hccake.ballcat.common.conf.config; + +import com.hccake.ballcat.common.conf.exception.ExceptionHandleTypeEnum; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * @author lingting 2020/6/12 0:15 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "ballcat.exception") +public class ExceptionHandleConfig { + /** + * 处理类型 + */ + private ExceptionHandleTypeEnum type = ExceptionHandleTypeEnum.NONE; + /** + * 通知间隔时间 单位秒 默认 5分钟 + */ + private long time = TimeUnit.MINUTES.toSeconds(5); + /** + * 消息阈值 即便间隔时间没有到达设定的时间, 但是异常发生的数量达到阈值 则立即发送消息 + */ + private long max = 5; + /** + * 堆栈转string 的长度 + */ + private int length = 3000; + /** + * 接收异常通知邮件的邮箱 + */ + private Set receiveEmails = new HashSet<>(); +} diff --git a/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/ExceptionHandleAutoConfiguration.java b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/ExceptionHandleAutoConfiguration.java index c18d35cd..4fee9149 100644 --- a/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/ExceptionHandleAutoConfiguration.java +++ b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/ExceptionHandleAutoConfiguration.java @@ -1,8 +1,18 @@ package com.hccake.ballcat.common.conf.exception; -import com.hccake.ballcat.common.core.exception.handler.DefaultGlobalExceptionHandler; +import com.hccake.ballcat.common.conf.config.ExceptionHandleConfig; +import com.hccake.ballcat.common.conf.exception.handler.DefaultGlobalExceptionHandler; +import com.hccake.ballcat.common.conf.exception.handler.DingTalkGlobalExceptionHandler; +import com.hccake.ballcat.common.conf.exception.handler.MailGlobalExceptionHandler; import com.hccake.ballcat.common.core.exception.handler.GlobalExceptionHandler; +import com.hccake.ballcat.common.mail.service.MailSender; +import com.hccake.extend.ding.talk.DingTalkSender; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; @@ -13,29 +23,60 @@ import org.springframework.scheduling.annotation.EnableAsync; * @date 2019/10/15 18:20 */ @EnableAsync -@Configuration +@RequiredArgsConstructor +@Configuration(proxyBeanMethods = false) public class ExceptionHandleAutoConfiguration { + @Value("${spring.application.name}") + private String applicationName; - /** - * 默认的日志处理器 - * @return DefaultExceptionHandler - */ - @Bean - @ConditionalOnMissingBean(GlobalExceptionHandler.class) - public GlobalExceptionHandler globalExceptionHandler(){ - return new DefaultGlobalExceptionHandler(); - } + /** + * 默认的日志处理器 + * + * @return DefaultExceptionHandler + */ + @Bean + @ConditionalOnMissingBean(GlobalExceptionHandler.class) + @ConditionalOnProperty(prefix = "ballcat.exception", name = "type", havingValue = "NONE") + public GlobalExceptionHandler defaultGlobalExceptionHandler() { + return new DefaultGlobalExceptionHandler(); + } + + /** + * 钉钉消息通知的日志处理器 + * + * @author lingting 2020-06-12 00:32:40 + */ + @Bean + @ConditionalOnMissingBean(GlobalExceptionHandler.class) + @ConditionalOnProperty(prefix = "ballcat.exception", name = "type", havingValue = "DING_TALK") + public GlobalExceptionHandler dingTalkGlobalExceptionHandler(ExceptionHandleConfig exceptionHandleConfig, ApplicationContext context) { + return new DingTalkGlobalExceptionHandler(exceptionHandleConfig, context.getBean(DingTalkSender.class), applicationName); + } + + /** + * 邮件消息通知的日志处理器 + * + * @author lingting 2020-06-12 00:32:40 + */ + @Bean + @ConditionalOnClass(MailSender.class) + @ConditionalOnMissingBean(GlobalExceptionHandler.class) + @ConditionalOnProperty(prefix = "ballcat.exception", name = "type", havingValue = "MAIL") + public GlobalExceptionHandler mailGlobalExceptionHandler(ExceptionHandleConfig exceptionHandleConfig, ApplicationContext context) { + return new MailGlobalExceptionHandler(exceptionHandleConfig, context.getBean(MailSender.class), applicationName); + } - /** - * 默认的日志处理器 - * @return DefaultExceptionHandler - */ - @Bean - @ConditionalOnMissingBean(GlobalExceptionHandlerResolver.class) - public GlobalExceptionHandlerResolver globalExceptionHandlerResolver(GlobalExceptionHandler globalExceptionHandler){ - return new GlobalExceptionHandlerResolver(globalExceptionHandler); - } + /** + * 默认的日志处理器 + * + * @return DefaultExceptionHandler + */ + @Bean + @ConditionalOnMissingBean(GlobalExceptionHandlerResolver.class) + public GlobalExceptionHandlerResolver globalExceptionHandlerResolver(GlobalExceptionHandler globalExceptionHandler) { + return new GlobalExceptionHandlerResolver(globalExceptionHandler); + } } diff --git a/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/ExceptionHandleTypeEnum.java b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/ExceptionHandleTypeEnum.java new file mode 100644 index 00000000..24afb968 --- /dev/null +++ b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/ExceptionHandleTypeEnum.java @@ -0,0 +1,22 @@ +package com.hccake.ballcat.common.conf.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 异常处理类型 + * + * @author lingting 2020/6/12 0:18 + */ +@Getter +@AllArgsConstructor +public enum ExceptionHandleTypeEnum { + /** + * 异常处理通知类型 说明 + */ + NONE("不通知"), + DING_TALK("通过钉钉通知"), + MAIL("邮件通知"), + ; + private final String text; +} diff --git a/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/GlobalExceptionHandlerResolver.java b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/GlobalExceptionHandlerResolver.java index a968c950..c4786872 100644 --- a/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/GlobalExceptionHandlerResolver.java +++ b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/GlobalExceptionHandlerResolver.java @@ -1,7 +1,7 @@ package com.hccake.ballcat.common.conf.exception; -import com.hccake.ballcat.common.core.exception.handler.GlobalExceptionHandler; import com.hccake.ballcat.common.core.exception.BusinessException; +import com.hccake.ballcat.common.core.exception.handler.GlobalExceptionHandler; import com.hccake.ballcat.common.core.result.R; import com.hccake.ballcat.common.core.result.SystemResultCode; import lombok.RequiredArgsConstructor; @@ -21,6 +21,7 @@ import java.util.List; /** * 全局异常处理 + * * @author Hccake */ @Slf4j @@ -31,12 +32,13 @@ public class GlobalExceptionHandlerResolver { /** * 全局异常捕获 + * * @param e the e * @return R */ @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - public R handleGlobalException(Exception e) { + public R handleGlobalException(Exception e) { log.error("全局异常信息 ex={}", e.getMessage(), e); globalExceptionHandler.handle(e); return R.failed(SystemResultCode.SERVER_ERROR, e.getLocalizedMessage()); @@ -47,12 +49,13 @@ public class GlobalExceptionHandlerResolver { * 自定义业务异常捕获 * 业务异常响应码推荐使用200 * 用 result 结构中的code做为业务错误码标识 + * * @param e the e * @return R */ @ExceptionHandler(BusinessException.class) @ResponseStatus(HttpStatus.OK) - public R handleBallCatException(BusinessException e) { + public R handleBallCatException(BusinessException e) { log.error("自定义异常信息 ex={}", e.getMessage(), e); globalExceptionHandler.handle(e); return R.failed(e.getCode(), e.getMsg()); @@ -67,7 +70,7 @@ public class GlobalExceptionHandlerResolver { */ @ExceptionHandler(AccessDeniedException.class) @ResponseStatus(HttpStatus.FORBIDDEN) - public R handleAccessDeniedException(AccessDeniedException e) { + public R handleAccessDeniedException(AccessDeniedException e) { String msg = SpringSecurityMessageSource.getAccessor() .getMessage("AbstractAccessDecisionManager.accessDenied" , e.getMessage()); @@ -84,13 +87,13 @@ public class GlobalExceptionHandlerResolver { */ @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class}) @ResponseStatus(HttpStatus.BAD_REQUEST) - public R handleBodyValidException(Exception exception) { + public R handleBodyValidException(Exception exception) { List fieldErrors; - if(exception instanceof BindException){ - BindException bindException = (BindException)exception; + if (exception instanceof BindException) { + BindException bindException = (BindException) exception; fieldErrors = bindException.getBindingResult().getFieldErrors(); - }else{ - MethodArgumentNotValidException e = (MethodArgumentNotValidException)exception; + } else { + MethodArgumentNotValidException e = (MethodArgumentNotValidException) exception; fieldErrors = e.getBindingResult().getFieldErrors(); } @@ -105,17 +108,17 @@ public class GlobalExceptionHandlerResolver { /** * 单体参数校验异常 * validation Exception + * * @param e the e * @return R */ @ExceptionHandler(ValidationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - public R handleValidationException(Exception e) { + public R handleValidationException(Exception e) { log.error("参数绑定异常 ex={}", e.getMessage(), e); globalExceptionHandler.handle(e); return R.failed(SystemResultCode.BAD_REQUEST, e.getLocalizedMessage()); } - } diff --git a/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/domain/ExceptionMessage.java b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/domain/ExceptionMessage.java new file mode 100644 index 00000000..68e5aa0e --- /dev/null +++ b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/domain/ExceptionMessage.java @@ -0,0 +1,54 @@ +package com.hccake.ballcat.common.conf.exception.domain; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +/** + * 异常通知消息 + * + * @author lingting 2020/6/12 16:07 + */ +@Getter +@Setter +@Accessors(chain = true) +public class ExceptionMessage { + /** + * 消息 + */ + private String message; + /** + * 数量 + */ + private int number; + /** + * 堆栈 + */ + private String stack; + /** + * 最新的触发时间 + */ + private String time; + /** + * 机器地址 + */ + private String mac; + /** + * 线程id + */ + private long threadId; + /** + * 服务名 + */ + private String applicationName; + + @Override + public String toString() { + return "服务名称:" + applicationName + + "\n机器地址:" + mac + + "\n触发时间:" + time + + "\n线程id:" + threadId + + "\n数量:" + number + + "\n堆栈:" + stack; + } +} diff --git a/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/domain/ExceptionNoticeResponse.java b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/domain/ExceptionNoticeResponse.java new file mode 100644 index 00000000..7d36511e --- /dev/null +++ b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/domain/ExceptionNoticeResponse.java @@ -0,0 +1,24 @@ +package com.hccake.ballcat.common.conf.exception.domain; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +/** + * 异常消息通知响应 + * + * @author lingting 2020/6/12 19:07 + */ +@Getter +@Setter +@Accessors(chain = true) +public class ExceptionNoticeResponse { + /** + * 是否成功 + */ + private boolean success; + /** + * 错误信息 + */ + private String errMsg; +} diff --git a/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/handler/AbstractNoticeGlobalExceptionHandler.java b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/handler/AbstractNoticeGlobalExceptionHandler.java new file mode 100644 index 00000000..8f8c824f --- /dev/null +++ b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/handler/AbstractNoticeGlobalExceptionHandler.java @@ -0,0 +1,119 @@ +package com.hccake.ballcat.common.conf.exception.handler; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.TimeInterval; +import cn.hutool.core.exceptions.ExceptionUtil; +import com.hccake.ballcat.common.conf.config.ExceptionHandleConfig; +import com.hccake.ballcat.common.conf.exception.domain.ExceptionMessage; +import com.hccake.ballcat.common.conf.exception.domain.ExceptionNoticeResponse; +import com.hccake.ballcat.common.core.exception.handler.GlobalExceptionHandler; +import lombok.extern.slf4j.Slf4j; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 消息通知顶层类 + * + * @author lingting 2020/6/12 0:35 + */ +@Slf4j +public abstract class AbstractNoticeGlobalExceptionHandler extends Thread implements GlobalExceptionHandler { + protected final ExceptionHandleConfig config; + /** + * 通知消息存放 e.message 堆栈信息 + */ + private Map messages = new ConcurrentHashMap<>(10); + /** + * 异常发生数 + */ + private long number = 0; + /** + * 用来当做锁 + */ + private final String lock = ""; + /** + * 本地物理地址 + */ + private String mac; + private final String applicationName; + + public AbstractNoticeGlobalExceptionHandler(ExceptionHandleConfig config, String applicationName) { + this.config = config; + this.applicationName = applicationName; + try { + byte[] mac = NetworkInterface.getByInetAddress(InetAddress.getLocalHost()).getHardwareAddress(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < mac.length; i++) { + sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : "")); + } + this.mac = sb.toString(); + } catch (Exception e) { + mac = "获取失败!"; + } + + this.start(); + } + + @Override + public void handle(Throwable e) { + synchronized (lock) { + number++; + ExceptionMessage message = messages.get(e.getMessage()); + + if (message == null) { + message = new ExceptionMessage().setNumber(0).setMac(mac).setApplicationName(applicationName); + } + + message.setNumber(message.getNumber() + 1) + .setStack(ExceptionUtil.stacktraceToString(e, config.getLength()).replaceAll("\\r", "")) + .setTime(DateUtil.now()) + .setThreadId(Thread.currentThread().getId()); + messages.put(e.getMessage(), message); + } + } + + @Override + public void run() { + this.setName("exception-notice-thread-" + config.getType().name()); + log.debug("异常消息通知线程启动!"); + TimeInterval interval = new TimeInterval(); + while (true) { + try { + if (interval.intervalSecond() >= config.getTime() || number >= config.getMax()) { + if (messages.size() == 0) { + interval.restart(); + continue; + } + + Map sendMessages; + synchronized (lock) { + sendMessages = messages; + messages = new ConcurrentHashMap<>(10); + number = 0; + interval.restart(); + } + sendMessages.forEach((k, v) -> { + ExceptionNoticeResponse response = send(v); + if (!response.isSuccess()) { + log.error("消息通知发送失败! msg: {}", response.getErrMsg()); + } + }); + } + } catch (Exception e) { + log.error("消息通知异常!", e); + } + } + } + + /** + * 发送通知 + * + * @param sendMessage 发送的消息 + * @return 返回消息发送状态,如果发送失败需要设置失败信息 + * @author lingting 2020-06-12 00:37:23 + */ + public abstract ExceptionNoticeResponse send(ExceptionMessage sendMessage); +} diff --git a/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/handler/DefaultGlobalExceptionHandler.java b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/handler/DefaultGlobalExceptionHandler.java new file mode 100644 index 00000000..43c1f80f --- /dev/null +++ b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/handler/DefaultGlobalExceptionHandler.java @@ -0,0 +1,21 @@ +package com.hccake.ballcat.common.conf.exception.handler; + +import com.hccake.ballcat.common.core.exception.handler.GlobalExceptionHandler; + +/** + * @author Hccake + * @version 1.0 + * @date 2019/10/18 17:06 + * 默认的异常日志处理类 + */ +public class DefaultGlobalExceptionHandler implements GlobalExceptionHandler { + /** + * 在此处理日志 + * 默认什么都不处理 + * + * @param throwable 异常信息 + */ + @Override + public void handle(Throwable throwable) { + } +} diff --git a/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/handler/DingTalkGlobalExceptionHandler.java b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/handler/DingTalkGlobalExceptionHandler.java new file mode 100644 index 00000000..f4e020c6 --- /dev/null +++ b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/handler/DingTalkGlobalExceptionHandler.java @@ -0,0 +1,32 @@ +package com.hccake.ballcat.common.conf.exception.handler; + +import com.hccake.ballcat.common.conf.config.ExceptionHandleConfig; +import com.hccake.ballcat.common.conf.exception.domain.ExceptionMessage; +import com.hccake.ballcat.common.conf.exception.domain.ExceptionNoticeResponse; +import com.hccake.extend.ding.talk.DingTalkResponse; +import com.hccake.extend.ding.talk.DingTalkSender; +import com.hccake.extend.ding.talk.message.DingTalkTextMessage; +import lombok.extern.slf4j.Slf4j; + +/** + * 钉钉消息通知 + * + * @author lingting 2020/6/12 0:25 + */ +@Slf4j +public class DingTalkGlobalExceptionHandler extends AbstractNoticeGlobalExceptionHandler { + private final DingTalkSender sender; + + public DingTalkGlobalExceptionHandler(ExceptionHandleConfig config, DingTalkSender sender, String applicationName) { + super(config, applicationName); + this.sender = sender; + } + + @Override + public ExceptionNoticeResponse send(ExceptionMessage sendMessage) { + DingTalkResponse response = sender.sendMessage(new DingTalkTextMessage().setContent(sendMessage.toString())); + return new ExceptionNoticeResponse() + .setErrMsg(response.getResponse()) + .setSuccess(response.isSuccess()); + } +} diff --git a/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/handler/MailGlobalExceptionHandler.java b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/handler/MailGlobalExceptionHandler.java new file mode 100644 index 00000000..342f46f7 --- /dev/null +++ b/ballcat-common/ballcat-common-conf/src/main/java/com/hccake/ballcat/common/conf/exception/handler/MailGlobalExceptionHandler.java @@ -0,0 +1,34 @@ +package com.hccake.ballcat.common.conf.exception.handler; + +import com.hccake.ballcat.common.conf.config.ExceptionHandleConfig; +import com.hccake.ballcat.common.conf.exception.domain.ExceptionMessage; +import com.hccake.ballcat.common.conf.exception.domain.ExceptionNoticeResponse; +import com.hccake.ballcat.common.mail.dto.MailDTO; +import com.hccake.ballcat.common.mail.service.MailSender; +import lombok.extern.slf4j.Slf4j; + +/** + * 钉钉消息通知 + * + * @author lingting 2020/6/12 0:25 + */ +@Slf4j +public class MailGlobalExceptionHandler extends AbstractNoticeGlobalExceptionHandler { + private final MailSender sender; + + public MailGlobalExceptionHandler(ExceptionHandleConfig config, MailSender sender, String applicationName) { + super(config, applicationName); + this.sender = sender; + } + + @Override + public ExceptionNoticeResponse send(ExceptionMessage sendMessage) { + MailDTO mail = new MailDTO(); + mail.setTo(String.join(MailDTO.MAIL_DELIMITER, config.getReceiveEmails())); + mail.setSubject("异常警告"); + mail.setContent(sendMessage.toString()); + sender.sendMail(mail); + // 邮箱发送失败会抛出异常,否则视作发送成功 + return new ExceptionNoticeResponse().setSuccess(mail.getSuccess()).setErrMsg(mail.getErrorMsg()); + } +} diff --git a/ballcat-common/ballcat-common-conf/src/main/resources/META-INF/spring.factories b/ballcat-common/ballcat-common-conf/src/main/resources/META-INF/spring.factories index 8557c25a..dcb5d1f4 100644 --- a/ballcat-common/ballcat-common-conf/src/main/resources/META-INF/spring.factories +++ b/ballcat-common/ballcat-common-conf/src/main/resources/META-INF/spring.factories @@ -3,5 +3,6 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.hccake.ballcat.common.conf.config.JacksonConfig,\ com.hccake.ballcat.common.conf.exception.ExceptionHandleAutoConfiguration,\ com.hccake.ballcat.common.conf.mybatis.MybatisPlusConfig,\ - com.hccake.ballcat.common.conf.web.WebMvcConfig + com.hccake.ballcat.common.conf.web.WebMvcConfig,\ + com.hccake.ballcat.common.conf.config.ExceptionHandleConfig diff --git a/ballcat-common/ballcat-common-core/src/main/java/com/hccake/ballcat/common/core/exception/handler/DefaultGlobalExceptionHandler.java b/ballcat-common/ballcat-common-core/src/main/java/com/hccake/ballcat/common/core/exception/handler/DefaultGlobalExceptionHandler.java deleted file mode 100644 index de6c2cb8..00000000 --- a/ballcat-common/ballcat-common-core/src/main/java/com/hccake/ballcat/common/core/exception/handler/DefaultGlobalExceptionHandler.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.hccake.ballcat.common.core.exception.handler; - -/** - * @author Hccake - * @version 1.0 - * @date 2019/10/18 17:06 - * 默认的异常日志处理类 - */ -public class DefaultGlobalExceptionHandler implements GlobalExceptionHandler { - /** - * 在此处理日志 - * 默认什么都不处理 - * @param throwable 异常信息 - */ - @Override - public void handle(Throwable throwable) {} -} diff --git a/ballcat-common/ballcat-common-core/src/main/java/com/hccake/ballcat/common/core/markdown/MarkdownBuilder.java b/ballcat-common/ballcat-common-core/src/main/java/com/hccake/ballcat/common/core/markdown/MarkdownBuilder.java index 052b7daa..2dedcaa1 100644 --- a/ballcat-common/ballcat-common-core/src/main/java/com/hccake/ballcat/common/core/markdown/MarkdownBuilder.java +++ b/ballcat-common/ballcat-common-core/src/main/java/com/hccake/ballcat/common/core/markdown/MarkdownBuilder.java @@ -29,6 +29,10 @@ public class MarkdownBuilder { */ private StringBuilder lineTextBuilder; + public MarkdownBuilder() { + this.lineTextBuilder = new StringBuilder(); + } + /** * 添加自定义内容 * @@ -153,14 +157,21 @@ public class MarkdownBuilder { * @author lingting 2020-06-10 22:58:04 */ public MarkdownBuilder quote(String content) { - forceLineBreak(); - lineTextBuilder.append(QUOTE_PREFIX).append(content); - // 下面得换行两次 - forceLineBreak(); - forceLineBreak(); + lineBreak(); + this.content.add(QUOTE_PREFIX + content); return this; } + /** + * 添加引用后 强制换行 + * + * @author lingting 2020-06-12 15:50:29 + */ + public MarkdownBuilder quoteLineBreak(String content) { + quote(content); + return forceLineBreak(); + } + /** * 强制换行 * @@ -191,7 +202,8 @@ public class MarkdownBuilder { * @author lingting 2020-06-10 22:55:39 */ private MarkdownBuilder title(int i, String content) { - lineTextBuilder = new StringBuilder(); + // 如果当前操作行已有字符,需要换行 + lineBreak(); for (int j = 0; j < i; j++) { lineTextBuilder.append(TITLE_PREFIX); } @@ -231,12 +243,9 @@ public class MarkdownBuilder { * @author lingting 2020-06-11 22:55:40 */ public String build() { - if (lineTextBuilder.length() != 0) { - // 最后一个操作的 如果不为空,也需要添加进入list - content.add(lineTextBuilder.toString()); - } + lineBreak(); StringBuilder res = new StringBuilder(); - content.forEach(content -> res.append(content).append("\\n")); + content.forEach(content -> res.append(content).append(" \n")); return res.toString(); } } diff --git a/ballcat-common/ballcat-common-core/src/main/java/com/hccake/ballcat/common/core/util/ApplicationContextUtil.java b/ballcat-common/ballcat-common-core/src/main/java/com/hccake/ballcat/common/core/util/ApplicationContextUtil.java new file mode 100644 index 00000000..c6f10839 --- /dev/null +++ b/ballcat-common/ballcat-common-core/src/main/java/com/hccake/ballcat/common/core/util/ApplicationContextUtil.java @@ -0,0 +1,34 @@ +package com.hccake.ballcat.common.core.util; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +/** + * @author lingting 2020/6/12 16:36 + */ +@Component +public class ApplicationContextUtil implements ApplicationContextAware { + private static ApplicationContext context; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + context = applicationContext; + } + + + /** + * 获取环境 + * + * @author lingting 2020-06-12 16:38:56 + */ + public Environment getEnvironment() { + return context.getEnvironment(); + } + + public ApplicationContext getContext() { + return context; + } +} diff --git a/ballcat-common/ballcat-common-core/src/main/resources/META-INF/spring.factories b/ballcat-common/ballcat-common-core/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..6994129f --- /dev/null +++ b/ballcat-common/ballcat-common-core/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.hccake.ballcat.common.core.util.ApplicationContextUtil \ No newline at end of file diff --git a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/DingTalkParams.java b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/DingTalkParams.java new file mode 100644 index 00000000..5edb84b3 --- /dev/null +++ b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/DingTalkParams.java @@ -0,0 +1,78 @@ +package com.hccake.extend.ding.talk; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hccake.extend.ding.talk.message.DingTalkActionCardMessage; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.SneakyThrows; +import lombok.experimental.Accessors; + +import java.util.List; +import java.util.Set; + +/** + * @author lingting 2020/6/12 19:35 + */ +@Getter +@Setter +@Accessors(chain = true) +public class DingTalkParams { + @JsonProperty("msgtype") + private String type; + private At at; + private ActionCard actionCard; + private Link link; + private Markdown markdown; + private Text text; + + @Override + @SneakyThrows + public String toString() { + return new ObjectMapper().writeValueAsString(this); + } + + @Data + @Accessors(chain = true) + public static class Text { + private String content; + } + + @Data + @Accessors(chain = true) + public static class Markdown { + private String title; + private String text; + } + + @Data + @Accessors(chain = true) + public static class Link { + private String text; + private String title; + private String picUrl; + private String messageUrl; + } + + @Data + @Accessors(chain = true) + public static class ActionCard { + private String title; + private String text; + private String btnOrientation; + private String singleTitle; + @JsonProperty("singleURL") + private String singleUrl; + @JsonProperty("btns") + private List buttons; + } + + @Data + @Accessors(chain = true) + public static class At { + @JsonProperty("isAtAll") + private boolean atAll; + private Set atMobiles; + } +} diff --git a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/DingTalkResponse.java b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/DingTalkResponse.java index 754f2781..03a59a13 100644 --- a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/DingTalkResponse.java +++ b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/DingTalkResponse.java @@ -13,6 +13,7 @@ import lombok.Setter; @Getter @Setter public class DingTalkResponse { + public static final String SUCCESS_CODE = "0"; private String errCode; /** * 值为ok表示无异常 @@ -22,6 +23,10 @@ public class DingTalkResponse { * 钉钉返回信息 */ private String response; + /** + * 是否发送成功 + */ + private boolean success; public static DingTalkResponse getInstance(String res) { JSONObject json = JSONUtil.parseObj(res); @@ -29,6 +34,7 @@ public class DingTalkResponse { response.errCode = json.getStr("errcode"); response.errMsg = json.getStr("errmsg"); response.response = res; + response.success = SUCCESS_CODE.equalsIgnoreCase(response.errCode); return response; } diff --git a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/DingTalkSender.java b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/DingTalkSender.java index 61a02459..38894663 100644 --- a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/DingTalkSender.java +++ b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/DingTalkSender.java @@ -53,7 +53,7 @@ public class DingTalkSender { * @author lingting 2020-06-11 00:09:23 */ public DingTalkResponse sendNormalMessage(DingTalkMessage message) { - return DingTalkResponse.getInstance(HttpUtil.post(url, message.toString())); + return DingTalkResponse.getInstance(HttpUtil.post(url, message.generate())); } /** @@ -63,7 +63,7 @@ public class DingTalkSender { */ @SneakyThrows public DingTalkResponse sendSecretMessage(DingTalkMessage message) { - return DingTalkResponse.getInstance(HttpUtil.post(secret(), message.toString())); + return DingTalkResponse.getInstance(HttpUtil.post(secret(), message.generate())); } /** diff --git a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/AbstractDingTalkMessage.java b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/AbstractDingTalkMessage.java index 7d02d3df..c2ab6eab 100644 --- a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/AbstractDingTalkMessage.java +++ b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/AbstractDingTalkMessage.java @@ -1,9 +1,7 @@ package com.hccake.extend.ding.talk.message; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; +import com.hccake.extend.ding.talk.DingTalkParams; import com.hccake.extend.ding.talk.enums.MessageTypeEnum; -import lombok.SneakyThrows; import java.util.HashSet; import java.util.Set; @@ -47,25 +45,21 @@ public abstract class AbstractDingTalkMessage implements DingTalkMessage { public abstract MessageTypeEnum getType(); /** - * 生成内容json + * 设置非公有属性 * - * @return 返回生成的json字符串 + * @param params 已设置完公有参数的参数类 + * @return 已设置完成的参数类 * @author lingting 2020-06-10 22:11:04 */ - public abstract JSONObject json(); + public abstract DingTalkParams put(DingTalkParams params); + @Override - @SneakyThrows - public String toString() { - JSONObject json = new JSONObject(); - json.put("msgtype", getType().getVal()); - json.putAll(json()); - json.put( - "at", - new JSONObject() - .put("isAtAll", atAll) - .put("atMobiles", JSONUtil.toJsonStr(atPhones)) + public String generate() { + DingTalkParams params = put(new DingTalkParams() + .setType(getType().getVal()) + .setAt(new DingTalkParams.At().setAtAll(atAll).setAtMobiles(atPhones)) ); - return json.toString(); + return params.toString(); } } diff --git a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkActionCardMessage.java b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkActionCardMessage.java index 9c41dbcc..c302a8f4 100644 --- a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkActionCardMessage.java +++ b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkActionCardMessage.java @@ -1,8 +1,7 @@ package com.hccake.extend.ding.talk.message; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; import com.hccake.ballcat.common.core.markdown.MarkdownBuilder; +import com.hccake.extend.ding.talk.DingTalkParams; import com.hccake.extend.ding.talk.enums.ActionBtnOrientationEnum; import com.hccake.extend.ding.talk.enums.MessageTypeEnum; import lombok.AllArgsConstructor; @@ -61,25 +60,19 @@ public class DingTalkActionCardMessage extends AbstractDingTalkMessage { } @Override - public JSONObject json() { - // 头 - String jsonStr = "{\"actionCard\":{" + - "\"title\":\"" + title + "\"," + - "\"text\":\"" + text.build() + "\"," + - "\"btnOrientation\":\"" + orientation.getVal() + "\","; + public DingTalkParams put(DingTalkParams params) { + DingTalkParams.ActionCard card = new DingTalkParams.ActionCard() + .setTitle(title) + .setText(text.build()) + .setBtnOrientation(orientation.getVal()); // 当 单按钮的 文本和链接都不为空时 if (buttons.size() == 0) { - jsonStr += "\"singleTitle\":\"" + singleTitle + "\"," + - "\"singleURL\":\"" + singleUrl + "\","; + card.setSingleTitle(singleTitle).setSingleUrl(singleUrl); } else { - // 否则使用自定义按钮组 - jsonStr += "\"btns\":" + JSONUtil.toJsonStr(buttons); + card.setButtons(buttons); } - - // 尾 - jsonStr += "}}"; - return JSONUtil.parseObj(jsonStr); + return params.setActionCard(card); } @Getter diff --git a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkLinkMessage.java b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkLinkMessage.java index b34add97..f4377055 100644 --- a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkLinkMessage.java +++ b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkLinkMessage.java @@ -1,7 +1,6 @@ package com.hccake.extend.ding.talk.message; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; +import com.hccake.extend.ding.talk.DingTalkParams; import com.hccake.extend.ding.talk.enums.MessageTypeEnum; import lombok.Getter; import lombok.Setter; @@ -37,7 +36,11 @@ public class DingTalkLinkMessage extends AbstractDingTalkMessage { } @Override - public JSONObject json() { - return JSONUtil.parseObj("{\"link\":{\"text\":\"" + text + "\",\"title\":\"" + title + "\",\"picUrl\":\"" + picUrl + "\",\"messageUrl\":\"" + messageUrl + "\"}}"); + public DingTalkParams put(DingTalkParams params) { + return params.setLink(new DingTalkParams.Link() + .setText(text) + .setTitle(title) + .setPicUrl(picUrl) + .setMessageUrl(messageUrl)); } } diff --git a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkMarkDownMessage.java b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkMarkDownMessage.java index 86c0b137..8682fede 100644 --- a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkMarkDownMessage.java +++ b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkMarkDownMessage.java @@ -1,8 +1,7 @@ package com.hccake.extend.ding.talk.message; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; import com.hccake.ballcat.common.core.markdown.MarkdownBuilder; +import com.hccake.extend.ding.talk.DingTalkParams; import com.hccake.extend.ding.talk.enums.MessageTypeEnum; import lombok.Getter; import lombok.Setter; @@ -30,7 +29,7 @@ public class DingTalkMarkDownMessage extends AbstractDingTalkMessage { } @Override - public JSONObject json() { - return JSONUtil.parseObj("{\"markdown\":{\"title\":\"" + title + "\",\"text\":\"" + text.build() + "\"}}"); + public DingTalkParams put(DingTalkParams params) { + return params.setMarkdown(new DingTalkParams.Markdown().setTitle(title).setText(text.build())); } } diff --git a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkMessage.java b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkMessage.java index 6cb43f51..7325b41d 100644 --- a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkMessage.java +++ b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkMessage.java @@ -4,4 +4,11 @@ package com.hccake.extend.ding.talk.message; * @author lingting 2020/6/11 21:58 */ public interface DingTalkMessage { + /** + * 生成钉钉消息发送参数 + * + * @return 钉钉文档要求的 jsonString + * @author lingting 2020-06-12 19:56:54 + */ + String generate(); } diff --git a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkTextMessage.java b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkTextMessage.java index 81dcd847..134335ac 100644 --- a/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkTextMessage.java +++ b/ballcat-extends/ballcat-extend-ding-talk/src/main/java/com/hccake/extend/ding/talk/message/DingTalkTextMessage.java @@ -1,7 +1,6 @@ package com.hccake.extend.ding.talk.message; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; +import com.hccake.extend.ding.talk.DingTalkParams; import com.hccake.extend.ding.talk.enums.MessageTypeEnum; import lombok.Getter; import lombok.Setter; @@ -25,7 +24,7 @@ public class DingTalkTextMessage extends AbstractDingTalkMessage { } @Override - public JSONObject json() { - return JSONUtil.parseObj("{\"text\":{\"content\":\"" + content + "\"}}"); + public DingTalkParams put(DingTalkParams params) { + return params.setText(new DingTalkParams.Text().setContent(content)); } } diff --git a/ballcat-starters/ballcat-spring-boot-starter-ding-talk/src/main/java/com/hccake/starter/ding/talk/DingTalkAutoConfiguration.java b/ballcat-starters/ballcat-spring-boot-starter-ding-talk/src/main/java/com/hccake/starter/ding/talk/DingTalkAutoConfiguration.java index 79eaafc8..d4c4c74a 100644 --- a/ballcat-starters/ballcat-spring-boot-starter-ding-talk/src/main/java/com/hccake/starter/ding/talk/DingTalkAutoConfiguration.java +++ b/ballcat-starters/ballcat-spring-boot-starter-ding-talk/src/main/java/com/hccake/starter/ding/talk/DingTalkAutoConfiguration.java @@ -15,7 +15,7 @@ import org.springframework.context.annotation.Configuration; @Slf4j @RequiredArgsConstructor @Configuration(proxyBeanMethods = false) -@ConditionalOnProperty(prefix = "ballcat.ding-talk.url") +@ConditionalOnProperty(name = "ballcat.ding-talk.url") @EnableConfigurationProperties({DingTalkProperties.class}) public class DingTalkAutoConfiguration { private final DingTalkProperties dingTalkProperties; diff --git a/ballcat-starters/ballcat-spring-boot-starter-mail/src/main/java/com/hccake/ballcat/common/mail/dto/MailDTO.java b/ballcat-starters/ballcat-spring-boot-starter-mail/src/main/java/com/hccake/ballcat/common/mail/dto/MailDTO.java index d7211e2d..e190608b 100644 --- a/ballcat-starters/ballcat-spring-boot-starter-mail/src/main/java/com/hccake/ballcat/common/mail/dto/MailDTO.java +++ b/ballcat-starters/ballcat-spring-boot-starter-mail/src/main/java/com/hccake/ballcat/common/mail/dto/MailDTO.java @@ -12,6 +12,8 @@ import java.time.LocalDate; */ @Data public class MailDTO { + public static final String MAIL_DELIMITER =","; + /** * 发件人 */ @@ -49,9 +51,9 @@ public class MailDTO { */ private LocalDate sentDate; /** - * 发送状态 1:成功 2:失败 + * 是否发送成功 */ - private Integer status; + private Boolean success; /** * 错误信息 errorMsg */ diff --git a/ballcat-starters/ballcat-spring-boot-starter-mail/src/main/java/com/hccake/ballcat/common/mail/service/impl/MailSenderImpl.java b/ballcat-starters/ballcat-spring-boot-starter-mail/src/main/java/com/hccake/ballcat/common/mail/service/impl/MailSenderImpl.java index 529a246a..18e6ca72 100644 --- a/ballcat-starters/ballcat-spring-boot-starter-mail/src/main/java/com/hccake/ballcat/common/mail/service/impl/MailSenderImpl.java +++ b/ballcat-starters/ballcat-spring-boot-starter-mail/src/main/java/com/hccake/ballcat/common/mail/service/impl/MailSenderImpl.java @@ -34,7 +34,7 @@ public class MailSenderImpl implements MailSender { /** * 发送邮件 * - * @param mailDTO + * @param mailDTO 邮件参数 */ @Override public void sendMail(MailDTO mailDTO) { @@ -43,9 +43,9 @@ public class MailSenderImpl implements MailSender { checkMail(mailDTO); //2.发送邮件 sendMimeMail(mailDTO); - mailDTO.setStatus(1); + mailDTO.setSuccess(true); } catch (Exception e) { - mailDTO.setStatus(2); + mailDTO.setSuccess(false); mailDTO.setErrorMsg(e.getMessage()); log.error("发送邮件失败:", e); }finally { @@ -57,7 +57,7 @@ public class MailSenderImpl implements MailSender { /** * 构建复杂邮件信息类 - * @param mailDTO + * @param mailDTO 邮件发送设置 */ private void sendMimeMail(MailDTO mailDTO) { try {