浏览代码

update tt

master
xhxu 3 年前
父节点
当前提交
f339e14398
共有 40 个文件被更改,包括 4203 次插入533 次删除
  1. +6
    -0
      mallinkService/pom.xml
  2. +295
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/FmTtOpenInRedisDBConfigStorage.java
  3. +71
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/FmTtOpenService.java
  4. +261
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenComponentService.java
  5. +486
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenMaService.java
  6. +23
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenService.java
  7. +23
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenAuthorizationInfo.java
  8. +43
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenAuthorizationInfoGsonAdapter.java
  9. +26
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenGsonBuilder.java
  10. +19
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenRetrieveCode.java
  11. +24
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenRetrieveCodeGsonAdapter.java
  12. +40
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenTicket.java
  13. +34
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenTicketGsonAdapter.java
  14. +517
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenComponentServiceImpl.java
  15. +643
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenMaServiceImpl.java
  16. +62
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceAbstractImpl.java
  17. +67
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceApacheHttpClientImpl.java
  18. +8
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceImpl.java
  19. +28
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaMsgService.java
  20. +39
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaQrcode.java
  21. +31
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaQrcodeService.java
  22. +132
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaService.java
  23. +52
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaUserService.java
  24. +69
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtQrcodeRequestExecutor.java
  25. +21
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaGsonBuilder.java
  26. +70
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaSubscribeMessage.java
  27. +33
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaSubscribeMessageGsonAdapter.java
  28. +30
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaMsgServiceImpl.java
  29. +26
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaQrcodeServiceImpl.java
  30. +312
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaServiceImpl.java
  31. +41
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaUserServiceImpl.java
  32. +88
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/utils/MsgDecrypt.java
  33. +55
    -0
      mallinkService/src/main/java/com/iformall/service/toutiao/utils/ServerVerification.java
  34. +131
    -0
      mlToutiaoOpen/src/main/java/com/iformall/controller/TtAuthController.java
  35. +388
    -0
      mlToutiaoOpen/src/main/java/com/iformall/controller/TtCalllbackController.java
  36. +0
    -106
      mlToutiaoOpen/src/main/java/com/iformall/controller/WechatAuthController.java
  37. +0
    -415
      mlToutiaoOpen/src/main/java/com/iformall/controller/WechatCalllbackController.java
  38. +1
    -0
      mlToutiaoOpen/src/main/java/com/iformall/controller/WxWeappInfoController.java
  39. +4
    -8
      mlToutiaoOpen/src/main/resources/application-dev.yml
  40. +4
    -4
      mlToutiaoOpen/src/main/resources/application-prod.yml

+ 6
- 0
mallinkService/pom.xml 查看文件

@@ -27,6 +27,12 @@
<version>6.4.0</version>
</dependency>
-->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
<scope>compile</scope>
</dependency>
</dependencies>

</project>

+ 295
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/FmTtOpenInRedisDBConfigStorage.java 查看文件

@@ -0,0 +1,295 @@
package com.iformall.service.toutiao;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.iformall.common.FmHttpClientBuilder;
import com.iformall.domain.po.WxAuthorizerInfo;
import com.iformall.domain.po.WxComponentVerifyTicket;
import com.iformall.mapper.WxAuthorizerInfoMapper;
import com.iformall.mapper.WxComponentVerifyTicketMapper;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.util.Pool;

import java.util.Date;

public class FmTtOpenInRedisDBConfigStorage extends WxOpenInMemoryConfigStorage {
private final static String COMPONENT_VERIFY_TICKET_KEY = "tt_component_verify_ticket:";
private final static String COMPONENT_ACCESS_TOKEN_KEY = "tt_component_access_token:";

private final static String AUTHORIZER_REFRESH_TOKEN_KEY = "tt_authorizer_refresh_token:";
private final static String AUTHORIZER_ACCESS_TOKEN_KEY = "tt_authorizer_access_token:";
private final static String JSAPI_TICKET_KEY = "tt_jsapi_ticket:";
private final static String CARD_API_TICKET_KEY = "tt_card_api_ticket:";

protected final Pool<Jedis> jedisPool;
private WxComponentVerifyTicketMapper wxComponentVerifyTicketMapper;
private WxAuthorizerInfoMapper wxAuthorizerInfoMapper;


/**
* redis 存储的 key 的前缀,可为空
*/
private String keyPrefix;
private String componentVerifyTicketKey;
private String componentAccessTokenKey;
private String authorizerRefreshTokenKey;
private String authorizerAccessTokenKey;
private String jsapiTicketKey;
private String cardApiTicket;

private final Logger logger = LoggerFactory.getLogger(this.getClass());


public FmTtOpenInRedisDBConfigStorage(Pool<Jedis> jedisPool) {
this.jedisPool = jedisPool;
}

public FmTtOpenInRedisDBConfigStorage(Pool<Jedis> jedisPool, String keyPrefix) {
this.jedisPool = jedisPool;
this.keyPrefix = keyPrefix;
}

public FmTtOpenInRedisDBConfigStorage(JedisPool jedisPool, WxComponentVerifyTicketMapper componentVerifyTicketMapper, WxAuthorizerInfoMapper authorizerInfoMapper) {
this.jedisPool = jedisPool;
this.wxComponentVerifyTicketMapper = componentVerifyTicketMapper;
this.wxAuthorizerInfoMapper = authorizerInfoMapper;
}

@Override
public void setComponentAppId(String componentAppId) {
super.setComponentAppId(componentAppId);
String prefix = StringUtils.isBlank(keyPrefix) ? "" :
(StringUtils.endsWith(keyPrefix, ":") ? keyPrefix : (keyPrefix + ":"));
componentVerifyTicketKey = prefix + COMPONENT_VERIFY_TICKET_KEY.concat(componentAppId);
componentAccessTokenKey = prefix + COMPONENT_ACCESS_TOKEN_KEY.concat(componentAppId);
authorizerRefreshTokenKey = prefix + AUTHORIZER_REFRESH_TOKEN_KEY.concat(componentAppId);
authorizerAccessTokenKey = prefix + AUTHORIZER_ACCESS_TOKEN_KEY.concat(componentAppId);
this.jsapiTicketKey = JSAPI_TICKET_KEY.concat(componentAppId);
this.cardApiTicket = CARD_API_TICKET_KEY.concat(componentAppId);
}

@Override
public String getComponentVerifyTicket() {
try (Jedis jedis = this.jedisPool.getResource()) {
logger.info("getComponentVerifyTicket " + this.componentVerifyTicketKey);
String ticket = jedis.get(this.componentVerifyTicketKey);
if(StringUtils.isBlank(ticket)) {
String appId;
String [] keys = componentAccessTokenKey.split(":");
if(StringUtils.isBlank(keyPrefix)) {
appId = keys[1];
} else {
appId = keys[2];
}
WxComponentVerifyTicket wxComponentVerifyTicket = new WxComponentVerifyTicket();
wxComponentVerifyTicket.setComponentAppid(getComponentAppId());
try {
wxComponentVerifyTicket = wxComponentVerifyTicketMapper.selectOne(new QueryWrapper<>(wxComponentVerifyTicket));
if(wxComponentVerifyTicket != null) {
ticket = wxComponentVerifyTicket.getComponentVerifyTicket();
}
}catch (Exception e) {
logger.error(e.getMessage());
}
}
return ticket;
}
}

@Override
public void setComponentVerifyTicket(String componentVerifyTicket) {
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.set(this.componentVerifyTicketKey, componentVerifyTicket);
logger.info("setComponentVerifyTicket " + componentVerifyTicket);
}
}

@Override
public String getComponentAccessToken() {
try (Jedis jedis = this.jedisPool.getResource()) {
return jedis.get(this.componentAccessTokenKey);
}
}

@Override
public boolean isComponentAccessTokenExpired() {
try (Jedis jedis = this.jedisPool.getResource()) {
return jedis.ttl(this.componentAccessTokenKey) < 2;
}
}

@Override
public void expireComponentAccessToken(){
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.expire(this.componentAccessTokenKey, 0);
}
}

@Override
public void updateComponentAccessToken(String componentAccessToken, int expiresInSeconds) {
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.setex(this.componentAccessTokenKey, expiresInSeconds - 200, componentAccessToken);
logger.info("updateComponentVerifyTicketAccessToken " + componentAccessToken);

saveComponentVerifyTicket(componentAccessToken, expiresInSeconds);
}
}

@Async
public void saveComponentVerifyTicket(String componentAccessToken, int expiresInSeconds) {
WxComponentVerifyTicket ticket = new WxComponentVerifyTicket();
ticket.setComponentAppid(getComponentAppId());
ticket.setAccessToken(componentAccessToken);
ticket.setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + (expiresInSeconds * 1000))));
int num = wxComponentVerifyTicketMapper.updateAccessToken(ticket);
logger.info("updateComponentVerifyTicketAccessToken rows: " + num);
}

private String getKey(String prefix, String appId) {
return prefix.endsWith(":") ? prefix.concat(appId) : prefix.concat(":").concat(appId);
}

@Override
public String getAuthorizerRefreshToken(String appId) {
try (Jedis jedis = this.jedisPool.getResource()) {
return jedis.get(this.getKey(this.authorizerRefreshTokenKey, appId));
}
}

@Override
public void setAuthorizerRefreshToken(String appId, String authorizerRefreshToken) {
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.set(this.getKey(this.authorizerRefreshTokenKey, appId), authorizerRefreshToken);
logger.info("setAuthorizerRefreshToken " + appId);

saveAuthorizerRefreshToken(appId, authorizerRefreshToken);
}
}

@Async
public void saveAuthorizerRefreshToken(String appId, String authorizerRefreshToken) {
WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo();
authorizerInfo.setAuthorizerAppid(appId);
authorizerInfo.setRefreshToken(authorizerRefreshToken);
int num = wxAuthorizerInfoMapper.updateRefreshToken(authorizerInfo);
}

@Override
public String getAuthorizerAccessToken(String appId) {
try (Jedis jedis = this.jedisPool.getResource()) {
return jedis.get(this.getKey(this.authorizerAccessTokenKey, appId));
}
}

@Override
public boolean isAuthorizerAccessTokenExpired(String appId) {
try (Jedis jedis = this.jedisPool.getResource()) {
return jedis.ttl(this.getKey(this.authorizerAccessTokenKey, appId)) < 2;
}
}

@Override
public void expireAuthorizerAccessToken(String appId) {
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.expire(this.getKey(this.authorizerAccessTokenKey, appId), 0);
}
}

@Override
public void updateAuthorizerAccessToken(String appId, WxOpenAuthorizerAccessToken authorizerAccessToken) {
this.updateAuthorizerAccessToken(appId, authorizerAccessToken.getAuthorizerAccessToken(), authorizerAccessToken.getExpiresIn());

logger.info("updateAuthorizerAccessToken " + appId);

dbUpdateAccessToken(appId, authorizerAccessToken);
}

@Async
public void dbUpdateAccessToken(String appId, WxOpenAuthorizerAccessToken authorizerAccessToken) {
WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo();
authorizerInfo.setAuthorizerAppid(appId);
authorizerInfo.setAccessToken(authorizerAccessToken.getAuthorizerAccessToken());
authorizerInfo.setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + (authorizerAccessToken.getExpiresIn() * 1000))));
int num = wxAuthorizerInfoMapper.updateAccessToken(authorizerInfo);
logger.info("updateAuthorizerAccessToken " + appId + " rows: " + num);
}

@Override
public void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds) {
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.setex(this.getKey(this.authorizerAccessTokenKey, appId), expiresInSeconds - 200, authorizerAccessToken);
}
}

@Override
public String getJsapiTicket(String appId) {
try (Jedis jedis = this.jedisPool.getResource()) {
return jedis.get(this.getKey(this.jsapiTicketKey, appId));
}
}

@Override
public boolean isJsapiTicketExpired(String appId) {
try (Jedis jedis = this.jedisPool.getResource()) {
return jedis.ttl(this.getKey(this.jsapiTicketKey, appId)) < 2;
}
}

@Override
public void expireJsapiTicket(String appId) {
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.expire(this.getKey(this.jsapiTicketKey, appId), 0);
}
}

@Override
public void updateJsapiTicket(String appId, String jsapiTicket, int expiresInSeconds) {
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.setex(this.getKey(this.jsapiTicketKey, appId), expiresInSeconds - 200, jsapiTicket);
}

// TODO update to DB
}

@Override
public String getCardApiTicket(String appId) {
try (Jedis jedis = this.jedisPool.getResource()) {
return jedis.get(this.getKey(this.cardApiTicket, appId));
}
}

@Override
public boolean isCardApiTicketExpired(String appId) {
try (Jedis jedis = this.jedisPool.getResource()) {
return jedis.ttl(this.getKey(this.cardApiTicket, appId)) < 2;
}
}

@Override
public void expireCardApiTicket(String appId) {
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.expire(this.getKey(this.cardApiTicket, appId), 0);
}
}

@Override
public void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds) {
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.setex(this.getKey(this.cardApiTicket, appId), expiresInSeconds - 200, cardApiTicket);
}

// TODO update to DB
}

@Override
public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
return FmHttpClientBuilder.get();
}
}

+ 71
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/FmTtOpenService.java 查看文件

@@ -0,0 +1,71 @@
package com.iformall.service.toutiao;

import com.iformall.config.WechatOpenProperties;
import com.iformall.config.WechatRedisProperies;
import com.iformall.mapper.WxAuthorizerInfoMapper;
import com.iformall.mapper.WxComponentVerifyTicketMapper;
import com.iformall.service.toutiao.api.impl.TtOpenServiceImpl;
import com.iformall.service.wechat.FmOpenInRedisDBConfigStorage;
import com.iformall.service.wechat.handler.*;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.mp.constant.WxMpEventConstants;
import me.chanjar.weixin.open.api.impl.WxOpenMessageRouter;
import me.chanjar.weixin.open.api.impl.WxOpenServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisPool;

import javax.annotation.PostConstruct;

/**
* Stormeye WU
*/
@Service
@EnableConfigurationProperties({WechatOpenProperties.class, WechatRedisProperies.class})
public class FmTtOpenService extends TtOpenServiceImpl {

private Logger logger = LoggerFactory.getLogger(getClass());

@Autowired
private WechatOpenProperties wechatProperties;

@Autowired
private WechatRedisProperies redisProperies;

@Autowired
private WxComponentVerifyTicketMapper componentVerifyTicketMapper;

@Autowired
private WxAuthorizerInfoMapper authorizerInfoMapper;

private static JedisPool pool;

@PostConstruct
public void init() {
FmTtOpenInRedisDBConfigStorage configStorage = new FmTtOpenInRedisDBConfigStorage(getJedisPool(), componentVerifyTicketMapper, authorizerInfoMapper);
configStorage.setComponentAppId(wechatProperties.getComponentAppId());
configStorage.setComponentAppSecret(wechatProperties.getComponentSecret());
configStorage.setComponentToken(wechatProperties.getComponentToken());
configStorage.setComponentAesKey(wechatProperties.getComponentAesKey());
setWxOpenConfigStorage(configStorage);
}

private JedisPool getJedisPool() {
if (pool == null) {
synchronized (FmTtOpenService.class) {
if (pool == null) {
pool = new JedisPool(redisProperies, redisProperies.getHost(),
redisProperies.getPort(), redisProperies.getConnectionTimeout(),
redisProperies.getSoTimeout(), redisProperies.getPassword(),
redisProperies.getDatabase(), redisProperies.getClientName(),
redisProperies.isSsl(), redisProperies.getSslSocketFactory(),
redisProperies.getSslParameters(), redisProperies.getHostnameVerifier());
}
}
}
return pool;
}
}

+ 261
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenComponentService.java 查看文件

@@ -0,0 +1,261 @@
package com.iformall.service.toutiao.api;

import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.iformall.service.toutiao.api.bean.TtOpenAuthorizationInfo;
import com.iformall.service.toutiao.api.bean.TtOpenTicket;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.open.api.WxOpenConfigStorage;
import me.chanjar.weixin.open.bean.WxOpenCreateResult;
import me.chanjar.weixin.open.bean.WxOpenGetResult;
import me.chanjar.weixin.open.bean.WxOpenMaCodeTemplate;
import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
import me.chanjar.weixin.open.bean.result.*;

import java.util.List;

/**
* .
*
* @author
*/
public interface TtOpenComponentService {

String API_COMPONENT_TOKEN_URL = "https://open.microapp.bytedance.com/openapi/v1/auth/tp/token";

String API_CREATE_PREAUTHCODE_URL = "https://open.microapp.bytedance.com/openapi/v2/auth/pre_auth_code";

String COMPONENT_LOGIN_PAGE_URL = "https://open.microapp.bytedance.com/mappconsole/tp/authorization?component_appid=%s&pre_auth_code=%s&redirect_uri=%s";

String API_QUERY_AUTH_URL = "https://open.microapp.bytedance.com/openapi/v1/oauth/token";

String API_AUTHORIZER_TOKEN_URL = "https://open.microapp.bytedance.com/openapi/v1/oauth/token";

String RETRIEVE_AUTHORIZER_TOKEN_URL = "https://open.microapp.bytedance.com/openapi/v1/auth/retrieve";

// String API_GET_AUTHORIZER_INFO_URL = "https://open.microapp.bytedance.com/openapi/v1/auth/retrieve";


String API_GET_AUTHORIZER_OPTION_URL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option";
String API_SET_AUTHORIZER_OPTION_URL = "https://api.weixin.qq.com/cgi-bin/component/api_set_authorizer_option";
String API_GET_AUTHORIZER_LIST = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list";


/**
* 手机端打开授权链接.
*/
String CONNECT_OAUTH2_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s&component_appid=%s#wechat_redirect";

/**
* 用code换取oauth2的access token.
*/
String OAUTH2_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/component/access_token?appid=%s&code=%s&grant_type=authorization_code&component_appid=%s";
/**
* 刷新oauth2的access token.
*/
String OAUTH2_REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/component/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s&component_appid=%s";

String MINIAPP_JSCODE_2_SESSION = "https://api.weixin.qq.com/sns/component/jscode2session?appid=%s&js_code=%s&grant_type=authorization_code&component_appid=%s";

String CREATE_OPEN_URL = "https://api.weixin.qq.com/cgi-bin/open/create";

String BIND_OPEN_URL = "https://api.weixin.qq.com/cgi-bin/open/bind";

String UNBIND_OPEN_URL = "https://api.weixin.qq.com/cgi-bin/open/unbind";

String GET_OPEN_URL = "https://api.weixin.qq.com/cgi-bin/open/get";

/**
* 快速创建小程序接口.
*/
String FAST_REGISTER_WEAPP_URL = "https://api.weixin.qq.com/cgi-bin/component/fastregisterweapp?action=create";
String FAST_REGISTER_WEAPP_SEARCH_URL = "https://api.weixin.qq.com/cgi-bin/component/fastregisterweapp?action=search";


/**
* 获取指定appid的开放平台小程序服务(继承一般小程序服务能力).
*
* @param appid .
* @return .
*/
TtOpenMaService getTtMaServiceByAppid(String appid);


WxOpenConfigStorage getWxOpenConfigStorage();

boolean checkSignature(String timestamp, String nonce, String encrypt, String signature);

TtOpenTicket decrypt(String encrypt);

String getComponentAccessToken(boolean forceRefresh) throws WxErrorException;

String post(String uri, String postData,String tokenKeyName) throws WxErrorException;

String postByAppAccessToken(String appId,String uri, String postData,String tokenKeyName) throws WxErrorException;
String get(String uri,String tokenKeyName) throws WxErrorException;

String getByAppAccessToken(String appId,String uri,String tokenKeyName) throws WxErrorException;

/**
* 获取用户授权页URL(来路URL和成功跳转URL 的域名都需要为三方平台设置的 登录授权的发起页域名).
*/
String getPreAuthUrl(String redirectUri) throws WxErrorException;


String route(TtOpenTicket openTicket) throws WxErrorException;

/**
* 使用授权码换取公众号或小程序的接口调用凭据和授权信息.
*/
TtOpenAuthorizationInfo getQueryAuth(String authorizationCode) throws WxErrorException;

/**
* 找回授权码.
*/
String retrieveCode(String authorizerAppid) throws WxErrorException;

// /**
// * 获取授权方的帐号基本信息.
// */
// WxOpenAuthorizerInfoResult getAuthorizerInfo(String authorizerAppid) throws WxErrorException;

/**
* 获取授权方的选项设置信息.
*/
WxOpenAuthorizerOptionResult getAuthorizerOption(String authorizerAppid, String optionName) throws WxErrorException;

/**
* 获取所有授权方列表.
*/
WxOpenAuthorizerListResult getAuthorizerList(int begin, int len) throws WxErrorException;

/**
* 设置授权方的选项信息.
*/
void setAuthorizerOption(String authorizerAppid, String optionName, String optionValue) throws WxErrorException;

String getAuthorizerAccessToken(String appid, boolean forceRefresh) throws WxErrorException;

WxMpOAuth2AccessToken oauth2getAccessToken(String appid, String code) throws WxErrorException;

WxMpOAuth2AccessToken oauth2refreshAccessToken(String appid, String refreshToken) throws WxErrorException;

String oauth2buildAuthorizationUrl(String appid, String redirectUri, String scope, String state);

WxMaJscode2SessionResult miniappJscode2Session(String appId, String jsCode) throws WxErrorException;

/**
* 代小程序实现业务.
* 小程序代码模版库管理:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1506504150_nMMh6&token=&lang=zh_CN
* access_token 为 component_access_token
*/
String GET_TEMPLATE_DRAFT_LIST_URL = "https://api.weixin.qq.com/wxa/gettemplatedraftlist";
String GET_TEMPLATE_LIST_URL = "https://api.weixin.qq.com/wxa/gettemplatelist";
String ADD_TO_TEMPLATE_URL = "https://api.weixin.qq.com/wxa/addtotemplate";
String DELETE_TEMPLATE_URL = "https://api.weixin.qq.com/wxa/deletetemplate";

/**
* 获取草稿箱内的所有临时代码草稿.
*
* @return 草稿箱代码模板列表(draftId)
* @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档
*/
List<WxOpenMaCodeTemplate> getTemplateDraftList() throws WxErrorException;

/**
* 获取代码模版库中的所有小程序代码模版.
*
* @return 小程序代码模版列表(templateId)
* @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档
*/
List<WxOpenMaCodeTemplate> getTemplateList() throws WxErrorException;

/**
* 将草稿箱的草稿选为小程序代码模版.
*
* @param draftId 草稿ID,本字段可通过“获取草稿箱内的所有临时代码草稿”接口获得
* @throws WxErrorException 操作失败时抛出,具体错误码请看此接口的注释文档
* @see #getTemplateDraftList
*/
void addToTemplate(long draftId) throws WxErrorException;

/**
* 删除指定小程序代码模版.
*
* @param templateId 要删除的模版ID
* @throws WxErrorException 操作失败时抛出,具体错误码请看此接口的注释文档
* @see #getTemplateList
*/
void deleteTemplate(long templateId) throws WxErrorException;

/**
* https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1498704199_1bcax&token=6df5e3650041eff2cd3ec3662425ad8d7beec8d9&lang=zh_CN
* 创建 开放平台帐号并绑定公众号/小程序.
* https://api.weixin.qq.com/cgi-bin/open/create
*
* @param appId 公众号/小程序的appId
* @return .
*/
WxOpenCreateResult createOpenAccount(String appId) throws WxErrorException;


/**
* https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/account/bind.html
* 将公众号/小程序绑定到开放平台帐号下
*
* @param appId 公众号/小程序的appId
* @param openAppid 开放平台帐号 appid,由创建开发平台帐号接口返回
*/
Boolean bindOpenAccount(String appId, String openAppid) throws WxErrorException;


/**
* https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/account/unbind.html
* 将公众号/小程序从开放平台帐号下解绑
*
* @param appId 公众号/小程序的appId
* @param openAppid 开放平台帐号 appid,由创建开发平台帐号接口返回
*/
Boolean unbindOpenAccount(String appId, String openAppid) throws WxErrorException;


/**
* https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/account/get.html
* 获取公众号/小程序所绑定的开放平台帐号
*
* @param appId 公众号/小程序的appId
* @return 开放平台帐号 appid,由创建开发平台帐号接口返回
*/
WxOpenGetResult getOpenAccount(String appId) throws WxErrorException;

/**
* https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21538208049W8uwq&token=&lang=zh_CN
* 第三方平台快速创建小程序.
* 注意:创建任务逻辑串行,单次任务结束后才可以使用相同信息下发第二次任务,请注意规避任务阻塞
*
* @param name 企业名(需与工商部门登记信息一致)
* @param code 企业代码
* @param codeType 企业代码类型 1:统一社会信用代码(18位) 2:组织机构代码(9位xxxxxxxx-x) 3:营业执照注册号(15位)
* @param legalPersonaWechat 法人微信号
* @param legalPersonaName 法人姓名(绑定银行卡)
* @param componentPhone 第三方联系电话(方便法人与第三方联系)
* @return .
* @throws WxErrorException .
*/
WxOpenResult fastRegisterWeapp(String name, String code, String codeType, String legalPersonaWechat, String legalPersonaName, String componentPhone) throws WxErrorException;

/**
* https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21538208049W8uwq&token=&lang=zh_CN
* 查询第三方平台快速创建小程序的任务状态
* 注意:该接口只提供当下任务结果查询,不建议过分依赖该接口查询所创建小程序。
* 小程序的成功状态可在第三方服务器中自行对账、查询。
* 不要频繁调用search接口,消息接收需通过服务器查看。调用search接口会消耗接口整体调用quato
*
* @param name 企业名(需与工商部门登记信息一致)
* @param legalPersonaWechat 法人微信号
* @param legalPersonaName 法人姓名(绑定银行卡)
* @throws WxErrorException .
*/
WxOpenResult fastRegisterWeappSearch(String name, String legalPersonaWechat, String legalPersonaName) throws WxErrorException;
}

+ 486
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenMaService.java 查看文件

@@ -0,0 +1,486 @@
package com.iformall.service.toutiao.api;

import com.iformall.service.toutiao.miniapp.TtMaService;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.open.bean.ma.WxMaOpenCommitExtInfo;
import me.chanjar.weixin.open.bean.message.WxOpenMaSubmitAuditMessage;
import me.chanjar.weixin.open.bean.result.*;

import java.io.File;
import java.util.List;
import java.util.Map;

/**
* <pre>
* 微信开放平台代小程序实现服务能力
* https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489144594_DhNoV&token=&lang=zh_CN
* </pre>
*
* @author yqx
* @date 2018/9/12
*/
public interface TtOpenMaService extends TtMaService {

/**
* 设置小程序服务器域名.
*
* <pre>
* 授权给第三方的小程序,其服务器域名只可以为第三方的服务器,当小程序通过第三方发布代码上线后,小程序原先自己配置的服务器域名将被删除,
* 只保留第三方平台的域名,所以第三方平台在代替小程序发布代码之前,需要调用接口为小程序添加第三方自身的域名。
* 提示:需要先将域名登记到第三方平台的小程序服务器域名中,才可以调用接口进行配置
* </pre>
*/
String API_MODIFY_DOMAIN = "https://api.weixin.qq.com/wxa/modify_domain";

/**
* 设置小程序业务域名(仅供第三方代小程序调用)
* <pre>
* 授权给第三方的小程序,其业务域名只可以为第三方的服务器,当小程序通过第三方发布代码上线后,小程序原先自己配置的业务域名将被删除,
* 只保留第三方平台的域名,所以第三方平台在代替小程序发布代码之前,需要调用接口为小程序添加业务域名。
* 提示:
* 1、需要先将域名登记到第三方平台的小程序业务域名中,才可以调用接口进行配置。
* 2、为授权的小程序配置域名时支持配置子域名,例如第三方登记的业务域名如为qq.com,则可以直接将qq.com及其子域名(如xxx.qq.com)也配置到授权的小程序中。
* </pre>
*/
String API_SET_WEBVIEW_DOMAIN = "https://api.weixin.qq.com/wxa/setwebviewdomain";

/**
* 获取帐号基本信息
* <pre>
* GET请求
* 注意:需要使用1.3环节获取到的新创建小程序appid及authorization_code换取authorizer_refresh_token进而得到authorizer_access_token。
* </pre>
*/
String API_GET_ACCOUNT_BASICINFO = "https://api.weixin.qq.com/cgi-bin/account/getaccountbasicinfo";

/**
* 绑定微信用户为小程序体验者
*/
String API_BIND_TESTER = "https://api.weixin.qq.com/wxa/bind_tester";


/**
* 解除绑定微信用户为小程序体验者
*/
String API_UNBIND_TESTER = "https://api.weixin.qq.com/wxa/unbind_tester";


/**
* 获取体验者列表
*/
String API_GET_TESTERLIST = "https://api.weixin.qq.com/wxa/memberauth";

/**
* 以下接口基础信息设置
* <p>
* https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21517799059ZSEMr&token=6f965b5daf30a98a6bbd2a386faea5c934e929bf&lang=zh_CN
* </p>
*/

/**
* 1. 设置小程序隐私设置(是否可被搜索)
*/
String API_CHANGE_WXA_SEARCH_STATUS = "https://api.weixin.qq.com/wxa/changewxasearchstatus";

/**
* 2. 查询小程序当前隐私设置(是否可被搜索)
*/
String API_GET_WXA_SEARCH_STATUS = "https://api.weixin.qq.com/wxa/getwxasearchstatus";

/**
* 3.1. 获取展示的公众号信息
*/
String API_GET_SHOW_WXA_ITEM = "https://api.weixin.qq.com/wxa/getshowwxaitem";

/**
* 3.2 设置展示的公众号
*/
String API_UPDATE_SHOW_WXA_ITEM = "https://api.weixin.qq.com/wxa/updateshowwxaitem";


/**
* 以下接口为三方平台代小程序实现的代码管理功能
* <p>
* https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=fe774228c66725425675810097f9e48d0737a4bf&lang=zh_CN
* </p>
*/

/**
* 1. 为授权的小程序帐号上传小程序代码
*/
String API_CODE_COMMIT = "https://api.weixin.qq.com/wxa/commit";

/**
* 2. 获取体验小程序的体验二维码
*/
String API_TEST_QRCODE = "https://api.weixin.qq.com/wxa/get_qrcode";

/**
* 3. 获取授权小程序帐号的可选类目
*/
String API_GET_CATEGORY = "https://api.weixin.qq.com/wxa/get_category";

/**
* 4. 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用)
*/
String API_GET_PAGE = "https://api.weixin.qq.com/wxa/get_page";

/**
* 5. 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用)
*/
String API_SUBMIT_AUDIT = "https://api.weixin.qq.com/wxa/submit_audit";

/**
* 7. 查询某个指定版本的审核状态(仅供第三方代小程序调用)
*/
String API_GET_AUDIT_STATUS = "https://api.weixin.qq.com/wxa/get_auditstatus";

/**
* 8. 查询最新一次提交的审核状态(仅供第三方代小程序调用)
*/
String API_GET_LATEST_AUDIT_STATUS = "https://api.weixin.qq.com/wxa/get_latest_auditstatus";

/**
* 9. 发布已通过审核的小程序(仅供第三方代小程序调用)
*/
String API_RELEASE = "https://api.weixin.qq.com/wxa/release";

/**
* 10. 修改小程序线上代码的可见状态(仅供第三方代小程序调用)
*/
String API_CHANGE_VISITSTATUS = "https://api.weixin.qq.com/wxa/change_visitstatus";

/**
* 11.小程序版本回退(仅供第三方代小程序调用)
*/
String API_REVERT_CODE_RELEASE = "https://api.weixin.qq.com/wxa/revertcoderelease";

/**
* 12.查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用)
*/
String API_GET_WEAPP_SUPPORT_VERSION = "https://api.weixin.qq.com/cgi-bin/wxopen/getweappsupportversion";

/**
* 13.设置最低基础库版本(仅供第三方代小程序调用)
*/
String API_SET_WEAPP_SUPPORT_VERSION = "https://api.weixin.qq.com/cgi-bin/wxopen/setweappsupportversion";

/**
* 14.设置小程序“扫普通链接二维码打开小程序”能力
*
* https://mp.weixin.qq.com/debug/wxadoc/introduction/qrcode.html
*/
/**
* 14.1 增加或修改二维码规则
*/
String API_QRCODE_JUMP_ADD = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpadd";

/**
* 14.2 获取已设置的二维码规则
*/
String API_QRCODE_JUMP_GET = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpget";

/**
* 14.3 获取校验文件名称及内容
*/
String API_QRCODE_JUMP_DOWNLOAD = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpdownload";

/**
* 14.4 删除已设置的二维码规则
*/
String API_QRCODE_JUMP_DELETE = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpdelete";

/**
* 14.5 发布已设置的二维码规则
*/
String API_QRCODE_JUMP_PUBLISH = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumppublish";

/**
* 15.小程序审核撤回
* <p>
* 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。
* </p>
*/
String API_UNDO_CODE_AUDIT = "https://api.weixin.qq.com/wxa/undocodeaudit";

/**
* 16.1 小程序分阶段发布-分阶段发布接口
*/
String API_GRAY_RELEASE = "https://api.weixin.qq.com/wxa/grayrelease";

/**
* 16.2 小程序分阶段发布-取消分阶段发布
*/
String API_REVERT_GRAY_RELEASE = "https://api.weixin.qq.com/wxa/revertgrayrelease";

/**
* 16.3 小程序分阶段发布-查询当前分阶段发布详情
*/
String API_GET_GRAY_RELEASE_PLAN = "https://api.weixin.qq.com/wxa/getgrayreleaseplan";


/**
* 查询服务商的当月提审限额和加急次数(Quota)
*/
String API_QUERY_QUOTA = "https://api.weixin.qq.com/wxa/queryquota";

/**
* 加急审核申请
*/
String API_SPEED_AUDIT = "https://api.weixin.qq.com/wxa/speedupaudit";


/**
* 获得小程序的域名配置信息
*/
WxOpenMaDomainResult getDomain() throws WxErrorException;

/**
* 修改域名
*
* @param action delete删除, set覆盖, get获取
*/
WxOpenMaDomainResult modifyDomain(String action, List<String> requestdomainList, List<String> wsrequestdomainList, List<String> uploaddomainList, List<String> downloaddomainList) throws WxErrorException;

/**
* 获取小程序的业务域名
*
* @return 直接返回字符串
*/
String getWebViewDomain() throws WxErrorException;

/**
* 获取小程序的业务域名
*
* @return
*/
public WxOpenMaWebDomainResult getWebViewDomainInfo() throws WxErrorException;

/**
* 设置小程序的业务域名
*
* @param action add添加, delete删除, set覆盖
* @return 直接返回字符串
*/
String setWebViewDomain(String action, List<String> domainList) throws WxErrorException;


/**
* 设置小程序的业务域名
*
* @param action add添加, delete删除, set覆盖
* @param domainList
* @return
*/
WxOpenMaWebDomainResult setWebViewDomainInfo(String action, List<String> domainList) throws WxErrorException;

/**
* 获取小程序的信息
*/
String getAccountBasicInfo() throws WxErrorException;

/**
* 绑定小程序体验者
*
* @param wechatid 体验者微信号(不是openid)
* @return
* @throws WxErrorException
*/
WxOpenMaBindTesterResult bindTester(String wechatid) throws WxErrorException;

/**
* 解除绑定小程序体验者
*
* @param wechatid 体验者微信号(不是openid)
*/
WxOpenResult unbindTester(String wechatid) throws WxErrorException;

/**
* 解除绑定小程序体验者,其他平台绑定的体验者无法获取到wechatid,可用此方法解绑,详见文档
* https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/unbind_tester.html
*
* @param userstr 人员对应的唯一字符串, 可通过获取已绑定的体验者列表获取人员对应的字符串
*/
WxOpenResult unbindTesterByUserstr(String userstr) throws WxErrorException;

/**
* 获得体验者列表
*/
WxOpenMaTesterListResult getTesterList() throws WxErrorException;


/**
* 设置小程序隐私设置(是否可被搜索)
*
* @param status 1表示不可搜索,0表示可搜索
*/
public WxOpenResult changeWxaSearchStatus(Integer status) throws WxErrorException;


/**
* 2. 查询小程序当前隐私设置(是否可被搜索)
*/
public WxOpenMaSearchStatusResult getWxaSearchStatus() throws WxErrorException;

/**
* 3.1 获取展示的公众号信息
*/
public WxOpenMaShowItemResult getShowWxaItem() throws WxErrorException;


/**
* 3.2 设置展示的公众号
*
* @param flag 0 关闭,1 开启
* @param mpappid 如果开启,需要传新的公众号appid
*/
public WxOpenResult updateShowwxaitem(Integer flag, String mpappid) throws WxErrorException;

/**
* 1、为授权的小程序帐号上传小程序代码
*
* @param templateId 代码模板ID
* @param userVersion 用户定义版本
* @param userDesc 用户定义版本描述
* @param extInfo 第三方自定义的配置
*/
WxOpenResult codeCommit(Long templateId, String userVersion, String userDesc, WxMaOpenCommitExtInfo extInfo) throws WxErrorException;

/**
* 获取体验小程序的体验二维码
*/
File getTestQrcode(String pagePath, Map<String, String> params) throws WxErrorException;

/**
* 获取授权小程序帐号的可选类目
* <p>
* 注意:该接口可获取已设置的二级类目及用于代码审核的可选三级类目。
* </p>
*/
WxOpenMaCategoryListResult getCategoryList() throws WxErrorException;

/**
* 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用)
*
* @return
* @throws WxErrorException
*/
WxOpenMaPageListResult getPageList() throws WxErrorException;

/**
* 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用)
*/
WxOpenMaSubmitAuditResult submitAudit(WxOpenMaSubmitAuditMessage submitAuditMessage) throws WxErrorException;

/**
* 查询某个指定版本的审核状态(仅供第三方代小程序调用)
*/
WxOpenMaQueryAuditResult getAuditStatus(Long auditid) throws WxErrorException;

/**
* 查询最新一次提交的审核状态(仅供第三方代小程序调用).
*/
WxOpenMaQueryAuditResult getLatestAuditStatus() throws WxErrorException;

/**
* 发布已通过审核的小程序(仅供第三方代小程序调用).
*/
WxOpenResult releaesAudited() throws WxErrorException;

/**
* 10. 修改小程序线上代码的可见状态(仅供第三方代小程序调用)
*/
public WxOpenResult changeVisitstatus(String action) throws WxErrorException;


/**
* 11. 小程序版本回退(仅供第三方代小程序调用)
*/
WxOpenResult revertCodeReleaes() throws WxErrorException;

/**
* 15. 小程序审核撤回
* 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。
*/
WxOpenResult undoCodeAudit() throws WxErrorException;

/**
* 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用)
*/
String getSupportVersion() throws WxErrorException;

/**
* 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用)
*/
WxOpenMaWeappSupportVersionResult getSupportVersionInfo() throws WxErrorException;

/**
* 设置最低基础库版本(仅供第三方代小程序调用)
*/
String setSupportVersion(String version) throws WxErrorException;

/**
* 设置最低基础库版本(仅供第三方代小程序调用)
*/
WxOpenResult setSupportVersionInfo(String version) throws WxErrorException;

/**
* 16. 小程序分阶段发布 - 1)分阶段发布接口
*/
WxOpenResult grayrelease(Integer grayPercentage) throws WxErrorException;

/**
* 16. 小程序分阶段发布 - 2)取消分阶段发布
*/
WxOpenResult revertgrayrelease() throws WxErrorException;

/**
* 16. 小程序分阶段发布 - 3)查询当前分阶段发布详情
*/
WxOpenMaGrayReleasePlanResult getgrayreleaseplan() throws WxErrorException;


/**
* 查询服务商的当月提审限额和加急次数(Quota)
* https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/code/query_quota.html
*/
WxOpenMaQueryQuotaResult queryQuota() throws WxErrorException;

/**
* 加急审核申请
* 有加急次数的第三方可以通过该接口,对已经提审的小程序进行加急操作,加急后的小程序预计2-12小时内审完。
*/
Boolean speedAudit(Long auditid) throws WxErrorException;

/**
* (1)增加或修改二维码规则
*/
WxOpenResult addQrcodeJump(WxQrcodeJumpRule wxQrcodeJumpRule) throws WxErrorException;

/**
* (2)获取已设置的二维码规则
*/
WxGetQrcodeJumpResult getQrcodeJump() throws WxErrorException;

/**
* (3)获取校验文件名称及内容
*/
WxDownlooadQrcodeJumpResult downloadQrcodeJump() throws WxErrorException;

/**
* (4)删除已设置的二维码规则
*/
WxOpenResult deleteQrcodeJump(String prefix) throws WxErrorException;

/**
* (5)发布已设置的二维码规则
*/
WxOpenResult publishQrcodeJump(String prefix) throws WxErrorException;

// /**
// * 小程序用户隐私保护指引服务
// *
// * @return 小程序用户隐私保护指引服务
// */
// WxOpenMaPrivacyService getPrivacyService();
}

+ 23
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenService.java 查看文件

@@ -0,0 +1,23 @@
package com.iformall.service.toutiao.api;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.open.api.WxOpenConfigStorage;

public interface TtOpenService {

TtOpenComponentService getTtOpenComponentService();

WxOpenConfigStorage getWxOpenConfigStorage();

void setWxOpenConfigStorage(WxOpenConfigStorage wxOpenConfigStorage);

/**
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求
*/
String get(String url, String queryParam) throws WxErrorException;

/**
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求
*/
String post(String url, String postData) throws WxErrorException;
}

+ 23
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenAuthorizationInfo.java 查看文件

@@ -0,0 +1,23 @@
package com.iformall.service.toutiao.api.bean;

import lombok.Data;

import java.io.Serializable;
import java.util.List;

/**
*
*/
@Data
public class TtOpenAuthorizationInfo implements Serializable {

private static final long serialVersionUID = -8354370210668120305L;

private String authorizerAppid;
private String authorizerAccessToken;
private int expiresIn;
private String authorizerRefreshToken;
private int refreshExpiresIn;

private List<Integer> authorizePermission;
}

+ 43
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenAuthorizationInfoGsonAdapter.java 查看文件

@@ -0,0 +1,43 @@
package com.iformall.service.toutiao.api.bean;

import com.google.gson.*;
import me.chanjar.weixin.common.util.json.GsonHelper;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/**
* @author <a href="https://github.com/007gzs">007</a>
*/
public class TtOpenAuthorizationInfoGsonAdapter implements JsonDeserializer<TtOpenAuthorizationInfo> {

@Override
public TtOpenAuthorizationInfo deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
TtOpenAuthorizationInfo authorizationInfo = new TtOpenAuthorizationInfo();
JsonObject jsonObject = jsonElement.getAsJsonObject();
authorizationInfo.setAuthorizerAppid(GsonHelper.getString(jsonObject, "authorizer_appid"));
authorizationInfo.setAuthorizerAccessToken(GsonHelper.getString(jsonObject, "authorizer_access_token"));
authorizationInfo.setExpiresIn(GsonHelper.getPrimitiveInteger(jsonObject, "expires_in"));
authorizationInfo.setAuthorizerRefreshToken(GsonHelper.getString(jsonObject, "authorizer_refresh_token"));
authorizationInfo.setRefreshExpiresIn(GsonHelper.getPrimitiveInteger(jsonObject, "refresh_expires_in"));

List<Integer> funcInfo = new ArrayList<>();
JsonArray jsonArray = GsonHelper.getAsJsonArray(jsonObject.get("authorize_permission"));
if (jsonArray != null && !jsonArray.isJsonNull()) {
for (int i = 0; i < jsonArray.size(); i++) {
jsonObject = jsonArray.get(i).getAsJsonObject();
if (jsonObject == null || jsonObject.isJsonNull()) {
continue;
}
Integer id = GsonHelper.getInteger(jsonObject, "id");
if (id == null) {
continue;
}
funcInfo.add(id);
}
}
authorizationInfo.setAuthorizePermission(funcInfo);
return authorizationInfo;
}
}

+ 26
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenGsonBuilder.java 查看文件

@@ -0,0 +1,26 @@
package com.iformall.service.toutiao.api.bean;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.iformall.service.toutiao.miniapp.bean.TtMaSubscribeMessage;
import com.iformall.service.toutiao.miniapp.bean.TtMaSubscribeMessageGsonAdapter;

/**
* @author
*/
public class TtOpenGsonBuilder {
private static final GsonBuilder INSTANCE = new GsonBuilder();

static {
INSTANCE.disableHtmlEscaping();
INSTANCE.registerTypeAdapter(TtOpenTicket.class, new TtOpenTicketGsonAdapter());
INSTANCE.registerTypeAdapter(TtOpenAuthorizationInfo.class, new TtOpenAuthorizationInfoGsonAdapter());
INSTANCE.registerTypeAdapter(TtOpenRetrieveCode.class, new TtOpenRetrieveCodeGsonAdapter());

}

public static Gson create() {
return INSTANCE.create();
}

}

+ 19
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenRetrieveCode.java 查看文件

@@ -0,0 +1,19 @@
package com.iformall.service.toutiao.api.bean;

import lombok.Data;

import java.io.Serializable;
import java.util.List;

/**
*
*/
@Data
public class TtOpenRetrieveCode implements Serializable {

private static final long serialVersionUID = -2169528610814376819L;

private String authorizationCode;
private int expiresIn;

}

+ 24
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenRetrieveCodeGsonAdapter.java 查看文件

@@ -0,0 +1,24 @@
package com.iformall.service.toutiao.api.bean;

import com.google.gson.*;
import me.chanjar.weixin.common.util.json.GsonHelper;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/**
* @author <a href="https://github.com/007gzs">007</a>
*/
public class TtOpenRetrieveCodeGsonAdapter implements JsonDeserializer<TtOpenRetrieveCode> {

@Override
public TtOpenRetrieveCode deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
TtOpenRetrieveCode retrieveCode = new TtOpenRetrieveCode();
JsonObject jsonObject = jsonElement.getAsJsonObject();
retrieveCode.setAuthorizationCode(GsonHelper.getString(jsonObject, "authorization_code"));
retrieveCode.setExpiresIn(GsonHelper.getPrimitiveInteger(jsonObject, "expires_in"));

return retrieveCode;
}
}

+ 40
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenTicket.java 查看文件

@@ -0,0 +1,40 @@
package com.iformall.service.toutiao.api.bean;

import com.iformall.service.toutiao.miniapp.bean.TtMaGsonBuilder;
import lombok.*;

import java.io.Serializable;

/**
* @author S
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TtOpenTicket implements Serializable {

private static final long serialVersionUID = -44544979421396117L;

private String accessToken;

private String ticket;

private String fromUserName;

//2019-01-14 12:45:10
private String createTime;

private String msgType;

private String event;

private String componentAppId;


public static TtOpenTicket fromJson(String json) {
return TtMaGsonBuilder.create().fromJson(json, TtOpenTicket.class);
}

}

+ 34
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenTicketGsonAdapter.java 查看文件

@@ -0,0 +1,34 @@
package com.iformall.service.toutiao.api.bean;

import com.google.gson.*;
import com.iformall.service.toutiao.miniapp.bean.TtMaSubscribeMessage;
import me.chanjar.weixin.common.util.json.GsonHelper;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* .
*
* @author S
*/
public class TtOpenTicketGsonAdapter implements JsonDeserializer<TtOpenTicket> {

@Override
public TtOpenTicket deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return convertFromJson(json.getAsJsonObject());
}

protected TtOpenTicket convertFromJson(JsonObject json) {
TtOpenTicket openTicket = new TtOpenTicket();
openTicket.setTicket(GsonHelper.getString(json,"Ticket"));
openTicket.setFromUserName(GsonHelper.getString(json,"FromUserName"));
openTicket.setCreateTime(GsonHelper.getString(json,"CreateTime"));
openTicket.setMsgType(GsonHelper.getString(json,"MsgType"));
openTicket.setEvent(GsonHelper.getString(json,"Event"));
return openTicket;
}

}

+ 517
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenComponentServiceImpl.java 查看文件

@@ -0,0 +1,517 @@
package com.iformall.service.toutiao.api.impl;

import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import com.iformall.service.toutiao.api.TtOpenComponentService;
import com.iformall.service.toutiao.api.TtOpenMaService;
import com.iformall.service.toutiao.api.TtOpenService;
import com.iformall.service.toutiao.api.bean.TtOpenAuthorizationInfo;
import com.iformall.service.toutiao.api.bean.TtOpenGsonBuilder;
import com.iformall.service.toutiao.api.bean.TtOpenRetrieveCode;
import com.iformall.service.toutiao.api.bean.TtOpenTicket;
import com.iformall.service.toutiao.utils.MsgDecrypt;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.crypto.SHA1;
import me.chanjar.weixin.common.util.http.URIUtil;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.open.api.WxOpenConfigStorage;
import me.chanjar.weixin.open.bean.*;
import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo;
import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
import me.chanjar.weixin.open.bean.result.*;
import me.chanjar.weixin.open.util.WxOpenCryptUtil;
import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;

/**
* @author
*/
public class TtOpenComponentServiceImpl implements TtOpenComponentService {
private static final JsonParser JSON_PARSER = new JsonParser();
private static final Map<String, TtOpenMaService> TT_OPEN_MA_SERVICE_MAP = new ConcurrentHashMap<>();

protected final Logger log = LoggerFactory.getLogger(this.getClass());
private TtOpenService ttOpenService;

public TtOpenComponentServiceImpl(TtOpenService ttOpenService) {
this.ttOpenService = ttOpenService;
}

enum RequestMethod {
POST,GET
}

@Override
public TtOpenMaService getTtMaServiceByAppid(String appId) {
TtOpenMaService ttOpenMaService = TT_OPEN_MA_SERVICE_MAP.get(appId);
if (ttOpenMaService == null) {
synchronized (TT_OPEN_MA_SERVICE_MAP) {
ttOpenMaService = TT_OPEN_MA_SERVICE_MAP.get(appId);
if (ttOpenMaService == null) {
ttOpenMaService = new TtOpenMaServiceImpl(this, appId, getWxOpenConfigStorage().getWxMaConfig(appId));
TT_OPEN_MA_SERVICE_MAP.put(appId, ttOpenMaService);
}
}
}
return ttOpenMaService;
}

public TtOpenService getTtOpenService() {
return ttOpenService;
}

@Override
public WxOpenConfigStorage getWxOpenConfigStorage() {
return ttOpenService.getWxOpenConfigStorage();
}

@Override
public boolean checkSignature(String timestamp, String nonce, String encrypt, String signature) {
try {
return SHA1.gen(getWxOpenConfigStorage().getComponentToken(), timestamp, nonce, encrypt)
.equals(signature);
} catch (Exception e) {
this.log.error("Checking signature failed, and the reason is :" + e.getMessage());
return false;
}
}

@Override
public TtOpenTicket decrypt(String encrypt) {
try {
MsgDecrypt msgDecrypt = new MsgDecrypt(getWxOpenConfigStorage().getComponentAesKey());
String plainText = msgDecrypt.decrypt(encrypt);
log.info("解密信息----------------------------------------"+ plainText);
TtOpenTicket openTicket = TtOpenTicket.fromJson(plainText);
openTicket.setComponentAppId(getWxOpenConfigStorage().getComponentAppId());
return openTicket;
} catch (Exception e) {
this.log.error("Checking signature failed, and the reason is :" + e.getMessage());
return null;
}

}

@Override
public String getComponentAccessToken(boolean forceRefresh) throws WxErrorException {
final WxOpenConfigStorage config = this.getWxOpenConfigStorage();
if (!config.isComponentAccessTokenExpired() && !forceRefresh) {
return config.getComponentAccessToken();
}
Lock lock = config.getComponentAccessTokenLock();
lock.lock();
try {
if (!config.isComponentAccessTokenExpired() && !forceRefresh) {
return config.getComponentAccessToken();
}
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
jsonObject.addProperty("component_appsecret", getWxOpenConfigStorage().getComponentAppSecret());
jsonObject.addProperty("component_ticket", getWxOpenConfigStorage().getComponentVerifyTicket());

String responseContent = this.getTtOpenService().get(API_COMPONENT_TOKEN_URL, jsonObject.toString());
WxOpenComponentAccessToken componentAccessToken = WxOpenComponentAccessToken.fromJson(responseContent);
config.updateComponentAccessToken(componentAccessToken);
return config.getComponentAccessToken();
} finally {
lock.unlock();
}
}

@Override
public String post(String uri, String postData,String tokenKeyName) throws WxErrorException {
//"component_access_token"
return postByCommonAccessToken(uri, postData, tokenKeyName);
}
@Override
public String get(String uri,String tokenKeyName) throws WxErrorException {
//"component_access_token"
return getByCommonAccessToken(uri, tokenKeyName);
}
private String getByCommonAccessToken(String uri, String accessTokenKey) throws WxErrorException {
String componentAccessToken = getComponentAccessToken(false);
return excuteRequet(uri, accessTokenKey, componentAccessToken, null, true, RequestMethod.GET);
}
private String postByCommonAccessToken(String uri, String postData ,String accessTokenKey) throws WxErrorException {
String componentAccessToken = getComponentAccessToken(false);
return excuteRequet(uri, accessTokenKey, componentAccessToken, postData, true, RequestMethod.POST);
}
@Override
public String getByAppAccessToken(String appId,String uri, String accessTokenKey) throws WxErrorException {
String accessToken = getAuthorizerAccessToken(appId,false);
return excuteRequet(uri, accessTokenKey, accessToken, null, true, RequestMethod.GET);
}
@Override
public String postByAppAccessToken(String appId,String uri,String postData, String accessTokenKey) throws WxErrorException {
String accessToken = getAuthorizerAccessToken(appId, false);
return excuteRequet(uri, accessTokenKey, accessToken, postData, true, RequestMethod.POST);
}
private String post(String uri, String postData, String accessTokenKey,String accessTokenValue, boolean isCommpontAccessToken) throws WxErrorException {
return excuteRequet(uri, accessTokenKey, accessTokenValue, postData, isCommpontAccessToken, RequestMethod.POST);
}
private String get(String uri, String accessTokenKey,String accessTokenValue, boolean isCommpontAccessToken) throws WxErrorException {
return excuteRequet(uri, accessTokenKey, accessTokenValue, null, isCommpontAccessToken, RequestMethod.GET);
}

@Override
public String getPreAuthUrl(String redirectUri) throws WxErrorException {
return createPreAuthUrl(redirectUri);
}

/**
* 创建预授权链接
*
* @param redirectURI
* @return
* @throws WxErrorException
*/
private String createPreAuthUrl(String redirectURI) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
String responseContent = post(API_CREATE_PREAUTHCODE_URL, jsonObject.toString(),"component_access_token");
jsonObject = WxGsonBuilder.create().fromJson(responseContent, JsonObject.class);

StringBuilder preAuthUrl = new StringBuilder(String.format(COMPONENT_LOGIN_PAGE_URL,
getWxOpenConfigStorage().getComponentAppId(),
jsonObject.get("pre_auth_code").getAsString(),
URIUtil.encodeURIComponent(redirectURI)));
String preAuthUrlStr = preAuthUrl.toString();
return preAuthUrlStr;
}


@Override
public String route(final TtOpenTicket ticket) throws WxErrorException {
if (ticket == null) {
throw new NullPointerException("message is empty");
}
if (StringUtils.equalsIgnoreCase(ticket.getMsgType(), "Ticket")) {
getWxOpenConfigStorage().setComponentVerifyTicket(ticket.getTicket());
return "success";
}
return "";
}

@Override
public TtOpenAuthorizationInfo getQueryAuth(String authorizationCode) throws WxErrorException {
String uri = API_QUERY_AUTH_URL + "?component_appid=" + getWxOpenConfigStorage().getComponentAppId() +
"&authorization_code=" + authorizationCode +
"&grant_type=app_to_tp_authorization_code";

String responseContent = get(uri,"component_access_token");
TtOpenAuthorizationInfo queryAuth = TtOpenGsonBuilder.create().fromJson(responseContent, TtOpenAuthorizationInfo.class);
if (queryAuth == null) {
return queryAuth;
}

if (queryAuth.getAuthorizerAccessToken() != null) {
getWxOpenConfigStorage().updateAuthorizerAccessToken(queryAuth.getAuthorizerAppid(),
queryAuth.getAuthorizerAccessToken(), queryAuth.getExpiresIn());
}
if (queryAuth.getAuthorizerRefreshToken() != null) {
getWxOpenConfigStorage().setAuthorizerRefreshToken(queryAuth.getAuthorizerAppid(), queryAuth.getAuthorizerRefreshToken());
}
return queryAuth;
}

@Override
public String retrieveCode(String authorizationCode) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
jsonObject.addProperty("authorization_appid", authorizationCode);
String responseContent = post(RETRIEVE_AUTHORIZER_TOKEN_URL, jsonObject.toString(), "component_access_token");
TtOpenRetrieveCode retrieveCode = TtOpenGsonBuilder.create().fromJson(responseContent, TtOpenRetrieveCode.class);
if(retrieveCode == null){
return null;
}
return retrieveCode.getAuthorizationCode();
}

// @Override
// public WxOpenAuthorizerInfoResult getAuthorizerInfo(String authorizerAppid) throws WxErrorException {
// JsonObject jsonObject = new JsonObject();
// jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
// jsonObject.addProperty("authorizer_appid", authorizerAppid);
// String responseContent = post(API_GET_AUTHORIZER_INFO_URL, jsonObject.toString(),"component_access_token");
// return WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenAuthorizerInfoResult.class);
// }

@Override
public WxOpenAuthorizerListResult getAuthorizerList(int begin, int len) throws WxErrorException {
begin = begin < 0 ? 0 : begin;
len = len == 0 ? 10 : len;
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
jsonObject.addProperty("offset", begin);
jsonObject.addProperty("count", len);
String responseContent = post(API_GET_AUTHORIZER_LIST, jsonObject.toString(),"component_access_token");
WxOpenAuthorizerListResult ret = WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenAuthorizerListResult.class);
if (ret != null && ret.getList() != null) {
for (Map<String, String> data : ret.getList()) {
String authorizerAppid = data.get("authorizer_appid");
String refreshToken = data.get("refresh_token");
if (authorizerAppid != null && refreshToken != null) {
this.getWxOpenConfigStorage().setAuthorizerRefreshToken(authorizerAppid, refreshToken);
}
}
}
return ret;
}

@Override
public WxOpenAuthorizerOptionResult getAuthorizerOption(String authorizerAppid, String optionName) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
jsonObject.addProperty("authorizer_appid", authorizerAppid);
jsonObject.addProperty("option_name", optionName);
String responseContent = post(API_GET_AUTHORIZER_OPTION_URL, jsonObject.toString(),"component_access_token");
return WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenAuthorizerOptionResult.class);
}

@Override
public void setAuthorizerOption(String authorizerAppid, String optionName, String optionValue) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId());
jsonObject.addProperty("authorizer_appid", authorizerAppid);
jsonObject.addProperty("option_name", optionName);
jsonObject.addProperty("option_value", optionValue);
post(API_SET_AUTHORIZER_OPTION_URL, jsonObject.toString(),"component_access_token");
}

@Override
public String getAuthorizerAccessToken(String appId, boolean forceRefresh) throws WxErrorException {
WxOpenConfigStorage config = getWxOpenConfigStorage();
if (!config.isAuthorizerAccessTokenExpired(appId) && !forceRefresh) {
return config.getAuthorizerAccessToken(appId);
}
Lock lock = config.getWxMpConfigStorage(appId).getAccessTokenLock();
lock.lock();
try {
if (!config.isAuthorizerAccessTokenExpired(appId) && !forceRefresh) {
return config.getAuthorizerAccessToken(appId);
}

String uri = API_AUTHORIZER_TOKEN_URL + "?component_appid=" + getWxOpenConfigStorage().getComponentAppId() +
"&authorizer_refresh_token=" + getWxOpenConfigStorage().getAuthorizerRefreshToken(appId) +
"&grant_type=app_to_tp_refresh_token";

String responseContent = get(uri,"component_access_token");

TtOpenAuthorizationInfo queryAuth = TtOpenGsonBuilder.create().fromJson(responseContent, TtOpenAuthorizationInfo.class);
if (queryAuth.getAuthorizerAccessToken() != null) {
config.updateAuthorizerAccessToken(queryAuth.getAuthorizerAppid(),
queryAuth.getAuthorizerAccessToken(), queryAuth.getExpiresIn());
}
if (queryAuth.getAuthorizerRefreshToken() != null) {
config.setAuthorizerRefreshToken(queryAuth.getAuthorizerAppid(), queryAuth.getAuthorizerRefreshToken());
}
return config.getAuthorizerAccessToken(appId);
} finally {
lock.unlock();
}
}

@Override
public WxMpOAuth2AccessToken oauth2getAccessToken(String appId, String code) throws WxErrorException {
String url = String.format(OAUTH2_ACCESS_TOKEN_URL, appId, code, getWxOpenConfigStorage().getComponentAppId());
String responseContent = get(url,"component_access_token");
return WxMpOAuth2AccessToken.fromJson(responseContent);
}

@Override
public WxMpOAuth2AccessToken oauth2refreshAccessToken(String appId, String refreshToken) throws WxErrorException {
String url = String.format(OAUTH2_REFRESH_TOKEN_URL, appId, refreshToken, getWxOpenConfigStorage().getComponentAppId());
String responseContent = get(url,"component_access_token");
return WxMpOAuth2AccessToken.fromJson(responseContent);
}

@Override
public String oauth2buildAuthorizationUrl(String appId, String redirectURI, String scope, String state) {
return String.format(CONNECT_OAUTH2_AUTHORIZE_URL,
appId, URIUtil.encodeURIComponent(redirectURI), scope, StringUtils.trimToEmpty(state), getWxOpenConfigStorage().getComponentAppId());
}

@Override
public WxMaJscode2SessionResult miniappJscode2Session(String appId, String jsCode) throws WxErrorException {
String url = String.format(MINIAPP_JSCODE_2_SESSION, appId, jsCode, getWxOpenConfigStorage().getComponentAppId());
String responseContent = get(url,"component_access_token");
return WxMaJscode2SessionResult.fromJson(responseContent);
}

@Override
public List<WxOpenMaCodeTemplate> getTemplateDraftList() throws WxErrorException {
String responseContent = get(GET_TEMPLATE_DRAFT_LIST_URL, "access_token");
JsonObject response = JSON_PARSER.parse(StringUtils.defaultString(responseContent, "{}")).getAsJsonObject();
boolean hasDraftList = response.has("draft_list");
if (hasDraftList) {
return WxOpenGsonBuilder.create().fromJson(response.getAsJsonArray("draft_list"),
new TypeToken<List<WxOpenMaCodeTemplate>>() {
}.getType());
} else {
return null;
}
}

@Override
public List<WxOpenMaCodeTemplate> getTemplateList() throws WxErrorException {
String responseContent = get(GET_TEMPLATE_LIST_URL, "access_token");
JsonObject response = JSON_PARSER.parse(StringUtils.defaultString(responseContent, "{}")).getAsJsonObject();
boolean hasTemplateList = response.has("template_list");
if (hasTemplateList) {
return WxOpenGsonBuilder.create().fromJson(response.getAsJsonArray("template_list"),
new TypeToken<List<WxOpenMaCodeTemplate>>() {
}.getType());
} else {
return null;
}
}

@Override
public void addToTemplate(long draftId) throws WxErrorException {
JsonObject param = new JsonObject();
param.addProperty("draft_id", draftId);
post(ADD_TO_TEMPLATE_URL, param.toString(), "access_token");
}

@Override
public void deleteTemplate(long templateId) throws WxErrorException {
JsonObject param = new JsonObject();
param.addProperty("template_id", templateId);
post(DELETE_TEMPLATE_URL, param.toString(), "access_token");
}

@Override
public WxOpenCreateResult createOpenAccount(String appId) throws WxErrorException {
JsonObject param = new JsonObject();
param.addProperty("appid", appId);

String json = postByAppAccessToken(appId, CREATE_OPEN_URL, param.toString(), "access_token");

return WxOpenCreateResult.fromJson(json);
}

private String excuteRequet(String uri,String tokenKey,String tokenValue,String postData,boolean isCompontAccessToken,RequestMethod method) throws WxErrorException {
String uriWithComponentAccessToken = uri + (uri.contains("?") ? "&" : "?") + tokenKey + "=" + tokenValue;
try {
if (method == RequestMethod.POST) {
return getTtOpenService().post(uriWithComponentAccessToken, postData);
}else {
return getTtOpenService().get(uriWithComponentAccessToken, null);
}
} catch (WxErrorException e) {
WxError error = e.getError();
/*
* 发生以下情况时尝试刷新access_token
* 40001 获取access_token时AppSecret错误,或者access_token无效
* 42001 access_token超时
* 40014 不合法的access_token,请开发者认真比对access_token的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口
*/
if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001 || error.getErrorCode() == 40014) {
// 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token
Lock lock = this.getWxOpenConfigStorage().getComponentAccessTokenLock();
lock.lock();
try {
if (isCompontAccessToken && StringUtils.equals(tokenValue, this.getWxOpenConfigStorage().getComponentAccessToken())) {
this.getWxOpenConfigStorage().expireComponentAccessToken();
}
} catch (Exception ex) {
if (isCompontAccessToken) {
this.getWxOpenConfigStorage().expireComponentAccessToken();
}
} finally {
lock.unlock();
}
if (isCompontAccessToken && this.getWxOpenConfigStorage().autoRefreshToken()) {
if (method == RequestMethod.POST) {
return this.post(uri, postData, tokenKey);
}else {
return this.get(uri, tokenKey);
}
}
}
if (error.getErrorCode() != 0) {
throw new WxErrorException(error, e);
}
return null;
}
}

@Override
public Boolean bindOpenAccount(String appId, String openAppid) throws WxErrorException {
JsonObject param = new JsonObject();
param.addProperty("appid", appId);
param.addProperty("open_appid", openAppid);

String json = postByAppAccessToken(appId, BIND_OPEN_URL, param.toString(), "access_token");

return WxOpenResult.fromJson(json).isSuccess();
}


@Override
public Boolean unbindOpenAccount(String appId, String openAppid) throws WxErrorException {
JsonObject param = new JsonObject();
param.addProperty("appid", appId);
param.addProperty("open_appid", openAppid);

String json = postByAppAccessToken(appId, UNBIND_OPEN_URL, param.toString(), "access_token");

return WxOpenResult.fromJson(json).isSuccess();
}


@Override
public WxOpenGetResult getOpenAccount(String appId) throws WxErrorException {
JsonObject param = new JsonObject();
param.addProperty("appid", appId);

String json = postByAppAccessToken(appId, GET_OPEN_URL, param.toString(), "access_token");
return WxOpenGetResult.fromJson(json);
}


@Override
public WxOpenResult fastRegisterWeapp(String name, String code, String codeType, String legalPersonaWechat, String legalPersonaName, String componentPhone) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("name", name);
jsonObject.addProperty("code", code);
jsonObject.addProperty("code_type", codeType);
jsonObject.addProperty("legal_persona_wechat", legalPersonaWechat);
jsonObject.addProperty("legal_persona_name", legalPersonaName);
jsonObject.addProperty("component_phone", componentPhone);
String response = post(FAST_REGISTER_WEAPP_URL, jsonObject.toString(), "component_access_token");
return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class);
}

@Override
public WxOpenResult fastRegisterWeappSearch(String name, String legalPersonaWechat, String legalPersonaName) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("name", name);
jsonObject.addProperty("legal_persona_wechat", legalPersonaWechat);
jsonObject.addProperty("legal_persona_name", legalPersonaName);
String response = post(FAST_REGISTER_WEAPP_SEARCH_URL, jsonObject.toString(), "component_access_token");
return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class);
}
}

+ 643
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenMaServiceImpl.java 查看文件

@@ -0,0 +1,643 @@
package com.iformall.service.toutiao.api.impl;

import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.iformall.service.toutiao.api.TtOpenComponentService;
import com.iformall.service.toutiao.api.TtOpenMaService;
import com.iformall.service.toutiao.miniapp.impl.TtMaServiceImpl;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.open.bean.ma.WxMaOpenCommitExtInfo;
import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam;
import me.chanjar.weixin.open.bean.message.WxOpenMaSubmitAuditMessage;
import me.chanjar.weixin.open.bean.result.*;
import me.chanjar.weixin.open.executor.MaQrCodeRequestExecutor;

import java.io.File;
import java.util.List;
import java.util.Map;

/**
* @author <a href="https://github.com/007gzs">007</a>
* <pre>
* 增加开放平台代小程序管理服务能力
* 说明:这里让这个服务公开便于调用者模拟本地测试服务
* </pre>
* @author yqx
* @date 2018-09-12
*/
public class TtOpenMaServiceImpl extends TtMaServiceImpl implements TtOpenMaService {
private TtOpenComponentService ttOpenComponentService;
private WxMaConfig wxMaConfig;
private String appId;


public TtOpenMaServiceImpl(TtOpenComponentService ttOpenComponentService, String appId, WxMaConfig wxMaConfig) {
this.ttOpenComponentService = ttOpenComponentService;
this.appId = appId;
this.wxMaConfig = wxMaConfig;
initHttp();
}

@Override
public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException {
return ttOpenComponentService.miniappJscode2Session(appId, jsCode);
}

@Override
public WxMaConfig getWxMaConfig() {
return wxMaConfig;
}

@Override
public String getAccessToken(boolean forceRefresh) throws WxErrorException {
return ttOpenComponentService.getAuthorizerAccessToken(appId, forceRefresh);
}

/**
* 获得小程序的域名配置信息
*
* @return
*/
@Override
public WxOpenMaDomainResult getDomain() throws WxErrorException {
return modifyDomain("get", null, null, null, null);
}

/**
* 修改服务器域名
*
* @param action delete删除, set覆盖, get获取
* @param requestdomainList
* @param wsrequestdomainList
* @param uploaddomainList
* @param downloaddomainList
* @return
* @throws WxErrorException
*/
@Override
public WxOpenMaDomainResult modifyDomain(String action, List<String> requestdomainList, List<String> wsrequestdomainList, List<String> uploaddomainList, List<String> downloaddomainList) throws WxErrorException {

// if (!"get".equals(action) && (requestdomainList == null || wsrequestdomainList == null || uploaddomainList == null || downloaddomainList == null)) {
// throw new WxErrorException(WxError.builder().errorCode(44004).errorMsg("域名参数不能为空").build());
// }
JsonObject requestJson = new JsonObject();
requestJson.addProperty("action", action);
if (!"get".equals(action)) {
requestJson.add("requestdomain", toJsonArray(requestdomainList));
requestJson.add("wsrequestdomain", toJsonArray(wsrequestdomainList));
requestJson.add("uploaddomain", toJsonArray(uploaddomainList));
requestJson.add("downloaddomain", toJsonArray(downloaddomainList));
}
String response = post(API_MODIFY_DOMAIN, GSON.toJson(requestJson));
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaDomainResult.class);
}

/**
* 获取小程序的业务域名
*
* @return
*/
@Override
public String getWebViewDomain() throws WxErrorException {
return setWebViewDomain("get", null);
}

/**
* 获取小程序的业务域名
*
* @return
*/
@Override
public WxOpenMaWebDomainResult getWebViewDomainInfo() throws WxErrorException {
return setWebViewDomainInfo("get", null);
}

/**
* 设置小程序的业务域名
*
* @param action add添加, delete删除, set覆盖
* @param domainList
* @return
*/
@Override
public String setWebViewDomain(String action, List<String> domainList) throws WxErrorException {
JsonObject requestJson = new JsonObject();
requestJson.addProperty("action", action);
if (!"get".equals(action)) {
requestJson.add("webviewdomain", toJsonArray(domainList));
}
String response = post(API_SET_WEBVIEW_DOMAIN, GSON.toJson(requestJson));
//TODO 转化为对象返回
return response;
}


/**
* 设置小程序的业务域名
*
* @param action add添加, delete删除, set覆盖
* @return
*/
@Override
public WxOpenMaWebDomainResult setWebViewDomainInfo(String action, List<String> domainList) throws WxErrorException {
String response = this.setWebViewDomain(action, domainList);
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaWebDomainResult.class);
}


/**
* 获取小程序的信息,GET请求
* <pre>
* 注意:这里不能直接用小程序的access_token
* //TODO 待调整
* </pre>
*
* @return
* @throws WxErrorException
*/
@Override
public String getAccountBasicInfo() throws WxErrorException {
String response = get(API_GET_ACCOUNT_BASICINFO, "");
return response;
}


/**
* 绑定小程序体验者
*
* @param wechatid 体验者微信号(不是openid)
* @return
* @throws WxErrorException
*/
@Override
public WxOpenMaBindTesterResult bindTester(String wechatid) throws WxErrorException {
JsonObject paramJson = new JsonObject();
paramJson.addProperty("wechatid", wechatid);
String response = post(API_BIND_TESTER, GSON.toJson(paramJson));
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaBindTesterResult.class);
}

/**
* 解除绑定小程序体验者
*
* @param wechatid 体验者微信号(不是openid)
* @return
* @throws WxErrorException
*/
@Override
public WxOpenResult unbindTester(String wechatid) throws WxErrorException {
JsonObject paramJson = new JsonObject();
paramJson.addProperty("wechatid", wechatid);
String response = post(API_UNBIND_TESTER, GSON.toJson(paramJson));
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}

/**
* 解除绑定小程序体验者
* @param userstr 人员对应的唯一字符串, 可通过获取已绑定的体验者列表获取人员对应的字符串
* @return
* @throws WxErrorException
*/
@Override
public WxOpenResult unbindTesterByUserstr(String userstr) throws WxErrorException {
JsonObject paramJson = new JsonObject();
paramJson.addProperty("userstr", userstr);
String response = post(API_UNBIND_TESTER, GSON.toJson(paramJson));
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}

/**
* 获得体验者列表
*
* @return
* @throws WxErrorException
*/
@Override
public WxOpenMaTesterListResult getTesterList() throws WxErrorException {
JsonObject paramJson = new JsonObject();
paramJson.addProperty("action", "get_experiencer");
String response = post(API_GET_TESTERLIST, GSON.toJson(paramJson));
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaTesterListResult.class);
}


/**
* 设置小程序隐私设置(是否可被搜索)
*
* @param status 1表示不可搜索,0表示可搜索
*/
@Override
public WxOpenResult changeWxaSearchStatus(Integer status) throws WxErrorException {
JsonObject paramJson = new JsonObject();
paramJson.addProperty("status", status);
String response = post(API_CHANGE_WXA_SEARCH_STATUS, GSON.toJson(paramJson));
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}


/**
* 2. 查询小程序当前隐私设置(是否可被搜索)
*/
@Override
public WxOpenMaSearchStatusResult getWxaSearchStatus() throws WxErrorException {
String response = get(API_GET_WXA_SEARCH_STATUS, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaSearchStatusResult.class);
}


/**
* 3.1 获取展示的公众号信息
*/
@Override
public WxOpenMaShowItemResult getShowWxaItem() throws WxErrorException {
String response = get(API_GET_SHOW_WXA_ITEM, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaShowItemResult.class);
}


/**
* 3.2 设置展示的公众号
*
* @param flag 0 关闭,1 开启
* @param mpappid 如果开启,需要传新的公众号appid
*/
@Override
public WxOpenResult updateShowwxaitem(Integer flag, String mpappid) throws WxErrorException {
JsonObject paramJson = new JsonObject();
paramJson.addProperty("wxa_subscribe_biz_flag", flag);
paramJson.addProperty("appid", mpappid);
String response = post(API_UPDATE_SHOW_WXA_ITEM, GSON.toJson(paramJson));
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}


/**
* 1、为授权的小程序帐号上传小程序代码
*
* @param templateId 代码模板ID
* @param userVersion 用户定义版本
* @param userDesc 用户定义版本描述
* @param extInfo 第三方自定义的配置
* @return
* @throws WxErrorException
*/
@Override
public WxOpenResult codeCommit(Long templateId, String userVersion, String userDesc, WxMaOpenCommitExtInfo extInfo) throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("template_id", templateId);
params.addProperty("user_version", userVersion);
params.addProperty("user_desc", userDesc);
//注意:ext_json必须是字符串类型
params.addProperty("ext_json", GSON.toJson(extInfo));
String response = post(API_CODE_COMMIT, GSON.toJson(params));
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}

/**
* 获取体验小程序的体验二维码
*
* @param pagePath
* @param params
* @return
*/
@Override
public File getTestQrcode(String pagePath, Map<String, String> params) throws WxErrorException {
WxMaQrcodeParam qrcodeParam = WxMaQrcodeParam.create(pagePath);
qrcodeParam.addPageParam(params);
return execute(MaQrCodeRequestExecutor.create(getRequestHttp()), API_TEST_QRCODE, qrcodeParam);
}

/**
* 获取授权小程序帐号的可选类目
* <p>
* 注意:该接口可获取已设置的二级类目及用于代码审核的可选三级类目。
* </p>
*
* @return WxOpenMaCategoryListResult
* @throws WxErrorException
*/
@Override
public WxOpenMaCategoryListResult getCategoryList() throws WxErrorException {
String response = get(API_GET_CATEGORY, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaCategoryListResult.class);
}

/**
* 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用)
*
* @return
* @throws WxErrorException
*/
@Override
public WxOpenMaPageListResult getPageList() throws WxErrorException {
String response = get(API_GET_PAGE, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaPageListResult.class);
}

/**
* 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用)
*
* @param submitAuditMessage
* @return
* @throws WxErrorException
*/
@Override
public WxOpenMaSubmitAuditResult submitAudit(WxOpenMaSubmitAuditMessage submitAuditMessage) throws WxErrorException {
String response = post(API_SUBMIT_AUDIT, GSON.toJson(submitAuditMessage));
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaSubmitAuditResult.class);
}

/**
* 7. 查询某个指定版本的审核状态(仅供第三方代小程序调用)
*
* @param auditid
* @return
* @throws WxErrorException
*/
@Override
public WxOpenMaQueryAuditResult getAuditStatus(Long auditid) throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("auditid", auditid);
String response = post(API_GET_AUDIT_STATUS, GSON.toJson(params));
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaQueryAuditResult.class);
}

/**
* 8. 查询最新一次提交的审核状态(仅供第三方代小程序调用)
*
* @return
* @throws WxErrorException
*/
@Override
public WxOpenMaQueryAuditResult getLatestAuditStatus() throws WxErrorException {
String response = get(API_GET_LATEST_AUDIT_STATUS, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaQueryAuditResult.class);
}

/**
* 9. 发布已通过审核的小程序(仅供第三方代小程序调用)
* <p>
* 请填写空的数据包,POST的json数据包为空即可。
* </p>
*
* @return
* @throws WxErrorException
*/
@Override
public WxOpenResult releaesAudited() throws WxErrorException {
JsonObject params = new JsonObject();
String response = post(API_RELEASE, GSON.toJson(params));
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}


/**
* 10. 修改小程序线上代码的可见状态(仅供第三方代小程序调用)
*
* @param action 设置可访问状态,发布后默认可访问,close为不可见,open为可见
* @return
* @throws WxErrorException
*/
@Override
public WxOpenResult changeVisitstatus(String action) throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("action", action);
String response = post(API_CHANGE_VISITSTATUS, GSON.toJson(params));
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}


/**
* 11. 小程序版本回退(仅供第三方代小程序调用)
*
* @return
* @throws WxErrorException
*/
@Override
public WxOpenResult revertCodeReleaes() throws WxErrorException {
String response = get(API_REVERT_CODE_RELEASE, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}


/**
* 15. 小程序审核撤回
* <p>
* 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。
* </p>
*
* @return
* @throws WxErrorException
*/
@Override
public WxOpenResult undoCodeAudit() throws WxErrorException {
String response = get(API_UNDO_CODE_AUDIT, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}

/**
* 12. 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用)
*
* @return
* @throws WxErrorException
*/
@Override
public String getSupportVersion() throws WxErrorException {
JsonObject params = new JsonObject();
String response = post(API_GET_WEAPP_SUPPORT_VERSION, GSON.toJson(params));
return response;
}


/**
* 12. 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用)
*
* @return
* @throws WxErrorException
*/
@Override
public WxOpenMaWeappSupportVersionResult getSupportVersionInfo() throws WxErrorException {
String response = this.getSupportVersion();
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaWeappSupportVersionResult.class);
}

/**
* 13. 设置最低基础库版本(仅供第三方代小程序调用)
*
* @param version
* @return
* @throws WxErrorException
*/
@Override
public String setSupportVersion(String version) throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("version", version);
String response = post(API_SET_WEAPP_SUPPORT_VERSION, GSON.toJson(params));
return response;
}


/**
* 13. 设置最低基础库版本(仅供第三方代小程序调用)
*
* @param version
* @return
* @throws WxErrorException
*/
@Override
public WxOpenResult setSupportVersionInfo(String version) throws WxErrorException {
String response = this.setSupportVersion(version);
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}


/**
* 16. 小程序分阶段发布 - 1)分阶段发布接口
*
* @param grayPercentage 灰度的百分比,1到100的整数
* @return
* @throws WxErrorException
*/
@Override
public WxOpenResult grayrelease(Integer grayPercentage) throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("gray_percentage", grayPercentage);
String response = post(API_GRAY_RELEASE, GSON.toJson(params));
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}


/**
* 16. 小程序分阶段发布 - 2)取消分阶段发布
*
* @return
* @throws WxErrorException
*/
@Override

public WxOpenResult revertgrayrelease() throws WxErrorException {
String response = get(API_REVERT_GRAY_RELEASE, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}


/**
* 16. 小程序分阶段发布 - 3)查询当前分阶段发布详情
*
* @return
* @throws WxErrorException
*/
@Override
public WxOpenMaGrayReleasePlanResult getgrayreleaseplan() throws WxErrorException {
String response = get(API_GET_GRAY_RELEASE_PLAN, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaGrayReleasePlanResult.class);
}


/**
* 查询服务商的当月提审限额和加急次数(Quota)
* https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/code/query_quota.html
*/
@Override
public WxOpenMaQueryQuotaResult queryQuota() throws WxErrorException {
String response = get(API_QUERY_QUOTA, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenMaQueryQuotaResult.class);
}



/**
* 加急审核申请
* 有加急次数的第三方可以通过该接口,对已经提审的小程序进行加急操作,加急后的小程序预计2-12小时内审完。
*/
@Override
public Boolean speedAudit(Long auditid) throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("auditid", auditid);
String response = post(API_SPEED_AUDIT, GSON.toJson(params));
WxOpenResult result = WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
return result.isSuccess();
}


/**
* (1)增加或修改二维码规则
* @param wxQrcodeJumpRule
* @return
* @throws WxErrorException
*/
@Override
public WxOpenResult addQrcodeJump(WxQrcodeJumpRule wxQrcodeJumpRule) throws WxErrorException {
String response = post(API_QRCODE_JUMP_ADD, GSON.toJson(wxQrcodeJumpRule));
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}

/**
* (2)获取已设置的二维码规则
* @return
* @throws WxErrorException
*/
@Override
public WxGetQrcodeJumpResult getQrcodeJump() throws WxErrorException {
String response = post(API_QRCODE_JUMP_GET, "{}");
return WxMaGsonBuilder.create().fromJson(response, WxGetQrcodeJumpResult.class);
}

/**
* (3)获取校验文件名称及内容
* @return
* @throws WxErrorException
*/
@Override
public WxDownlooadQrcodeJumpResult downloadQrcodeJump() throws WxErrorException {
String response = post(API_QRCODE_JUMP_DOWNLOAD, "{}");
return WxMaGsonBuilder.create().fromJson(response, WxDownlooadQrcodeJumpResult.class);
}

/**
* (4)删除已设置的二维码规则
* @param prefix
* @return
* @throws WxErrorException
*/
@Override
public WxOpenResult deleteQrcodeJump(String prefix) throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("prefix", prefix);
String response = post(API_QRCODE_JUMP_DELETE, GSON.toJson(params));
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}

/**
* (5)发布已设置的二维码规则
* @param prefix
* @return
* @throws WxErrorException
*/
@Override
public WxOpenResult publishQrcodeJump(String prefix) throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("prefix", prefix);
String response = post(API_QRCODE_JUMP_PUBLISH, GSON.toJson(params));
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}

/**
* 将字符串对象转化为GsonArray对象
*
* @param strList
* @return
*/
private JsonArray toJsonArray(List<String> strList) {
JsonArray jsonArray = new JsonArray();
if (strList != null && !strList.isEmpty()) {
for (String str : strList) {
jsonArray.add(str);
}
}
return jsonArray;
}
}

+ 62
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceAbstractImpl.java 查看文件

@@ -0,0 +1,62 @@
package com.iformall.service.toutiao.api.impl;

import com.iformall.service.toutiao.api.TtOpenComponentService;
import com.iformall.service.toutiao.api.TtOpenService;
import me.chanjar.weixin.common.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.open.api.WxOpenConfigStorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
* @author
*/
public abstract class TtOpenServiceAbstractImpl<H, P> implements TtOpenService, RequestHttp<H, P> {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private TtOpenComponentService ttOpenComponentService = new TtOpenComponentServiceImpl(this);
private WxOpenConfigStorage wxOpenConfigStorage;

@Override
public TtOpenComponentService getTtOpenComponentService() {
return ttOpenComponentService;
}

@Override
public WxOpenConfigStorage getWxOpenConfigStorage() {
return wxOpenConfigStorage;
}

@Override
public void setWxOpenConfigStorage(WxOpenConfigStorage wxOpenConfigStorage) {
this.wxOpenConfigStorage = wxOpenConfigStorage;
this.initHttp();
}

/**
* 初始化 RequestHttp.
*/
public abstract void initHttp();

protected <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
try {
T result = executor.execute(uri, data, WxType.Open);
this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uri, data, result);
return result;
} catch (WxErrorException e) {
WxError error = e.getError();
if (error.getErrorCode() != 0) {
this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uri, data, error);
throw new WxErrorException(error, e);
}
return null;
} catch (IOException e) {
this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uri, data, e.getMessage());
throw new RuntimeException(e);
}
}
}

+ 67
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceApacheHttpClientImpl.java 查看文件

@@ -0,0 +1,67 @@
package com.iformall.service.toutiao.api.impl;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.HttpType;
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor;
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
import me.chanjar.weixin.open.api.WxOpenConfigStorage;
import org.apache.http.HttpHost;
import org.apache.http.impl.client.CloseableHttpClient;

/**
* apache-http方式实现
*
* @author <a href="https://github.com/007gzs">007</a>
*/
public class TtOpenServiceApacheHttpClientImpl extends TtOpenServiceAbstractImpl<CloseableHttpClient, HttpHost> {
private CloseableHttpClient httpClient;
private HttpHost httpProxy;

@Override
public void initHttp() {
WxOpenConfigStorage configStorage = this.getWxOpenConfigStorage();
ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder();
if (null == apacheHttpClientBuilder) {
apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get();
}

apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost())
.httpProxyPort(configStorage.getHttpProxyPort())
.httpProxyUsername(configStorage.getHttpProxyUsername())
.httpProxyPassword(configStorage.getHttpProxyPassword());

if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) {
this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort());
}

this.httpClient = apacheHttpClientBuilder.build();

}

@Override
public CloseableHttpClient getRequestHttpClient() {
return httpClient;
}

@Override
public HttpHost getRequestHttpProxy() {
return httpProxy;
}

@Override
public HttpType getRequestType() {
return HttpType.APACHE_HTTP;
}

@Override
public String get(String url, String queryParam) throws WxErrorException {
return execute(SimpleGetRequestExecutor.create(this), url, queryParam);
}

@Override
public String post(String url, String postData) throws WxErrorException {
return execute(SimplePostRequestExecutor.create(this), url, postData);
}
}

+ 8
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceImpl.java 查看文件

@@ -0,0 +1,8 @@
package com.iformall.service.toutiao.api.impl;

/**
* @author
*/
public class TtOpenServiceImpl extends TtOpenServiceApacheHttpClientImpl {

}

+ 28
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaMsgService.java 查看文件

@@ -0,0 +1,28 @@
package com.iformall.service.toutiao.miniapp;

import com.iformall.service.toutiao.miniapp.bean.TtMaSubscribeMessage;
import me.chanjar.weixin.common.error.WxErrorException;

/**
* <pre>
* 消息发送接口
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface TtMaMsgService {

String SUBSCRIBE_MSG_SEND_URL = "https://developer.toutiao.com/api/apps/subscribe_notification/developer/v1/notify";

/**
* <pre>
* 发送订阅消息
* https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/subscribe-notification/notify/
* </pre>
*
* @param subscribeMessage 订阅消息
* @throws WxErrorException .
*/
void sendSubscribeMsg(TtMaSubscribeMessage subscribeMessage) throws WxErrorException;

}

+ 39
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaQrcode.java 查看文件

@@ -0,0 +1,39 @@
package com.iformall.service.toutiao.miniapp;

import cn.binarywang.wx.miniapp.bean.AbstractWxMaQrcodeWrapper;
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class TtMaQrcode extends AbstractWxMaQrcodeWrapper implements Serializable {

private static final long serialVersionUID = 5777119669111011584L;

private String access_token;

private String appname;
private String path;
private int width = 430;

public TtMaQrcode(String appname, String path, int width) {
this.appname = appname;
this.path = path;
this.width = width;
}

public static TtMaQrcode fromJson(String json) {
return WxMaGsonBuilder.create().fromJson(json, TtMaQrcode.class);
}

@Override
public String toString() {
return WxMaGsonBuilder.create().toJson(this);
}
}

+ 31
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaQrcodeService.java 查看文件

@@ -0,0 +1,31 @@
package com.iformall.service.toutiao.miniapp;

import me.chanjar.weixin.common.error.WxErrorException;

import java.io.File;

/**
* <pre>
* 二维码相关操作接口.
*
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface TtMaQrcodeService {

String GET_TTACODE_URL = "https://developer.toutiao.com/api/apps/qrcode";


/**
* 接口A: 获取小程序码.
*
* @param path 不能为空,最大长度 128 字节
* @param width 默认430 二维码的宽度
* @return 文件对象
* @throws WxErrorException 异常
*/
File createTtaCode(String appname, String path, int width) throws WxErrorException;


}

+ 132
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaService.java 查看文件

@@ -0,0 +1,132 @@
package com.iformall.service.toutiao.miniapp;

import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface TtMaService {
/**
* 获取access_token.
*/
String GET_ACCESS_TOKEN_URL = "https://developer.toutiao.com/api/apps/token?grant_type=client_credential&appid=%s&secret=%s";

String JSCODE_TO_SESSION_URL = "https://developer.toutiao.com/api/apps/jscode2session";

String ORDER_PUSH = "https://developer.toutiao.com/api/apps/order/v2/push";

/**
* 获取登录后的session信息.
*
* @param jsCode 登录时获取的 code
*/
WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException;

/**
* <pre>
* 验证消息的确来自微信服务器.
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN
* </pre>
*/
boolean checkSignature(String timestamp, String nonce, String signature);

/**
* <pre>
* 获取access_token,本方法线程安全.
* 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
*
* 另:本service的所有方法都会在access_token过期是调用此方法
*
* 程序员在非必要情况下尽量不要主动调用此方法
*
* 详情请见: https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/interface-request-credential/get-access-token
* </pre>
*
* @param forceRefresh 强制刷新
*/
String getAccessToken(boolean forceRefresh) throws WxErrorException;

/**
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求.
*/
String get(String url, String queryParam) throws WxErrorException;

/**
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求.
*/
String post(String url, String postData) throws WxErrorException;

/**
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求.
*/
String post(String url, Object obj) throws WxErrorException;

/**
* <pre>
* Service没有实现某个API的时候,可以用这个,
* 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
* 可以参考,{@link MediaUploadRequestExecutor}的实现方法
* </pre>
*/
<T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException;

/**
* <pre>
* 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试.
* 默认:1000ms
* </pre>
*/
void setRetrySleepMillis(int retrySleepMillis);

/**
* <pre>
* 设置当微信系统响应系统繁忙时,最大重试次数.
* 默认:5次
* </pre>
*/
void setMaxRetryTimes(int maxRetryTimes);

/**
* 获取WxMaConfig 对象.
*
* @return WxMaConfig
*/
WxMaConfig getWxMaConfig();

/**
* 注入 {@link WxMaConfig} 的实现.
*/
void setWxMaConfig(WxMaConfig wxConfigProvider);

/**
* 初始化http请求对象.
*/
void initHttp();

/**
* 请求http请求相关信息.
*/
RequestHttp getRequestHttp();

/**
* 返回用户相关接口方法的实现类对象,以方便调用其各个接口.
*
* @return TtMaUserService
*/
TtMaUserService getUserService();

/**
* 返回用户相关接口方法的实现类对象,以方便调用其各个接口.
*
* @return TtMaUserService
*/
TtMaQrcodeService getQrcodeService();

TtMaMsgService getMsgService();

}

+ 52
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaUserService.java 查看文件

@@ -0,0 +1,52 @@
package com.iformall.service.toutiao.miniapp;

import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import me.chanjar.weixin.common.error.WxErrorException;

/**
* 用户信息相关操作接口.
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface TtMaUserService {

/**
* 获取登录后的session信息.
*
* @param jsCode 登录时获取的 code
* @return .
* @throws WxErrorException .
*/
WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException;

/**
* 解密用户敏感数据.
*
* @param sessionKey 会话密钥
* @param encryptedData 消息密文
* @param ivStr 加密算法的初始向量
*/
WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr);

/**
* 解密用户手机号信息.
*
* @param sessionKey 会话密钥
* @param encryptedData 消息密文
* @param ivStr 加密算法的初始向量
* @return .
*/
WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr);

/**
* 验证用户信息完整性.
*
* @param sessionKey 会话密钥
* @param rawData 微信用户基本信息
* @param signature 数据签名
* @return .
*/
boolean checkUserInfo(String sessionKey, String rawData, String signature);
}

+ 69
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtQrcodeRequestExecutor.java 查看文件

@@ -0,0 +1,69 @@
package com.iformall.service.toutiao.miniapp;

import cn.binarywang.wx.miniapp.bean.AbstractWxMaQrcodeWrapper;
import me.chanjar.weixin.common.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.fs.FileUtils;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.common.util.http.ResponseHandler;
import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler;
import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class TtQrcodeRequestExecutor implements RequestExecutor<File, AbstractWxMaQrcodeWrapper> {
protected RequestHttp<CloseableHttpClient, HttpHost> requestHttp;

public TtQrcodeRequestExecutor(RequestHttp requestHttp) {
this.requestHttp = requestHttp;
}

@Override
public void execute(String uri, AbstractWxMaQrcodeWrapper data, ResponseHandler<File> handler, WxType wxType) throws WxErrorException, IOException {
handler.handle(this.execute(uri, data, wxType));
}

@Override
public File execute(String uri, AbstractWxMaQrcodeWrapper qrcodeWrapper, WxType wxType) throws WxErrorException, IOException {
HttpPost httpPost = new HttpPost(uri);
if (requestHttp.getRequestHttpProxy() != null) {
httpPost.setConfig(
RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build()
);
}
httpPost.addHeader("Content-Type","application/json");

httpPost.setEntity(new StringEntity(qrcodeWrapper.toJson()));

try (final CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost);
final InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response)) {
Header[] contentTypeHeader = response.getHeaders("Content-Type");
if (contentTypeHeader != null && contentTypeHeader.length > 0
&& ContentType.APPLICATION_JSON.getMimeType()
.equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) {
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
throw new WxErrorException(WxError.fromJson(responseContent, wxType));
}

return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg");
} finally {
httpPost.releaseConnection();
}
}
}

+ 21
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaGsonBuilder.java 查看文件

@@ -0,0 +1,21 @@
package com.iformall.service.toutiao.miniapp.bean;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class TtMaGsonBuilder {
private static final GsonBuilder INSTANCE = new GsonBuilder();

static {
INSTANCE.disableHtmlEscaping();
INSTANCE.registerTypeAdapter(TtMaSubscribeMessage.class, new TtMaSubscribeMessageGsonAdapter());
}

public static Gson create() {
return INSTANCE.create();
}

}

+ 70
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaSubscribeMessage.java 查看文件

@@ -0,0 +1,70 @@
package com.iformall.service.toutiao.miniapp.bean;

import com.google.gson.JsonObject;
import lombok.*;

import java.io.Serializable;

/**
* 订阅消息.

* @author S
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TtMaSubscribeMessage implements Serializable {

private static final long serialVersionUID = -5931206947637944325L;

private String accessToken;

private String appId;

/**
* 接收者(用户)的 openid.
* <pre>
* 参数:touser
* 是否必填: 是
* 描述: 接收者(用户)的 openid
* </pre>
*/
private String openId;

/**
* 所需下发的模板消息的id.
* <pre>
* 参数:template_id
* 是否必填: 是
* 描述: 所需下发的模板消息的id
* </pre>
*/
private String tplId;

/**
* 点击模板卡片后的跳转页面,仅限本小程序内的页面.
* <pre>
* 参数:page
* 是否必填: 否
* 描述: 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。
* </pre>
*/
private String page;

/**
* 模板内容,不填则下发空模板.
* <pre>
* 参数:data
* 是否必填: 是
* 描述: 模板内容,不填则下发空模板
* </pre>
*/
private JsonObject data;

public String toJson() {
return TtMaGsonBuilder.create().toJson(this);
}

}

+ 33
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaSubscribeMessageGsonAdapter.java 查看文件

@@ -0,0 +1,33 @@
package com.iformall.service.toutiao.miniapp.bean;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import java.lang.reflect.Type;

/**
* .
*
* @author S
*/
public class TtMaSubscribeMessageGsonAdapter implements JsonSerializer<TtMaSubscribeMessage> {
@Override
public JsonElement serialize(TtMaSubscribeMessage message, Type typeOfSrc, JsonSerializationContext context) {
JsonObject messageJson = new JsonObject();

messageJson.addProperty("access_token", message.getAccessToken());
messageJson.addProperty("app_id", message.getAppId());
messageJson.addProperty("tpl_id", message.getTplId());
messageJson.addProperty("open_id", message.getOpenId());
if (message.getPage() != null) {
messageJson.addProperty("page", message.getPage());
}

messageJson.add("data", message.getData());

return messageJson;
}

}

+ 30
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaMsgServiceImpl.java 查看文件

@@ -0,0 +1,30 @@
package com.iformall.service.toutiao.miniapp.impl;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.iformall.service.toutiao.miniapp.TtMaMsgService;
import com.iformall.service.toutiao.miniapp.TtMaService;
import com.iformall.service.toutiao.miniapp.bean.TtMaSubscribeMessage;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@AllArgsConstructor
public class TtMaMsgServiceImpl implements TtMaMsgService {
private static final JsonParser JSON_PARSER = new JsonParser();
private TtMaService ttMaService;

@Override
public void sendSubscribeMsg(TtMaSubscribeMessage subscribeMessage) throws WxErrorException {
String responseContent = this.ttMaService.post(SUBSCRIBE_MSG_SEND_URL, subscribeMessage);
JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject();
if (jsonObject.get("err_no").getAsInt() != 0) {
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
}
}

}

+ 26
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaQrcodeServiceImpl.java 查看文件

@@ -0,0 +1,26 @@
package com.iformall.service.toutiao.miniapp.impl;

import com.iformall.service.toutiao.miniapp.TtMaQrcode;
import com.iformall.service.toutiao.miniapp.TtMaQrcodeService;
import com.iformall.service.toutiao.miniapp.TtMaService;
import com.iformall.service.toutiao.miniapp.TtQrcodeRequestExecutor;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;

import java.io.File;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@AllArgsConstructor
public class TtMaQrcodeServiceImpl implements TtMaQrcodeService {

private TtMaService ttMaService;

@Override
public File createTtaCode(String appname, String path, int width) throws WxErrorException {
final TtQrcodeRequestExecutor executor = new TtQrcodeRequestExecutor(this.ttMaService.getRequestHttp());
return this.ttMaService.execute(executor, GET_TTACODE_URL, new TtMaQrcode(appname,path, width));
}

}

+ 312
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaServiceImpl.java 查看文件

@@ -0,0 +1,312 @@
package com.iformall.service.toutiao.miniapp.impl;

import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import com.google.common.base.Joiner;
import com.google.gson.Gson;
import com.google.gson.JsonParser;
import com.iformall.service.toutiao.miniapp.TtMaMsgService;
import com.iformall.service.toutiao.miniapp.TtMaQrcodeService;
import com.iformall.service.toutiao.miniapp.TtMaService;
import com.iformall.service.toutiao.miniapp.TtMaUserService;
import com.iformall.service.toutiao.miniapp.bean.TtMaGsonBuilder;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.WxType;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.DataUtils;
import me.chanjar.weixin.common.util.crypto.SHA1;
import me.chanjar.weixin.common.util.http.*;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;

import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*;

/**
* @author
*/
@Slf4j
public class TtMaServiceImpl implements TtMaService, RequestHttp<CloseableHttpClient, HttpHost> {
private static final JsonParser JSON_PARSER = new JsonParser();
private CloseableHttpClient httpClient;
private HttpHost httpProxy;
private WxMaConfig wxMaConfig;

private TtMaUserService userService = new TtMaUserServiceImpl(this);
private TtMaQrcodeService qrcodeService = new TtMaQrcodeServiceImpl(this);
private TtMaMsgService msgService = new TtMaMsgServiceImpl(this);

private int retrySleepMillis = 1000;
private int maxRetryTimes = 5;

protected static final Gson GSON = new Gson();

@Override
public CloseableHttpClient getRequestHttpClient() {
return httpClient;
}

@Override
public HttpHost getRequestHttpProxy() {
return httpProxy;
}

@Override
public HttpType getRequestType() {
return HttpType.APACHE_HTTP;
}

@Override
public void initHttp() {
WxMaConfig configStorage = this.getWxMaConfig();
ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder();
if (null == apacheHttpClientBuilder) {
apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get();
}

apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost())
.httpProxyPort(configStorage.getHttpProxyPort())
.httpProxyUsername(configStorage.getHttpProxyUsername())
.httpProxyPassword(configStorage.getHttpProxyPassword());

if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) {
this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort());
}

this.httpClient = apacheHttpClientBuilder.build();
}

@Override
public RequestHttp getRequestHttp() {
return this;
}

@Override
public String getAccessToken(boolean forceRefresh) throws WxErrorException {
if (!this.getWxMaConfig().isAccessTokenExpired() && !forceRefresh) {
return this.getWxMaConfig().getAccessToken();
}

Lock lock = this.getWxMaConfig().getAccessTokenLock();
lock.lock();
try {
String url = String.format(this.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(),
this.getWxMaConfig().getSecret());
try {
HttpGet httpGet = new HttpGet(url);
if (this.getRequestHttpProxy() != null) {
RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build();
httpGet.setConfig(config);
}
try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) {
String resultContent = new BasicResponseHandler().handleResponse(response);
WxError error = WxError.fromJson(resultContent, WxType.MiniApp);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());

return this.getWxMaConfig().getAccessToken();
} finally {
httpGet.releaseConnection();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
} finally {
lock.unlock();
}

}



@Override
public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException {
final WxMaConfig config = getWxMaConfig();
Map<String, String> params = new HashMap<>(8);
params.put("appid", config.getAppid());
params.put("secret", config.getSecret());
params.put("code", jsCode);

String result = get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params));
return WxMaJscode2SessionResult.fromJson(result);
}

@Override
public boolean checkSignature(String timestamp, String nonce, String signature) {
try {
return SHA1.gen(this.getWxMaConfig().getToken(), timestamp, nonce).equals(signature);
} catch (Exception e) {
log.error("Checking signature failed, and the reason is :" + e.getMessage());
return false;
}
}

@Override
public String get(String url, String queryParam) throws WxErrorException {
if (url.contains("access_token=")) {
throw new IllegalArgumentException("uri参数中不允许有access_token: " + url);
}
String accessToken = getAccessToken(false);
String uriWithAccessToken = url + (url.contains("?") ? "&" : "?") + "access_token=" + accessToken;

return execute(SimpleGetRequestExecutor.create(this), uriWithAccessToken, queryParam);
}

@Override
public String post(String url, String postData) throws WxErrorException {

if (url.contains("access_token=")) {
throw new IllegalArgumentException("uri参数中不允许有access_token: " + url);
}
String accessToken = getAccessToken(false);
String uriWithAccessToken = url + (url.contains("?") ? "&" : "?") + "access_token=" + accessToken;

return execute(SimplePostRequestExecutor.create(this), uriWithAccessToken, postData);
}

@Override
public String post(String url, Object obj) throws WxErrorException {
if (url.contains("access_token=")) {
throw new IllegalArgumentException("uri参数中不允许有access_token: " + url);
}
try {
String accessToken = getAccessToken(false);
Class<?> aClass = obj.getClass();
if(!aClass.equals(String.class)){
if(aClass.equals(HashMap.class)){
((HashMap) obj).put("access_token",accessToken);
}else{
Field accessToken1 = aClass.getDeclaredField("accessToken");
accessToken1.setAccessible(true);
accessToken1.set(obj,accessToken);
}
}
} catch (Exception e) {
e.printStackTrace();
log.error("【请求参数】:{}添加accessToken失败" + obj.getClass());
}
return this.execute(SimplePostRequestExecutor.create(this), url, TtMaGsonBuilder.create().toJson(obj));
}

/**
* 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求
*/
@Override
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
int retryTimes = 0;
do {
try {
return this.executeInternal(executor, uri, data);
} catch (WxErrorException e) {
if (retryTimes + 1 > this.maxRetryTimes) {
log.warn("重试达到最大次数【{}】", maxRetryTimes);
//最后一次重试失败后,直接抛出异常,不再等待
throw new RuntimeException("微信服务端异常,超出重试次数");
}

WxError error = e.getError();
// -1 系统繁忙, 1000ms后重试
if (error.getErrorCode() == -1) {
int sleepMillis = this.retrySleepMillis * (1 << retryTimes);
try {
log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
Thread.sleep(sleepMillis);
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
}
} else {
throw e;
}
}
} while (retryTimes++ < this.maxRetryTimes);

log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
throw new RuntimeException("微信服务端异常,超出重试次数");
}

private <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
E dataForLog = DataUtils.handleDataWithSecret(data);

try {
T result = executor.execute(uri, data, WxType.MiniApp);
log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uri, data, result);
return result;
} catch (WxErrorException e) {
WxError error = e.getError();
/*
* 发生以下情况时尝试刷新access_token
*/
if (error.getErrorCode() == ERR_40001
|| error.getErrorCode() == ERR_42001
|| error.getErrorCode() == ERR_40014) {
// 强制设置WxMaConfig的access token过期了,这样在下一次请求里就会刷新access token
this.getWxMaConfig().expireAccessToken();
if (this.getWxMaConfig().autoRefreshToken()) {
return this.execute(executor, uri, data);
}
}

if (error.getErrorCode() != 0) {
log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uri, dataForLog, error);
throw new WxErrorException(error, e);
}
return null;
} catch (IOException e) {
log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uri, dataForLog, e.getMessage());
throw new RuntimeException(e);
}
}

@Override
public WxMaConfig getWxMaConfig() {
return this.wxMaConfig;
}

@Override
public void setWxMaConfig(WxMaConfig wxConfigProvider) {
this.wxMaConfig = wxConfigProvider;
this.initHttp();
}

@Override
public void setRetrySleepMillis(int retrySleepMillis) {
this.retrySleepMillis = retrySleepMillis;
}

@Override
public void setMaxRetryTimes(int maxRetryTimes) {
this.maxRetryTimes = maxRetryTimes;
}

@Override
public TtMaUserService getUserService() {
return this.userService;
}

@Override
public TtMaQrcodeService getQrcodeService() {
return this.qrcodeService;
}

@Override
public TtMaMsgService getMsgService() {
return this.msgService;
}

}

+ 41
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaUserServiceImpl.java 查看文件

@@ -0,0 +1,41 @@
package com.iformall.service.toutiao.miniapp.impl;

import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils;
import com.iformall.service.toutiao.miniapp.TtMaService;
import com.iformall.service.toutiao.miniapp.TtMaUserService;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.codec.digest.DigestUtils;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@AllArgsConstructor
public class TtMaUserServiceImpl implements TtMaUserService {
private TtMaService service;

@Override
public WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException {
return service.jsCode2SessionInfo(jsCode);
}

@Override
public WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr) {
return WxMaUserInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr));
}

@Override
public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr) {
return WxMaPhoneNumberInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr));
}

@Override
public boolean checkUserInfo(String sessionKey, String rawData, String signature) {
final String generatedSignature = DigestUtils.sha1Hex(rawData + sessionKey);
return generatedSignature.equals(signature);
}

}

+ 88
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/utils/MsgDecrypt.java 查看文件

@@ -0,0 +1,88 @@
package com.iformall.service.toutiao.utils;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;


public class MsgDecrypt {

private Cipher cipher;
static int RANDOM_BYTES_POS = 32;

public MsgDecrypt(String encodingAesKey) throws Exception {
//AES key长度固定
if (encodingAesKey.length() != 43) {
throw new Exception("AES key 长度不合法");
}
byte[] aesKey = Base64.decodeBase64(encodingAesKey + "=");

SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
Cipher encCipher = Cipher.getInstance("AES/CBC/NoPadding");
encCipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);

cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
}

//将四个byte解析为一个32位的数字,数字的值也就是消息体的String格式下的长度
private int getLength(byte[] orderBytes) {
ByteBuffer buf = ByteBuffer.wrap(orderBytes);
buf.order(ByteOrder.BIG_ENDIAN);
return buf.getInt();
}

private byte[] decode(byte[] decrypted) {
int pad = decrypted[decrypted.length - 1];
if (pad < 1 || pad > 32) {
pad = 0;
}
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
}

public String decrypt(String text) throws Exception {
byte[] original;
try {
byte[] encrypted = Base64.decodeBase64(text);
original = cipher.doFinal(encrypted);
} catch (Exception e) {
e.printStackTrace();
throw new Exception("解码异常");
}

String Content;
String AppId;
try {
byte[] bytes = this.decode(original);

//byte数组的第32、33、34、35个元素代表了消息体的真实字符个数,也就是长度
byte[] pos = Arrays.copyOfRange(bytes, RANDOM_BYTES_POS, RANDOM_BYTES_POS + 4);
int msgLength = getLength(pos);

//根据解析出来的消息体长度值,截取真实的消息体
Content = new String(Arrays.copyOfRange(bytes, RANDOM_BYTES_POS + 4, RANDOM_BYTES_POS + 4 + msgLength), StandardCharsets.UTF_8);
//byte数组截去真实消息后,末尾剩下的字符就是appid
AppId = new String(Arrays.copyOfRange(bytes, RANDOM_BYTES_POS + 4 + msgLength, bytes.length), StandardCharsets.UTF_8);

// System.out.println("Content: " + Content);
// System.out.println("ThirdParty AppID: " + AppId);
} catch (Exception e) {
e.printStackTrace();
throw new Exception("Buffer异常");
}
return Content;
}

public static void main(String[] args) throws Exception {
MsgDecrypt test = new MsgDecrypt("XXX");
test.decrypt("XXX");
}
}

+ 55
- 0
mallinkService/src/main/java/com/iformall/service/toutiao/utils/ServerVerification.java 查看文件

@@ -0,0 +1,55 @@
package com.iformall.service.toutiao.utils;

import java.util.Arrays;
import java.security.MessageDigest;

public class ServerVerification {

private String getMsgSignature(String tpToken, String timestamp, String nonce, String encrypt) throws Exception {
String[] values = new String[] {tpToken, timestamp, nonce, encrypt};
Arrays.sort(values);

StringBuilder sb = new StringBuilder();
for (String value : values) {
sb.append(value);
}

String str = sb.toString();

try {
//指定sha1算法
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
messageDigest.update(str.getBytes());

//获取字节数组
byte[] messageDigestByte = messageDigest.digest();

StringBuilder hexStr = new StringBuilder();
// 字节数组转换为十六进制数
for (byte b : messageDigestByte) {
String shaHex = Integer.toHexString(b & 0xFF);
if (shaHex.length() < 2) {
hexStr.append(0);
}
hexStr.append(shaHex);
}

return hexStr.toString();
} catch (Exception e) {
e.printStackTrace();
throw new Exception("不能生成签名");
}
}

private void verify(String msgSignature, String newMsgSignature) {
boolean res = msgSignature.equals(newMsgSignature);
System.out.println(res);
}

public static void main(String[] args) throws Exception {
ServerVerification test = new ServerVerification();
String msgSignature = "XXX";
String newMsgSignature = test.getMsgSignature("XXX", "XXX", "XXX", "XXX");
test.verify(msgSignature, newMsgSignature);
}
}

+ 131
- 0
mlToutiaoOpen/src/main/java/com/iformall/controller/TtAuthController.java 查看文件

@@ -0,0 +1,131 @@
package com.iformall.controller;

import com.iformall.common.ResultData;
import com.iformall.domain.po.WxAuthorizerInfo;
import com.iformall.domain.vo.WxWeappInfo;
import com.iformall.enums.EnumAppPlat;
import com.iformall.enums.EnumWxAuthorizationStatus;
import com.iformall.service.WxAuthorizerInfoService;
import com.iformall.service.toutiao.FmTtOpenService;
import com.iformall.service.toutiao.api.bean.TtOpenAuthorizationInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import me.chanjar.weixin.common.error.WxErrorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.List;

/**
* Stormeye Wu
*/
@Controller
@RequestMapping("/wt_auth")
@Api(description = "微信第三方开放平台授权相关接口")
public class TtAuthController {
private final Logger logger = LoggerFactory.getLogger(getClass());

@Autowired
protected FmTtOpenService openService;

@Autowired
private WxAuthorizerInfoService authorizerInfoService;

@ApiOperation(value = "获取授权跳转页", notes = "")
@GetMapping("/goto_auth_url_show")
@ResponseBody
public String gotoPreAuthUrlShow() {
return "<html>" +
"<body>" +
"<a href='/T/wt_auth/goto_auth_url'>go</a>" +
"</body>" +
"</html>";
}

@ApiOperation(value = "授权跳转页", notes = "")
@GetMapping("/goto_auth_url")
public void gotoPreAuthUrl(HttpServletRequest request, HttpServletResponse response) {
String host = request.getHeader("host");
String url = "https://" + host + "/T/wt_auth/jump";
try {
url = openService.getTtOpenComponentService().getPreAuthUrl(url);
response.sendRedirect(url);
} catch (WxErrorException | IOException e) {
logger.error("gotoPreAuthUrl", e);
throw new RuntimeException(e);
}
}

@ApiOperation(value = "授权", notes = "")
@GetMapping("/jump")
@ResponseBody
public void jump(@RequestParam("auth_code") String authorizationCode) {
try {
TtOpenAuthorizationInfo queryAuthResult = openService.getTtOpenComponentService().getQueryAuth(authorizationCode);
logger.info("getQueryAuth", queryAuthResult);
// WxOpenAuthorizerInfoResult openAuthorizerInfoResult = openService.getTtOpenComponentService().getAuthorizerInfo(
// queryAuthResult.getAuthorizerAppid());
// logger.info(openAuthorizerInfoResult.toString());
// save auth info
authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo(){
{
setAuthorizerAppid(queryAuthResult.getAuthorizerAppid());
setRefreshToken(queryAuthResult.getAuthorizerRefreshToken());
setAccessToken(queryAuthResult.getAuthorizerAccessToken());
setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + queryAuthResult.getExpiresIn()*1000)));
setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode());
setAuthTime(new Date());
}
});
} catch (WxErrorException e) {
logger.error("gotoPreAuthUrl", e);
throw new RuntimeException(e);
}
}

@ApiOperation(value = "刷新", notes = "")
@GetMapping("/retrieve")
@ResponseBody
public ResultData retrieve() {

WxWeappInfo weappInfo = new WxWeappInfo();
weappInfo.setPlat(EnumAppPlat.TOUTIAO.getCode());
List<WxWeappInfo> list = authorizerInfoService.getList(weappInfo);
if(list != null && list.size() > 0){
for (WxWeappInfo info: list) {
try {
String code = openService.getTtOpenComponentService().retrieveCode(info.getAuthorizerAppid());

TtOpenAuthorizationInfo queryAuthResult = openService.getTtOpenComponentService().getQueryAuth(code);
logger.info("getQueryAuth", queryAuthResult);
// save auth info
authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo(){
{
setAuthorizerAppid(queryAuthResult.getAuthorizerAppid());
setRefreshToken(queryAuthResult.getAuthorizerRefreshToken());
setAccessToken(queryAuthResult.getAuthorizerAccessToken());
setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + queryAuthResult.getExpiresIn()*1000)));
setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode());
setAuthTime(new Date());
}
});
} catch (WxErrorException e) {
logger.error(info.getAuthorizerAppid()+"找回授权码", e);
throw new RuntimeException(e);
}
}
}
return new ResultData();
}

}

+ 388
- 0
mlToutiaoOpen/src/main/java/com/iformall/controller/TtCalllbackController.java 查看文件

@@ -0,0 +1,388 @@
package com.iformall.controller;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.iformall.common.Result;
import com.iformall.common.ResultData;
import com.iformall.controller.base.BaseController;
import com.iformall.domain.po.WxAuthorizerInfo;
import com.iformall.domain.po.WxComponentVerifyTicket;
import com.iformall.enums.EnumWxAuthorizationStatus;
import com.iformall.service.WxAuthorizerInfoService;
import com.iformall.service.WxComponentVerifyTicketService;
import com.iformall.service.toutiao.FmTtOpenService;
import com.iformall.service.toutiao.api.bean.TtOpenTicket;
import com.iformall.utils.DateUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.open.bean.WxOpenCreateResult;
import me.chanjar.weixin.open.bean.WxOpenGetResult;
import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult;
import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerListResult;
import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Map;

@Controller
@RequestMapping("/ttOpen")
@Api(description = "头条第三方开放平台回调相关接口")
public class TtCalllbackController extends BaseController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Autowired
protected FmTtOpenService openService;

@Autowired
private WxComponentVerifyTicketService componentVerifyTicketService;

@Autowired
private WxAuthorizerInfoService authorizerInfoService;

@PostMapping(value = "/notify")
@ApiOperation("接收ticket回调")
@ResponseBody
public String receiveTicket(HttpServletRequest request) throws IOException {
logger.info("[" + getIpAddr() + "]接收微信回调 /ttOpen/notify");

String nonce = request.getParameter("Nonce");
String timestamp = request.getParameter("TimeStamp");
// String signature = request.getParameter("signature");
String encrypt = request.getParameter("Encrypt");
String msgSignature = request.getParameter("MsgSignature");

if (!openService.getTtOpenComponentService().checkSignature(timestamp, nonce, encrypt, msgSignature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
}
// aes加密的消息, 解密
TtOpenTicket openTicket = openService.getTtOpenComponentService().decrypt(encrypt);
// 处理
handleTicket(openTicket);
return "success";
}

/**
* 处理ticket消息
* @param
* @return
*/
private void handleTicket(TtOpenTicket ticket) {
try {
String out = openService.getTtOpenComponentService().route(ticket);
this.logger.info("组装回复信息:{}", out);
} catch (WxErrorException e) {
this.logger.error("notify", e);
}
if (StringUtils.equalsIgnoreCase(ticket.getMsgType(), "Ticket")) {
// 保存component_verify_ticket
componentVerifyTicketService.saveOrUpdate(new WxComponentVerifyTicket() {
{
setComponentVerifyTicket(ticket.getTicket());
setCreateTime(DateUtils.stringToDate(ticket.getCreateTime(),"yyyy-MM-dd HH:mm:ss"));
setComponentAppid(ticket.getComponentAppId());
setDeadline(new Date(Long.valueOf(System.currentTimeMillis() + (60 * 60 * 1000))));
}
});
}
}

// /**
// * 处理小程序审核消息
// * @param inMessage
// * @return
// */
// private boolean handleWeappAuditedMessage(WxOpenXmlMessage inMessage) {
// // 审核通过
// try {
// WxOpenQueryAuthResult queryAuthResult = openService.getWxOpenComponentService().getQueryAuth(inMessage.getAuthorizationCode());
// logger.info("getQueryAuth", queryAuthResult);
//
// WxOpenAuthorizerInfoResult openAuthorizerInfoResult = openService.getWxOpenComponentService().getAuthorizerInfo(
// queryAuthResult.getAuthorizationInfo().getAuthorizerAppid());
// logger.info(openAuthorizerInfoResult.toString());
// // save auth info
// authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() {
// {
// setAuthorizerAppid(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAppid());
// setAlias(openAuthorizerInfoResult.getAuthorizerInfo().getAlias());
// setHeadImg(openAuthorizerInfoResult.getAuthorizerInfo().getHeadImg());
// setQrcodeUrl(openAuthorizerInfoResult.getAuthorizerInfo().getQrcodeUrl());
// setRefreshToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerRefreshToken());
// setAccessToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAccessToken());
// setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + openAuthorizerInfoResult.getAuthorizationInfo().getExpiresIn()*1000)));
// setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode());
// setAuthTime(new Date());
// }
// });
// return true;
// } catch (WxErrorException e) {
// logger.error(e.getMessage());
// }
// return false;
// }

@RequestMapping(value = "/{appId}/callback")
@ApiOperation("消息回调")
public void callback(@PathVariable("appId") String appId, HttpServletRequest request, HttpServletResponse response) throws IOException {
logger.info("[" + getIpAddr() + "]接收微信APP回调 /{appId}/callback");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String signature = request.getParameter("signature");
String encType = request.getParameter("encrypt_type");
String openid = request.getParameter("openid");
String msgSignature = request.getParameter("msg_signature");

// 第三方平台一定接收的是加密的消息
if (!StringUtils.equalsIgnoreCase("aes", encType)
|| !openService.getTtOpenComponentService().checkSignature(timestamp, nonce,encType, signature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
}

// String resultxml = getRequestXML(request);

// logger.info("接收微信APP回调:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}],"
// + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
// appId, openid, signature, encType, msgSignature, timestamp, nonce, resultxml);


// String out = "";
// // aes加密的消息, 解密
// WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(resultxml,
// openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
// logger.info("接收微信APP回调,消息解密后内容为:\n{}", inMessage.toString());



// // 实际 消息 处理
// //handleCallbackMessage(appId, response, inMessage);
// WxMpXmlOutMessage outMessage = openService.getWxOpenMessageRouter().route(inMessage, appId);
// if(outMessage == null) {
// PrintWriter printWriter = response.getWriter();
// wechatWriteSuccess(printWriter);
// }
PrintWriter printWriter = response.getWriter();
wechatWriteSuccess(printWriter);
}

private void wechatWriteSuccess(PrintWriter printWriter) {
printWriter.print("success");
printWriter.flush();
printWriter.close();
}

// /**
// * 测试账号特殊处理
// * @param appId
// * @param response
// * @param inMessage
// * @throws IOException
// */
// private void handleWechatTest(@PathVariable("appId") String appId, HttpServletResponse response, WxMpXmlMessage inMessage) throws IOException {
// String out;
// try {
// if (StringUtils.equals(inMessage.getMsgType(), "text")) {
// if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) {
// out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(
// WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback")
// .fromUser(inMessage.getToUser())
// .toUser(inMessage.getFromUser())
// .build(),
// openService.getWxOpenConfigStorage()
// );
// } else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) {
// String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api";
// WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build();
// openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
// }
// } else if (StringUtils.equals(inMessage.getMsgType(), "event")) {
// WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build();
// openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
// }
// } catch (WxErrorException e) {
// logger.error("callback", e);
// }
// }
//
// private String getRequestXML(HttpServletRequest request) throws IOException {
// InputStream inStream = request.getInputStream();
// ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
// byte[] buffer = new byte[1024];
// int len = 0;
// while ((len = inStream.read(buffer)) != -1) {
// outSteam.write(buffer, 0, len);
// }
// String resultxml = new String(outSteam.toByteArray(), Charset.forName("UTF-8"));
// logger.info(resultxml);
//
// outSteam.close();
// inStream.close();
// return resultxml;
// }
//
// private void getThreadCount( int i) {
// ThreadGroup group = Thread.currentThread().getThreadGroup();
// ThreadGroup topGroup = group;
// // 遍历线程组树,获取根线程组
// while (group != null) {
// topGroup = group;
// group = group.getParent();
// }
// // 激活的线程数加倍
// int activeCount = topGroup.activeCount();
// // copy into a list that is the exact size
// logger.info("Thread list size == " + i + " : " + activeCount);
//
// }
//
//
// @GetMapping(value = "/createOpenPlatform")
// @ApiOperation("创建微信开放平台")
// public ResultData createOpenPlatform(@RequestParam(value = "appId") String appId) {
// Gson gson = new GsonBuilder().create();
// try {
// WxOpenCreateResult ret = openService.getWxOpenComponentService().createOpenAccount(appId);
// logger.info(ret.toString());
// if (ret.isSuccess()) {
// WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId);
// if (authorizerInfo != null) {
// authorizerInfo.setId(authorizerInfo.getId());
// authorizerInfo.setOpenAppid(ret.getOpenAppid());
// authorizerInfoService.updateOpenAppid(authorizerInfo);
// }
// }
// return new ResultData(ret.toString());
// } catch (WxErrorException e) {
// logger.error(e.getMessage());
// return new ResultData(Result.ERROR, e.getMessage());
// }
// }
//
// @GetMapping(value = "/bindOpenPlatform")
// @ApiOperation("绑定微信开放平台")
// public ResultData bindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) {
// Gson gson = new GsonBuilder().create();
// try {
// Boolean ret = openService.getWxOpenComponentService().bindOpenAccount(appId, openAppId);
// if (ret) {
// WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId);
// if (authorizerInfo != null) {
// authorizerInfo.setId(authorizerInfo.getId());
// authorizerInfo.setOpenAppid(openAppId);
// authorizerInfoService.updateOpenAppid(authorizerInfo);
// }
// return new ResultData();
// }
// return new ResultData(Result.ERROR, ret.toString());
// } catch (WxErrorException e) {
// logger.error(e.getMessage());
// return new ResultData(Result.ERROR, e.getMessage());
// }
// }
//
// @GetMapping(value = "/unbindOpenPlatform")
// @ApiOperation("解绑微信开放平台")
// public ResultData unbindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) {
// Gson gson = new GsonBuilder().create();
// try {
// Boolean ret = openService.getWxOpenComponentService().unbindOpenAccount(appId, openAppId);
// if (ret) {
// WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo();
// authorizerInfo.setAuthorizerAppid(appId);
// authorizerInfoService.removeOpenAppid(authorizerInfo);
// return new ResultData();
// }
// return new ResultData(Result.ERROR, ret.toString());
// } catch (WxErrorException e) {
// logger.error(e.getMessage());
// return new ResultData(Result.ERROR, e.getMessage());
// }
// }
//
// @GetMapping(value = "/getOpenPlatform")
// @ApiOperation("获取微信开放平台appId")
// public ResultData getOpenPlatform(@RequestParam(value = "appId") String appId) {
// Gson gson = new GsonBuilder().create();
// try {
// WxOpenGetResult ret = openService.getWxOpenComponentService().getOpenAccount(appId);
// logger.info(ret.toString());
// if (ret.isSuccess()) {
// WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId);
// if (authorizerInfo != null) {
// if(StringUtils.isBlank(authorizerInfo.getOpenAppid()) && StringUtils.isNotBlank(ret.getOpenAppid())) {
// authorizerInfo.setId(authorizerInfo.getId());
// authorizerInfo.setOpenAppid(ret.getOpenAppid());
// authorizerInfoService.updateOpenAppid(authorizerInfo);
// }
// }
// return new ResultData(ret.getOpenAppid());
// }
// return new ResultData(Result.ERROR, ret.toString());
// } catch (WxErrorException e) {
// logger.error(e.getMessage());
// return new ResultData(Result.ERROR, e.getMessage());
// }
// }
//
// @GetMapping(value = "/clearQuota")
// @ApiOperation("代公众号清零quota,每个公众号每个月有10次清零机会,包括在微信公众平台上的清零以及调用API进行清零")
// public ResultData clearQuota(@RequestParam(value = "appId") String appId) {
// try {
// openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).clearQuota(appId);
// return new ResultData();
// } catch (WxErrorException e) {
// logger.error(e.getMessage());
// return new ResultData(Result.ERROR, e.getMessage());
// }
// }
//
// @GetMapping(value = "/getAuthorizerList")
// @ApiOperation("拉取当前所有已授权的帐号基本信息")
// public ResultData getAuthorizerList() {
// try {
// WxOpenAuthorizerListResult authorizerListResult = openService.getWxOpenComponentService().getAuthorizerList(0, 100);
// logger.info("getAuthorizerList: " + authorizerListResult);
// if(authorizerListResult.getTotalCount() > 0) {
// for (Map<String, String> data : authorizerListResult.getList()) {
// String appId = data.get("authorizer_appid");
// String refreshToken = data.get("refresh_token");
// Long authTime = Long.valueOf(data.get("auth_time"));
// Date authDate = new Date(authTime*1000);
//
// if(StringUtils.isBlank(refreshToken)) {
// continue;
// }
//
// WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo();
// authorizerInfo.setAuthorizerAppid(appId);
// authorizerInfo.setRefreshToken(refreshToken);
// authorizerInfo.setAuthTime(authDate);
// authorizerInfoService.updateAuthAppidInfo(authorizerInfo);
//
// openService.getWxOpenComponentService().getWxOpenConfigStorage().setAuthorizerRefreshToken(appId, refreshToken);
// }
// }
// return new ResultData(authorizerListResult.toString());
// } catch (WxErrorException e) {
// logger.error(e.getMessage());
// return new ResultData(Result.ERROR, e.getMessage());
// }
// }
}

+ 0
- 106
mlToutiaoOpen/src/main/java/com/iformall/controller/WechatAuthController.java 查看文件

@@ -1,106 +0,0 @@
package com.iformall.controller;

import com.iformall.domain.po.WxAuthorizerInfo;
import com.iformall.enums.EnumWxAuthorizationStatus;
import com.iformall.service.WxAuthorizerInfoService;
import com.iformall.service.wechat.FmOpenService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult;
import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;

/**
* Stormeye Wu
*/
@Controller
@RequestMapping("/wt_auth")
@Api(description = "微信第三方开放平台授权相关接口")
public class WechatAuthController {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private FmOpenService openService;

@Autowired
private WxAuthorizerInfoService authorizerInfoService;

@ApiOperation(value = "获取授权跳转页", notes = "")
@GetMapping("/goto_auth_url_show")
@ResponseBody
public String gotoPreAuthUrlShow() {
return "<html>" +
"<body>" +
"<a href='/O/wt_auth/goto_auth_url'>go</a>" +
"</body>" +
"</html>";
}

@ApiOperation(value = "授权跳转页", notes = "")
@GetMapping("/goto_auth_url")
public void gotoPreAuthUrl(HttpServletRequest request, HttpServletResponse response) {
String host = request.getHeader("host");
String url = "https://" + host + "/O/wt_auth/jump";
try {
url = openService.getWxOpenComponentService().getPreAuthUrl(url);
response.sendRedirect(url);
} catch (WxErrorException | IOException e) {
logger.error("gotoPreAuthUrl", e);
throw new RuntimeException(e);
}
}

@ApiOperation(value = "授权", notes = "")
@GetMapping("/jump")
@ResponseBody
public void jump(@RequestParam("auth_code") String authorizationCode) {
try {
WxOpenQueryAuthResult queryAuthResult = openService.getWxOpenComponentService().getQueryAuth(authorizationCode);
logger.info("getQueryAuth", queryAuthResult);
WxOpenAuthorizerInfoResult openAuthorizerInfoResult = openService.getWxOpenComponentService().getAuthorizerInfo(
queryAuthResult.getAuthorizationInfo().getAuthorizerAppid());
logger.info(openAuthorizerInfoResult.toString());
// save auth info
authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo(){
{
setAuthorizerAppid(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAppid());
setAlias(openAuthorizerInfoResult.getAuthorizerInfo().getAlias());
setHeadImg(openAuthorizerInfoResult.getAuthorizerInfo().getHeadImg());
setQrcodeUrl(openAuthorizerInfoResult.getAuthorizerInfo().getQrcodeUrl());
setRefreshToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerRefreshToken());
setAccessToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAccessToken());
setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + openAuthorizerInfoResult.getAuthorizationInfo().getExpiresIn()*1000)));
setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode());
setAuthTime(new Date());
}
});
} catch (WxErrorException e) {
logger.error("gotoPreAuthUrl", e);
throw new RuntimeException(e);
}
}

@ApiOperation(value = "手机跳转链接url", notes = "")
@GetMapping("/getMobilePreAuthUrl")
@ResponseBody
public String getMobilePreAuthUrl(HttpServletRequest request, HttpServletResponse response) {
String host = request.getHeader("host");
String url = "https://" + host + "/O/wt_auth/jump";
try {
url = openService.getWxOpenComponentService().getMobilePreAuthUrl(url);
return url;
} catch (WxErrorException e) {
logger.error("getMobilePreAuthUrl", e);
throw new RuntimeException(e);
}
}
}

+ 0
- 415
mlToutiaoOpen/src/main/java/com/iformall/controller/WechatCalllbackController.java 查看文件

@@ -1,415 +0,0 @@
package com.iformall.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.iformall.common.Result;
import com.iformall.common.ResultData;
import com.iformall.controller.base.BaseController;
import com.iformall.domain.po.*;
import com.iformall.enums.*;
import com.iformall.service.*;
import com.iformall.service.wechat.FmOpenService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.open.bean.WxOpenCreateResult;
import me.chanjar.weixin.open.bean.WxOpenGetResult;
import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
import me.chanjar.weixin.open.bean.result.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Map;

@Controller
@RequestMapping("/wxOpen")
@Api(description = "微信第三方开放平台回调相关接口")
public class WechatCalllbackController extends BaseController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Autowired
protected FmOpenService openService;

@Autowired
private WxComponentVerifyTicketService componentVerifyTicketService;

@Autowired
private WxAuthorizerInfoService authorizerInfoService;

@PostMapping(value = "/notify")
@ApiOperation("接收ticket回调")
@ResponseBody
public String receiveTicket(HttpServletRequest request) throws IOException {
logger.info("[" + getIpAddr() + "]接收微信回调 /wxOpen/notify");

String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String signature = request.getParameter("signature");
String encType = request.getParameter("encrypt_type");
String msgSignature = request.getParameter("msg_signature");

if (!StringUtils.equalsIgnoreCase("aes", encType)
|| !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
}

String resultxml = getRequestXML(request);

logger.info("\n接收微信回调:[signature=[{}], encType=[{}], msgSignature=[{}],"
+ " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
signature, encType, msgSignature, timestamp, nonce, resultxml);

// aes加密的消息, 解密
WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(resultxml,
openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
this.logger.info("接收微信回调,消息解密后内容为:{} ", inMessage.toString());

// 处理
handleTicket(inMessage);
return "success";
}

/**
* 处理ticket消息
* @param inMessage
* @return
*/
private void handleTicket(WxOpenXmlMessage inMessage) {
try {
String out = openService.getWxOpenComponentService().route(inMessage);
this.logger.info("组装回复信息:{}", out);
} catch (WxErrorException e) {
this.logger.error("notify", e);
}
if (inMessage.getInfoType().equalsIgnoreCase(EnumWxAuthorizationInfoType.COMPONENT_VERIFY_TICKET.getMessage())) {
// 保存component_verify_ticket
componentVerifyTicketService.saveOrUpdate(new WxComponentVerifyTicket() {
{
setComponentVerifyTicket(inMessage.getComponentVerifyTicket());
setCreateTime(new Date(Long.valueOf(inMessage.getCreateTime() * 1000)));
setComponentAppid(inMessage.getAppId());
setDeadline(new Date(Long.valueOf(System.currentTimeMillis() + (60 * 60 * 1000))));
}
});
} else if (inMessage.getInfoType().equalsIgnoreCase(EnumWxAuthorizationInfoType.AUTHORIZED.getMessage()) ||
inMessage.getInfoType().equalsIgnoreCase(EnumWxAuthorizationInfoType.UPDATEAUTHORIZED.getMessage())) {
// 审核通过
handleWeappAuditedMessage(inMessage);
} else if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.UNAUTHORIZED.getMessage())) {
// 授权取消
authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() {
{
setAuthorizerAppid(inMessage.getAuthorizerAppid());
setAuthorizationStatus(EnumWxAuthorizationStatus.UNAUTHORIZED.getCode());
setAuthTime(new Date());
}
});
}
}

/**
* 处理小程序审核消息
* @param inMessage
* @return
*/
private boolean handleWeappAuditedMessage(WxOpenXmlMessage inMessage) {
// 审核通过
try {
WxOpenQueryAuthResult queryAuthResult = openService.getWxOpenComponentService().getQueryAuth(inMessage.getAuthorizationCode());
logger.info("getQueryAuth", queryAuthResult);

WxOpenAuthorizerInfoResult openAuthorizerInfoResult = openService.getWxOpenComponentService().getAuthorizerInfo(
queryAuthResult.getAuthorizationInfo().getAuthorizerAppid());
logger.info(openAuthorizerInfoResult.toString());
// save auth info
authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() {
{
setAuthorizerAppid(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAppid());
setAlias(openAuthorizerInfoResult.getAuthorizerInfo().getAlias());
setHeadImg(openAuthorizerInfoResult.getAuthorizerInfo().getHeadImg());
setQrcodeUrl(openAuthorizerInfoResult.getAuthorizerInfo().getQrcodeUrl());
setRefreshToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerRefreshToken());
setAccessToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAccessToken());
setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + openAuthorizerInfoResult.getAuthorizationInfo().getExpiresIn()*1000)));
setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode());
setAuthTime(new Date());
}
});
return true;
} catch (WxErrorException e) {
logger.error(e.getMessage());
}
return false;
}

@RequestMapping(value = "/{appId}/callback")
@ApiOperation("消息回调")
public void callback(@PathVariable("appId") String appId, HttpServletRequest request, HttpServletResponse response) throws IOException {
logger.info("[" + getIpAddr() + "]接收微信APP回调 /{appId}/callback");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String signature = request.getParameter("signature");
String encType = request.getParameter("encrypt_type");
String openid = request.getParameter("openid");
String msgSignature = request.getParameter("msg_signature");

// 第三方平台一定接收的是加密的消息
if (!StringUtils.equalsIgnoreCase("aes", encType)
|| !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
}

String resultxml = getRequestXML(request);

logger.info("接收微信APP回调:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}],"
+ " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
appId, openid, signature, encType, msgSignature, timestamp, nonce, resultxml);


String out = "";
// aes加密的消息, 解密
WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(resultxml,
openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
logger.info("接收微信APP回调,消息解密后内容为:\n{}", inMessage.toString());

// 全网发布测试用例
if (StringUtils.equalsAnyIgnoreCase(appId, "wx570bc396a51b8ff8", "wxd101a85aa106f53e")) {
handleWechatTest(appId, response, inMessage);
PrintWriter printWriter = response.getWriter();
wechatWriteSuccess(printWriter);
return;
}

// 实际 消息 处理
//handleCallbackMessage(appId, response, inMessage);
WxMpXmlOutMessage outMessage = openService.getWxOpenMessageRouter().route(inMessage, appId);
if(outMessage == null) {
PrintWriter printWriter = response.getWriter();
wechatWriteSuccess(printWriter);
}
PrintWriter printWriter = response.getWriter();
wechatWriteSuccess(printWriter);
}

private void wechatWriteSuccess(PrintWriter printWriter) {
printWriter.print("success");
printWriter.flush();
printWriter.close();
}

/**
* 测试账号特殊处理
* @param appId
* @param response
* @param inMessage
* @throws IOException
*/
private void handleWechatTest(@PathVariable("appId") String appId, HttpServletResponse response, WxMpXmlMessage inMessage) throws IOException {
String out;
try {
if (StringUtils.equals(inMessage.getMsgType(), "text")) {
if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) {
out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(
WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback")
.fromUser(inMessage.getToUser())
.toUser(inMessage.getFromUser())
.build(),
openService.getWxOpenConfigStorage()
);
} else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) {
String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api";
WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build();
openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
}
} else if (StringUtils.equals(inMessage.getMsgType(), "event")) {
WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build();
openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
}
} catch (WxErrorException e) {
logger.error("callback", e);
}
}

private String getRequestXML(HttpServletRequest request) throws IOException {
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
String resultxml = new String(outSteam.toByteArray(), Charset.forName("UTF-8"));
logger.info(resultxml);

outSteam.close();
inStream.close();
return resultxml;
}

private void getThreadCount( int i) {
ThreadGroup group = Thread.currentThread().getThreadGroup();
ThreadGroup topGroup = group;
// 遍历线程组树,获取根线程组
while (group != null) {
topGroup = group;
group = group.getParent();
}
// 激活的线程数加倍
int activeCount = topGroup.activeCount();
// copy into a list that is the exact size
logger.info("Thread list size == " + i + " : " + activeCount);

}


@GetMapping(value = "/createOpenPlatform")
@ApiOperation("创建微信开放平台")
public ResultData createOpenPlatform(@RequestParam(value = "appId") String appId) {
Gson gson = new GsonBuilder().create();
try {
WxOpenCreateResult ret = openService.getWxOpenComponentService().createOpenAccount(appId);
logger.info(ret.toString());
if (ret.isSuccess()) {
WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId);
if (authorizerInfo != null) {
authorizerInfo.setId(authorizerInfo.getId());
authorizerInfo.setOpenAppid(ret.getOpenAppid());
authorizerInfoService.updateOpenAppid(authorizerInfo);
}
}
return new ResultData(ret.toString());
} catch (WxErrorException e) {
logger.error(e.getMessage());
return new ResultData(Result.ERROR, e.getMessage());
}
}

@GetMapping(value = "/bindOpenPlatform")
@ApiOperation("绑定微信开放平台")
public ResultData bindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) {
Gson gson = new GsonBuilder().create();
try {
Boolean ret = openService.getWxOpenComponentService().bindOpenAccount(appId, openAppId);
if (ret) {
WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId);
if (authorizerInfo != null) {
authorizerInfo.setId(authorizerInfo.getId());
authorizerInfo.setOpenAppid(openAppId);
authorizerInfoService.updateOpenAppid(authorizerInfo);
}
return new ResultData();
}
return new ResultData(Result.ERROR, ret.toString());
} catch (WxErrorException e) {
logger.error(e.getMessage());
return new ResultData(Result.ERROR, e.getMessage());
}
}

@GetMapping(value = "/unbindOpenPlatform")
@ApiOperation("解绑微信开放平台")
public ResultData unbindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) {
Gson gson = new GsonBuilder().create();
try {
Boolean ret = openService.getWxOpenComponentService().unbindOpenAccount(appId, openAppId);
if (ret) {
WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo();
authorizerInfo.setAuthorizerAppid(appId);
authorizerInfoService.removeOpenAppid(authorizerInfo);
return new ResultData();
}
return new ResultData(Result.ERROR, ret.toString());
} catch (WxErrorException e) {
logger.error(e.getMessage());
return new ResultData(Result.ERROR, e.getMessage());
}
}

@GetMapping(value = "/getOpenPlatform")
@ApiOperation("获取微信开放平台appId")
public ResultData getOpenPlatform(@RequestParam(value = "appId") String appId) {
Gson gson = new GsonBuilder().create();
try {
WxOpenGetResult ret = openService.getWxOpenComponentService().getOpenAccount(appId);
logger.info(ret.toString());
if (ret.isSuccess()) {
WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId);
if (authorizerInfo != null) {
if(StringUtils.isBlank(authorizerInfo.getOpenAppid()) && StringUtils.isNotBlank(ret.getOpenAppid())) {
authorizerInfo.setId(authorizerInfo.getId());
authorizerInfo.setOpenAppid(ret.getOpenAppid());
authorizerInfoService.updateOpenAppid(authorizerInfo);
}
}
return new ResultData(ret.getOpenAppid());
}
return new ResultData(Result.ERROR, ret.toString());
} catch (WxErrorException e) {
logger.error(e.getMessage());
return new ResultData(Result.ERROR, e.getMessage());
}
}

@GetMapping(value = "/clearQuota")
@ApiOperation("代公众号清零quota,每个公众号每个月有10次清零机会,包括在微信公众平台上的清零以及调用API进行清零")
public ResultData clearQuota(@RequestParam(value = "appId") String appId) {
try {
openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).clearQuota(appId);
return new ResultData();
} catch (WxErrorException e) {
logger.error(e.getMessage());
return new ResultData(Result.ERROR, e.getMessage());
}
}

@GetMapping(value = "/getAuthorizerList")
@ApiOperation("拉取当前所有已授权的帐号基本信息")
public ResultData getAuthorizerList() {
try {
WxOpenAuthorizerListResult authorizerListResult = openService.getWxOpenComponentService().getAuthorizerList(0, 100);
logger.info("getAuthorizerList: " + authorizerListResult);
if(authorizerListResult.getTotalCount() > 0) {
for (Map<String, String> data : authorizerListResult.getList()) {
String appId = data.get("authorizer_appid");
String refreshToken = data.get("refresh_token");
Long authTime = Long.valueOf(data.get("auth_time"));
Date authDate = new Date(authTime*1000);

if(StringUtils.isBlank(refreshToken)) {
continue;
}

WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo();
authorizerInfo.setAuthorizerAppid(appId);
authorizerInfo.setRefreshToken(refreshToken);
authorizerInfo.setAuthTime(authDate);
authorizerInfoService.updateAuthAppidInfo(authorizerInfo);

openService.getWxOpenComponentService().getWxOpenConfigStorage().setAuthorizerRefreshToken(appId, refreshToken);
}
}
return new ResultData(authorizerListResult.toString());
} catch (WxErrorException e) {
logger.error(e.getMessage());
return new ResultData(Result.ERROR, e.getMessage());
}
}
}

+ 1
- 0
mlToutiaoOpen/src/main/java/com/iformall/controller/WxWeappInfoController.java 查看文件

@@ -98,6 +98,7 @@ public class WxWeappInfoController extends BaseController {
weappInfo.setName(null);
}
}
weappInfo.setPlat(EnumAppPlat.TOUTIAO.getCode());
weappInfo.setSortColumns(BaseEntity.SortField.TenantId_ASC,BaseEntity.SortField.Type_ASC,BaseEntity.SortField.Name_ASC);
final PageInfo<WxWeappInfo> page = authorizerInfoService.listVoAsPage(weappInfo, pageNum, pageSize);
return new ResultData(page);


+ 4
- 8
mlToutiaoOpen/src/main/resources/application-dev.yml 查看文件

@@ -82,15 +82,11 @@ aws:
# min-idle: 10

wechat:
web:
appId: "wx091907dd0bfd3f6b"
secret: "2a2ca10738998b9ef92c1fe8a4d366a6"
url: "https://admintest.malls.iformall.com"
open:
componentAppId: wxdfc8fb4e62d6b52b
componentSecret: 98daa62b316dd6feabaad708327ce233
componentToken: formall2018
componentAesKey: htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN
componentAppId: tt146862d58b42b88c
componentSecret: d681a8873b81276dc93fd499bbf0bcbafb82b31c
componentToken: iformallDouyinAuthorizeToken
componentAesKey: iformallDouyinEntryKey202204082101028888888
redis:
host: 101.200.130.134
port: 6379


+ 4
- 4
mlToutiaoOpen/src/main/resources/application-prod.yml 查看文件

@@ -77,10 +77,10 @@ aws:

wechat:
open:
componentAppId: ENC(b3JG0MUZgQfz5Zj+DC2ZM1zDeLOiGTmmeokfe2O8kaM=)
componentSecret: ENC(QVyc4BPGdnSjXGs2ivgpDBE1v8wPWHLLRMYI7Vv8uYwC0SiZHQz0QpyyQV/b48Pb)
componentToken: ENC(rkxj0733WxFFDLgA9x01m2s5Fi2L+0PC)
componentAesKey: ENC(EIbJUBpbYOrLb4YQ/HXLQmxlxgAqIp2ZmpnGICC8pu5xiTz3Cqfkbwd2S8raCcK/IvYcX2GmedI=)
componentAppId: tt146862d58b42b88c
componentSecret: d681a8873b81276dc93fd499bbf0bcbafb82b31c
componentToken: iformallDouyinAuthorizeToken
componentAesKey: iformallDouyinEntryKey202204082101028888888
redis:
host: r-2zeaglwf13qqmnllj5.redis.rds.aliyuncs.com
port: 6379


正在加载...
取消
保存