From f339e143986311201cfd96999b6fbec574dd7dbf Mon Sep 17 00:00:00 2001 From: xhxu Date: Mon, 11 Apr 2022 21:33:50 +0800 Subject: [PATCH] update tt --- mallinkService/pom.xml | 6 + .../FmTtOpenInRedisDBConfigStorage.java | 295 ++++++++ .../service/toutiao/FmTtOpenService.java | 71 ++ .../toutiao/api/TtOpenComponentService.java | 261 +++++++ .../service/toutiao/api/TtOpenMaService.java | 486 +++++++++++++ .../service/toutiao/api/TtOpenService.java | 23 + .../api/bean/TtOpenAuthorizationInfo.java | 23 + .../TtOpenAuthorizationInfoGsonAdapter.java | 43 ++ .../toutiao/api/bean/TtOpenGsonBuilder.java | 26 + .../toutiao/api/bean/TtOpenRetrieveCode.java | 19 + .../bean/TtOpenRetrieveCodeGsonAdapter.java | 24 + .../toutiao/api/bean/TtOpenTicket.java | 40 ++ .../api/bean/TtOpenTicketGsonAdapter.java | 34 + .../api/impl/TtOpenComponentServiceImpl.java | 517 ++++++++++++++ .../toutiao/api/impl/TtOpenMaServiceImpl.java | 643 ++++++++++++++++++ .../api/impl/TtOpenServiceAbstractImpl.java | 62 ++ .../TtOpenServiceApacheHttpClientImpl.java | 67 ++ .../toutiao/api/impl/TtOpenServiceImpl.java | 8 + .../toutiao/miniapp/TtMaMsgService.java | 28 + .../service/toutiao/miniapp/TtMaQrcode.java | 39 ++ .../toutiao/miniapp/TtMaQrcodeService.java | 31 + .../service/toutiao/miniapp/TtMaService.java | 132 ++++ .../toutiao/miniapp/TtMaUserService.java | 52 ++ .../miniapp/TtQrcodeRequestExecutor.java | 69 ++ .../toutiao/miniapp/bean/TtMaGsonBuilder.java | 21 + .../miniapp/bean/TtMaSubscribeMessage.java | 70 ++ .../bean/TtMaSubscribeMessageGsonAdapter.java | 33 + .../miniapp/impl/TtMaMsgServiceImpl.java | 30 + .../miniapp/impl/TtMaQrcodeServiceImpl.java | 26 + .../toutiao/miniapp/impl/TtMaServiceImpl.java | 312 +++++++++ .../miniapp/impl/TtMaUserServiceImpl.java | 41 ++ .../service/toutiao/utils/MsgDecrypt.java | 88 +++ .../toutiao/utils/ServerVerification.java | 55 ++ .../iformall/controller/TtAuthController.java | 131 ++++ .../controller/TtCalllbackController.java | 388 +++++++++++ .../controller/WechatAuthController.java | 106 --- .../controller/WechatCalllbackController.java | 415 ----------- .../controller/WxWeappInfoController.java | 1 + .../src/main/resources/application-dev.yml | 12 +- .../src/main/resources/application-prod.yml | 8 +- 40 files changed, 4203 insertions(+), 533 deletions(-) create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/FmTtOpenInRedisDBConfigStorage.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/FmTtOpenService.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenComponentService.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenMaService.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenService.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenAuthorizationInfo.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenAuthorizationInfoGsonAdapter.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenGsonBuilder.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenRetrieveCode.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenRetrieveCodeGsonAdapter.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenTicket.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenTicketGsonAdapter.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenComponentServiceImpl.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenMaServiceImpl.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceAbstractImpl.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceApacheHttpClientImpl.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceImpl.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaMsgService.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaQrcode.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaQrcodeService.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaService.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaUserService.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtQrcodeRequestExecutor.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaGsonBuilder.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaSubscribeMessage.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaSubscribeMessageGsonAdapter.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaMsgServiceImpl.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaQrcodeServiceImpl.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaServiceImpl.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaUserServiceImpl.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/utils/MsgDecrypt.java create mode 100644 mallinkService/src/main/java/com/iformall/service/toutiao/utils/ServerVerification.java create mode 100644 mlToutiaoOpen/src/main/java/com/iformall/controller/TtAuthController.java create mode 100644 mlToutiaoOpen/src/main/java/com/iformall/controller/TtCalllbackController.java delete mode 100644 mlToutiaoOpen/src/main/java/com/iformall/controller/WechatAuthController.java delete mode 100644 mlToutiaoOpen/src/main/java/com/iformall/controller/WechatCalllbackController.java diff --git a/mallinkService/pom.xml b/mallinkService/pom.xml index 4b32ba3..34c7ef8 100644 --- a/mallinkService/pom.xml +++ b/mallinkService/pom.xml @@ -27,6 +27,12 @@ 6.4.0 --> + + org.jetbrains + annotations + 13.0 + compile + \ No newline at end of file diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/FmTtOpenInRedisDBConfigStorage.java b/mallinkService/src/main/java/com/iformall/service/toutiao/FmTtOpenInRedisDBConfigStorage.java new file mode 100644 index 0000000..87bb9e2 --- /dev/null +++ b/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 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 jedisPool) { + this.jedisPool = jedisPool; + } + + public FmTtOpenInRedisDBConfigStorage(Pool 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(); + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/FmTtOpenService.java b/mallinkService/src/main/java/com/iformall/service/toutiao/FmTtOpenService.java new file mode 100644 index 0000000..0027167 --- /dev/null +++ b/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; + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenComponentService.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenComponentService.java new file mode 100644 index 0000000..ac6438c --- /dev/null +++ b/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 getTemplateDraftList() throws WxErrorException; + + /** + * 获取代码模版库中的所有小程序代码模版. + * + * @return 小程序代码模版列表(templateId) + * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 + */ + List 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; +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenMaService.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenMaService.java new file mode 100644 index 0000000..130512f --- /dev/null +++ b/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; + +/** + *
+ *     微信开放平台代小程序实现服务能力
+ *     https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489144594_DhNoV&token=&lang=zh_CN
+ * 
+ * + * @author yqx + * @date 2018/9/12 + */ +public interface TtOpenMaService extends TtMaService { + + /** + * 设置小程序服务器域名. + * + *
+   *     授权给第三方的小程序,其服务器域名只可以为第三方的服务器,当小程序通过第三方发布代码上线后,小程序原先自己配置的服务器域名将被删除,
+   *     只保留第三方平台的域名,所以第三方平台在代替小程序发布代码之前,需要调用接口为小程序添加第三方自身的域名。
+   *     提示:需要先将域名登记到第三方平台的小程序服务器域名中,才可以调用接口进行配置
+   * 
+ */ + String API_MODIFY_DOMAIN = "https://api.weixin.qq.com/wxa/modify_domain"; + + /** + * 设置小程序业务域名(仅供第三方代小程序调用) + *
+   *     授权给第三方的小程序,其业务域名只可以为第三方的服务器,当小程序通过第三方发布代码上线后,小程序原先自己配置的业务域名将被删除,
+   *     只保留第三方平台的域名,所以第三方平台在代替小程序发布代码之前,需要调用接口为小程序添加业务域名。
+   * 提示:
+   * 1、需要先将域名登记到第三方平台的小程序业务域名中,才可以调用接口进行配置。
+   * 2、为授权的小程序配置域名时支持配置子域名,例如第三方登记的业务域名如为qq.com,则可以直接将qq.com及其子域名(如xxx.qq.com)也配置到授权的小程序中。
+   * 
+ */ + String API_SET_WEBVIEW_DOMAIN = "https://api.weixin.qq.com/wxa/setwebviewdomain"; + + /** + * 获取帐号基本信息 + *
+   * GET请求
+   * 注意:需要使用1.3环节获取到的新创建小程序appid及authorization_code换取authorizer_refresh_token进而得到authorizer_access_token。
+   * 
+ */ + 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"; + + /** + * 以下接口基础信息设置 + *

+ * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21517799059ZSEMr&token=6f965b5daf30a98a6bbd2a386faea5c934e929bf&lang=zh_CN + *

+ */ + + /** + * 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"; + + + /** + * 以下接口为三方平台代小程序实现的代码管理功能 + *

+ * 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 + *

+ */ + + /** + * 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.小程序审核撤回 + *

+ * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。 + *

+ */ + 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 requestdomainList, List wsrequestdomainList, List uploaddomainList, List 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 domainList) throws WxErrorException; + + + /** + * 设置小程序的业务域名 + * + * @param action add添加, delete删除, set覆盖 + * @param domainList + * @return + */ + WxOpenMaWebDomainResult setWebViewDomainInfo(String action, List 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 params) throws WxErrorException; + + /** + * 获取授权小程序帐号的可选类目 + *

+ * 注意:该接口可获取已设置的二级类目及用于代码审核的可选三级类目。 + *

+ */ + 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(); +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenService.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/TtOpenService.java new file mode 100644 index 0000000..9b47cd1 --- /dev/null +++ b/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; +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenAuthorizationInfo.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenAuthorizationInfo.java new file mode 100644 index 0000000..8610d2c --- /dev/null +++ b/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 authorizePermission; +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenAuthorizationInfoGsonAdapter.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenAuthorizationInfoGsonAdapter.java new file mode 100644 index 0000000..3d0de7e --- /dev/null +++ b/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 007 + */ +public class TtOpenAuthorizationInfoGsonAdapter implements JsonDeserializer { + + @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 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; + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenGsonBuilder.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenGsonBuilder.java new file mode 100644 index 0000000..2f9ff8a --- /dev/null +++ b/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(); + } + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenRetrieveCode.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenRetrieveCode.java new file mode 100644 index 0000000..fd6abed --- /dev/null +++ b/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; + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenRetrieveCodeGsonAdapter.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenRetrieveCodeGsonAdapter.java new file mode 100644 index 0000000..1c44363 --- /dev/null +++ b/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 007 + */ +public class TtOpenRetrieveCodeGsonAdapter implements JsonDeserializer { + + @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; + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenTicket.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenTicket.java new file mode 100644 index 0000000..95d151f --- /dev/null +++ b/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); + } + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenTicketGsonAdapter.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/bean/TtOpenTicketGsonAdapter.java new file mode 100644 index 0000000..bcc2dca --- /dev/null +++ b/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 { + + @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; + } + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenComponentServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenComponentServiceImpl.java new file mode 100644 index 0000000..4dd8167 --- /dev/null +++ b/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 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 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 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>() { + }.getType()); + } else { + return null; + } + } + + @Override + public List 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>() { + }.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); + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenMaServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenMaServiceImpl.java new file mode 100644 index 0000000..ad49821 --- /dev/null +++ b/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 007 + *
+ *     增加开放平台代小程序管理服务能力
+ *     说明:这里让这个服务公开便于调用者模拟本地测试服务
+ * 
+ * @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 requestdomainList, List wsrequestdomainList, List uploaddomainList, List 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 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 domainList) throws WxErrorException { + String response = this.setWebViewDomain(action, domainList); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaWebDomainResult.class); + } + + + /** + * 获取小程序的信息,GET请求 + *
+   *     注意:这里不能直接用小程序的access_token
+   *     //TODO 待调整
+   * 
+ * + * @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 params) throws WxErrorException { + WxMaQrcodeParam qrcodeParam = WxMaQrcodeParam.create(pagePath); + qrcodeParam.addPageParam(params); + return execute(MaQrCodeRequestExecutor.create(getRequestHttp()), API_TEST_QRCODE, qrcodeParam); + } + + /** + * 获取授权小程序帐号的可选类目 + *

+ * 注意:该接口可获取已设置的二级类目及用于代码审核的可选三级类目。 + *

+ * + * @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. 发布已通过审核的小程序(仅供第三方代小程序调用) + *

+ * 请填写空的数据包,POST的json数据包为空即可。 + *

+ * + * @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. 小程序审核撤回 + *

+ * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。 + *

+ * + * @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 strList) { + JsonArray jsonArray = new JsonArray(); + if (strList != null && !strList.isEmpty()) { + for (String str : strList) { + jsonArray.add(str); + } + } + return jsonArray; + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceAbstractImpl.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceAbstractImpl.java new file mode 100644 index 0000000..6309e26 --- /dev/null +++ b/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 implements TtOpenService, RequestHttp { + 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 execute(RequestExecutor 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); + } + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceApacheHttpClientImpl.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceApacheHttpClientImpl.java new file mode 100644 index 0000000..42665ad --- /dev/null +++ b/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 007 + */ +public class TtOpenServiceApacheHttpClientImpl extends TtOpenServiceAbstractImpl { + 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); + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/toutiao/api/impl/TtOpenServiceImpl.java new file mode 100644 index 0000000..cbd3ffa --- /dev/null +++ b/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 { + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaMsgService.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaMsgService.java new file mode 100644 index 0000000..243277e --- /dev/null +++ b/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; + +/** + *
+ * 消息发送接口
+ * 
+ * + * @author Binary Wang + */ +public interface TtMaMsgService { + + String SUBSCRIBE_MSG_SEND_URL = "https://developer.toutiao.com/api/apps/subscribe_notification/developer/v1/notify"; + + /** + *
+   * 发送订阅消息
+   * https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/subscribe-notification/notify/
+   * 
+ * + * @param subscribeMessage 订阅消息 + * @throws WxErrorException . + */ + void sendSubscribeMsg(TtMaSubscribeMessage subscribeMessage) throws WxErrorException; + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaQrcode.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaQrcode.java new file mode 100644 index 0000000..2a83715 --- /dev/null +++ b/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 Binary Wang + */ +@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); + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaQrcodeService.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaQrcodeService.java new file mode 100644 index 0000000..0a19e7e --- /dev/null +++ b/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; + +/** + *
+ * 二维码相关操作接口.
+ *
+ * 
+ * + * @author Binary Wang + */ +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; + + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaService.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaService.java new file mode 100644 index 0000000..0066647 --- /dev/null +++ b/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 Binary Wang + */ +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; + + /** + *
+   * 验证消息的确来自微信服务器.
+   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN
+   * 
+ */ + boolean checkSignature(String timestamp, String nonce, String signature); + + /** + *
+   * 获取access_token,本方法线程安全.
+   * 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
+   *
+   * 另:本service的所有方法都会在access_token过期是调用此方法
+   *
+   * 程序员在非必要情况下尽量不要主动调用此方法
+   *
+   * 详情请见: https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/interface-request-credential/get-access-token
+   * 
+ * + * @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; + + /** + *
+   * Service没有实现某个API的时候,可以用这个,
+   * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
+   * 可以参考,{@link MediaUploadRequestExecutor}的实现方法
+   * 
+ */ + T execute(RequestExecutor executor, String uri, E data) throws WxErrorException; + + /** + *
+   * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试.
+   * 默认:1000ms
+   * 
+ */ + void setRetrySleepMillis(int retrySleepMillis); + + /** + *
+   * 设置当微信系统响应系统繁忙时,最大重试次数.
+   * 默认:5次
+   * 
+ */ + 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(); + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaUserService.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtMaUserService.java new file mode 100644 index 0000000..5f437c6 --- /dev/null +++ b/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 Binary Wang + */ +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); +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtQrcodeRequestExecutor.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/TtQrcodeRequestExecutor.java new file mode 100644 index 0000000..8ddf348 --- /dev/null +++ b/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 Binary Wang + */ +public class TtQrcodeRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public TtQrcodeRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, AbstractWxMaQrcodeWrapper data, ResponseHandler 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(); + } + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaGsonBuilder.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaGsonBuilder.java new file mode 100644 index 0000000..7c3b89a --- /dev/null +++ b/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 Binary Wang + */ +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(); + } + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaSubscribeMessage.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaSubscribeMessage.java new file mode 100644 index 0000000..8874721 --- /dev/null +++ b/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. + *
+   * 参数:touser
+   * 是否必填: 是
+   * 描述: 接收者(用户)的 openid
+   * 
+ */ + private String openId; + + /** + * 所需下发的模板消息的id. + *
+   * 参数:template_id
+   * 是否必填: 是
+   * 描述: 所需下发的模板消息的id
+   * 
+ */ + private String tplId; + + /** + * 点击模板卡片后的跳转页面,仅限本小程序内的页面. + *
+   * 参数:page
+   * 是否必填: 否
+   * 描述: 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。
+   * 
+ */ + private String page; + + /** + * 模板内容,不填则下发空模板. + *
+   * 参数:data
+   * 是否必填: 是
+   * 描述: 模板内容,不填则下发空模板
+   * 
+ */ + private JsonObject data; + + public String toJson() { + return TtMaGsonBuilder.create().toJson(this); + } + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaSubscribeMessageGsonAdapter.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/bean/TtMaSubscribeMessageGsonAdapter.java new file mode 100644 index 0000000..238e58e --- /dev/null +++ b/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 { + @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; + } + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaMsgServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaMsgServiceImpl.java new file mode 100644 index 0000000..388189f --- /dev/null +++ b/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 Binary Wang + */ +@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)); + } + } + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaQrcodeServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaQrcodeServiceImpl.java new file mode 100644 index 0000000..d6c61c3 --- /dev/null +++ b/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 Binary Wang + */ +@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)); + } + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaServiceImpl.java new file mode 100644 index 0000000..b7233a6 --- /dev/null +++ b/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 { + 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 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 execute(RequestExecutor 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 executeInternal(RequestExecutor 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; + } + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaUserServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/toutiao/miniapp/impl/TtMaUserServiceImpl.java new file mode 100644 index 0000000..5b8b66e --- /dev/null +++ b/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 Binary Wang + */ +@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); + } + +} diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/utils/MsgDecrypt.java b/mallinkService/src/main/java/com/iformall/service/toutiao/utils/MsgDecrypt.java new file mode 100644 index 0000000..1aa241c --- /dev/null +++ b/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"); + } +} \ No newline at end of file diff --git a/mallinkService/src/main/java/com/iformall/service/toutiao/utils/ServerVerification.java b/mallinkService/src/main/java/com/iformall/service/toutiao/utils/ServerVerification.java new file mode 100644 index 0000000..b521248 --- /dev/null +++ b/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); + } +} diff --git a/mlToutiaoOpen/src/main/java/com/iformall/controller/TtAuthController.java b/mlToutiaoOpen/src/main/java/com/iformall/controller/TtAuthController.java new file mode 100644 index 0000000..6e7eef0 --- /dev/null +++ b/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 "" + + "" + + "go" + + "" + + ""; + } + + @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 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(); + } + +} diff --git a/mlToutiaoOpen/src/main/java/com/iformall/controller/TtCalllbackController.java b/mlToutiaoOpen/src/main/java/com/iformall/controller/TtCalllbackController.java new file mode 100644 index 0000000..2a86dc9 --- /dev/null +++ b/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 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()); +// } +// } +} diff --git a/mlToutiaoOpen/src/main/java/com/iformall/controller/WechatAuthController.java b/mlToutiaoOpen/src/main/java/com/iformall/controller/WechatAuthController.java deleted file mode 100644 index e3da84e..0000000 --- a/mlToutiaoOpen/src/main/java/com/iformall/controller/WechatAuthController.java +++ /dev/null @@ -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 "" + - "" + - "go" + - "" + - ""; - } - - @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); - } - } -} diff --git a/mlToutiaoOpen/src/main/java/com/iformall/controller/WechatCalllbackController.java b/mlToutiaoOpen/src/main/java/com/iformall/controller/WechatCalllbackController.java deleted file mode 100644 index 484e14c..0000000 --- a/mlToutiaoOpen/src/main/java/com/iformall/controller/WechatCalllbackController.java +++ /dev/null @@ -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 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()); - } - } -} diff --git a/mlToutiaoOpen/src/main/java/com/iformall/controller/WxWeappInfoController.java b/mlToutiaoOpen/src/main/java/com/iformall/controller/WxWeappInfoController.java index 1bd88f8..73df4af 100644 --- a/mlToutiaoOpen/src/main/java/com/iformall/controller/WxWeappInfoController.java +++ b/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 page = authorizerInfoService.listVoAsPage(weappInfo, pageNum, pageSize); return new ResultData(page); diff --git a/mlToutiaoOpen/src/main/resources/application-dev.yml b/mlToutiaoOpen/src/main/resources/application-dev.yml index 79628f4..95e7ceb 100644 --- a/mlToutiaoOpen/src/main/resources/application-dev.yml +++ b/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 diff --git a/mlToutiaoOpen/src/main/resources/application-prod.yml b/mlToutiaoOpen/src/main/resources/application-prod.yml index b0ff82e..569f536 100644 --- a/mlToutiaoOpen/src/main/resources/application-prod.yml +++ b/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