Skip to content

Commit

Permalink
feat: dinger 1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
AnswerAIL committed Dec 26, 2020
1 parent c4e556f commit 9fc1c55
Show file tree
Hide file tree
Showing 42 changed files with 749 additions and 227 deletions.
90 changes: 73 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,106 @@
# Dinger(叮鸽) ![GitHub license](https://img.shields.io/github/license/AnswerAIL/dingtalk-spring-boot-starter)
[![Dinger Logo](https://gitee.com/jaemon/docs/raw/master/dinger.png)](https://github.com/AnswerAIL/dingtalk-spring-boot-starter)


[![Maven Central](https://img.shields.io/maven-central/v/com.github.answerail/dingtalk-spring-boot-starter)](https://mvnrepository.com/artifact/com.github.answerail/dingtalk-spring-boot-starter)
[![GitHub stars](https://img.shields.io/github/stars/AnswerAIL/dingtalk-spring-boot-starter.svg?style=social)](https://github.com/AnswerAIL/dingtalk-spring-boot-starter)
[![Gitee stars](https://gitee.com/jaemon/dingtalk-spring-boot-starter/badge/star.svg?theme=dark)](https://gitee.com/jaemon/dingtalk-spring-boot-starter)
[![GitHub issues](https://img.shields.io/github/issues/AnswerAIL/dingtalk-spring-boot-starter)](https://github.com/AnswerAIL/dingtalk-spring-boot-starter/issues?q=is%3Aopen+is%3Aissue)
[![GitHub closed issues](https://img.shields.io/github/issues-closed/AnswerAIL/dingtalk-spring-boot-starter)](https://github.com/AnswerAIL/dingtalk-spring-boot-starter/issues?q=is%3Aissue+is%3Aclosed)
![JDK](https://img.shields.io/badge/JDK-%3E=1.8-green?logo=appveyor)
![SpringBoot](https://img.shields.io/badge/springboot-1.x%20&%202.x-green?logo=appveyor)


 


## What
springboot集成钉钉机器人实现消息通知中间件。项目基于[钉钉开放平台-群机器人](https://ding-doc.dingtalk.com/doc#/serverapi3/iydd5) API的基础上做了一层封装,让使用更简单便捷。
## What(Dinger是什么)
Dinger一个是以SpringBoot框架为基础开发的消息发送中间件, 对现有两大移动办公系统[钉钉](https://ding-doc.dingtalk.com/doc#/serverapi3/iydd5)[企业微信](https://work.weixin.qq.com/api/doc/90000/90136/91770)的群机器人API做了一层封装,让使用更简单便捷。

只需要简单的配置(最简单的发送功能只需要一行代码),即可快速的在springboot项目中将消息发送到指定的钉钉群聊中
只需要简单的配置(最简单的发送功能只需要一行代码),即可快速的在springboot项目中将消息发送到指定的钉钉或企业微信群聊中


 


## Why
- **`上手简单`**: 配置简单,无需花费太多精力在群机器人的使用上;
- **`代码解耦`**: 插拔式功能组件,和业务代码解耦;
- **`扩展性强`**: 核心功能面向接口编程, 可以据具体业务对功能进行定制化;
- **`用法多样`**: 消息体支持[DingTalk V1.x](https://github.com/AnswerAIL/dingtalk-spring-boot-starter/wiki/Docs-for-DingTalk-1.x)的完全自定义和[DingTalk V2.x](https://github.com/AnswerAIL/dingtalk-spring-boot-starter/wiki/Docs-for-DingTalk-2.x)的XML方式配置及注解方式定义;
## Why(为什么用Dinger)
- 配置简单,上手容易,无需花费太多精力在群机器人API的使用上;
- 插拔式功能组件,和业务代码解耦;
- 核心功能面向接口编程, 可以据具体业务对功能进行定制化来满足不同的业务需求;
- 支持集中式管理消息,提供xml标签,支持编写动态消息体;
- 基于具体消息编程,消息体可支持XML标签方式配置和注解方式定义;
- 支持钉钉和企业微信群机器人一键切换使用和混合使用;


 


## How
- [V1.X快速入门](https://github.com/AnswerAIL/dingtalk-spring-boot-starter/wiki/Getting-Started-V1.x)

- [V2.X快速入门](https://github.com/AnswerAIL/dingtalk-spring-boot-starter/wiki/Getting-Started-V2.x)
## How(如何使用Dinger-快速使用)
### 引入依赖
```xml
<dependency>
<groupId>com.github.answerail</groupId>
<artifactId>dinger-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
```

### 配置文件配置
**使用钉钉群机器人配置**
```yaml
spring:
dinger:
project-id: ${spring.application.name}
dingers:
# 使用钉钉机器人, 请根据自己机器人配置信息进行修改
dingtalk:
tokenId: 87dbeb7bc28894c3ycyl3d12457228ad309966275b5f427cd85f9025ebb520cf
secret: AEQ74a9039ai01f2ljm017b90ycye9asg6335f97c658ff37ff371ec8120581c7f09
```
**使用企业群机器人配置**
```yaml
spring:
dinger:
project-id: ${spring.application.name}
dingers:
# 使用企业微信机器人, 请根据自己机器人配置信息进行修改
wetalk:
token-id: 32865206-7082-46l5-8j39-2m7ycy6d868
```
### 代码中使用
```java
@Component
public class AppInit implements InitializingBean {
@Autowired
private DingerSender dingerSender;
@Override
public void afterPropertiesSet() throws Exception {
// 发送text类型消息
dingerSender.send(
MessageSubType.TEXT,
DingerRequest.request("Hello World, Hello Dinger")
);

// 发送markdown类型消息
dingerSender.send(
MessageSubType.MARKDOWN,
DingerRequest.request("Hello World, Hello Dinger", "启动通知")
);
}
}
```


&nbsp;

## Documentation, Getting Started and Developer Guides
- [Dingtalk Wiki](https://github.com/AnswerAIL/dingtalk-spring-boot-starter/wiki)
- [Dinger Wiki](https://github.com/AnswerAIL/dingtalk-spring-boot-starter/wiki)

&nbsp;


## Upgrade Log
- [版本变更日志](https://github.com/AnswerAIL/dingtalk-spring-boot-starter/wiki/Dingtalk-Upgrade-Log)

- [版本变更日志](https://github.com/AnswerAIL/dingtalk-spring-boot-starter/wiki/Dinger-Upgrade-Log)


&nbsp;
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/github/jaemon/dinger/DingerSender.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.github.jaemon.dinger;

import com.github.jaemon.dinger.core.entity.DingerProperties;
import com.github.jaemon.dinger.core.entity.DingerRequest;
import com.github.jaemon.dinger.core.entity.enums.DingerType;
import com.github.jaemon.dinger.core.entity.enums.MessageSubType;
Expand All @@ -32,6 +33,10 @@ public interface DingerSender {
/**
* 发送预警消息到钉钉
*
* <pre>
* 使用配置的默认钉钉机器人, {@link DingerProperties#getDefaultDinger()}
* </pre>
*
* @param messageSubType
* 消息类型{@link MessageSubType}
* @param request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public DingerIdGenerator dingerIdGenerator() {
@Bean
@ConditionalOnMissingBean(DingerAsyncCallback.class)
public DingerAsyncCallback dingerAsyncCallback() {
return new DefaultDkCallable();
return new DefaultDingerAsyncCallable();
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,23 @@
public class DingerThreadPoolProperties {
private static final int DEFAULT_CORE_SIZE = Runtime.getRuntime().availableProcessors() + 1;
/**
* 线程池维护线程的最小数量
* 线程池维护线程的最小数量, 选填
*/
private int coreSize = DEFAULT_CORE_SIZE;
/**
* 线程池维护线程的最大数量
* 线程池维护线程的最大数量, 选填
*/
private int maxSize = DEFAULT_CORE_SIZE * 2;
/**
* 空闲线程的存活时间
* 空闲线程的存活时间, 选填
*/
private int keepAliveSeconds = 60;
/**
* 持有等待执行的任务队列
* 持有等待执行的任务队列, 选填
*/
private int queueCapacity = 10;
/**
* 线程名称前缀
* 线程名称前缀, 选填
*/
private String threadNamePrefix = DingerConstant.DEFAULT_THREAD_NAME_PREFIX;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ protected List<Class<?>> dingerAnnotationResolver() throws Exception {
PackageUtils.classNames(basePackage, dingerClasses, true);
}
} else {
log.warn("annotation dingerScan is not configured.");
log.warn("annotation dingerScan is not configured and will execute Dinger scanner registrar.");
}

if (dingerClasses.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import com.github.jaemon.dinger.exception.DingerException;
import com.github.jaemon.dinger.core.entity.DingerCallback;
import com.github.jaemon.dinger.support.CustomMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* AbstractDingTalkSender
Expand All @@ -31,6 +33,7 @@
public abstract class AbstractDingerSender
extends DingerHelper
implements DingerSender {
protected static final Logger log = LoggerFactory.getLogger(AbstractDingerSender.class);

protected DingerProperties dingerProperties;
protected DingerManagerBuilder dingTalkManagerBuilder;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* Copyright ©2015-2020 Jaemon. All Rights Reserved.
*
* 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 com.github.jaemon.dinger.core;

import com.github.jaemon.dinger.exception.DingerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static com.github.jaemon.dinger.core.entity.enums.ExceptionEnum.RESOURCE_CONFIG_EXCEPTION;

/**
* ClassPathScanForResources
*
* @author Jaemon
* @since 1.0
*/
public final class ClassPathScanForResources {
private static final Logger log = LoggerFactory.getLogger(ClassPathScanForResources.class);
private static final String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
private static final ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

/**
* 扫描包
*
* @param packageSearchPath
* 扫描的包路径, <code>classpath*:com.jaemon.dinger/**\/*.class</code>
* @return
* 包下的所有资源文件集
*/
public static Resource[] doScanPackage(String packageSearchPath) {
try {
return resolver.getResources(packageSearchPath);
} catch (IOException ex) {
log.error(packageSearchPath, ex);
throw new DingerException(RESOURCE_CONFIG_EXCEPTION, packageSearchPath);
}
}

/**
* @param basePackage
* 包名, eg: <code>com.jaemon.dinger</code>
* @return
* 包下的所有接口集合
*/
public static List<Class<?>> scanInterfaces(String basePackage) {
return scanClasses(basePackage, true);
}


/**
* @param basePackage
* 包名, eg: <code>com.jaemon.dinger</code>
* @return
* 包下的所有类集合
*/
public static List<Class<?>> scanClasses(String basePackage) {
return scanClasses(basePackage, false);
}


/**
* @param basePackage
* 包名, eg: <code>com.jaemon.dinger</code>
* @param filterInterface
* 是否过滤接口
* @return
* 包下的所有类集合
*/
private static List<Class<?>> scanClasses(String basePackage, boolean filterInterface) {
boolean debugEnabled = log.isDebugEnabled();
String packageSearchPath = CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + DEFAULT_RESOURCE_PATTERN;
Resource[] resources = doScanPackage(packageSearchPath);

List<Class<?>> classes = new ArrayList<>();

if (resources.length == 0) {
return classes;
}

SimpleMetadataReaderFactory factory = new SimpleMetadataReaderFactory();
for (Resource resource : resources) {
String resourceFilename = resource.getFilename();
if (!resource.isReadable()) {
if (debugEnabled) {
log.debug("Ignored because not readable: {} ", resourceFilename);
}
continue;
}
try {
MetadataReader metadataReader = factory.getMetadataReader(resource);
ClassMetadata classMetadata = metadataReader.getClassMetadata();
Class<?> clazz = Class.forName(classMetadata.getClassName());
if (filterInterface && !clazz.isInterface()) {
if (debugEnabled) {
log.debug("source class={} is interface and skip.", resourceFilename);
}
continue;
}
classes.add(clazz);
} catch (IOException | ClassNotFoundException e) {
log.warn("resource={} read exception and message={}.", resourceFilename, e.getMessage());
continue;
}
}
return classes;
}

private static String resolveBasePackage(String basePackage) {
return ClassUtils.convertClassNameToResourcePath(basePackage);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
package com.github.jaemon.dinger.core;

import com.github.jaemon.dinger.exception.DingerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
Expand All @@ -29,6 +31,7 @@
* @since 1.0
*/
public class DingerDefinitionGeneratorFactory {
private static final Logger log = LoggerFactory.getLogger(DingerDefinitionGeneratorFactory.class);
/** dingerDefinition生成器 */
static final Map<String, DingerDefinitionGenerator> dingTalkDefinitionGeneratorMap = new HashMap<>();

Expand All @@ -43,13 +46,13 @@ public class DingerDefinitionGeneratorFactory {
public static DingerDefinitionGenerator get(String key) {
DingerDefinitionGenerator dingTalkDefinitionGenerator = dingTalkDefinitionGeneratorMap.get(key);
if (dingTalkDefinitionGenerator == null) {
throw new DingerException(key + "无对应的Dinger定义生成器", DINGERDEFINITION_ERROR);
if (log.isDebugEnabled()) {
log.debug("key={}, dingTalkDefinitionGeneratorMap={}.",
key, dingTalkDefinitionGeneratorMap.keySet());
}
throw new DingerException(DINGERDEFINITION_ERROR, key);
}
return dingTalkDefinitionGenerator;
}

static void clear() {
dingTalkDefinitionGeneratorMap.clear();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ protected static DingerDefinition xmlHandler(
MessageMainType messageMainType = dingerDefinitionType.messageMainType();
if (messageMainType != MessageMainType.XML) {
throw new DingerException(
dingerDefinitionType + "中消息体定义主类型应该为" + MessageMainType.XML, DINGERDEFINITIONTYPE_ERROR
DINGERDEFINITIONTYPE_ERROR, dingerDefinitionType, MessageMainType.XML, messageMainType
);
}

Expand Down
Loading

0 comments on commit 9fc1c55

Please sign in to comment.