context,
25 | DtService dtService) throws DtErrorException;
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/message/DtMessageMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.message;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.bean.message.DtEventMessage;
4 |
5 | /**
6 | * 消息匹配器,用在消息路由的时候
7 | */
8 | public interface DtMessageMatcher {
9 |
10 | /**
11 | * 消息是否匹配某种模式
12 | *
13 | * @param message the message
14 | * @return the boolean
15 | */
16 | boolean match(DtEventMessage message);
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/message/DtMessageRouter.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.message;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.api.DtService;
4 | import com.github.tingyugetc520.ali.dingtalk.bean.message.DtEventMessage;
5 | import com.github.tingyugetc520.ali.dingtalk.constant.DtConstant;
6 | import com.github.tingyugetc520.ali.dingtalk.error.DtRuntimeException;
7 | import com.github.tingyugetc520.ali.dingtalk.message.processor.DtCheckUrlMessageHandler;
8 | import com.github.tingyugetc520.ali.dingtalk.message.processor.DtLogExceptionHandler;
9 | import com.google.common.util.concurrent.ThreadFactoryBuilder;
10 | import lombok.extern.slf4j.Slf4j;
11 |
12 | import java.util.*;
13 | import java.util.concurrent.*;
14 |
15 | /**
16 | *
17 | * 消息路由器,通过代码化的配置,把来自钉钉的消息交给handler处理
18 | *
19 | * 说明:
20 | * 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理
21 | * 2. 默认情况下消息只会被处理一次,除非使用 {@link DtMessageRouterRule#next()}
22 | * 3. 规则的结束必须用{@link DtMessageRouterRule#end()}或者{@link DtMessageRouterRule#next()},否则不会生效
23 | *
24 | * 使用方法:
25 | * DtMessageRouter router = new DtMessageRouter();
26 | * router
27 | * .rule()
28 | * .eventType("eventType")
29 | * .interceptor(interceptor, ...).handler(handler, ...)
30 | * .end()
31 | * .rule()
32 | * // 另外一个匹配规则
33 | * .end()
34 | * ;
35 | *
36 | * // 将DtMessage交给消息路由器
37 | * router.route(message);
38 | *
39 | *
40 | */
41 | @Slf4j
42 | public class DtMessageRouter {
43 | private static final int DEFAULT_THREAD_POOL_SIZE = 100;
44 | private final List rules = new ArrayList<>();
45 |
46 | private DtService dtService;
47 | private ExecutorService executorService;
48 | private DtErrorExceptionHandler exceptionHandler;
49 |
50 | /**
51 | * 构造方法.
52 | */
53 | public DtMessageRouter() {
54 | ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("dtMessageRouter-pool-%d").build();
55 | this.executorService = new ThreadPoolExecutor(DEFAULT_THREAD_POOL_SIZE, DEFAULT_THREAD_POOL_SIZE,
56 | 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), namedThreadFactory);
57 |
58 | this.exceptionHandler = new DtLogExceptionHandler();
59 | this.rules.add(new DtMessageRouterRule(this).async(false).eventType(DtConstant.EventType.CHECK_URL).handler(new DtCheckUrlMessageHandler()));
60 | }
61 |
62 | /**
63 | * 构造方法.
64 | */
65 | public DtMessageRouter(DtService dtService) {
66 | this();
67 | this.dtService = dtService;
68 | }
69 |
70 | /**
71 | *
72 | * 设置自定义的 {@link ExecutorService}
73 | * 如果不调用该方法,默认使用 Executors.newFixedThreadPool(100)
74 | *
75 | */
76 | public void setExecutorService(ExecutorService executorService) {
77 | this.executorService = executorService;
78 | }
79 |
80 | /**
81 | *
82 | * 设置自定义的{@link DtErrorExceptionHandler}
83 | * 如果不调用该方法,默认使用 {@link DtLogExceptionHandler}
84 | *
85 | */
86 | public void setExceptionHandler(DtErrorExceptionHandler exceptionHandler) {
87 | this.exceptionHandler = exceptionHandler;
88 | }
89 |
90 | List getRules() {
91 | return this.rules;
92 | }
93 |
94 | /**
95 | * 开始一个新的Route规则.
96 | */
97 | public DtMessageRouterRule rule() {
98 | return new DtMessageRouterRule(this);
99 | }
100 |
101 | /**
102 | * 处理消息.
103 | */
104 | protected Boolean doRoute(final DtService dtService, final DtEventMessage message, final Map context) {
105 | // 消息为空,则说明回调有问题
106 | if (Objects.isNull(message)) {
107 | throw new DtRuntimeException("回调消息为空");
108 | }
109 |
110 | final List matchRules = new ArrayList<>();
111 | // 收集匹配的规则
112 | for (final DtMessageRouterRule rule : this.rules) {
113 | if (rule.test(message)) {
114 | matchRules.add(rule);
115 | if (!rule.isReEnter()) {
116 | break;
117 | }
118 | }
119 | }
120 |
121 | // 没有处理器
122 | if (matchRules.size() == 0) {
123 | return null;
124 | }
125 |
126 | // todo 当存在多条匹配规则时,应该判断返回全部规则是否都正确处理
127 | Boolean res = null;
128 | final List> futures = new ArrayList<>();
129 | for (final DtMessageRouterRule rule : matchRules) {
130 | // 返回最后一个非异步的rule的执行结果
131 | if (rule.isAsync()) {
132 | futures.add(
133 | this.executorService.submit(() ->
134 | rule.service(message, context, dtService, DtMessageRouter.this.exceptionHandler)
135 | )
136 | );
137 | } else {
138 | res = rule.service(message, context, dtService, this.exceptionHandler);
139 | }
140 | }
141 |
142 | if (futures.size() > 0) {
143 | this.executorService.submit(() -> {
144 | for (Future future : futures) {
145 | try {
146 | future.get();
147 | } catch (InterruptedException e) {
148 | log.error("Error happened when wait task finish", e);
149 | Thread.currentThread().interrupt();
150 | } catch (ExecutionException e) {
151 | log.error("Error happened when wait task finish", e);
152 | }
153 | }
154 | });
155 | }
156 | return res;
157 | }
158 |
159 | /**
160 | * 处理消息.
161 | */
162 | public Boolean route(final DtService dtService, final DtEventMessage message, final Map context) {
163 | return this.doRoute(dtService, message, context);
164 | }
165 |
166 | /**
167 | * 处理消息.
168 | */
169 | public Boolean route(final DtEventMessage message) {
170 | return this.route(this.dtService, message, new HashMap<>(2));
171 | }
172 |
173 | /**
174 | * 指定由dtService来处理消息
175 | */
176 | public Boolean route(final DtService dtService, final DtEventMessage message) {
177 | return this.route(dtService, message, new HashMap<>(2));
178 | }
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/message/DtMessageRouterRule.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.message;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.api.DtService;
4 | import com.github.tingyugetc520.ali.dingtalk.bean.message.DtEventMessage;
5 | import com.github.tingyugetc520.ali.dingtalk.error.DtErrorException;
6 | import lombok.Data;
7 | import org.apache.commons.collections4.CollectionUtils;
8 | import org.apache.commons.lang3.StringUtils;
9 |
10 | import java.util.*;
11 | import java.util.regex.Pattern;
12 |
13 | /**
14 | * The message router rule
15 | */
16 | @Data
17 | public class DtMessageRouterRule {
18 | private final DtMessageRouter routerBuilder;
19 |
20 | private boolean async = true;
21 |
22 | private String corpId;
23 |
24 | private String eventType;
25 |
26 | private List eventTypeGroup;
27 |
28 | private String eventTypeRegex;
29 |
30 | private DtMessageMatcher matcher;
31 |
32 | private boolean reEnter = false;
33 |
34 |
35 | private List handlers = new ArrayList<>();
36 |
37 | private List interceptors = new ArrayList<>();
38 |
39 | protected DtMessageRouterRule(DtMessageRouter routerBuilder) {
40 | this.routerBuilder = routerBuilder;
41 | }
42 |
43 | /**
44 | * 设置是否异步执行,默认是true
45 | *
46 | * @param async the async
47 | * @return the message router rule
48 | */
49 | public DtMessageRouterRule async(boolean async) {
50 | this.async = async;
51 | return this;
52 | }
53 |
54 | /**
55 | * 如果corpId匹配
56 | *
57 | * @param corpId the corp id
58 | * @return the message router rule
59 | */
60 | public DtMessageRouterRule corpId(String corpId) {
61 | this.corpId = corpId;
62 | return this;
63 | }
64 |
65 | /**
66 | * 如果eventType等于某值
67 | *
68 | * @param eventType the event type
69 | * @return the message router rule
70 | */
71 | public DtMessageRouterRule eventType(String eventType) {
72 | this.eventType = eventType;
73 | return this;
74 | }
75 |
76 | /**
77 | * 如果是eventGroup中的某一个
78 | * @return rule
79 | */
80 | public DtMessageRouterRule eventTypeGroup(List eventGroup) {
81 | this.eventTypeGroup = eventGroup;
82 | return this;
83 | }
84 |
85 | /**
86 | * 如果eventKey匹配该正则表达式
87 | *
88 | * @param regex the regex
89 | * @return the message router rule
90 | */
91 | public DtMessageRouterRule eventTypeRegex(String regex) {
92 | this.eventTypeRegex = regex;
93 | return this;
94 | }
95 |
96 | /**
97 | * 如果消息匹配某个matcher,用在用户需要自定义更复杂的匹配规则的时候
98 | *
99 | * @param matcher the matcher
100 | * @return the message router rule
101 | */
102 | public DtMessageRouterRule matcher(DtMessageMatcher matcher) {
103 | this.matcher = matcher;
104 | return this;
105 | }
106 |
107 | /**
108 | * 设置消息拦截器
109 | *
110 | * @param interceptor the interceptor
111 | * @return the message router rule
112 | */
113 | public DtMessageRouterRule interceptor(DtMessageInterceptor interceptor) {
114 | return interceptor(interceptor, (DtMessageInterceptor[]) null);
115 | }
116 |
117 | /**
118 | * 设置消息拦截器
119 | *
120 | * @param interceptor the interceptor
121 | * @param otherInterceptors the other interceptors
122 | * @return the message router rule
123 | */
124 | public DtMessageRouterRule interceptor(DtMessageInterceptor interceptor, DtMessageInterceptor... otherInterceptors) {
125 | this.interceptors.add(interceptor);
126 | if (otherInterceptors != null && otherInterceptors.length > 0) {
127 | Collections.addAll(this.interceptors, otherInterceptors);
128 | }
129 | return this;
130 | }
131 |
132 | /**
133 | * 设置消息处理器
134 | *
135 | * @param handler the handler
136 | * @return the message router rule
137 | */
138 | public DtMessageRouterRule handler(DtMessageHandler handler) {
139 | return handler(handler, (DtMessageHandler[]) null);
140 | }
141 |
142 | /**
143 | * 设置消息处理器
144 | *
145 | * @param handler the handler
146 | * @param otherHandlers the other handlers
147 | * @return the message router rule
148 | */
149 | public DtMessageRouterRule handler(DtMessageHandler handler, DtMessageHandler... otherHandlers) {
150 | this.handlers.add(handler);
151 | if (otherHandlers != null && otherHandlers.length > 0) {
152 | Collections.addAll(this.handlers, otherHandlers);
153 | }
154 | return this;
155 | }
156 |
157 | /**
158 | * 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则
159 | *
160 | * @return the message router
161 | */
162 | public DtMessageRouter end() {
163 | this.routerBuilder.getRules().add(this);
164 | return this.routerBuilder;
165 | }
166 |
167 | /**
168 | * 规则结束,但是消息还会进入其他规则
169 | *
170 | * @return the message router
171 | */
172 | public DtMessageRouter next() {
173 | this.reEnter = true;
174 | return end();
175 | }
176 |
177 | /**
178 | * Test boolean.
179 | *
180 | * @param message the ding talk event message
181 | * @return the boolean
182 | */
183 | protected boolean test(DtEventMessage message) {
184 | return (this.corpId == null || this.corpId.equals(message.getCorpId()))
185 | &&
186 | (this.eventType == null || this.eventType.equalsIgnoreCase(message.getEventType()))
187 | &&
188 | (CollectionUtils.isEmpty(this.eventTypeGroup) || this.eventTypeGroup.contains(message.getEventType()))
189 | &&
190 | (this.eventTypeRegex == null || Pattern.matches(this.eventTypeRegex, StringUtils.trimToEmpty(message.getEventType())))
191 | &&
192 | (this.matcher == null || this.matcher.match(message))
193 | ;
194 | }
195 |
196 | /**
197 | * 处理推送过来的消息
198 | *
199 | * @param message the dt message
200 | * @param context the context
201 | * @param dtService the service
202 | * @param exceptionHandler the exception handler
203 | * @return true 代表消息回调成功,false 代表消息回调失败
204 | */
205 | protected Boolean service(DtEventMessage message,
206 | Map context,
207 | DtService dtService,
208 | DtErrorExceptionHandler exceptionHandler) {
209 |
210 | if (context == null) {
211 | context = new HashMap<>();
212 | }
213 |
214 | try {
215 | // 如果拦截器不通过
216 | for (DtMessageInterceptor interceptor : this.interceptors) {
217 | if (!interceptor.intercept(message, context, dtService)) {
218 | return false;
219 | }
220 | }
221 |
222 | // 交给handler处理
223 | Boolean res = null;
224 | for (DtMessageHandler handler : this.handlers) {
225 | // 返回最后handler的结果
226 | res = handler.handle(message, context, dtService);
227 | }
228 | return res;
229 | } catch (DtErrorException e) {
230 | exceptionHandler.handle(e);
231 | return false;
232 | }
233 | }
234 |
235 |
236 | }
237 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/message/processor/DtCheckUrlMessageHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.message.processor;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.api.DtService;
4 | import com.github.tingyugetc520.ali.dingtalk.bean.message.DtEventMessage;
5 | import com.github.tingyugetc520.ali.dingtalk.error.DtErrorException;
6 | import com.github.tingyugetc520.ali.dingtalk.message.DtMessageHandler;
7 | import lombok.extern.slf4j.Slf4j;
8 |
9 | import java.util.Map;
10 |
11 | @Slf4j
12 | public class DtCheckUrlMessageHandler implements DtMessageHandler {
13 |
14 | @Override
15 | public Boolean handle(DtEventMessage message, Map context, DtService dtService) throws DtErrorException {
16 | // check url事件直接返回true,因为已经解析成功了
17 | return true;
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/message/processor/DtLogExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.message.processor;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.error.DtErrorException;
4 | import com.github.tingyugetc520.ali.dingtalk.message.DtErrorExceptionHandler;
5 | import lombok.extern.slf4j.Slf4j;
6 |
7 | @Slf4j
8 | public class DtLogExceptionHandler implements DtErrorExceptionHandler {
9 | @Override
10 | public void handle(DtErrorException e) {
11 | log.error("Error happens", e);
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/DataUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 |
5 | /**
6 | * 数据处理工具类
7 | */
8 | public class DataUtils {
9 | /**
10 | * 将数据中包含的secret字符使用星号替换,防止日志打印时被输出
11 | */
12 | public static E handleDataWithSecret(E data) {
13 | E dataForLog = data;
14 | if(data instanceof String && StringUtils.contains((String)data, "&secret=")){
15 | dataForLog = (E) StringUtils.replaceAll((String)data,"&secret=\\w+&","&secret=******&");
16 | }
17 | return dataForLog;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/DtConstantUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util;
2 |
3 | import com.google.common.collect.Lists;
4 |
5 | import java.lang.reflect.Modifier;
6 | import java.util.List;
7 | import java.util.Objects;
8 | import java.util.stream.Collectors;
9 |
10 | public class DtConstantUtils {
11 |
12 | @SuppressWarnings("unchecked")
13 | public static List getEventTypeGroup(Class clazz) {
14 | return Lists.newArrayList(clazz.getDeclaredFields())
15 | .stream()
16 | .filter(field -> Modifier.isStatic(field.getModifiers()))
17 | .map(field -> {
18 | try {
19 | return (E) field.get(null);
20 | } catch (IllegalAccessException|IllegalArgumentException e) {
21 | e.printStackTrace();
22 | return null;
23 | }
24 | }).filter(Objects::nonNull).collect(Collectors.toList());
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/crypto/DtCryptUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.crypto;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.config.DtConfigStorage;
4 | import com.github.tingyugetc520.ali.dingtalk.error.DtRuntimeErrorEnum;
5 | import com.github.tingyugetc520.ali.dingtalk.error.DtRuntimeException;
6 | import org.apache.commons.codec.binary.Base64;
7 |
8 | import javax.crypto.Cipher;
9 | import javax.crypto.spec.IvParameterSpec;
10 | import javax.crypto.spec.SecretKeySpec;
11 | import java.io.ByteArrayOutputStream;
12 | import java.nio.charset.Charset;
13 | import java.nio.charset.StandardCharsets;
14 | import java.security.MessageDigest;
15 | import java.util.Arrays;
16 | import java.util.HashMap;
17 | import java.util.Map;
18 | import java.util.Random;
19 |
20 | /**
21 | * 钉钉开放平台加解密方法
22 | */
23 | public class DtCryptUtil {
24 | private static final Charset CHARSET = StandardCharsets.UTF_8;
25 | private static final Base64 BASE64 = new Base64();
26 |
27 | protected byte[] aesKey;
28 | protected String token;
29 | protected String appKeyOrCorpId;
30 | /**
31 | * ask getPaddingBytes key固定长度
32 | **/
33 | private static final Integer AES_ENCODE_KEY_LENGTH = 43;
34 | /**
35 | * 加密随机字符串字节长度
36 | **/
37 | private static final Integer RANDOM_LENGTH = 16;
38 |
39 | public DtCryptUtil(DtConfigStorage configStorage) {
40 | this(configStorage.getToken(), configStorage.getAesKey(), configStorage.getAppKeyOrCorpId());
41 | }
42 |
43 | /**
44 | * 构造函数
45 | *
46 | * @param token 钉钉开放平台上,开发者设置的token
47 | * @param aesKey 钉钉开放台上,开发者设置的EncodingAESKey
48 | * @param appKeyOrCorpId 企业的corpId
49 | */
50 | public DtCryptUtil(String token, String aesKey, String appKeyOrCorpId) {
51 | if (null == aesKey || aesKey.length() != AES_ENCODE_KEY_LENGTH) {
52 | throw new DtRuntimeException(DtRuntimeErrorEnum.AES_KEY_ILLEGAL);
53 | }
54 | this.token = token;
55 | this.appKeyOrCorpId = appKeyOrCorpId;
56 | this.aesKey = Base64.decodeBase64(aesKey + "=");
57 | }
58 |
59 | public Map getEncryptedMap(String plaintext) {
60 | return getEncryptedMap(plaintext, System.currentTimeMillis(), Utils.getRandomStr(16));
61 | }
62 |
63 | /**
64 | * 将和钉钉开放平台同步的消息体加密,返回加密Map
65 | *
66 | * @param plaintext 传递的消息体明文
67 | * @param timeStamp 时间戳
68 | * @param nonce 随机字符串
69 | * @return map
70 | */
71 | public Map getEncryptedMap(String plaintext, Long timeStamp, String nonce) {
72 | if (null == plaintext) {
73 | throw new DtRuntimeException(DtRuntimeErrorEnum.ENCRYPTION_PLAINTEXT_ILLEGAL);
74 | }
75 | if (null == timeStamp) {
76 | throw new DtRuntimeException(DtRuntimeErrorEnum.ENCRYPTION_TIMESTAMP_ILLEGAL);
77 | }
78 | if (null == nonce || nonce.length() != 16) {
79 | throw new DtRuntimeException(DtRuntimeErrorEnum.ENCRYPTION_NONCE_ILLEGAL);
80 | }
81 | // 加密
82 | String encrypt = encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext);
83 | String signature = getSignature(token, String.valueOf(timeStamp), nonce, encrypt);
84 | Map resultMap = new HashMap();
85 | resultMap.put("msg_signature", signature);
86 | resultMap.put("encrypt", encrypt);
87 | resultMap.put("timeStamp", String.valueOf(timeStamp));
88 | resultMap.put("nonce", nonce);
89 | return resultMap;
90 | }
91 |
92 | /**
93 | * 密文解密
94 | *
95 | * @param msgSignature 签名串
96 | * @param timeStamp 时间戳
97 | * @param nonce 随机串
98 | * @param encryptMsg 密文
99 | * @return 解密后的原文
100 | */
101 | public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg) {
102 | //校验签名
103 | String signature = getSignature(token, timeStamp, nonce, encryptMsg);
104 | if (!signature.equals(msgSignature)) {
105 | throw new DtRuntimeException(DtRuntimeErrorEnum.COMPUTE_SIGNATURE_ERROR);
106 | }
107 | // 解密
108 | return decrypt(encryptMsg);
109 | }
110 |
111 | /**
112 | * 对明文加密.
113 | * @param plaintext 需要加密的明文
114 | * @return 加密后base64编码的字符串
115 | */
116 | private String encrypt(String random, String plaintext) {
117 | try {
118 | byte[] randomBytes = random.getBytes(CHARSET);
119 | byte[] plainTextBytes = plaintext.getBytes(CHARSET);
120 | byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length);
121 | byte[] corpIdBytes = appKeyOrCorpId.getBytes(CHARSET);
122 |
123 | ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
124 | byteStream.write(randomBytes);
125 | byteStream.write(lengthByte);
126 | byteStream.write(plainTextBytes);
127 | byteStream.write(corpIdBytes);
128 | byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size());
129 | byteStream.write(padBytes);
130 | byte[] unencrypted = byteStream.toByteArray();
131 | byteStream.close();
132 | Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
133 | SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
134 | IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
135 | cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
136 | byte[] encrypted = cipher.doFinal(unencrypted);
137 |
138 | return BASE64.encodeToString(encrypted);
139 | } catch (Exception e) {
140 | throw new DtRuntimeException(DtRuntimeErrorEnum.COMPUTE_ENCRYPT_TEXT_ERROR);
141 | }
142 | }
143 |
144 | /**
145 | * 对密文进行解密.
146 | * @param text 需要解密的密文
147 | * @return 解密得到的明文
148 | */
149 | private String decrypt(String text) {
150 | byte[] originalArr;
151 | try {
152 | // 设置解密模式为AES的CBC模式
153 | Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
154 | SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
155 | IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
156 | cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
157 | // 使用BASE64对密文进行解码
158 | byte[] encrypted = Base64.decodeBase64(text);
159 | // 解密
160 | originalArr = cipher.doFinal(encrypted);
161 | } catch (Exception e) {
162 | throw new DtRuntimeException(DtRuntimeErrorEnum.COMPUTE_DECRYPT_TEXT_ERROR);
163 | }
164 |
165 | String plainText;
166 | String fromAppKeyOrCorpId;
167 | try {
168 | // 去除补位字符
169 | byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr);
170 | // 分离16位随机字符串,网络字节序和corpId
171 | byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
172 | int plainTextLength = Utils.bytes2int(networkOrder);
173 | plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLength), CHARSET);
174 | fromAppKeyOrCorpId = new String(Arrays.copyOfRange(bytes, 20 + plainTextLength, bytes.length), CHARSET);
175 | } catch (Exception e) {
176 | throw new DtRuntimeException(DtRuntimeErrorEnum.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR);
177 | }
178 |
179 | // appKeyOrCorpId不相同的情况
180 | if (!fromAppKeyOrCorpId.equals(appKeyOrCorpId)) {
181 | throw new DtRuntimeException(DtRuntimeErrorEnum.COMPUTE_DECRYPT_TEXT_CORPID_ERROR);
182 | }
183 | return plainText;
184 | }
185 |
186 | /**
187 | * 数字签名
188 | *
189 | * @param token isv token
190 | * @param timestamp 时间戳
191 | * @param nonce 随机串
192 | * @param encrypt 加密文本
193 | * @return
194 | */
195 | public static String getSignature(String token, String timestamp, String nonce, String encrypt) {
196 | try {
197 | String[] array = new String[] {token, timestamp, nonce, encrypt};
198 | Arrays.sort(array);
199 |
200 | StringBuilder sb = new StringBuilder();
201 | for (int i = 0; i < 4; i++) {
202 | sb.append(array[i]);
203 | }
204 | String str = sb.toString();
205 |
206 | MessageDigest md = MessageDigest.getInstance("SHA-1");
207 | md.update(str.getBytes());
208 | byte[] digest = md.digest();
209 |
210 | StringBuilder hexStr = new StringBuilder();
211 | String shaHex = "";
212 | for (byte b : digest) {
213 | shaHex = Integer.toHexString(b & 0xFF);
214 | if (shaHex.length() < 2) {
215 | hexStr.append(0);
216 | }
217 | hexStr.append(shaHex);
218 | }
219 | return hexStr.toString();
220 | } catch (Exception e) {
221 | throw new DtRuntimeException(DtRuntimeErrorEnum.COMPUTE_SIGNATURE_ERROR);
222 | }
223 | }
224 |
225 | public static class Utils {
226 | public Utils() {
227 | }
228 |
229 | public static String getRandomStr(int count) {
230 | String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
231 | Random random = new Random();
232 | StringBuilder sb = new StringBuilder();
233 |
234 | for (int i = 0; i < count; ++i) {
235 | int number = random.nextInt(base.length());
236 | sb.append(base.charAt(number));
237 | }
238 |
239 | return sb.toString();
240 | }
241 |
242 | public static byte[] int2Bytes(int count) {
243 | return new byte[] {(byte)(count >> 24 & 255), (byte)(count >> 16 & 255), (byte)(count >> 8 & 255),
244 | (byte)(count & 255)};
245 | }
246 |
247 | public static int bytes2int(byte[] byteArr) {
248 | int count = 0;
249 |
250 | for (int i = 0; i < 4; ++i) {
251 | count <<= 8;
252 | count |= byteArr[i] & 255;
253 | }
254 |
255 | return count;
256 | }
257 | }
258 |
259 | public static class PKCS7Padding {
260 | private static final Charset CHARSET = StandardCharsets.UTF_8;
261 | private static final int BLOCK_SIZE = 32;
262 |
263 | public PKCS7Padding() {
264 | }
265 |
266 | public static byte[] getPaddingBytes(int count) {
267 | int amountToPad = 32 - count % 32;
268 |
269 | char padChr = chr(amountToPad);
270 | StringBuilder tmp = new StringBuilder();
271 |
272 | for (int index = 0; index < amountToPad; ++index) {
273 | tmp.append(padChr);
274 | }
275 |
276 | return tmp.toString().getBytes(CHARSET);
277 | }
278 |
279 | public static byte[] removePaddingBytes(byte[] decrypted) {
280 | int pad = decrypted[decrypted.length - 1];
281 | if (pad < 1 || pad > 32) {
282 | pad = 0;
283 | }
284 |
285 | return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
286 | }
287 |
288 | private static char chr(int a) {
289 | byte target = (byte)(a & 255);
290 | return (char)target;
291 | }
292 | }
293 |
294 | }
295 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/http/HttpProxyType.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.http;
2 |
3 | /**
4 | * Http Proxy types
5 | */
6 | public enum HttpProxyType {
7 | /**
8 | * none
9 | */
10 | NONE(0),
11 | /**
12 | * 反向代理
13 | */
14 | REVERSE(1),
15 | /**
16 | * 正向代理
17 | */
18 | FORWARD(2),
19 | ;
20 |
21 | private int code;
22 |
23 | HttpProxyType(int code) {
24 | this.code = code;
25 | }
26 |
27 | public int getCode() {
28 | return code;
29 | }
30 |
31 | /**
32 | * 通过错误代码查找其中文含义..
33 | */
34 | public static HttpProxyType findByCode(int code) {
35 | for (HttpProxyType value : HttpProxyType.values()) {
36 | if (value.code == code) {
37 | return value;
38 | }
39 | }
40 |
41 | return null;
42 | }
43 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/http/OkHttpProxyInfo.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.http;
2 |
3 | import java.net.InetSocketAddress;
4 | import java.net.Proxy;
5 |
6 | /**
7 | * Proxy information.
8 | */
9 | public class OkHttpProxyInfo {
10 | private final String proxyAddress;
11 | private final int proxyPort;
12 | private final String proxyUsername;
13 | private final String proxyPassword;
14 | private final ProxyType proxyType;
15 |
16 | public OkHttpProxyInfo(ProxyType proxyType, String proxyHost, int proxyPort, String proxyUser, String proxyPassword) {
17 | this.proxyType = proxyType;
18 | this.proxyAddress = proxyHost;
19 | this.proxyPort = proxyPort;
20 | this.proxyUsername = proxyUser;
21 | this.proxyPassword = proxyPassword;
22 | }
23 |
24 | /**
25 | * Creates directProxy.
26 | */
27 | public static OkHttpProxyInfo directProxy() {
28 | return new OkHttpProxyInfo(ProxyType.NONE, null, 0, null, null);
29 | }
30 |
31 | // ---------------------------------------------------------------- factory
32 |
33 | /**
34 | * Creates SOCKS4 proxy.
35 | */
36 | public static OkHttpProxyInfo socks4Proxy(String proxyAddress, int proxyPort, String proxyUser) {
37 | return new OkHttpProxyInfo(ProxyType.SOCKS4, proxyAddress, proxyPort, proxyUser, null);
38 | }
39 |
40 | /**
41 | * Creates SOCKS5 proxy.
42 | */
43 | public static OkHttpProxyInfo socks5Proxy(String proxyAddress, int proxyPort, String proxyUser, String proxyPassword) {
44 | return new OkHttpProxyInfo(ProxyType.SOCKS5, proxyAddress, proxyPort, proxyUser, proxyPassword);
45 | }
46 |
47 | /**
48 | * Creates HTTP proxy.
49 | */
50 | public static OkHttpProxyInfo httpProxy(String proxyAddress, int proxyPort, String proxyUser, String proxyPassword) {
51 | return new OkHttpProxyInfo(ProxyType.HTTP, proxyAddress, proxyPort, proxyUser, proxyPassword);
52 | }
53 |
54 | /**
55 | * Returns proxy type.
56 | */
57 | public ProxyType getProxyType() {
58 | return proxyType;
59 | }
60 |
61 | // ---------------------------------------------------------------- getter
62 |
63 | /**
64 | * Returns proxy address.
65 | */
66 | public String getProxyAddress() {
67 | return proxyAddress;
68 | }
69 |
70 | /**
71 | * Returns proxy port.
72 | */
73 | public int getProxyPort() {
74 | return proxyPort;
75 | }
76 |
77 | /**
78 | * Returns proxy user name or null
if
79 | * no authentication required.
80 | */
81 | public String getProxyUsername() {
82 | return proxyUsername;
83 | }
84 |
85 | /**
86 | * Returns proxy password or null
.
87 | */
88 | public String getProxyPassword() {
89 | return proxyPassword;
90 | }
91 |
92 | /**
93 | * 返回 java.net.Proxy
94 | */
95 | public Proxy getProxy() {
96 | Proxy proxy = null;
97 | if (getProxyType().equals(ProxyType.SOCKS5)) {
98 | proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(getProxyAddress(), getProxyPort()));
99 | } else if (getProxyType().equals(ProxyType.SOCKS4)) {
100 | proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(getProxyAddress(), getProxyPort()));
101 | } else if (getProxyType().equals(ProxyType.HTTP)) {
102 | proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(getProxyAddress(), getProxyPort()));
103 | } else if (getProxyType().equals(ProxyType.NONE)) {
104 | proxy = new Proxy(Proxy.Type.DIRECT, new InetSocketAddress(getProxyAddress(), getProxyPort()));
105 | }
106 | return proxy;
107 | }
108 |
109 | /**
110 | * Proxy types.
111 | */
112 | public enum ProxyType {
113 | /**
114 | * none
115 | */
116 | NONE,
117 | /**
118 | * http
119 | */
120 | HTTP,
121 | /**
122 | * socks4
123 | */
124 | SOCKS4,
125 | /**
126 | * socks5
127 | */
128 | SOCKS5
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/http/OkHttpSimpleGetRequestExecutor.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.http;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.error.DtError;
4 | import com.github.tingyugetc520.ali.dingtalk.error.DtErrorException;
5 | import okhttp3.OkHttpClient;
6 | import okhttp3.Request;
7 | import okhttp3.Response;
8 |
9 | import java.io.IOException;
10 | import java.util.Objects;
11 |
12 | /**
13 | * 简单的GET请求执行器.
14 | * 请求的参数是String, 返回的结果也是String
15 | */
16 | public class OkHttpSimpleGetRequestExecutor implements RequestExecutor {
17 | protected OkHttpClient httpClient;
18 |
19 | public OkHttpSimpleGetRequestExecutor(OkHttpClient httpClient) {
20 | this.httpClient = httpClient;
21 | }
22 |
23 | @Override
24 | public String execute(String uri, String queryParam) throws DtErrorException, IOException {
25 | if (queryParam != null) {
26 | if (uri.indexOf('?') == -1) {
27 | uri += '?';
28 | }
29 | uri += uri.endsWith("?") ? queryParam : '&' + queryParam;
30 | }
31 |
32 | Request request = new Request.Builder().url(uri).build();
33 | Response response = httpClient.newCall(request).execute();
34 | return handleResponse(Objects.requireNonNull(response.body()).string());
35 | }
36 |
37 | protected String handleResponse(String responseContent) throws DtErrorException {
38 | DtError error = DtError.fromJson(responseContent);
39 | if (error.getErrorCode() != 0) {
40 | throw new DtErrorException(error);
41 | }
42 |
43 | return responseContent;
44 | }
45 |
46 | public static RequestExecutor create(OkHttpClient httpClient) {
47 | return new OkHttpSimpleGetRequestExecutor(httpClient);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/http/OkHttpSimplePostRequestExecutor.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.http;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.error.DtError;
4 | import com.github.tingyugetc520.ali.dingtalk.error.DtErrorException;
5 | import okhttp3.*;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.io.IOException;
9 | import java.util.Objects;
10 |
11 | /**
12 | * 简单的POST请求执行器,请求的参数是String, 返回的结果也是String
13 | */
14 | public class OkHttpSimplePostRequestExecutor implements RequestExecutor {
15 | protected OkHttpClient httpClient;
16 |
17 | public OkHttpSimplePostRequestExecutor(OkHttpClient httpClient) {
18 | this.httpClient = httpClient;
19 | }
20 |
21 | @Override
22 | public String execute(String uri, String postEntity) throws DtErrorException, IOException {
23 | RequestBody body = RequestBody.Companion.create(postEntity, MediaType.parse("text/plain; charset=utf-8"));
24 | Request request = new Request.Builder().url(uri).post(body).build();
25 | Response response = httpClient.newCall(request).execute();
26 | return this.handleResponse(Objects.requireNonNull(response.body()).string());
27 | }
28 |
29 | public String handleResponse(String responseContent) throws DtErrorException {
30 | if (responseContent.isEmpty()) {
31 | throw new DtErrorException("无响应内容");
32 | }
33 |
34 | if (responseContent.startsWith("")) {
35 | //xml格式输出直接返回
36 | return responseContent;
37 | }
38 |
39 | DtError error = DtError.fromJson(responseContent);
40 | if (error.getErrorCode() != 0) {
41 | throw new DtErrorException(error);
42 | }
43 | return responseContent;
44 | }
45 |
46 | public static RequestExecutor create(OkHttpClient httpClient) {
47 | return new OkHttpSimplePostRequestExecutor(httpClient);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/http/RequestExecutor.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.http;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.error.DtErrorException;
4 |
5 | import java.io.IOException;
6 |
7 | /**
8 | * http请求执行器.
9 | *
10 | * @param 返回值类型
11 | * @param 请求参数类型
12 | */
13 | public interface RequestExecutor {
14 |
15 | /**
16 | * 执行http请求.
17 | *
18 | * @param uri uri
19 | * @param data 数据
20 | * @return 响应结果
21 | * @throws DtErrorException 自定义异常
22 | * @throws IOException io异常
23 | */
24 | T execute(String uri, E data) throws DtErrorException, IOException;
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/http/ResponseHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.http;
2 |
3 | /**
4 | * http请求响应回调处理接口.
5 | */
6 | public interface ResponseHandler {
7 | /**
8 | * 响应结果处理.
9 | *
10 | * @param t 要处理的对象
11 | */
12 | void handle(T t);
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/http/ResponseHttpStatusInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.http;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.error.DtRuntimeErrorEnum;
4 | import com.github.tingyugetc520.ali.dingtalk.error.DtRuntimeException;
5 | import lombok.extern.slf4j.Slf4j;
6 | import okhttp3.Interceptor;
7 | import okhttp3.Request;
8 | import okhttp3.Response;
9 | import org.apache.http.HttpStatus;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | import java.io.IOException;
13 |
14 | @Slf4j
15 | public class ResponseHttpStatusInterceptor implements Interceptor {
16 |
17 | @NotNull
18 | @Override
19 | public Response intercept(@NotNull Chain chain) throws IOException {
20 | Request request = chain.request();
21 |
22 | log.debug("");
23 | Response response = chain.proceed(request);
24 | if (response.code() != HttpStatus.SC_OK) {
25 | throw new DtRuntimeException(DtRuntimeErrorEnum.DT_HTTP_CALL_FAILED);
26 | }
27 |
28 | return response;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/json/DtDateAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.json;
2 |
3 | import com.google.gson.*;
4 |
5 | import java.lang.reflect.Type;
6 | import java.util.Date;
7 |
8 | /**
9 | * json date
10 | */
11 | public class DtDateAdapter implements JsonSerializer, JsonDeserializer {
12 |
13 | @Override
14 | public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
15 | if (src == null) {
16 | return null;
17 | }
18 | return new JsonPrimitive(src.getTime());
19 | }
20 |
21 | @Override
22 | public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
23 | throws JsonParseException {
24 | if (json == null) {
25 | return null;
26 | }
27 | return new Date(json.getAsJsonPrimitive().getAsLong());
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/json/DtErrorAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.json;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.error.DtError;
4 | import com.google.gson.*;
5 |
6 | import java.lang.reflect.Type;
7 |
8 | /**
9 | * json error
10 | */
11 | public class DtErrorAdapter implements JsonDeserializer {
12 |
13 | @Override
14 | public DtError deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
15 | throws JsonParseException {
16 | DtError.DtErrorBuilder errorBuilder = DtError.builder();
17 | JsonObject jsonObject = json.getAsJsonObject();
18 |
19 | if (jsonObject.get("errcode") != null && !jsonObject.get("errcode").isJsonNull()) {
20 | errorBuilder.errorCode(GsonHelper.getAsPrimitiveInt(jsonObject.get("errcode")));
21 | }
22 | if (jsonObject.get("errmsg") != null && !jsonObject.get("errmsg").isJsonNull()) {
23 | errorBuilder.errorMsg(GsonHelper.getAsString(jsonObject.get("errmsg")));
24 | }
25 |
26 | errorBuilder.json(json.toString());
27 |
28 | return errorBuilder.build();
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/json/DtGsonBuilder.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.json;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.bean.user.DtUser;
4 | import com.github.tingyugetc520.ali.dingtalk.error.DtError;
5 | import com.google.gson.Gson;
6 | import com.google.gson.GsonBuilder;
7 |
8 | import java.util.Date;
9 |
10 | /**
11 | *
12 | */
13 | public class DtGsonBuilder {
14 | private static final GsonBuilder INSTANCE = new GsonBuilder();
15 |
16 | static {
17 | INSTANCE.disableHtmlEscaping();
18 | INSTANCE.registerTypeAdapter(Date.class, new DtDateAdapter());
19 | INSTANCE.registerTypeAdapter(DtError.class, new DtErrorAdapter());
20 | INSTANCE.registerTypeAdapter(DtUser.class, new DtUserAdapter());
21 | }
22 |
23 | public static Gson create() {
24 | return INSTANCE.create();
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/json/DtUserAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.json;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.bean.user.DtUser;
4 | import com.google.gson.*;
5 |
6 | import java.lang.reflect.Type;
7 | import java.util.*;
8 |
9 | /**
10 | * dt user adapter
11 | */
12 | public class DtUserAdapter implements JsonDeserializer, JsonSerializer {
13 |
14 | @Override
15 | public DtUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
16 | JsonObject o = json.getAsJsonObject();
17 | DtUser user = new DtUser();
18 |
19 | if (o.get("department") != null) {
20 | JsonArray departJsonArray = o.get("department").getAsJsonArray();
21 | List departIds = new ArrayList<>(departJsonArray.size());
22 | for (JsonElement jsonElement : departJsonArray) {
23 | departIds.add(jsonElement.getAsLong());
24 | }
25 | user.setDepartIds(departIds);
26 | }
27 |
28 | if (o.get("orderInDepts") != null) {
29 | String orderString = o.get("orderInDepts").getAsString();
30 | JsonObject departJsonObject = GsonParser.parse(orderString);
31 | Map departOrders = new HashMap<>(departJsonObject.size());
32 | for (Map.Entry entry : departJsonObject.entrySet()) {
33 | departOrders.put(Long.parseLong(entry.getKey()), entry.getValue().getAsLong());
34 | }
35 | user.setDepartOrders(departOrders);
36 | }
37 |
38 | if (o.get("isLeaderInDepts") != null) {
39 | String leaderString = o.get("isLeaderInDepts").getAsString();
40 | JsonObject leaderJsonObject = GsonParser.parse(leaderString);
41 | Map leaderInDeparts = new HashMap<>(leaderJsonObject.size());
42 | for (Map.Entry entry : leaderJsonObject.entrySet()) {
43 | leaderInDeparts.put(Long.parseLong(entry.getKey()), entry.getValue().getAsBoolean());
44 | }
45 | user.setIsLeaderInDeparts(leaderInDeparts);
46 | }
47 |
48 | if (o.get("extattr") != null) {
49 | JsonObject extJsonObject = o.get("extattr").getAsJsonObject();
50 | Map extAttr = new HashMap<>(extJsonObject.size());
51 | for (Map.Entry entry : extJsonObject.entrySet()) {
52 | extAttr.put(entry.getKey(), entry.getValue().getAsString());
53 | }
54 | user.setExtAttr(extAttr);
55 | }
56 |
57 | if (o.get("roles") != null) {
58 | JsonArray roleJsonElements = o.get("roles").getAsJsonArray();
59 | List roles = new ArrayList<>(roleJsonElements.size());
60 | for (JsonElement roleJsonElement : roleJsonElements) {
61 | JsonObject jsonObject = roleJsonElement.getAsJsonObject();
62 | roles.add(DtUser.Role.builder()
63 | .id(GsonHelper.getLong(jsonObject, "id"))
64 | .name(GsonHelper.getString(jsonObject, "name"))
65 | .type(GsonHelper.getInteger(jsonObject, "type"))
66 | .groupName(GsonHelper.getString(jsonObject, "groupName"))
67 | .build());
68 | }
69 | user.setRoles(roles);
70 | }
71 |
72 | user.setUserId(GsonHelper.getString(o, "userid"));
73 | user.setUnionId(GsonHelper.getString(o, "unionid"));
74 |
75 | user.setManagerUserId(GsonHelper.getString(o, "managerUserId"));
76 | user.setHiredDate(new Date(GsonHelper.getLong(o, "hiredDate")));
77 | user.setTel(GsonHelper.getString(o, "tel"));
78 |
79 | user.setRemark(GsonHelper.getString(o, "remark"));
80 | user.setWorkPlace(GsonHelper.getString(o, "workPlace"));
81 |
82 | user.setName(GsonHelper.getString(o, "name"));
83 | user.setPosition(GsonHelper.getString(o, "position"));
84 | user.setMobile(GsonHelper.getString(o, "mobile"));
85 | user.setStateCode(GsonHelper.getString(o, "stateCode"));
86 | user.setEmail(GsonHelper.getString(o, "email"));
87 | user.setOrgEmail(GsonHelper.getString(o, "orgEmail"));
88 |
89 | user.setIsSenior(GsonHelper.getBoolean(o, "isSenior"));
90 | user.setJobNumber(GsonHelper.getString(o, "jobnumber"));
91 | user.setActive(GsonHelper.getBoolean(o, "active"));
92 | user.setAvatar(GsonHelper.getString(o, "avatar"));
93 |
94 | user.setIsAdmin(GsonHelper.getBoolean(o, "isAdmin"));
95 |
96 | user.setIsHide(GsonHelper.getBoolean(o, "isHide"));
97 | user.setIsBoss(GsonHelper.getBoolean(o, "isBoss"));
98 | user.setRealAuthed(GsonHelper.getBoolean(o, "realAuthed"));
99 | return user;
100 | }
101 |
102 | @Override
103 | public JsonElement serialize(DtUser user, Type typeOfSrc, JsonSerializationContext context) {
104 | JsonObject o = new JsonObject();
105 | if (user.getUserId() != null) {
106 | o.addProperty("userid", user.getUserId());
107 | }
108 | if (user.getUnionId() != null) {
109 | o.addProperty("unionid", user.getUnionId());
110 | }
111 |
112 | if (user.getManagerUserId() != null) {
113 | o.addProperty("managerUserId", user.getManagerUserId());
114 | }
115 | if (user.getHiredDate() != null) {
116 | o.addProperty("hiredDate", user.getHiredDate().getTime());
117 | }
118 | if (user.getTel() != null) {
119 | o.addProperty("tel", user.getTel());
120 | }
121 |
122 | if (user.getRemark() != null) {
123 | o.addProperty("remark", user.getRemark());
124 | }
125 | if (user.getWorkPlace() != null) {
126 | o.addProperty("workPlace", user.getWorkPlace());
127 | }
128 |
129 | if (user.getName() != null) {
130 | o.addProperty("name", user.getName());
131 | }
132 | if (user.getPosition() != null) {
133 | o.addProperty("position", user.getPosition());
134 | }
135 | if (user.getMobile() != null) {
136 | o.addProperty("mobile", user.getMobile());
137 | }
138 | if (user.getStateCode() != null) {
139 | o.addProperty("stateCode", user.getStateCode());
140 | }
141 | if (user.getEmail() != null) {
142 | o.addProperty("email", user.getEmail());
143 | }
144 | if (user.getOrgEmail() != null) {
145 | o.addProperty("orgEmail", user.getOrgEmail());
146 | }
147 | if (user.getIsSenior() != null) {
148 | o.addProperty("isSenior", user.getIsSenior());
149 | }
150 | if (user.getJobNumber() != null) {
151 | o.addProperty("jobnumber", user.getJobNumber());
152 | }
153 | if (user.getActive() != null) {
154 | o.addProperty("active", user.getActive());
155 | }
156 | if (user.getAvatar() != null) {
157 | o.addProperty("avatar", user.getAvatar());
158 | }
159 | if (user.getExtAttr() != null) {
160 | JsonObject jsonObject = new JsonObject();
161 | for (Map.Entry entry : user.getExtAttr().entrySet()) {
162 | jsonObject.addProperty(entry.getKey(), entry.getValue());
163 | }
164 | o.add("extattr", jsonObject);
165 | }
166 | if (user.getRoles() != null) {
167 | JsonArray jsonArray = new JsonArray();
168 | for (DtUser.Role role : user.getRoles()) {
169 | JsonObject jsonObject = GsonHelper.buildJsonObject(
170 | "id", role.getId(),
171 | "name", role.getName(),
172 | "type", role.getType(),
173 | "groupName", role.getGroupName()
174 | );
175 | jsonArray.add(jsonObject);
176 | }
177 | o.add("roles", jsonArray);
178 | }
179 | if (user.getDepartIds() != null) {
180 | JsonArray jsonArray = new JsonArray();
181 | for (Long departId : user.getDepartIds()) {
182 | jsonArray.add(new JsonPrimitive(departId));
183 | }
184 | o.add("department", jsonArray);
185 | }
186 | if (user.getDepartOrders() != null) {
187 | JsonObject jsonObject = new JsonObject();
188 | for (Map.Entry entry : user.getDepartOrders().entrySet()) {
189 | jsonObject.addProperty(entry.getKey().toString(), entry.getValue());
190 | }
191 | o.addProperty("orderInDepts", jsonObject.toString());
192 | }
193 | if (user.getIsAdmin() != null) {
194 | o.addProperty("isAdmin", user.getIsAdmin());
195 | }
196 | if (user.getIsLeaderInDeparts() != null) {
197 | JsonObject jsonObject = new JsonObject();
198 | for (Map.Entry entry : user.getIsLeaderInDeparts().entrySet()) {
199 | jsonObject.addProperty(entry.getKey().toString(), entry.getValue());
200 | }
201 | o.addProperty("isLeaderInDepts", new Gson().toJson(jsonObject));
202 | }
203 | if (user.getIsHide() != null) {
204 | o.addProperty("isHide", user.getIsHide());
205 | }
206 | if (user.getIsBoss() != null) {
207 | o.addProperty("isBoss", user.getIsBoss());
208 | }
209 | if (user.getRealAuthed() != null) {
210 | o.addProperty("realAuthed", user.getRealAuthed());
211 | }
212 |
213 | return o;
214 | }
215 |
216 | }
217 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/json/GsonHelper.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.json;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.error.DtRuntimeException;
4 | import com.google.common.collect.Lists;
5 | import com.google.gson.JsonArray;
6 | import com.google.gson.JsonElement;
7 | import com.google.gson.JsonObject;
8 | import jodd.util.MathUtil;
9 |
10 | import java.util.List;
11 |
12 | public class GsonHelper {
13 |
14 | public static boolean isNull(JsonElement element) {
15 | return element == null || element.isJsonNull();
16 | }
17 |
18 | public static boolean isNotNull(JsonElement element) {
19 | return !isNull(element);
20 | }
21 |
22 | public static Long getLong(JsonObject json, String property) {
23 | return getAsLong(json.get(property));
24 | }
25 |
26 | public static long getPrimitiveLong(JsonObject json, String property) {
27 | return getAsPrimitiveLong(json.get(property));
28 | }
29 |
30 | public static Integer getInteger(JsonObject json, String property) {
31 | return getAsInteger(json.get(property));
32 | }
33 |
34 | public static int getPrimitiveInteger(JsonObject json, String property) {
35 | return getAsPrimitiveInt(json.get(property));
36 | }
37 |
38 | public static Double getDouble(JsonObject json, String property) {
39 | return getAsDouble(json.get(property));
40 | }
41 |
42 | public static double getPrimitiveDouble(JsonObject json, String property) {
43 | return getAsPrimitiveDouble(json.get(property));
44 | }
45 |
46 | public static Float getFloat(JsonObject json, String property) {
47 | return getAsFloat(json.get(property));
48 | }
49 |
50 | public static float getPrimitiveFloat(JsonObject json, String property) {
51 | return getAsPrimitiveFloat(json.get(property));
52 | }
53 |
54 | public static Boolean getBoolean(JsonObject json, String property) {
55 | return getAsBoolean(json.get(property));
56 | }
57 |
58 | public static String getString(JsonObject json, String property) {
59 | return getAsString(json.get(property));
60 | }
61 |
62 | public static String getAsString(JsonElement element) {
63 | return isNull(element) ? null : element.getAsString();
64 | }
65 |
66 | public static Long getAsLong(JsonElement element) {
67 | return isNull(element) ? null : element.getAsLong();
68 | }
69 |
70 | public static long getAsPrimitiveLong(JsonElement element) {
71 | Long r = getAsLong(element);
72 | return r == null ? 0L : r;
73 | }
74 |
75 | public static Integer getAsInteger(JsonElement element) {
76 | return isNull(element) ? null : element.getAsInt();
77 | }
78 |
79 | public static int getAsPrimitiveInt(JsonElement element) {
80 | Integer r = getAsInteger(element);
81 | return r == null ? 0 : r;
82 | }
83 |
84 | public static Boolean getAsBoolean(JsonElement element) {
85 | return isNull(element) ? null : element.getAsBoolean();
86 | }
87 |
88 | public static boolean getAsPrimitiveBool(JsonElement element) {
89 | Boolean r = getAsBoolean(element);
90 | return r != null && r;
91 | }
92 |
93 | public static Double getAsDouble(JsonElement element) {
94 | return isNull(element) ? null : element.getAsDouble();
95 | }
96 |
97 | public static double getAsPrimitiveDouble(JsonElement element) {
98 | Double r = getAsDouble(element);
99 | return r == null ? 0d : r;
100 | }
101 |
102 | public static Float getAsFloat(JsonElement element) {
103 | return isNull(element) ? null : element.getAsFloat();
104 | }
105 |
106 | public static float getAsPrimitiveFloat(JsonElement element) {
107 | Float r = getAsFloat(element);
108 | return r == null ? 0f : r;
109 | }
110 |
111 | public static Integer[] getIntArray(JsonObject o, String string) {
112 | JsonArray jsonArray = getAsJsonArray(o.getAsJsonArray(string));
113 | if (jsonArray == null) {
114 | return null;
115 | }
116 |
117 | List result = Lists.newArrayList();
118 | for (int i = 0; i < jsonArray.size(); i++) {
119 | result.add(jsonArray.get(i).getAsInt());
120 | }
121 |
122 | return result.toArray(new Integer[0]);
123 | }
124 |
125 | public static String[] getStringArray(JsonObject o, String string) {
126 | JsonArray jsonArray = getAsJsonArray(o.getAsJsonArray(string));
127 | if (jsonArray == null) {
128 | return null;
129 | }
130 |
131 | List result = Lists.newArrayList();
132 | for (int i = 0; i < jsonArray.size(); i++) {
133 | result.add(jsonArray.get(i).getAsString());
134 | }
135 |
136 | return result.toArray(new String[0]);
137 | }
138 |
139 | public static Long[] getLongArray(JsonObject o, String string) {
140 | JsonArray jsonArray = getAsJsonArray(o.getAsJsonArray(string));
141 | if (jsonArray == null) {
142 | return null;
143 | }
144 |
145 | List result = Lists.newArrayList();
146 | for (int i = 0; i < jsonArray.size(); i++) {
147 | result.add(jsonArray.get(i).getAsLong());
148 | }
149 |
150 | return result.toArray(new Long[0]);
151 | }
152 |
153 | public static JsonArray getAsJsonArray(JsonElement element) {
154 | return element == null ? null : element.getAsJsonArray();
155 | }
156 |
157 | /**
158 | * 快速构建JsonObject对象,批量添加一堆属性
159 | *
160 | * @param keyOrValue 包含key或value的数组
161 | * @return JsonObject对象.
162 | */
163 | public static JsonObject buildJsonObject(Object... keyOrValue) {
164 | JsonObject result = new JsonObject();
165 | put(result, keyOrValue);
166 | return result;
167 | }
168 |
169 | /**
170 | * 批量向JsonObject对象中添加属性
171 | *
172 | * @param jsonObject 原始JsonObject对象
173 | * @param keyOrValue 包含key或value的数组
174 | */
175 | public static void put(JsonObject jsonObject, Object... keyOrValue) {
176 | if (MathUtil.isOdd(keyOrValue.length)) {
177 | throw new DtRuntimeException("参数个数必须为偶数");
178 | }
179 |
180 | for (int i = 0; i < keyOrValue.length / 2; i++) {
181 | final Object key = keyOrValue[2 * i];
182 | final Object value = keyOrValue[2 * i + 1];
183 | if (value == null) {
184 | jsonObject.add(key.toString(), null);
185 | continue;
186 | }
187 |
188 | if (value instanceof Boolean) {
189 | jsonObject.addProperty(key.toString(), (Boolean) value);
190 | } else if (value instanceof Character) {
191 | jsonObject.addProperty(key.toString(), (Character) value);
192 | } else if (value instanceof Number) {
193 | jsonObject.addProperty(key.toString(), (Number) value);
194 | } else if (value instanceof JsonElement) {
195 | jsonObject.add(key.toString(), (JsonElement) value);
196 | } else if (value instanceof List) {
197 | JsonArray array = new JsonArray();
198 | ((List>) value).forEach(a -> array.add(a.toString()));
199 | jsonObject.add(key.toString(), array);
200 | } else {
201 | jsonObject.addProperty(key.toString(), value.toString());
202 | }
203 | }
204 |
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tingyugetc520/ali/dingtalk/util/json/GsonParser.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.util.json;
2 |
3 | import com.google.gson.JsonObject;
4 | import com.google.gson.JsonParser;
5 | import com.google.gson.stream.JsonReader;
6 |
7 | import java.io.Reader;
8 |
9 | /**
10 | *
11 | */
12 | public class GsonParser {
13 | private static final JsonParser JSON_PARSER = new JsonParser();
14 |
15 | public static JsonObject parse(String json) {
16 | return JSON_PARSER.parse(json).getAsJsonObject();
17 | }
18 |
19 | public static JsonObject parse(Reader json) {
20 | return JSON_PARSER.parse(json).getAsJsonObject();
21 | }
22 |
23 | public static JsonObject parse(JsonReader json) {
24 | return JSON_PARSER.parse(json).getAsJsonObject();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/java/com/github/tingyugetc520/ali/dingtalk/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tingyugetc520/DtJava/4d4e286b2a125386f0313381da2595f66f466d19/src/test/java/com/github/tingyugetc520/ali/dingtalk/.DS_Store
--------------------------------------------------------------------------------
/src/test/java/com/github/tingyugetc520/ali/dingtalk/bean/user/DtUserTest.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.bean.user;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.testng.annotations.Test;
5 |
6 | import java.util.Date;
7 |
8 | @Test
9 | @Slf4j
10 | public class DtUserTest {
11 |
12 | public void testFromJson() {
13 | String json = "{" +
14 | " \"errcode\":0," +
15 | " \"unionEmpExt\":{}," +
16 | " \"unionid\":\"unionid\"," +
17 | " \"openId\":\"openid\"," +
18 | " \"roles\":[" +
19 | " {" +
20 | " \"groupName\":\"默认\"," +
21 | " \"name\":\"主管理员\"," +
22 | " \"id\":1834797999," +
23 | " \"type\":101" +
24 | " }" +
25 | " ]," +
26 | " \"remark\":\"\"," +
27 | " \"userid\":\"manager6666\"," +
28 | " \"isLeaderInDepts\":\"{1:false}\"," +
29 | " \"isBoss\":false," +
30 | " \"hiredDate\":1613577600000," +
31 | " \"isSenior\":false," +
32 | " \"tel\":\"\"," +
33 | " \"department\":[" +
34 | " 1" +
35 | " ]," +
36 | " \"workPlace\":\"\"," +
37 | " \"email\":\"emial@dtjava.com\"," +
38 | " \"orderInDepts\":\"{1:176305901987690512}\"," +
39 | " \"mobile\":\"1234567890\"," +
40 | " \"errmsg\":\"ok\"," +
41 | " \"active\":true," +
42 | " \"avatar\":\"\"," +
43 | " \"isAdmin\":true," +
44 | " \"tags\":{}," +
45 | " \"isHide\":false," +
46 | " \"jobnumber\":\"\"," +
47 | " \"name\":\"DtJava\"," +
48 | " \"extattr\":{" +
49 | " \"爱好\":\"写代码\"," +
50 | " \"职级\":\"架构师\"," +
51 | " \"花名\":\"花的名字\"" +
52 | " }," +
53 | " \"stateCode\":\"86\"," +
54 | " \"position\":\"\"," +
55 | " \"realAuthed\":true" +
56 | "}";
57 | DtUser dtUser = DtUser.fromJson(json);
58 | log.info("user: {}", dtUser);
59 | }
60 |
61 | public void testToJson() {
62 | DtUser dtUser = DtUser.builder()
63 | .hiredDate(new Date(1613577600000L))
64 | .build();
65 | log.info("json: {}", dtUser.toJson());
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/test/java/com/github/tingyugetc520/ali/dingtalk/demo/DtDemoApp.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.demo;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.api.DtService;
4 | import com.github.tingyugetc520.ali.dingtalk.api.impl.DtServiceImpl;
5 | import com.github.tingyugetc520.ali.dingtalk.bean.user.DtUser;
6 | import com.github.tingyugetc520.ali.dingtalk.constant.DtConstant;
7 | import com.github.tingyugetc520.ali.dingtalk.demo.servlet.DtEndpointServlet;
8 | import com.github.tingyugetc520.ali.dingtalk.message.DtMessageRouter;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.eclipse.jetty.server.Server;
11 | import org.eclipse.jetty.servlet.ServletHandler;
12 | import org.eclipse.jetty.servlet.ServletHolder;
13 |
14 | import java.io.InputStream;
15 |
16 | import static com.github.tingyugetc520.ali.dingtalk.constant.DtConstant.EventType.ChangeContactGroup;
17 |
18 | @Slf4j
19 | public class DtDemoApp {
20 |
21 | private static DtDemoConfigStorage dtConfigStorage;
22 | private static DtService dtService;
23 | private static DtMessageRouter dtMessageRouter;
24 |
25 | public static void main(String[] args) throws Exception {
26 | log.info("application start");
27 |
28 | initDt();
29 |
30 | initServer();
31 |
32 | DtUser user = dtService.getUserService().getById(dtConfigStorage.getUserId());
33 | log.info("dt user:{}", user);
34 | }
35 |
36 | private static void initDt() {
37 | InputStream inputStream = ClassLoader.getSystemResourceAsStream("test-config.json");
38 | dtConfigStorage = DtDemoConfigStorage.fromJson(inputStream);
39 |
40 | dtService = new DtServiceImpl();
41 | dtService.setDtConfigStorage(dtConfigStorage);
42 |
43 | dtMessageRouter = new DtMessageRouter(dtService);
44 | dtMessageRouter
45 | .rule().async(false).eventTypeGroup(ChangeContactGroup).eventType(DtConstant.EventType.ChangeContact.USER_ADD_ORG).handler(((message, context, dtService) -> {
46 | log.info("收到消息");
47 | return true;
48 | })).end();
49 | }
50 |
51 | private static void initServer() throws Exception {
52 | Server server = new Server(8080);
53 |
54 | ServletHandler servletHandler = new ServletHandler();
55 | server.setHandler(servletHandler);
56 |
57 | ServletHolder endpointServletHolder = new ServletHolder(new DtEndpointServlet(dtConfigStorage, dtService, dtMessageRouter));
58 | servletHandler.addServletWithMapping(endpointServletHolder, "/*");
59 |
60 | server.start();
61 | server.join();
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/src/test/java/com/github/tingyugetc520/ali/dingtalk/demo/DtDemoConfigStorage.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.demo;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.config.impl.DtDefaultConfigImpl;
4 | import com.github.tingyugetc520.ali.dingtalk.util.json.DtGsonBuilder;
5 | import com.google.gson.stream.JsonReader;
6 | import lombok.Data;
7 | import lombok.EqualsAndHashCode;
8 | import lombok.ToString;
9 |
10 | import java.io.InputStream;
11 | import java.io.InputStreamReader;
12 | import java.nio.charset.StandardCharsets;
13 |
14 | @EqualsAndHashCode(callSuper = true)
15 | @Data
16 | @ToString
17 | public class DtDemoConfigStorage extends DtDefaultConfigImpl {
18 | private static final long serialVersionUID = 4554681793802089123L;
19 | private String userId;
20 |
21 | public static DtDemoConfigStorage fromJson(InputStream is) {
22 | JsonReader reader = new JsonReader(new InputStreamReader(is, StandardCharsets.UTF_8));
23 | return DtGsonBuilder.create().fromJson(reader, DtDemoConfigStorage.class);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/java/com/github/tingyugetc520/ali/dingtalk/demo/servlet/DtEndpointServlet.java:
--------------------------------------------------------------------------------
1 | package com.github.tingyugetc520.ali.dingtalk.demo.servlet;
2 |
3 | import com.github.tingyugetc520.ali.dingtalk.api.DtService;
4 | import com.github.tingyugetc520.ali.dingtalk.bean.message.DtEventMessage;
5 | import com.github.tingyugetc520.ali.dingtalk.bean.message.DtEventOutMessage;
6 | import com.github.tingyugetc520.ali.dingtalk.config.DtConfigStorage;
7 | import com.github.tingyugetc520.ali.dingtalk.error.DtRuntimeException;
8 | import com.github.tingyugetc520.ali.dingtalk.message.DtMessageRouter;
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | import javax.servlet.ServletException;
12 | import javax.servlet.http.HttpServlet;
13 | import javax.servlet.http.HttpServletRequest;
14 | import javax.servlet.http.HttpServletResponse;
15 | import java.io.IOException;
16 |
17 | /**
18 | *
19 | */
20 | @Slf4j
21 | public class DtEndpointServlet extends HttpServlet {
22 | private static final long serialVersionUID = 1L;
23 | protected DtConfigStorage configStorage;
24 | protected DtService dtService;
25 | protected DtMessageRouter dtMessageRouter;
26 |
27 | public DtEndpointServlet(DtConfigStorage configStorage, DtService dtService,
28 | DtMessageRouter dtMessageRouter) {
29 | this.configStorage = configStorage;
30 | this.dtService = dtService;
31 | this.dtMessageRouter = dtMessageRouter;
32 | }
33 |
34 | @Override
35 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
36 | log.info("收到消息");
37 | response.setContentType("text/html;charset=utf-8");
38 | response.setStatus(HttpServletResponse.SC_OK);
39 |
40 | String signature = request.getParameter("signature");
41 | String nonce = request.getParameter("nonce");
42 | String timestamp = request.getParameter("timestamp");
43 | DtEventMessage message = null;
44 | try {
45 | message = DtEventMessage.fromEncryptedJson(request.getInputStream(), configStorage, timestamp, nonce, signature);
46 | } catch (DtRuntimeException e) {
47 | log.error("error", e);
48 | }
49 | if (message == null) {
50 | response.getWriter().println("非法请求");
51 | log.info("非法请求");
52 | return;
53 | }
54 |
55 | Boolean outMessage = this.dtMessageRouter.route(message);
56 | String res = DtEventOutMessage.toEncrypted(configStorage, outMessage).toEncryptedJson();
57 | log.info("合法请求 {} 返回响应 {}", message, res);
58 | response.getWriter().println(res);
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/test/resources/test-config.sample.json:
--------------------------------------------------------------------------------
1 | {
2 | "corpId": "钉钉的corpId",
3 | "agentId": "钉钉应用的agentId",
4 | "appKey": "钉钉应用的appKey",
5 | "appSecret": "钉钉应用的appSecret",
6 | "aesKey": "钉钉应用的aesKey",
7 | "token": "钉钉应用的token",
8 | "httpProxyType": "HTTP代理类型;0:不代理,1:反向代理,2:正向代理",
9 | "httpProxyHost": "HTTP正向代理的HOST",
10 | "httpProxyPort": "HTTP正向代理的Port",
11 | "httpProxyServer": "HTTP反向代理的Server",
12 | "userId": "钉钉应用的userId,主要拿来做test用"
13 | }
--------------------------------------------------------------------------------