企业微信服务商开发(二)

企业微信服务商开发(一)中已经把准备工作就绪了,结下来就是开发环境配置,框架搭建。

开发环境

本次开发所用的技术spring-boot以及开源的微信API接口SDK企业微信开发模块weixin-java-cp

框架搭建

首先创建一个spring-boot项目,如下:

这里只把框架中关键的一些配置和类例出。

bootstrap.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
spring:
application:
name: weixin
profiles:
active: dev

server:
servlet:
contextPath: /weixin
port: 9700

wechat:
cp:
corpId: 111 #服务商的corpid
providerSecret: 111 #服务商的secret
suiteId: 111 #服务商创建的应用或者关联小程序的id
suiteSecret: 111 #服务商提供的应用或者关联小程序的secret
token: 111 #这里的token可以随便写
aesKey: 111 #这里的aesKey可以随便写

上面就是启动配置文件配置参数

WxCpProperties.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import com.alibaba.fastjson.JSON;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* 该类是在程序启动时,将企业微信服务商的配置信息映射到该属性类中。
* @author dgb
* @create 2019-03-05 16:14
**/
@Data
@ConfigurationProperties("wechat.cp")
public class WxCpProperties {

/**
* 服务商的corpid
*/
private String corpId;
/**
* 服务商的secret
*/
private String providerSecret;
/**
* 服务商的token
*/
private String token;
/**
* 服务商的EncodingAESKey
*/
private String aesKey;
/**
* 服务商的应用Id
*/
private String suiteId;
/**
* 服务商的应用secret
*/
private String suiteSecret;
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
WxCpConfiguration.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import com.itwork.weixin.cp.handler.*;
import com.itwork.weixin.cp.properties.WxCpProperties;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.cp.WxCpConsts;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage;
import me.chanjar.weixin.cp.message.WxCpMessageRouter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.Map;

/**
* 该类是初始化微信API的SDK,
* 需要在工程中依赖在上文中提到的`weixin-java-cp`
* 具体怎么配置以及使用请移步到(https://gitee.com/binary/weixin-java-cp-demo)demo
* @author dgb
*/
@Slf4j
@Configuration
@EnableConfigurationProperties(WxCpProperties.class)
public class WxCpConfiguration {

@Autowired
private LogHandler logHandler;
@Autowired
private NullHandler nullHandler;
@Autowired
private LocationHandler locationHandler;
@Autowired
private MenuHandler menuHandler;
@Autowired
private MsgHandler msgHandler;
@Autowired
private UnsubscribeHandler unsubscribeHandler;
@Autowired
private SubscribeHandler subscribeHandler;
@Autowired
private SuiteTicketHandler suiteTicketHandler;
@Autowired
private CreateAuthHandler createAuthHandler;
@Autowired
private CannelAuthHandler cannelAuthHandler;
@Autowired
private ContactChangeHandler contactChangeHandler;
@Autowired
private EnterAgentHandler enterAgentHandler;
@Autowired
private ChangeContactHandler changeContactHandler;
@Autowired
private WxCpProperties properties;

private static Map<String, WxCpMessageRouter> routers = Maps.newHashMap();
private static Map<String, WxCpService> cpServices = Maps.newHashMap();

public static WxCpMessageRouter getRouters(String corpId) {
return routers.get(corpId);
}

public static WxCpService getCpServices(String corpId) {
return cpServices.get(corpId);
}

public WxCpService setCpServices(String corpId) {
WxCpService cpService = new WxCpServiceImpl();
val configStorage = new WxCpInMemoryConfigStorage();
configStorage.setCorpId(corpId);
configStorage.setCorpSecret(this.properties.getSuiteSecret());
configStorage.setToken(this.properties.getToken());
configStorage.setAesKey(this.properties.getAesKey());
cpService.setWxCpConfigStorage(configStorage);
cpServices.put(corpId, cpService);
routers.put(corpId, this.newRouter(cpService));
return cpService;
}

@PostConstruct
public void initService() {
WxCpService cpService = new WxCpServiceImpl();
val configStorage = new WxCpInMemoryConfigStorage();
configStorage.setCorpId(this.properties.getSuiteId());
configStorage.setCorpSecret(this.properties.getSuiteSecret());
configStorage.setToken(this.properties.getToken());
configStorage.setAesKey(this.properties.getAesKey());
cpService.setWxCpConfigStorage(configStorage);
cpServices.put(this.properties.getSuiteId(), cpService);
routers.put(this.properties.getSuiteId(), this.newRouter(cpService));
}

protected WxCpMessageRouter newRouter(WxCpService wxCpService) {
final val newRouter = new WxCpMessageRouter(wxCpService);
// 记录所有事件的日志 (异步执行)
newRouter.rule().handler(this.logHandler).next();
// 自定义菜单事件
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxConsts.MenuButtonType.CLICK).handler(this.menuHandler).end();
// 点击菜单链接事件(这里使用了一个空的处理器,可以根据自己需要进行扩展)
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxConsts.MenuButtonType.VIEW).handler(this.nullHandler).end();
// 关注事件
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxConsts.EventType.SUBSCRIBE).handler(this.subscribeHandler)
.end();
// 取消关注事件
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxConsts.EventType.UNSUBSCRIBE)
.handler(this.unsubscribeHandler).end();
// 上报地理位置事件
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxConsts.EventType.LOCATION).handler(this.locationHandler)
.end();
// 接收地理位置消息
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION)
.handler(this.locationHandler).end();
// 扫码事件(这里使用了一个空的处理器,可以根据自己需要进行扩展)
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxConsts.EventType.SCAN).handler(this.nullHandler).end();
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxCpConsts.EventType.CHANGE_CONTACT)
.handler(this.contactChangeHandler).end();
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxCpConsts.EventType.ENTER_AGENT)
.handler(this.enterAgentHandler).end();
// 处理应用suite_ticket
newRouter.rule().async(false).infoType(WxConsts.XmlInfoType.SUITE_TICKET)
.handler(this.suiteTicketHandler).end();
// 处理企业安装应用授权成功通知
newRouter.rule().async(false).infoType(WxConsts.XmlInfoType.CREATE_AUTH)
.handler(this.createAuthHandler).end();
// 处理企业取消应用通知
newRouter.rule().async(false).infoType(WxConsts.XmlInfoType.CANCEL_AUTH)
.handler(this.cannelAuthHandler).end();
// 处理通讯录变更事件通知
newRouter.rule().async(false).infoType(WxConsts.XmlInfoType.CHANGE_CONTACT)
.handler(this.changeContactHandler).end();
// 默认
newRouter.rule().async(false).handler(this.msgHandler).end();

return newRouter;
}
}
SuiteServcie
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import me.chanjar.weixin.common.error.WxErrorException;

/**
* 这个类是我封装的获取企业微信各种凭证的方法
*
*/
public interface SuiteServcie {

/**
* 获取服务商凭证
*
* @return
*/
String getProviderAccessToken() throws WxErrorException;

/**
* 获取suite_access_token
*
* @return
* @throws WxErrorException
*/
String getSuiteAccessToken() throws WxErrorException;

/**
* 获取预授权码
*
* @return
*/
String getPreAuthCode() throws WxErrorException;

/**
* 设置授权配置
*/
void setSessionInfo() throws WxErrorException;

/**
* 获取企业永久授权码
*
* @param authCode 临时授权码,会在授权成功时附加在redirect_uri中跳转回第三方服务商网站,
* 或通过回调推送给服务商
*/
String getPermanentCode(String authCode) throws WxErrorException;

/**
* 获取企业凭证
*
* @param authCorpId 授权方corpid
* @return
*/
String getCorpToken(String authCorpId) throws WxErrorException;

/**
* 获取企业授权信息
*
* @param authCorpId
*/
void getAuthInfo(String authCorpId) throws WxErrorException;

/**
* 获取企业操作通讯录的access_token
*
* @param authCorpId
* @return
*/
String getContactAccessToken(String authCorpId) throws WxErrorException;
}
SuiteServiceImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
import com.alibaba.fastjson.JSON;
import com.itwork.business.api.AuthInfoApi;
import com.itwork.business.api.NoticeEventApi;
import com.itwork.common.config.consts.Consts;
import com.itwork.common.dto.AuthInfoInDto;
import com.itwork.common.dto.NoticeEventOutDto;
import com.itwork.common.utils.DtoUtils;
import com.itwork.weixin.cp.bean.WxAccessToken;
import com.itwork.weixin.cp.bean.WxCorpAccessToken;
import com.itwork.weixin.cp.bean.WxCorpAuthInfo;
import com.itwork.weixin.cp.bean.WxProAuthCode;
import com.itwork.weixin.cp.bean.WxProviderAccessToken;
import com.itwork.weixin.cp.bean.WxSuiteAccessToken;
import com.itwork.weixin.cp.properties.WxCpProperties;
import com.itwork.weixin.cp.service.SuiteServcie;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;
import java.util.Map;

/**
* @author dgb
* @create 2019-03-21 10:50
**/
@Slf4j
@Service
public class SuiteServiceImpl implements SuiteServcie {

@Autowired
private RestTemplate restTemplate;

@Autowired
private RedisTemplate<Object, Object> redisTemplate;

@Autowired
private WxCpProperties properties;

/**
* 全局的是否正在刷新token的锁
*/
protected final Object getGlobalProviderAccessTokenRefreshLock = new Object();
protected final Object getGlobalSuiteAccessTokenRefreshLock = new Object();
protected final Object getGlobalProAuthCodeRefreshLock = new Object();
protected final Object getGlobalCorpAccessTokenRefreshLock = new Object();
protected final Object getGlobalContactAccessTokenRefreshLock = new Object();


@Override
public String getProviderAccessToken() throws WxErrorException {
if (this.isProviderAddessTokenExpires()) {
synchronized (this.getGlobalProviderAccessTokenRefreshLock) {
log.info("获取服务商凭证...................start.....................");
String url = "https://qyapi.weixin.qq.com/cgi-bin/service/get_provider_token";
Map<String, String> params = Maps.newHashMap();
params.put("corpid", this.properties.getCorpId());
params.put("provider_secret", this.properties.getProviderSecret());

log.info("获取服务商凭证请求参数:{}", params);
String response = restTemplate.postForObject(url, JSON.toJSONString(params), String.class);
log.info("获取服务商凭证响应参数:{}", response);

WxError error = WxError.fromJson(response);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}

WxProviderAccessToken token = WxProviderAccessToken.formJson(response);
long expiresIn = System.currentTimeMillis() + (token.getExpiresIn() - 200) * 1000L;
redisTemplate.opsForValue().set(Consts.PROVIDER_ACCESS_TOKEN + this.properties.getCorpId(), token.getProviderAccessToken());
redisTemplate.opsForValue().set(Consts.PROVIDER_ACCESS_TOKEN_EXPIRES + this.properties.getCorpId(), expiresIn);
log.info("获取服务商凭证...................end.....................");

}
}
return redisTemplate.opsForValue().get(Consts.PROVIDER_ACCESS_TOKEN + this.properties.getCorpId()).toString();
}

/**
* 判断服务商凭证是否过期
*
* @return
*/
private boolean isProviderAddessTokenExpires() {
Long expiresIn = (Long) redisTemplate.opsForValue().get(Consts.PROVIDER_ACCESS_TOKEN_EXPIRES + this.properties.getSuiteId());
if (expiresIn != null) {
return System.currentTimeMillis() > expiresIn;
}
return true;
}

@Override
public String getSuiteAccessToken() throws WxErrorException {
if (this.isSuiteAccessTokeExpires()) {
synchronized (this.getGlobalSuiteAccessTokenRefreshLock) {
log.info("获取suite_access_token.................start....................");
String url = "https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token";

Map<Object, Object> params = Maps.newHashMap();
params.put("suite_id", this.properties.getSuiteId());
params.put("suite_secret", this.properties.getSuiteSecret());
params.put("suite_ticket", redisTemplate.opsForValue().get(WxConsts.XmlInfoType.SUITE_TICKET + ":" + this.properties.getSuiteId()));

log.info("获取suite_access_token请求参数:{}", params);
String response = restTemplate.postForObject(url, JSON.toJSONString(params), String.class);
log.info("获取suite_access_token响应参数:{}", response);

WxError error = WxError.fromJson(response);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxSuiteAccessToken accessToken = WxSuiteAccessToken.fromJson(response);

long expiresIn = System.currentTimeMillis() + (accessToken.getExpiresIn() - 200) * 1000L;

redisTemplate.opsForValue().set(Consts.SUITE_ACCESS_TOKEN + this.properties.getSuiteId(), accessToken.getSuiteAccessToken());
redisTemplate.opsForValue().set(Consts.SUITE_ACCESS_TOKEN_EXPIRES + this.properties.getSuiteId(), expiresIn);
log.info("获取suite_access_token.................end....................");
}
}
return redisTemplate.opsForValue().get(Consts.SUITE_ACCESS_TOKEN + this.properties.getSuiteId()).toString();
}

/**
* 判断suite_access_token是否过期
*
* @return
*/
private boolean isSuiteAccessTokeExpires() {
Long expiresIn = (Long) redisTemplate.opsForValue().get(Consts.SUITE_ACCESS_TOKEN_EXPIRES + this.properties.getSuiteId());
if (expiresIn != null) {
return System.currentTimeMillis() > expiresIn;
}
return true;
}

@Override
public String getPreAuthCode() throws WxErrorException {
if (this.isPreAuthCodeExpires()) {
synchronized (this.getGlobalProAuthCodeRefreshLock) {
log.info("获取pro_auth_code.................start....................");
String suiteAccessToken = getSuiteAccessToken();
String url = "https://qyapi.weixin.qq.com/cgi-bin/service/get_pre_auth_code?suite_access_token=" + suiteAccessToken;

String response = restTemplate.getForObject(url, String.class);
log.info("获取pro_auth_code响应参数:{}", response);

WxError error = WxError.fromJson(response);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}

WxProAuthCode code = WxProAuthCode.fromJson(response);
long expiresIn = System.currentTimeMillis() + (code.getExpiresIn() - 200) * 1000L;

redisTemplate.opsForValue().set(Consts.PRO_AUTH_CODE + this.properties.getSuiteId(), code.getPreAuthCode());
redisTemplate.opsForValue().set(Consts.PRO_AUTH_CODE_EXPIRES + this.properties.getSuiteId(), expiresIn);
log.info("获取pro_auth_code.................end....................");
}
}

return redisTemplate.opsForValue().get(Consts.PRO_AUTH_CODE + this.properties.getSuiteId()).toString();
}

/**
* 判断获取预授权码是否过期
*
* @return
*/
private boolean isPreAuthCodeExpires() {
Long expiresIn = (Long) redisTemplate.opsForValue().get(Consts.PRO_AUTH_CODE_EXPIRES + this.properties.getSuiteId());
if (expiresIn != null) {
return System.currentTimeMillis() > expiresIn;
}
return true;
}

@Override
public void setSessionInfo() throws WxErrorException {
log.info("设置授权配置......................start...................");
String proAuthCode = this.getPreAuthCode();

String suiteAccessToken = this.getSuiteAccessToken();

String url = "https://qyapi.weixin.qq.com/cgi-bin/service/set_session_info?suite_access_token=" + suiteAccessToken;

Map<String, Object> params = Maps.newHashMap();
params.put("pre_auth_code", proAuthCode);
Map<String, Object> sessInfo = Maps.newHashMap();
//sessInfo.put("appid", "[" + this.properties.getSuiteId() + "]");
sessInfo.put("auth_type", 1);//授权类型:0 正式授权, 1 测试授权。 默认值为0。注意,请确保应用在正式发布后的授权类型为“正式授权”
params.put("session_info", sessInfo);

log.info("设置授权配置请求参数:{}", params);
String response = restTemplate.postForObject(url, JSON.toJSONString(params), String.class);
log.info("设置授权配置响应参数:{}", response);

WxError error = WxError.fromJson(response);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
log.info("设置授权配置......................end...................");
}


@Override
public String getPermanentCode(String authCode) throws WxErrorException {
log.info("获取企业永久授权码...................start...................");
String suiteAccessToken = this.getSuiteAccessToken();
String url = "https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=" + suiteAccessToken;

Map<String, String> params = Maps.newHashMap();
params.put("auth_code", authCode);
log.info("获取企业永久授权码请求参数:{}", params);
String response = restTemplate.postForObject(url, JSON.toJSONString(params), String.class);
log.info("获取企业永久授权码响应参数:{}", response);

WxError error = WxError.fromJson(response);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxCorpAuthInfo authInfo = WxCorpAuthInfo.formJson(response);
String corpId = authInfo.getAuthCorpInfo().getCorpid();
long expiresIn = System.currentTimeMillis() + (authInfo.getExpiresIn() - 200) * 1000L;
redisTemplate.opsForValue().set(Consts.PERMANENT_CODE + corpId, authInfo.getPermanentCode());
redisTemplate.opsForValue().set(Consts.CORP_ACCESS_TOKEN + corpId, authInfo.getAccessToken());
redisTemplate.opsForValue().set(Consts.CORP_ACCESS_TOKEN_EXPIRES + corpId, expiresIn);
log.info("获取企业永久授权码...................end...................");
// TODO 这里可以把获取到的企业永久授权码持久到数据库中

// 获取企业授权信息
this.getAuthInfo(corpId);
return authInfo.getPermanentCode();
}


@Override
public String getCorpToken(String authCorpId) throws WxErrorException {
if (this.isCorpAccessTokenExpires(authCorpId)) {
synchronized (this.getGlobalCorpAccessTokenRefreshLock) {
log.info("获取企业凭证...................start..........................");
String suiteAccessToken = this.getSuiteAccessToken();
String url = "https://qyapi.weixin.qq.com/cgi-bin/service/get_corp_token?suite_access_token=" + suiteAccessToken;
String permanentCode = "企业永久授权码,这里持久到数据库了";
Map<String, String> params = Maps.newHashMap();
params.put("auth_corpid", authCorpId);
params.put("permanent_code", permanentCode);

log.info("获取企业凭证请求参数:{}", params);
String response = restTemplate.postForObject(url, JSON.toJSONString(params), String.class);
log.info("获取企业凭证响应参数:{}", response);

WxError error = WxError.fromJson(response);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}

WxCorpAccessToken token = WxCorpAccessToken.fromJson(response);
long expiresIn = System.currentTimeMillis() + (token.getExpiresIn() - 200) * 1000L;
redisTemplate.opsForValue().set(Consts.CORP_ACCESS_TOKEN + authCorpId, token.getAccessToken());
redisTemplate.opsForValue().set(Consts.CORP_ACCESS_TOKEN_EXPIRES + authCorpId, expiresIn);
log.info("获取企业凭证...................end..........................");
}
}

return redisTemplate.opsForValue().get(Consts.CORP_ACCESS_TOKEN + authCorpId).toString();
}

/**
* 判断企业凭证是否过期
*
* @param authCorpId
* @return
*/
private boolean isCorpAccessTokenExpires(String authCorpId) {
Long expiresIn = (Long) redisTemplate.opsForValue().get(Consts.CORP_ACCESS_TOKEN_EXPIRES + authCorpId);
if (expiresIn != null) {
return System.currentTimeMillis() > expiresIn;
}
return true;
}

@Override
public void getAuthInfo(String authCorpId) throws WxErrorException {
log.info("获取企业授权信息....................start.....................");
String suiteAccessToken = this.getSuiteAccessToken();
String url = "https://qyapi.weixin.qq.com/cgi-bin/service/get_auth_info?suite_access_token=" + suiteAccessToken;
String permanentCode = redisTemplate.opsForValue().get(Consts.PERMANENT_CODE + authCorpId).toString();

Map<String, String> params = Maps.newHashMap();
params.put("auth_corpid", authCorpId);
params.put("permanent_code", permanentCode);

log.info("获取企业授权信息请求参数:{}", params);
String response = restTemplate.postForObject(url, JSON.toJSONString(params), String.class);
log.info("获取企业授权信息响应参数:{}", response);

WxError error = WxError.fromJson(response);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}

WxCorpAuthInfo authInfo = WxCorpAuthInfo.formJson(response);
// TODO 这里可以把获取到的企业授权信息持久到数据库中

log.info("获取企业授权信息....................end.....................");
}


@Override
public String getContactAccessToken(String authCorpId) throws WxErrorException {
if (this.isContactAccessTokenExpires(authCorpId)) {
synchronized (this.getGlobalContactAccessTokenRefreshLock) {

String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s";
String secert = "这里的secert是用到企业微信中通讯录同步助手中的secert";
url = String.format(url, authCorpId, secert);
String response = restTemplate.getForObject(url, String.class);
WxError error = WxError.fromJson(response);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}

WxAccessToken accessToken = WxAccessToken.fromJson(response);
long expiresIn = System.currentTimeMillis() + (accessToken.getExpiresIn() - 200) * 1000L;
redisTemplate.opsForValue().set(Consts.CONTACT_ACCESS_TOKEN + authCorpId, accessToken.getAccessToken());
redisTemplate.opsForValue().set(Consts.CONTACT_ACCESS_TOKEN_EXPIRES + authCorpId, expiresIn);
}
}
return redisTemplate.opsForValue().get(Consts.CONTACT_ACCESS_TOKEN + authCorpId).toString();
}

/**
* 判断企业通讯录同步凭证是否过期
*
* @param authCorpId
* @return
*/
private boolean isContactAccessTokenExpires(String authCorpId) {
Long expiresIn = (Long) redisTemplate.opsForValue().get(Consts.CONTACT_ACCESS_TOKEN_EXPIRES + authCorpId);
if (expiresIn != null) {
return System.currentTimeMillis() > expiresIn;
}
return true;
}
}
Consts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* @author dgb
* @create 2019-03-21 11:06
**/

public class Consts {

/**
* 服务商凭证KEY
**/
public static final String PROVIDER_ACCESS_TOKEN = "provider_access_token:";
public static final String PROVIDER_ACCESS_TOKEN_EXPIRES = "provider_access_token_expires:";
/**
* 应用凭证KEY
**/
public static final String SUITE_ACCESS_TOKEN = "suite_access_token:";
public static final String SUITE_ACCESS_TOKEN_EXPIRES = "suite_access_token_expires:";
/**
* 预授权码KEY
**/
public static final String PRO_AUTH_CODE = "pro_auth_code:";
public static final String PRO_AUTH_CODE_EXPIRES = "pro_auth_code_expires:";
/**
* 企业凭证KEY
**/
public static final String CORP_ACCESS_TOKEN = "corp_access_token:";
public static final String CORP_ACCESS_TOKEN_EXPIRES = "corp_access_token_expires:";
/**
* 企业通讯录同步凭证KEY
**/
public static final String CONTACT_ACCESS_TOKEN = "contact_access_token:";
public static final String CONTACT_ACCESS_TOKEN_EXPIRES = "contact_access_token_expires:";
/**
* 企业永久授权KEY
**/
public static final String PERMANENT_CODE = "permanent_code:";

}

以上就是在开发环境中用到的一些关键配置类,接下来配置各回调URL

获取示例源码

请关注微信公众号:「特想学英语」并回复:企业微信

原文作者: dgb8901,yinxing

原文链接: https://www.itwork.club/2019/03/26/corp-develop-2/

版权声明: 转载请注明出处

为您推荐

体验小程序「简易记账」

关注公众号「特想学英语」

企业微信服务商开发(三)