| @@ -27,6 +27,12 @@ | |||||
| <version>6.4.0</version> | <version>6.4.0</version> | ||||
| </dependency> | </dependency> | ||||
| --> | --> | ||||
| <dependency> | |||||
| <groupId>org.jetbrains</groupId> | |||||
| <artifactId>annotations</artifactId> | |||||
| <version>13.0</version> | |||||
| <scope>compile</scope> | |||||
| </dependency> | |||||
| </dependencies> | </dependencies> | ||||
| </project> | </project> | ||||
| @@ -0,0 +1,295 @@ | |||||
| package com.iformall.service.toutiao; | |||||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |||||
| import com.iformall.common.FmHttpClientBuilder; | |||||
| import com.iformall.domain.po.WxAuthorizerInfo; | |||||
| import com.iformall.domain.po.WxComponentVerifyTicket; | |||||
| import com.iformall.mapper.WxAuthorizerInfoMapper; | |||||
| import com.iformall.mapper.WxComponentVerifyTicketMapper; | |||||
| import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||||
| import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage; | |||||
| import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.scheduling.annotation.Async; | |||||
| import redis.clients.jedis.Jedis; | |||||
| import redis.clients.jedis.JedisPool; | |||||
| import redis.clients.jedis.util.Pool; | |||||
| import java.util.Date; | |||||
| public class FmTtOpenInRedisDBConfigStorage extends WxOpenInMemoryConfigStorage { | |||||
| private final static String COMPONENT_VERIFY_TICKET_KEY = "tt_component_verify_ticket:"; | |||||
| private final static String COMPONENT_ACCESS_TOKEN_KEY = "tt_component_access_token:"; | |||||
| private final static String AUTHORIZER_REFRESH_TOKEN_KEY = "tt_authorizer_refresh_token:"; | |||||
| private final static String AUTHORIZER_ACCESS_TOKEN_KEY = "tt_authorizer_access_token:"; | |||||
| private final static String JSAPI_TICKET_KEY = "tt_jsapi_ticket:"; | |||||
| private final static String CARD_API_TICKET_KEY = "tt_card_api_ticket:"; | |||||
| protected final Pool<Jedis> jedisPool; | |||||
| private WxComponentVerifyTicketMapper wxComponentVerifyTicketMapper; | |||||
| private WxAuthorizerInfoMapper wxAuthorizerInfoMapper; | |||||
| /** | |||||
| * redis 存储的 key 的前缀,可为空 | |||||
| */ | |||||
| private String keyPrefix; | |||||
| private String componentVerifyTicketKey; | |||||
| private String componentAccessTokenKey; | |||||
| private String authorizerRefreshTokenKey; | |||||
| private String authorizerAccessTokenKey; | |||||
| private String jsapiTicketKey; | |||||
| private String cardApiTicket; | |||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||||
| public FmTtOpenInRedisDBConfigStorage(Pool<Jedis> jedisPool) { | |||||
| this.jedisPool = jedisPool; | |||||
| } | |||||
| public FmTtOpenInRedisDBConfigStorage(Pool<Jedis> jedisPool, String keyPrefix) { | |||||
| this.jedisPool = jedisPool; | |||||
| this.keyPrefix = keyPrefix; | |||||
| } | |||||
| public FmTtOpenInRedisDBConfigStorage(JedisPool jedisPool, WxComponentVerifyTicketMapper componentVerifyTicketMapper, WxAuthorizerInfoMapper authorizerInfoMapper) { | |||||
| this.jedisPool = jedisPool; | |||||
| this.wxComponentVerifyTicketMapper = componentVerifyTicketMapper; | |||||
| this.wxAuthorizerInfoMapper = authorizerInfoMapper; | |||||
| } | |||||
| @Override | |||||
| public void setComponentAppId(String componentAppId) { | |||||
| super.setComponentAppId(componentAppId); | |||||
| String prefix = StringUtils.isBlank(keyPrefix) ? "" : | |||||
| (StringUtils.endsWith(keyPrefix, ":") ? keyPrefix : (keyPrefix + ":")); | |||||
| componentVerifyTicketKey = prefix + COMPONENT_VERIFY_TICKET_KEY.concat(componentAppId); | |||||
| componentAccessTokenKey = prefix + COMPONENT_ACCESS_TOKEN_KEY.concat(componentAppId); | |||||
| authorizerRefreshTokenKey = prefix + AUTHORIZER_REFRESH_TOKEN_KEY.concat(componentAppId); | |||||
| authorizerAccessTokenKey = prefix + AUTHORIZER_ACCESS_TOKEN_KEY.concat(componentAppId); | |||||
| this.jsapiTicketKey = JSAPI_TICKET_KEY.concat(componentAppId); | |||||
| this.cardApiTicket = CARD_API_TICKET_KEY.concat(componentAppId); | |||||
| } | |||||
| @Override | |||||
| public String getComponentVerifyTicket() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| logger.info("getComponentVerifyTicket " + this.componentVerifyTicketKey); | |||||
| String ticket = jedis.get(this.componentVerifyTicketKey); | |||||
| if(StringUtils.isBlank(ticket)) { | |||||
| String appId; | |||||
| String [] keys = componentAccessTokenKey.split(":"); | |||||
| if(StringUtils.isBlank(keyPrefix)) { | |||||
| appId = keys[1]; | |||||
| } else { | |||||
| appId = keys[2]; | |||||
| } | |||||
| WxComponentVerifyTicket wxComponentVerifyTicket = new WxComponentVerifyTicket(); | |||||
| wxComponentVerifyTicket.setComponentAppid(getComponentAppId()); | |||||
| try { | |||||
| wxComponentVerifyTicket = wxComponentVerifyTicketMapper.selectOne(new QueryWrapper<>(wxComponentVerifyTicket)); | |||||
| if(wxComponentVerifyTicket != null) { | |||||
| ticket = wxComponentVerifyTicket.getComponentVerifyTicket(); | |||||
| } | |||||
| }catch (Exception e) { | |||||
| logger.error(e.getMessage()); | |||||
| } | |||||
| } | |||||
| return ticket; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void setComponentVerifyTicket(String componentVerifyTicket) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.set(this.componentVerifyTicketKey, componentVerifyTicket); | |||||
| logger.info("setComponentVerifyTicket " + componentVerifyTicket); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public String getComponentAccessToken() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.get(this.componentAccessTokenKey); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public boolean isComponentAccessTokenExpired() { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.ttl(this.componentAccessTokenKey) < 2; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void expireComponentAccessToken(){ | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.expire(this.componentAccessTokenKey, 0); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void updateComponentAccessToken(String componentAccessToken, int expiresInSeconds) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.setex(this.componentAccessTokenKey, expiresInSeconds - 200, componentAccessToken); | |||||
| logger.info("updateComponentVerifyTicketAccessToken " + componentAccessToken); | |||||
| saveComponentVerifyTicket(componentAccessToken, expiresInSeconds); | |||||
| } | |||||
| } | |||||
| @Async | |||||
| public void saveComponentVerifyTicket(String componentAccessToken, int expiresInSeconds) { | |||||
| WxComponentVerifyTicket ticket = new WxComponentVerifyTicket(); | |||||
| ticket.setComponentAppid(getComponentAppId()); | |||||
| ticket.setAccessToken(componentAccessToken); | |||||
| ticket.setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + (expiresInSeconds * 1000)))); | |||||
| int num = wxComponentVerifyTicketMapper.updateAccessToken(ticket); | |||||
| logger.info("updateComponentVerifyTicketAccessToken rows: " + num); | |||||
| } | |||||
| private String getKey(String prefix, String appId) { | |||||
| return prefix.endsWith(":") ? prefix.concat(appId) : prefix.concat(":").concat(appId); | |||||
| } | |||||
| @Override | |||||
| public String getAuthorizerRefreshToken(String appId) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.get(this.getKey(this.authorizerRefreshTokenKey, appId)); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void setAuthorizerRefreshToken(String appId, String authorizerRefreshToken) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.set(this.getKey(this.authorizerRefreshTokenKey, appId), authorizerRefreshToken); | |||||
| logger.info("setAuthorizerRefreshToken " + appId); | |||||
| saveAuthorizerRefreshToken(appId, authorizerRefreshToken); | |||||
| } | |||||
| } | |||||
| @Async | |||||
| public void saveAuthorizerRefreshToken(String appId, String authorizerRefreshToken) { | |||||
| WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo(); | |||||
| authorizerInfo.setAuthorizerAppid(appId); | |||||
| authorizerInfo.setRefreshToken(authorizerRefreshToken); | |||||
| int num = wxAuthorizerInfoMapper.updateRefreshToken(authorizerInfo); | |||||
| } | |||||
| @Override | |||||
| public String getAuthorizerAccessToken(String appId) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.get(this.getKey(this.authorizerAccessTokenKey, appId)); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public boolean isAuthorizerAccessTokenExpired(String appId) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.ttl(this.getKey(this.authorizerAccessTokenKey, appId)) < 2; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void expireAuthorizerAccessToken(String appId) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.expire(this.getKey(this.authorizerAccessTokenKey, appId), 0); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void updateAuthorizerAccessToken(String appId, WxOpenAuthorizerAccessToken authorizerAccessToken) { | |||||
| this.updateAuthorizerAccessToken(appId, authorizerAccessToken.getAuthorizerAccessToken(), authorizerAccessToken.getExpiresIn()); | |||||
| logger.info("updateAuthorizerAccessToken " + appId); | |||||
| dbUpdateAccessToken(appId, authorizerAccessToken); | |||||
| } | |||||
| @Async | |||||
| public void dbUpdateAccessToken(String appId, WxOpenAuthorizerAccessToken authorizerAccessToken) { | |||||
| WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo(); | |||||
| authorizerInfo.setAuthorizerAppid(appId); | |||||
| authorizerInfo.setAccessToken(authorizerAccessToken.getAuthorizerAccessToken()); | |||||
| authorizerInfo.setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + (authorizerAccessToken.getExpiresIn() * 1000)))); | |||||
| int num = wxAuthorizerInfoMapper.updateAccessToken(authorizerInfo); | |||||
| logger.info("updateAuthorizerAccessToken " + appId + " rows: " + num); | |||||
| } | |||||
| @Override | |||||
| public void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.setex(this.getKey(this.authorizerAccessTokenKey, appId), expiresInSeconds - 200, authorizerAccessToken); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public String getJsapiTicket(String appId) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.get(this.getKey(this.jsapiTicketKey, appId)); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public boolean isJsapiTicketExpired(String appId) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.ttl(this.getKey(this.jsapiTicketKey, appId)) < 2; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void expireJsapiTicket(String appId) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.expire(this.getKey(this.jsapiTicketKey, appId), 0); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void updateJsapiTicket(String appId, String jsapiTicket, int expiresInSeconds) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.setex(this.getKey(this.jsapiTicketKey, appId), expiresInSeconds - 200, jsapiTicket); | |||||
| } | |||||
| // TODO update to DB | |||||
| } | |||||
| @Override | |||||
| public String getCardApiTicket(String appId) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.get(this.getKey(this.cardApiTicket, appId)); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public boolean isCardApiTicketExpired(String appId) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| return jedis.ttl(this.getKey(this.cardApiTicket, appId)) < 2; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void expireCardApiTicket(String appId) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.expire(this.getKey(this.cardApiTicket, appId), 0); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds) { | |||||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||||
| jedis.setex(this.getKey(this.cardApiTicket, appId), expiresInSeconds - 200, cardApiTicket); | |||||
| } | |||||
| // TODO update to DB | |||||
| } | |||||
| @Override | |||||
| public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | |||||
| return FmHttpClientBuilder.get(); | |||||
| } | |||||
| } | |||||
| @@ -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; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,261 @@ | |||||
| package com.iformall.service.toutiao.api; | |||||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||||
| import com.iformall.service.toutiao.api.bean.TtOpenAuthorizationInfo; | |||||
| import com.iformall.service.toutiao.api.bean.TtOpenTicket; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | |||||
| import me.chanjar.weixin.open.api.WxOpenConfigStorage; | |||||
| import me.chanjar.weixin.open.bean.WxOpenCreateResult; | |||||
| import me.chanjar.weixin.open.bean.WxOpenGetResult; | |||||
| import me.chanjar.weixin.open.bean.WxOpenMaCodeTemplate; | |||||
| import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage; | |||||
| import me.chanjar.weixin.open.bean.result.*; | |||||
| import java.util.List; | |||||
| /** | |||||
| * . | |||||
| * | |||||
| * @author | |||||
| */ | |||||
| public interface TtOpenComponentService { | |||||
| String API_COMPONENT_TOKEN_URL = "https://open.microapp.bytedance.com/openapi/v1/auth/tp/token"; | |||||
| String API_CREATE_PREAUTHCODE_URL = "https://open.microapp.bytedance.com/openapi/v2/auth/pre_auth_code"; | |||||
| String COMPONENT_LOGIN_PAGE_URL = "https://open.microapp.bytedance.com/mappconsole/tp/authorization?component_appid=%s&pre_auth_code=%s&redirect_uri=%s"; | |||||
| String API_QUERY_AUTH_URL = "https://open.microapp.bytedance.com/openapi/v1/oauth/token"; | |||||
| String API_AUTHORIZER_TOKEN_URL = "https://open.microapp.bytedance.com/openapi/v1/oauth/token"; | |||||
| String RETRIEVE_AUTHORIZER_TOKEN_URL = "https://open.microapp.bytedance.com/openapi/v1/auth/retrieve"; | |||||
| // String API_GET_AUTHORIZER_INFO_URL = "https://open.microapp.bytedance.com/openapi/v1/auth/retrieve"; | |||||
| String API_GET_AUTHORIZER_OPTION_URL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option"; | |||||
| String API_SET_AUTHORIZER_OPTION_URL = "https://api.weixin.qq.com/cgi-bin/component/api_set_authorizer_option"; | |||||
| String API_GET_AUTHORIZER_LIST = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list"; | |||||
| /** | |||||
| * 手机端打开授权链接. | |||||
| */ | |||||
| String CONNECT_OAUTH2_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s&component_appid=%s#wechat_redirect"; | |||||
| /** | |||||
| * 用code换取oauth2的access token. | |||||
| */ | |||||
| String OAUTH2_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/component/access_token?appid=%s&code=%s&grant_type=authorization_code&component_appid=%s"; | |||||
| /** | |||||
| * 刷新oauth2的access token. | |||||
| */ | |||||
| String OAUTH2_REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/component/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s&component_appid=%s"; | |||||
| String MINIAPP_JSCODE_2_SESSION = "https://api.weixin.qq.com/sns/component/jscode2session?appid=%s&js_code=%s&grant_type=authorization_code&component_appid=%s"; | |||||
| String CREATE_OPEN_URL = "https://api.weixin.qq.com/cgi-bin/open/create"; | |||||
| String BIND_OPEN_URL = "https://api.weixin.qq.com/cgi-bin/open/bind"; | |||||
| String UNBIND_OPEN_URL = "https://api.weixin.qq.com/cgi-bin/open/unbind"; | |||||
| String GET_OPEN_URL = "https://api.weixin.qq.com/cgi-bin/open/get"; | |||||
| /** | |||||
| * 快速创建小程序接口. | |||||
| */ | |||||
| String FAST_REGISTER_WEAPP_URL = "https://api.weixin.qq.com/cgi-bin/component/fastregisterweapp?action=create"; | |||||
| String FAST_REGISTER_WEAPP_SEARCH_URL = "https://api.weixin.qq.com/cgi-bin/component/fastregisterweapp?action=search"; | |||||
| /** | |||||
| * 获取指定appid的开放平台小程序服务(继承一般小程序服务能力). | |||||
| * | |||||
| * @param appid . | |||||
| * @return . | |||||
| */ | |||||
| TtOpenMaService getTtMaServiceByAppid(String appid); | |||||
| WxOpenConfigStorage getWxOpenConfigStorage(); | |||||
| boolean checkSignature(String timestamp, String nonce, String encrypt, String signature); | |||||
| TtOpenTicket decrypt(String encrypt); | |||||
| String getComponentAccessToken(boolean forceRefresh) throws WxErrorException; | |||||
| String post(String uri, String postData,String tokenKeyName) throws WxErrorException; | |||||
| String postByAppAccessToken(String appId,String uri, String postData,String tokenKeyName) throws WxErrorException; | |||||
| String get(String uri,String tokenKeyName) throws WxErrorException; | |||||
| String getByAppAccessToken(String appId,String uri,String tokenKeyName) throws WxErrorException; | |||||
| /** | |||||
| * 获取用户授权页URL(来路URL和成功跳转URL 的域名都需要为三方平台设置的 登录授权的发起页域名). | |||||
| */ | |||||
| String getPreAuthUrl(String redirectUri) throws WxErrorException; | |||||
| String route(TtOpenTicket openTicket) throws WxErrorException; | |||||
| /** | |||||
| * 使用授权码换取公众号或小程序的接口调用凭据和授权信息. | |||||
| */ | |||||
| TtOpenAuthorizationInfo getQueryAuth(String authorizationCode) throws WxErrorException; | |||||
| /** | |||||
| * 找回授权码. | |||||
| */ | |||||
| String retrieveCode(String authorizerAppid) throws WxErrorException; | |||||
| // /** | |||||
| // * 获取授权方的帐号基本信息. | |||||
| // */ | |||||
| // WxOpenAuthorizerInfoResult getAuthorizerInfo(String authorizerAppid) throws WxErrorException; | |||||
| /** | |||||
| * 获取授权方的选项设置信息. | |||||
| */ | |||||
| WxOpenAuthorizerOptionResult getAuthorizerOption(String authorizerAppid, String optionName) throws WxErrorException; | |||||
| /** | |||||
| * 获取所有授权方列表. | |||||
| */ | |||||
| WxOpenAuthorizerListResult getAuthorizerList(int begin, int len) throws WxErrorException; | |||||
| /** | |||||
| * 设置授权方的选项信息. | |||||
| */ | |||||
| void setAuthorizerOption(String authorizerAppid, String optionName, String optionValue) throws WxErrorException; | |||||
| String getAuthorizerAccessToken(String appid, boolean forceRefresh) throws WxErrorException; | |||||
| WxMpOAuth2AccessToken oauth2getAccessToken(String appid, String code) throws WxErrorException; | |||||
| WxMpOAuth2AccessToken oauth2refreshAccessToken(String appid, String refreshToken) throws WxErrorException; | |||||
| String oauth2buildAuthorizationUrl(String appid, String redirectUri, String scope, String state); | |||||
| WxMaJscode2SessionResult miniappJscode2Session(String appId, String jsCode) throws WxErrorException; | |||||
| /** | |||||
| * 代小程序实现业务. | |||||
| * 小程序代码模版库管理:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1506504150_nMMh6&token=&lang=zh_CN | |||||
| * access_token 为 component_access_token | |||||
| */ | |||||
| String GET_TEMPLATE_DRAFT_LIST_URL = "https://api.weixin.qq.com/wxa/gettemplatedraftlist"; | |||||
| String GET_TEMPLATE_LIST_URL = "https://api.weixin.qq.com/wxa/gettemplatelist"; | |||||
| String ADD_TO_TEMPLATE_URL = "https://api.weixin.qq.com/wxa/addtotemplate"; | |||||
| String DELETE_TEMPLATE_URL = "https://api.weixin.qq.com/wxa/deletetemplate"; | |||||
| /** | |||||
| * 获取草稿箱内的所有临时代码草稿. | |||||
| * | |||||
| * @return 草稿箱代码模板列表(draftId) | |||||
| * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 | |||||
| */ | |||||
| List<WxOpenMaCodeTemplate> getTemplateDraftList() throws WxErrorException; | |||||
| /** | |||||
| * 获取代码模版库中的所有小程序代码模版. | |||||
| * | |||||
| * @return 小程序代码模版列表(templateId) | |||||
| * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 | |||||
| */ | |||||
| List<WxOpenMaCodeTemplate> getTemplateList() throws WxErrorException; | |||||
| /** | |||||
| * 将草稿箱的草稿选为小程序代码模版. | |||||
| * | |||||
| * @param draftId 草稿ID,本字段可通过“获取草稿箱内的所有临时代码草稿”接口获得 | |||||
| * @throws WxErrorException 操作失败时抛出,具体错误码请看此接口的注释文档 | |||||
| * @see #getTemplateDraftList | |||||
| */ | |||||
| void addToTemplate(long draftId) throws WxErrorException; | |||||
| /** | |||||
| * 删除指定小程序代码模版. | |||||
| * | |||||
| * @param templateId 要删除的模版ID | |||||
| * @throws WxErrorException 操作失败时抛出,具体错误码请看此接口的注释文档 | |||||
| * @see #getTemplateList | |||||
| */ | |||||
| void deleteTemplate(long templateId) throws WxErrorException; | |||||
| /** | |||||
| * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1498704199_1bcax&token=6df5e3650041eff2cd3ec3662425ad8d7beec8d9&lang=zh_CN | |||||
| * 创建 开放平台帐号并绑定公众号/小程序. | |||||
| * https://api.weixin.qq.com/cgi-bin/open/create | |||||
| * | |||||
| * @param appId 公众号/小程序的appId | |||||
| * @return . | |||||
| */ | |||||
| WxOpenCreateResult createOpenAccount(String appId) throws WxErrorException; | |||||
| /** | |||||
| * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/account/bind.html | |||||
| * 将公众号/小程序绑定到开放平台帐号下 | |||||
| * | |||||
| * @param appId 公众号/小程序的appId | |||||
| * @param openAppid 开放平台帐号 appid,由创建开发平台帐号接口返回 | |||||
| */ | |||||
| Boolean bindOpenAccount(String appId, String openAppid) throws WxErrorException; | |||||
| /** | |||||
| * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/account/unbind.html | |||||
| * 将公众号/小程序从开放平台帐号下解绑 | |||||
| * | |||||
| * @param appId 公众号/小程序的appId | |||||
| * @param openAppid 开放平台帐号 appid,由创建开发平台帐号接口返回 | |||||
| */ | |||||
| Boolean unbindOpenAccount(String appId, String openAppid) throws WxErrorException; | |||||
| /** | |||||
| * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/account/get.html | |||||
| * 获取公众号/小程序所绑定的开放平台帐号 | |||||
| * | |||||
| * @param appId 公众号/小程序的appId | |||||
| * @return 开放平台帐号 appid,由创建开发平台帐号接口返回 | |||||
| */ | |||||
| WxOpenGetResult getOpenAccount(String appId) throws WxErrorException; | |||||
| /** | |||||
| * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21538208049W8uwq&token=&lang=zh_CN | |||||
| * 第三方平台快速创建小程序. | |||||
| * 注意:创建任务逻辑串行,单次任务结束后才可以使用相同信息下发第二次任务,请注意规避任务阻塞 | |||||
| * | |||||
| * @param name 企业名(需与工商部门登记信息一致) | |||||
| * @param code 企业代码 | |||||
| * @param codeType 企业代码类型 1:统一社会信用代码(18位) 2:组织机构代码(9位xxxxxxxx-x) 3:营业执照注册号(15位) | |||||
| * @param legalPersonaWechat 法人微信号 | |||||
| * @param legalPersonaName 法人姓名(绑定银行卡) | |||||
| * @param componentPhone 第三方联系电话(方便法人与第三方联系) | |||||
| * @return . | |||||
| * @throws WxErrorException . | |||||
| */ | |||||
| WxOpenResult fastRegisterWeapp(String name, String code, String codeType, String legalPersonaWechat, String legalPersonaName, String componentPhone) throws WxErrorException; | |||||
| /** | |||||
| * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21538208049W8uwq&token=&lang=zh_CN | |||||
| * 查询第三方平台快速创建小程序的任务状态 | |||||
| * 注意:该接口只提供当下任务结果查询,不建议过分依赖该接口查询所创建小程序。 | |||||
| * 小程序的成功状态可在第三方服务器中自行对账、查询。 | |||||
| * 不要频繁调用search接口,消息接收需通过服务器查看。调用search接口会消耗接口整体调用quato | |||||
| * | |||||
| * @param name 企业名(需与工商部门登记信息一致) | |||||
| * @param legalPersonaWechat 法人微信号 | |||||
| * @param legalPersonaName 法人姓名(绑定银行卡) | |||||
| * @throws WxErrorException . | |||||
| */ | |||||
| WxOpenResult fastRegisterWeappSearch(String name, String legalPersonaWechat, String legalPersonaName) throws WxErrorException; | |||||
| } | |||||
| @@ -0,0 +1,486 @@ | |||||
| package com.iformall.service.toutiao.api; | |||||
| import com.iformall.service.toutiao.miniapp.TtMaService; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.open.bean.ma.WxMaOpenCommitExtInfo; | |||||
| import me.chanjar.weixin.open.bean.message.WxOpenMaSubmitAuditMessage; | |||||
| import me.chanjar.weixin.open.bean.result.*; | |||||
| import java.io.File; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * <pre> | |||||
| * 微信开放平台代小程序实现服务能力 | |||||
| * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489144594_DhNoV&token=&lang=zh_CN | |||||
| * </pre> | |||||
| * | |||||
| * @author yqx | |||||
| * @date 2018/9/12 | |||||
| */ | |||||
| public interface TtOpenMaService extends TtMaService { | |||||
| /** | |||||
| * 设置小程序服务器域名. | |||||
| * | |||||
| * <pre> | |||||
| * 授权给第三方的小程序,其服务器域名只可以为第三方的服务器,当小程序通过第三方发布代码上线后,小程序原先自己配置的服务器域名将被删除, | |||||
| * 只保留第三方平台的域名,所以第三方平台在代替小程序发布代码之前,需要调用接口为小程序添加第三方自身的域名。 | |||||
| * 提示:需要先将域名登记到第三方平台的小程序服务器域名中,才可以调用接口进行配置 | |||||
| * </pre> | |||||
| */ | |||||
| String API_MODIFY_DOMAIN = "https://api.weixin.qq.com/wxa/modify_domain"; | |||||
| /** | |||||
| * 设置小程序业务域名(仅供第三方代小程序调用) | |||||
| * <pre> | |||||
| * 授权给第三方的小程序,其业务域名只可以为第三方的服务器,当小程序通过第三方发布代码上线后,小程序原先自己配置的业务域名将被删除, | |||||
| * 只保留第三方平台的域名,所以第三方平台在代替小程序发布代码之前,需要调用接口为小程序添加业务域名。 | |||||
| * 提示: | |||||
| * 1、需要先将域名登记到第三方平台的小程序业务域名中,才可以调用接口进行配置。 | |||||
| * 2、为授权的小程序配置域名时支持配置子域名,例如第三方登记的业务域名如为qq.com,则可以直接将qq.com及其子域名(如xxx.qq.com)也配置到授权的小程序中。 | |||||
| * </pre> | |||||
| */ | |||||
| String API_SET_WEBVIEW_DOMAIN = "https://api.weixin.qq.com/wxa/setwebviewdomain"; | |||||
| /** | |||||
| * 获取帐号基本信息 | |||||
| * <pre> | |||||
| * GET请求 | |||||
| * 注意:需要使用1.3环节获取到的新创建小程序appid及authorization_code换取authorizer_refresh_token进而得到authorizer_access_token。 | |||||
| * </pre> | |||||
| */ | |||||
| String API_GET_ACCOUNT_BASICINFO = "https://api.weixin.qq.com/cgi-bin/account/getaccountbasicinfo"; | |||||
| /** | |||||
| * 绑定微信用户为小程序体验者 | |||||
| */ | |||||
| String API_BIND_TESTER = "https://api.weixin.qq.com/wxa/bind_tester"; | |||||
| /** | |||||
| * 解除绑定微信用户为小程序体验者 | |||||
| */ | |||||
| String API_UNBIND_TESTER = "https://api.weixin.qq.com/wxa/unbind_tester"; | |||||
| /** | |||||
| * 获取体验者列表 | |||||
| */ | |||||
| String API_GET_TESTERLIST = "https://api.weixin.qq.com/wxa/memberauth"; | |||||
| /** | |||||
| * 以下接口基础信息设置 | |||||
| * <p> | |||||
| * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21517799059ZSEMr&token=6f965b5daf30a98a6bbd2a386faea5c934e929bf&lang=zh_CN | |||||
| * </p> | |||||
| */ | |||||
| /** | |||||
| * 1. 设置小程序隐私设置(是否可被搜索) | |||||
| */ | |||||
| String API_CHANGE_WXA_SEARCH_STATUS = "https://api.weixin.qq.com/wxa/changewxasearchstatus"; | |||||
| /** | |||||
| * 2. 查询小程序当前隐私设置(是否可被搜索) | |||||
| */ | |||||
| String API_GET_WXA_SEARCH_STATUS = "https://api.weixin.qq.com/wxa/getwxasearchstatus"; | |||||
| /** | |||||
| * 3.1. 获取展示的公众号信息 | |||||
| */ | |||||
| String API_GET_SHOW_WXA_ITEM = "https://api.weixin.qq.com/wxa/getshowwxaitem"; | |||||
| /** | |||||
| * 3.2 设置展示的公众号 | |||||
| */ | |||||
| String API_UPDATE_SHOW_WXA_ITEM = "https://api.weixin.qq.com/wxa/updateshowwxaitem"; | |||||
| /** | |||||
| * 以下接口为三方平台代小程序实现的代码管理功能 | |||||
| * <p> | |||||
| * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=fe774228c66725425675810097f9e48d0737a4bf&lang=zh_CN | |||||
| * </p> | |||||
| */ | |||||
| /** | |||||
| * 1. 为授权的小程序帐号上传小程序代码 | |||||
| */ | |||||
| String API_CODE_COMMIT = "https://api.weixin.qq.com/wxa/commit"; | |||||
| /** | |||||
| * 2. 获取体验小程序的体验二维码 | |||||
| */ | |||||
| String API_TEST_QRCODE = "https://api.weixin.qq.com/wxa/get_qrcode"; | |||||
| /** | |||||
| * 3. 获取授权小程序帐号的可选类目 | |||||
| */ | |||||
| String API_GET_CATEGORY = "https://api.weixin.qq.com/wxa/get_category"; | |||||
| /** | |||||
| * 4. 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用) | |||||
| */ | |||||
| String API_GET_PAGE = "https://api.weixin.qq.com/wxa/get_page"; | |||||
| /** | |||||
| * 5. 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用) | |||||
| */ | |||||
| String API_SUBMIT_AUDIT = "https://api.weixin.qq.com/wxa/submit_audit"; | |||||
| /** | |||||
| * 7. 查询某个指定版本的审核状态(仅供第三方代小程序调用) | |||||
| */ | |||||
| String API_GET_AUDIT_STATUS = "https://api.weixin.qq.com/wxa/get_auditstatus"; | |||||
| /** | |||||
| * 8. 查询最新一次提交的审核状态(仅供第三方代小程序调用) | |||||
| */ | |||||
| String API_GET_LATEST_AUDIT_STATUS = "https://api.weixin.qq.com/wxa/get_latest_auditstatus"; | |||||
| /** | |||||
| * 9. 发布已通过审核的小程序(仅供第三方代小程序调用) | |||||
| */ | |||||
| String API_RELEASE = "https://api.weixin.qq.com/wxa/release"; | |||||
| /** | |||||
| * 10. 修改小程序线上代码的可见状态(仅供第三方代小程序调用) | |||||
| */ | |||||
| String API_CHANGE_VISITSTATUS = "https://api.weixin.qq.com/wxa/change_visitstatus"; | |||||
| /** | |||||
| * 11.小程序版本回退(仅供第三方代小程序调用) | |||||
| */ | |||||
| String API_REVERT_CODE_RELEASE = "https://api.weixin.qq.com/wxa/revertcoderelease"; | |||||
| /** | |||||
| * 12.查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用) | |||||
| */ | |||||
| String API_GET_WEAPP_SUPPORT_VERSION = "https://api.weixin.qq.com/cgi-bin/wxopen/getweappsupportversion"; | |||||
| /** | |||||
| * 13.设置最低基础库版本(仅供第三方代小程序调用) | |||||
| */ | |||||
| String API_SET_WEAPP_SUPPORT_VERSION = "https://api.weixin.qq.com/cgi-bin/wxopen/setweappsupportversion"; | |||||
| /** | |||||
| * 14.设置小程序“扫普通链接二维码打开小程序”能力 | |||||
| * | |||||
| * https://mp.weixin.qq.com/debug/wxadoc/introduction/qrcode.html | |||||
| */ | |||||
| /** | |||||
| * 14.1 增加或修改二维码规则 | |||||
| */ | |||||
| String API_QRCODE_JUMP_ADD = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpadd"; | |||||
| /** | |||||
| * 14.2 获取已设置的二维码规则 | |||||
| */ | |||||
| String API_QRCODE_JUMP_GET = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpget"; | |||||
| /** | |||||
| * 14.3 获取校验文件名称及内容 | |||||
| */ | |||||
| String API_QRCODE_JUMP_DOWNLOAD = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpdownload"; | |||||
| /** | |||||
| * 14.4 删除已设置的二维码规则 | |||||
| */ | |||||
| String API_QRCODE_JUMP_DELETE = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpdelete"; | |||||
| /** | |||||
| * 14.5 发布已设置的二维码规则 | |||||
| */ | |||||
| String API_QRCODE_JUMP_PUBLISH = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumppublish"; | |||||
| /** | |||||
| * 15.小程序审核撤回 | |||||
| * <p> | |||||
| * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。 | |||||
| * </p> | |||||
| */ | |||||
| String API_UNDO_CODE_AUDIT = "https://api.weixin.qq.com/wxa/undocodeaudit"; | |||||
| /** | |||||
| * 16.1 小程序分阶段发布-分阶段发布接口 | |||||
| */ | |||||
| String API_GRAY_RELEASE = "https://api.weixin.qq.com/wxa/grayrelease"; | |||||
| /** | |||||
| * 16.2 小程序分阶段发布-取消分阶段发布 | |||||
| */ | |||||
| String API_REVERT_GRAY_RELEASE = "https://api.weixin.qq.com/wxa/revertgrayrelease"; | |||||
| /** | |||||
| * 16.3 小程序分阶段发布-查询当前分阶段发布详情 | |||||
| */ | |||||
| String API_GET_GRAY_RELEASE_PLAN = "https://api.weixin.qq.com/wxa/getgrayreleaseplan"; | |||||
| /** | |||||
| * 查询服务商的当月提审限额和加急次数(Quota) | |||||
| */ | |||||
| String API_QUERY_QUOTA = "https://api.weixin.qq.com/wxa/queryquota"; | |||||
| /** | |||||
| * 加急审核申请 | |||||
| */ | |||||
| String API_SPEED_AUDIT = "https://api.weixin.qq.com/wxa/speedupaudit"; | |||||
| /** | |||||
| * 获得小程序的域名配置信息 | |||||
| */ | |||||
| WxOpenMaDomainResult getDomain() throws WxErrorException; | |||||
| /** | |||||
| * 修改域名 | |||||
| * | |||||
| * @param action delete删除, set覆盖, get获取 | |||||
| */ | |||||
| WxOpenMaDomainResult modifyDomain(String action, List<String> requestdomainList, List<String> wsrequestdomainList, List<String> uploaddomainList, List<String> downloaddomainList) throws WxErrorException; | |||||
| /** | |||||
| * 获取小程序的业务域名 | |||||
| * | |||||
| * @return 直接返回字符串 | |||||
| */ | |||||
| String getWebViewDomain() throws WxErrorException; | |||||
| /** | |||||
| * 获取小程序的业务域名 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| public WxOpenMaWebDomainResult getWebViewDomainInfo() throws WxErrorException; | |||||
| /** | |||||
| * 设置小程序的业务域名 | |||||
| * | |||||
| * @param action add添加, delete删除, set覆盖 | |||||
| * @return 直接返回字符串 | |||||
| */ | |||||
| String setWebViewDomain(String action, List<String> domainList) throws WxErrorException; | |||||
| /** | |||||
| * 设置小程序的业务域名 | |||||
| * | |||||
| * @param action add添加, delete删除, set覆盖 | |||||
| * @param domainList | |||||
| * @return | |||||
| */ | |||||
| WxOpenMaWebDomainResult setWebViewDomainInfo(String action, List<String> domainList) throws WxErrorException; | |||||
| /** | |||||
| * 获取小程序的信息 | |||||
| */ | |||||
| String getAccountBasicInfo() throws WxErrorException; | |||||
| /** | |||||
| * 绑定小程序体验者 | |||||
| * | |||||
| * @param wechatid 体验者微信号(不是openid) | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| WxOpenMaBindTesterResult bindTester(String wechatid) throws WxErrorException; | |||||
| /** | |||||
| * 解除绑定小程序体验者 | |||||
| * | |||||
| * @param wechatid 体验者微信号(不是openid) | |||||
| */ | |||||
| WxOpenResult unbindTester(String wechatid) throws WxErrorException; | |||||
| /** | |||||
| * 解除绑定小程序体验者,其他平台绑定的体验者无法获取到wechatid,可用此方法解绑,详见文档 | |||||
| * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/unbind_tester.html | |||||
| * | |||||
| * @param userstr 人员对应的唯一字符串, 可通过获取已绑定的体验者列表获取人员对应的字符串 | |||||
| */ | |||||
| WxOpenResult unbindTesterByUserstr(String userstr) throws WxErrorException; | |||||
| /** | |||||
| * 获得体验者列表 | |||||
| */ | |||||
| WxOpenMaTesterListResult getTesterList() throws WxErrorException; | |||||
| /** | |||||
| * 设置小程序隐私设置(是否可被搜索) | |||||
| * | |||||
| * @param status 1表示不可搜索,0表示可搜索 | |||||
| */ | |||||
| public WxOpenResult changeWxaSearchStatus(Integer status) throws WxErrorException; | |||||
| /** | |||||
| * 2. 查询小程序当前隐私设置(是否可被搜索) | |||||
| */ | |||||
| public WxOpenMaSearchStatusResult getWxaSearchStatus() throws WxErrorException; | |||||
| /** | |||||
| * 3.1 获取展示的公众号信息 | |||||
| */ | |||||
| public WxOpenMaShowItemResult getShowWxaItem() throws WxErrorException; | |||||
| /** | |||||
| * 3.2 设置展示的公众号 | |||||
| * | |||||
| * @param flag 0 关闭,1 开启 | |||||
| * @param mpappid 如果开启,需要传新的公众号appid | |||||
| */ | |||||
| public WxOpenResult updateShowwxaitem(Integer flag, String mpappid) throws WxErrorException; | |||||
| /** | |||||
| * 1、为授权的小程序帐号上传小程序代码 | |||||
| * | |||||
| * @param templateId 代码模板ID | |||||
| * @param userVersion 用户定义版本 | |||||
| * @param userDesc 用户定义版本描述 | |||||
| * @param extInfo 第三方自定义的配置 | |||||
| */ | |||||
| WxOpenResult codeCommit(Long templateId, String userVersion, String userDesc, WxMaOpenCommitExtInfo extInfo) throws WxErrorException; | |||||
| /** | |||||
| * 获取体验小程序的体验二维码 | |||||
| */ | |||||
| File getTestQrcode(String pagePath, Map<String, String> params) throws WxErrorException; | |||||
| /** | |||||
| * 获取授权小程序帐号的可选类目 | |||||
| * <p> | |||||
| * 注意:该接口可获取已设置的二级类目及用于代码审核的可选三级类目。 | |||||
| * </p> | |||||
| */ | |||||
| WxOpenMaCategoryListResult getCategoryList() throws WxErrorException; | |||||
| /** | |||||
| * 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用) | |||||
| * | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| WxOpenMaPageListResult getPageList() throws WxErrorException; | |||||
| /** | |||||
| * 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用) | |||||
| */ | |||||
| WxOpenMaSubmitAuditResult submitAudit(WxOpenMaSubmitAuditMessage submitAuditMessage) throws WxErrorException; | |||||
| /** | |||||
| * 查询某个指定版本的审核状态(仅供第三方代小程序调用) | |||||
| */ | |||||
| WxOpenMaQueryAuditResult getAuditStatus(Long auditid) throws WxErrorException; | |||||
| /** | |||||
| * 查询最新一次提交的审核状态(仅供第三方代小程序调用). | |||||
| */ | |||||
| WxOpenMaQueryAuditResult getLatestAuditStatus() throws WxErrorException; | |||||
| /** | |||||
| * 发布已通过审核的小程序(仅供第三方代小程序调用). | |||||
| */ | |||||
| WxOpenResult releaesAudited() throws WxErrorException; | |||||
| /** | |||||
| * 10. 修改小程序线上代码的可见状态(仅供第三方代小程序调用) | |||||
| */ | |||||
| public WxOpenResult changeVisitstatus(String action) throws WxErrorException; | |||||
| /** | |||||
| * 11. 小程序版本回退(仅供第三方代小程序调用) | |||||
| */ | |||||
| WxOpenResult revertCodeReleaes() throws WxErrorException; | |||||
| /** | |||||
| * 15. 小程序审核撤回 | |||||
| * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。 | |||||
| */ | |||||
| WxOpenResult undoCodeAudit() throws WxErrorException; | |||||
| /** | |||||
| * 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用) | |||||
| */ | |||||
| String getSupportVersion() throws WxErrorException; | |||||
| /** | |||||
| * 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用) | |||||
| */ | |||||
| WxOpenMaWeappSupportVersionResult getSupportVersionInfo() throws WxErrorException; | |||||
| /** | |||||
| * 设置最低基础库版本(仅供第三方代小程序调用) | |||||
| */ | |||||
| String setSupportVersion(String version) throws WxErrorException; | |||||
| /** | |||||
| * 设置最低基础库版本(仅供第三方代小程序调用) | |||||
| */ | |||||
| WxOpenResult setSupportVersionInfo(String version) throws WxErrorException; | |||||
| /** | |||||
| * 16. 小程序分阶段发布 - 1)分阶段发布接口 | |||||
| */ | |||||
| WxOpenResult grayrelease(Integer grayPercentage) throws WxErrorException; | |||||
| /** | |||||
| * 16. 小程序分阶段发布 - 2)取消分阶段发布 | |||||
| */ | |||||
| WxOpenResult revertgrayrelease() throws WxErrorException; | |||||
| /** | |||||
| * 16. 小程序分阶段发布 - 3)查询当前分阶段发布详情 | |||||
| */ | |||||
| WxOpenMaGrayReleasePlanResult getgrayreleaseplan() throws WxErrorException; | |||||
| /** | |||||
| * 查询服务商的当月提审限额和加急次数(Quota) | |||||
| * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/code/query_quota.html | |||||
| */ | |||||
| WxOpenMaQueryQuotaResult queryQuota() throws WxErrorException; | |||||
| /** | |||||
| * 加急审核申请 | |||||
| * 有加急次数的第三方可以通过该接口,对已经提审的小程序进行加急操作,加急后的小程序预计2-12小时内审完。 | |||||
| */ | |||||
| Boolean speedAudit(Long auditid) throws WxErrorException; | |||||
| /** | |||||
| * (1)增加或修改二维码规则 | |||||
| */ | |||||
| WxOpenResult addQrcodeJump(WxQrcodeJumpRule wxQrcodeJumpRule) throws WxErrorException; | |||||
| /** | |||||
| * (2)获取已设置的二维码规则 | |||||
| */ | |||||
| WxGetQrcodeJumpResult getQrcodeJump() throws WxErrorException; | |||||
| /** | |||||
| * (3)获取校验文件名称及内容 | |||||
| */ | |||||
| WxDownlooadQrcodeJumpResult downloadQrcodeJump() throws WxErrorException; | |||||
| /** | |||||
| * (4)删除已设置的二维码规则 | |||||
| */ | |||||
| WxOpenResult deleteQrcodeJump(String prefix) throws WxErrorException; | |||||
| /** | |||||
| * (5)发布已设置的二维码规则 | |||||
| */ | |||||
| WxOpenResult publishQrcodeJump(String prefix) throws WxErrorException; | |||||
| // /** | |||||
| // * 小程序用户隐私保护指引服务 | |||||
| // * | |||||
| // * @return 小程序用户隐私保护指引服务 | |||||
| // */ | |||||
| // WxOpenMaPrivacyService getPrivacyService(); | |||||
| } | |||||
| @@ -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; | |||||
| } | |||||
| @@ -0,0 +1,23 @@ | |||||
| package com.iformall.service.toutiao.api.bean; | |||||
| import lombok.Data; | |||||
| import java.io.Serializable; | |||||
| import java.util.List; | |||||
| /** | |||||
| * | |||||
| */ | |||||
| @Data | |||||
| public class TtOpenAuthorizationInfo implements Serializable { | |||||
| private static final long serialVersionUID = -8354370210668120305L; | |||||
| private String authorizerAppid; | |||||
| private String authorizerAccessToken; | |||||
| private int expiresIn; | |||||
| private String authorizerRefreshToken; | |||||
| private int refreshExpiresIn; | |||||
| private List<Integer> authorizePermission; | |||||
| } | |||||
| @@ -0,0 +1,43 @@ | |||||
| package com.iformall.service.toutiao.api.bean; | |||||
| import com.google.gson.*; | |||||
| import me.chanjar.weixin.common.util.json.GsonHelper; | |||||
| import java.lang.reflect.Type; | |||||
| import java.util.ArrayList; | |||||
| import java.util.List; | |||||
| /** | |||||
| * @author <a href="https://github.com/007gzs">007</a> | |||||
| */ | |||||
| public class TtOpenAuthorizationInfoGsonAdapter implements JsonDeserializer<TtOpenAuthorizationInfo> { | |||||
| @Override | |||||
| public TtOpenAuthorizationInfo deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { | |||||
| TtOpenAuthorizationInfo authorizationInfo = new TtOpenAuthorizationInfo(); | |||||
| JsonObject jsonObject = jsonElement.getAsJsonObject(); | |||||
| authorizationInfo.setAuthorizerAppid(GsonHelper.getString(jsonObject, "authorizer_appid")); | |||||
| authorizationInfo.setAuthorizerAccessToken(GsonHelper.getString(jsonObject, "authorizer_access_token")); | |||||
| authorizationInfo.setExpiresIn(GsonHelper.getPrimitiveInteger(jsonObject, "expires_in")); | |||||
| authorizationInfo.setAuthorizerRefreshToken(GsonHelper.getString(jsonObject, "authorizer_refresh_token")); | |||||
| authorizationInfo.setRefreshExpiresIn(GsonHelper.getPrimitiveInteger(jsonObject, "refresh_expires_in")); | |||||
| List<Integer> funcInfo = new ArrayList<>(); | |||||
| JsonArray jsonArray = GsonHelper.getAsJsonArray(jsonObject.get("authorize_permission")); | |||||
| if (jsonArray != null && !jsonArray.isJsonNull()) { | |||||
| for (int i = 0; i < jsonArray.size(); i++) { | |||||
| jsonObject = jsonArray.get(i).getAsJsonObject(); | |||||
| if (jsonObject == null || jsonObject.isJsonNull()) { | |||||
| continue; | |||||
| } | |||||
| Integer id = GsonHelper.getInteger(jsonObject, "id"); | |||||
| if (id == null) { | |||||
| continue; | |||||
| } | |||||
| funcInfo.add(id); | |||||
| } | |||||
| } | |||||
| authorizationInfo.setAuthorizePermission(funcInfo); | |||||
| return authorizationInfo; | |||||
| } | |||||
| } | |||||
| @@ -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(); | |||||
| } | |||||
| } | |||||
| @@ -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; | |||||
| } | |||||
| @@ -0,0 +1,24 @@ | |||||
| package com.iformall.service.toutiao.api.bean; | |||||
| import com.google.gson.*; | |||||
| import me.chanjar.weixin.common.util.json.GsonHelper; | |||||
| import java.lang.reflect.Type; | |||||
| import java.util.ArrayList; | |||||
| import java.util.List; | |||||
| /** | |||||
| * @author <a href="https://github.com/007gzs">007</a> | |||||
| */ | |||||
| public class TtOpenRetrieveCodeGsonAdapter implements JsonDeserializer<TtOpenRetrieveCode> { | |||||
| @Override | |||||
| public TtOpenRetrieveCode deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { | |||||
| TtOpenRetrieveCode retrieveCode = new TtOpenRetrieveCode(); | |||||
| JsonObject jsonObject = jsonElement.getAsJsonObject(); | |||||
| retrieveCode.setAuthorizationCode(GsonHelper.getString(jsonObject, "authorization_code")); | |||||
| retrieveCode.setExpiresIn(GsonHelper.getPrimitiveInteger(jsonObject, "expires_in")); | |||||
| return retrieveCode; | |||||
| } | |||||
| } | |||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,34 @@ | |||||
| package com.iformall.service.toutiao.api.bean; | |||||
| import com.google.gson.*; | |||||
| import com.iformall.service.toutiao.miniapp.bean.TtMaSubscribeMessage; | |||||
| import me.chanjar.weixin.common.util.json.GsonHelper; | |||||
| import java.lang.reflect.Type; | |||||
| import java.util.ArrayList; | |||||
| import java.util.Arrays; | |||||
| import java.util.List; | |||||
| /** | |||||
| * . | |||||
| * | |||||
| * @author S | |||||
| */ | |||||
| public class TtOpenTicketGsonAdapter implements JsonDeserializer<TtOpenTicket> { | |||||
| @Override | |||||
| public TtOpenTicket deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { | |||||
| return convertFromJson(json.getAsJsonObject()); | |||||
| } | |||||
| protected TtOpenTicket convertFromJson(JsonObject json) { | |||||
| TtOpenTicket openTicket = new TtOpenTicket(); | |||||
| openTicket.setTicket(GsonHelper.getString(json,"Ticket")); | |||||
| openTicket.setFromUserName(GsonHelper.getString(json,"FromUserName")); | |||||
| openTicket.setCreateTime(GsonHelper.getString(json,"CreateTime")); | |||||
| openTicket.setMsgType(GsonHelper.getString(json,"MsgType")); | |||||
| openTicket.setEvent(GsonHelper.getString(json,"Event")); | |||||
| return openTicket; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,517 @@ | |||||
| package com.iformall.service.toutiao.api.impl; | |||||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||||
| import com.google.gson.JsonObject; | |||||
| import com.google.gson.JsonParser; | |||||
| import com.google.gson.reflect.TypeToken; | |||||
| import com.iformall.service.toutiao.api.TtOpenComponentService; | |||||
| import com.iformall.service.toutiao.api.TtOpenMaService; | |||||
| import com.iformall.service.toutiao.api.TtOpenService; | |||||
| import com.iformall.service.toutiao.api.bean.TtOpenAuthorizationInfo; | |||||
| import com.iformall.service.toutiao.api.bean.TtOpenGsonBuilder; | |||||
| import com.iformall.service.toutiao.api.bean.TtOpenRetrieveCode; | |||||
| import com.iformall.service.toutiao.api.bean.TtOpenTicket; | |||||
| import com.iformall.service.toutiao.utils.MsgDecrypt; | |||||
| import me.chanjar.weixin.common.error.WxError; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.common.util.crypto.SHA1; | |||||
| import me.chanjar.weixin.common.util.http.URIUtil; | |||||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | |||||
| import me.chanjar.weixin.open.api.WxOpenConfigStorage; | |||||
| import me.chanjar.weixin.open.bean.*; | |||||
| import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo; | |||||
| import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage; | |||||
| import me.chanjar.weixin.open.bean.result.*; | |||||
| import me.chanjar.weixin.open.util.WxOpenCryptUtil; | |||||
| import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| import java.util.concurrent.ConcurrentHashMap; | |||||
| import java.util.concurrent.locks.Lock; | |||||
| /** | |||||
| * @author | |||||
| */ | |||||
| public class TtOpenComponentServiceImpl implements TtOpenComponentService { | |||||
| private static final JsonParser JSON_PARSER = new JsonParser(); | |||||
| private static final Map<String, TtOpenMaService> TT_OPEN_MA_SERVICE_MAP = new ConcurrentHashMap<>(); | |||||
| protected final Logger log = LoggerFactory.getLogger(this.getClass()); | |||||
| private TtOpenService ttOpenService; | |||||
| public TtOpenComponentServiceImpl(TtOpenService ttOpenService) { | |||||
| this.ttOpenService = ttOpenService; | |||||
| } | |||||
| enum RequestMethod { | |||||
| POST,GET | |||||
| } | |||||
| @Override | |||||
| public TtOpenMaService getTtMaServiceByAppid(String appId) { | |||||
| TtOpenMaService ttOpenMaService = TT_OPEN_MA_SERVICE_MAP.get(appId); | |||||
| if (ttOpenMaService == null) { | |||||
| synchronized (TT_OPEN_MA_SERVICE_MAP) { | |||||
| ttOpenMaService = TT_OPEN_MA_SERVICE_MAP.get(appId); | |||||
| if (ttOpenMaService == null) { | |||||
| ttOpenMaService = new TtOpenMaServiceImpl(this, appId, getWxOpenConfigStorage().getWxMaConfig(appId)); | |||||
| TT_OPEN_MA_SERVICE_MAP.put(appId, ttOpenMaService); | |||||
| } | |||||
| } | |||||
| } | |||||
| return ttOpenMaService; | |||||
| } | |||||
| public TtOpenService getTtOpenService() { | |||||
| return ttOpenService; | |||||
| } | |||||
| @Override | |||||
| public WxOpenConfigStorage getWxOpenConfigStorage() { | |||||
| return ttOpenService.getWxOpenConfigStorage(); | |||||
| } | |||||
| @Override | |||||
| public boolean checkSignature(String timestamp, String nonce, String encrypt, String signature) { | |||||
| try { | |||||
| return SHA1.gen(getWxOpenConfigStorage().getComponentToken(), timestamp, nonce, encrypt) | |||||
| .equals(signature); | |||||
| } catch (Exception e) { | |||||
| this.log.error("Checking signature failed, and the reason is :" + e.getMessage()); | |||||
| return false; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public TtOpenTicket decrypt(String encrypt) { | |||||
| try { | |||||
| MsgDecrypt msgDecrypt = new MsgDecrypt(getWxOpenConfigStorage().getComponentAesKey()); | |||||
| String plainText = msgDecrypt.decrypt(encrypt); | |||||
| log.info("解密信息----------------------------------------"+ plainText); | |||||
| TtOpenTicket openTicket = TtOpenTicket.fromJson(plainText); | |||||
| openTicket.setComponentAppId(getWxOpenConfigStorage().getComponentAppId()); | |||||
| return openTicket; | |||||
| } catch (Exception e) { | |||||
| this.log.error("Checking signature failed, and the reason is :" + e.getMessage()); | |||||
| return null; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public String getComponentAccessToken(boolean forceRefresh) throws WxErrorException { | |||||
| final WxOpenConfigStorage config = this.getWxOpenConfigStorage(); | |||||
| if (!config.isComponentAccessTokenExpired() && !forceRefresh) { | |||||
| return config.getComponentAccessToken(); | |||||
| } | |||||
| Lock lock = config.getComponentAccessTokenLock(); | |||||
| lock.lock(); | |||||
| try { | |||||
| if (!config.isComponentAccessTokenExpired() && !forceRefresh) { | |||||
| return config.getComponentAccessToken(); | |||||
| } | |||||
| JsonObject jsonObject = new JsonObject(); | |||||
| jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); | |||||
| jsonObject.addProperty("component_appsecret", getWxOpenConfigStorage().getComponentAppSecret()); | |||||
| jsonObject.addProperty("component_ticket", getWxOpenConfigStorage().getComponentVerifyTicket()); | |||||
| String responseContent = this.getTtOpenService().get(API_COMPONENT_TOKEN_URL, jsonObject.toString()); | |||||
| WxOpenComponentAccessToken componentAccessToken = WxOpenComponentAccessToken.fromJson(responseContent); | |||||
| config.updateComponentAccessToken(componentAccessToken); | |||||
| return config.getComponentAccessToken(); | |||||
| } finally { | |||||
| lock.unlock(); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public String post(String uri, String postData,String tokenKeyName) throws WxErrorException { | |||||
| //"component_access_token" | |||||
| return postByCommonAccessToken(uri, postData, tokenKeyName); | |||||
| } | |||||
| @Override | |||||
| public String get(String uri,String tokenKeyName) throws WxErrorException { | |||||
| //"component_access_token" | |||||
| return getByCommonAccessToken(uri, tokenKeyName); | |||||
| } | |||||
| private String getByCommonAccessToken(String uri, String accessTokenKey) throws WxErrorException { | |||||
| String componentAccessToken = getComponentAccessToken(false); | |||||
| return excuteRequet(uri, accessTokenKey, componentAccessToken, null, true, RequestMethod.GET); | |||||
| } | |||||
| private String postByCommonAccessToken(String uri, String postData ,String accessTokenKey) throws WxErrorException { | |||||
| String componentAccessToken = getComponentAccessToken(false); | |||||
| return excuteRequet(uri, accessTokenKey, componentAccessToken, postData, true, RequestMethod.POST); | |||||
| } | |||||
| @Override | |||||
| public String getByAppAccessToken(String appId,String uri, String accessTokenKey) throws WxErrorException { | |||||
| String accessToken = getAuthorizerAccessToken(appId,false); | |||||
| return excuteRequet(uri, accessTokenKey, accessToken, null, true, RequestMethod.GET); | |||||
| } | |||||
| @Override | |||||
| public String postByAppAccessToken(String appId,String uri,String postData, String accessTokenKey) throws WxErrorException { | |||||
| String accessToken = getAuthorizerAccessToken(appId, false); | |||||
| return excuteRequet(uri, accessTokenKey, accessToken, postData, true, RequestMethod.POST); | |||||
| } | |||||
| private String post(String uri, String postData, String accessTokenKey,String accessTokenValue, boolean isCommpontAccessToken) throws WxErrorException { | |||||
| return excuteRequet(uri, accessTokenKey, accessTokenValue, postData, isCommpontAccessToken, RequestMethod.POST); | |||||
| } | |||||
| private String get(String uri, String accessTokenKey,String accessTokenValue, boolean isCommpontAccessToken) throws WxErrorException { | |||||
| return excuteRequet(uri, accessTokenKey, accessTokenValue, null, isCommpontAccessToken, RequestMethod.GET); | |||||
| } | |||||
| @Override | |||||
| public String getPreAuthUrl(String redirectUri) throws WxErrorException { | |||||
| return createPreAuthUrl(redirectUri); | |||||
| } | |||||
| /** | |||||
| * 创建预授权链接 | |||||
| * | |||||
| * @param redirectURI | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| private String createPreAuthUrl(String redirectURI) throws WxErrorException { | |||||
| JsonObject jsonObject = new JsonObject(); | |||||
| jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); | |||||
| String responseContent = post(API_CREATE_PREAUTHCODE_URL, jsonObject.toString(),"component_access_token"); | |||||
| jsonObject = WxGsonBuilder.create().fromJson(responseContent, JsonObject.class); | |||||
| StringBuilder preAuthUrl = new StringBuilder(String.format(COMPONENT_LOGIN_PAGE_URL, | |||||
| getWxOpenConfigStorage().getComponentAppId(), | |||||
| jsonObject.get("pre_auth_code").getAsString(), | |||||
| URIUtil.encodeURIComponent(redirectURI))); | |||||
| String preAuthUrlStr = preAuthUrl.toString(); | |||||
| return preAuthUrlStr; | |||||
| } | |||||
| @Override | |||||
| public String route(final TtOpenTicket ticket) throws WxErrorException { | |||||
| if (ticket == null) { | |||||
| throw new NullPointerException("message is empty"); | |||||
| } | |||||
| if (StringUtils.equalsIgnoreCase(ticket.getMsgType(), "Ticket")) { | |||||
| getWxOpenConfigStorage().setComponentVerifyTicket(ticket.getTicket()); | |||||
| return "success"; | |||||
| } | |||||
| return ""; | |||||
| } | |||||
| @Override | |||||
| public TtOpenAuthorizationInfo getQueryAuth(String authorizationCode) throws WxErrorException { | |||||
| String uri = API_QUERY_AUTH_URL + "?component_appid=" + getWxOpenConfigStorage().getComponentAppId() + | |||||
| "&authorization_code=" + authorizationCode + | |||||
| "&grant_type=app_to_tp_authorization_code"; | |||||
| String responseContent = get(uri,"component_access_token"); | |||||
| TtOpenAuthorizationInfo queryAuth = TtOpenGsonBuilder.create().fromJson(responseContent, TtOpenAuthorizationInfo.class); | |||||
| if (queryAuth == null) { | |||||
| return queryAuth; | |||||
| } | |||||
| if (queryAuth.getAuthorizerAccessToken() != null) { | |||||
| getWxOpenConfigStorage().updateAuthorizerAccessToken(queryAuth.getAuthorizerAppid(), | |||||
| queryAuth.getAuthorizerAccessToken(), queryAuth.getExpiresIn()); | |||||
| } | |||||
| if (queryAuth.getAuthorizerRefreshToken() != null) { | |||||
| getWxOpenConfigStorage().setAuthorizerRefreshToken(queryAuth.getAuthorizerAppid(), queryAuth.getAuthorizerRefreshToken()); | |||||
| } | |||||
| return queryAuth; | |||||
| } | |||||
| @Override | |||||
| public String retrieveCode(String authorizationCode) throws WxErrorException { | |||||
| JsonObject jsonObject = new JsonObject(); | |||||
| jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); | |||||
| jsonObject.addProperty("authorization_appid", authorizationCode); | |||||
| String responseContent = post(RETRIEVE_AUTHORIZER_TOKEN_URL, jsonObject.toString(), "component_access_token"); | |||||
| TtOpenRetrieveCode retrieveCode = TtOpenGsonBuilder.create().fromJson(responseContent, TtOpenRetrieveCode.class); | |||||
| if(retrieveCode == null){ | |||||
| return null; | |||||
| } | |||||
| return retrieveCode.getAuthorizationCode(); | |||||
| } | |||||
| // @Override | |||||
| // public WxOpenAuthorizerInfoResult getAuthorizerInfo(String authorizerAppid) throws WxErrorException { | |||||
| // JsonObject jsonObject = new JsonObject(); | |||||
| // jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); | |||||
| // jsonObject.addProperty("authorizer_appid", authorizerAppid); | |||||
| // String responseContent = post(API_GET_AUTHORIZER_INFO_URL, jsonObject.toString(),"component_access_token"); | |||||
| // return WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenAuthorizerInfoResult.class); | |||||
| // } | |||||
| @Override | |||||
| public WxOpenAuthorizerListResult getAuthorizerList(int begin, int len) throws WxErrorException { | |||||
| begin = begin < 0 ? 0 : begin; | |||||
| len = len == 0 ? 10 : len; | |||||
| JsonObject jsonObject = new JsonObject(); | |||||
| jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); | |||||
| jsonObject.addProperty("offset", begin); | |||||
| jsonObject.addProperty("count", len); | |||||
| String responseContent = post(API_GET_AUTHORIZER_LIST, jsonObject.toString(),"component_access_token"); | |||||
| WxOpenAuthorizerListResult ret = WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenAuthorizerListResult.class); | |||||
| if (ret != null && ret.getList() != null) { | |||||
| for (Map<String, String> data : ret.getList()) { | |||||
| String authorizerAppid = data.get("authorizer_appid"); | |||||
| String refreshToken = data.get("refresh_token"); | |||||
| if (authorizerAppid != null && refreshToken != null) { | |||||
| this.getWxOpenConfigStorage().setAuthorizerRefreshToken(authorizerAppid, refreshToken); | |||||
| } | |||||
| } | |||||
| } | |||||
| return ret; | |||||
| } | |||||
| @Override | |||||
| public WxOpenAuthorizerOptionResult getAuthorizerOption(String authorizerAppid, String optionName) throws WxErrorException { | |||||
| JsonObject jsonObject = new JsonObject(); | |||||
| jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); | |||||
| jsonObject.addProperty("authorizer_appid", authorizerAppid); | |||||
| jsonObject.addProperty("option_name", optionName); | |||||
| String responseContent = post(API_GET_AUTHORIZER_OPTION_URL, jsonObject.toString(),"component_access_token"); | |||||
| return WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenAuthorizerOptionResult.class); | |||||
| } | |||||
| @Override | |||||
| public void setAuthorizerOption(String authorizerAppid, String optionName, String optionValue) throws WxErrorException { | |||||
| JsonObject jsonObject = new JsonObject(); | |||||
| jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); | |||||
| jsonObject.addProperty("authorizer_appid", authorizerAppid); | |||||
| jsonObject.addProperty("option_name", optionName); | |||||
| jsonObject.addProperty("option_value", optionValue); | |||||
| post(API_SET_AUTHORIZER_OPTION_URL, jsonObject.toString(),"component_access_token"); | |||||
| } | |||||
| @Override | |||||
| public String getAuthorizerAccessToken(String appId, boolean forceRefresh) throws WxErrorException { | |||||
| WxOpenConfigStorage config = getWxOpenConfigStorage(); | |||||
| if (!config.isAuthorizerAccessTokenExpired(appId) && !forceRefresh) { | |||||
| return config.getAuthorizerAccessToken(appId); | |||||
| } | |||||
| Lock lock = config.getWxMpConfigStorage(appId).getAccessTokenLock(); | |||||
| lock.lock(); | |||||
| try { | |||||
| if (!config.isAuthorizerAccessTokenExpired(appId) && !forceRefresh) { | |||||
| return config.getAuthorizerAccessToken(appId); | |||||
| } | |||||
| String uri = API_AUTHORIZER_TOKEN_URL + "?component_appid=" + getWxOpenConfigStorage().getComponentAppId() + | |||||
| "&authorizer_refresh_token=" + getWxOpenConfigStorage().getAuthorizerRefreshToken(appId) + | |||||
| "&grant_type=app_to_tp_refresh_token"; | |||||
| String responseContent = get(uri,"component_access_token"); | |||||
| TtOpenAuthorizationInfo queryAuth = TtOpenGsonBuilder.create().fromJson(responseContent, TtOpenAuthorizationInfo.class); | |||||
| if (queryAuth.getAuthorizerAccessToken() != null) { | |||||
| config.updateAuthorizerAccessToken(queryAuth.getAuthorizerAppid(), | |||||
| queryAuth.getAuthorizerAccessToken(), queryAuth.getExpiresIn()); | |||||
| } | |||||
| if (queryAuth.getAuthorizerRefreshToken() != null) { | |||||
| config.setAuthorizerRefreshToken(queryAuth.getAuthorizerAppid(), queryAuth.getAuthorizerRefreshToken()); | |||||
| } | |||||
| return config.getAuthorizerAccessToken(appId); | |||||
| } finally { | |||||
| lock.unlock(); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public WxMpOAuth2AccessToken oauth2getAccessToken(String appId, String code) throws WxErrorException { | |||||
| String url = String.format(OAUTH2_ACCESS_TOKEN_URL, appId, code, getWxOpenConfigStorage().getComponentAppId()); | |||||
| String responseContent = get(url,"component_access_token"); | |||||
| return WxMpOAuth2AccessToken.fromJson(responseContent); | |||||
| } | |||||
| @Override | |||||
| public WxMpOAuth2AccessToken oauth2refreshAccessToken(String appId, String refreshToken) throws WxErrorException { | |||||
| String url = String.format(OAUTH2_REFRESH_TOKEN_URL, appId, refreshToken, getWxOpenConfigStorage().getComponentAppId()); | |||||
| String responseContent = get(url,"component_access_token"); | |||||
| return WxMpOAuth2AccessToken.fromJson(responseContent); | |||||
| } | |||||
| @Override | |||||
| public String oauth2buildAuthorizationUrl(String appId, String redirectURI, String scope, String state) { | |||||
| return String.format(CONNECT_OAUTH2_AUTHORIZE_URL, | |||||
| appId, URIUtil.encodeURIComponent(redirectURI), scope, StringUtils.trimToEmpty(state), getWxOpenConfigStorage().getComponentAppId()); | |||||
| } | |||||
| @Override | |||||
| public WxMaJscode2SessionResult miniappJscode2Session(String appId, String jsCode) throws WxErrorException { | |||||
| String url = String.format(MINIAPP_JSCODE_2_SESSION, appId, jsCode, getWxOpenConfigStorage().getComponentAppId()); | |||||
| String responseContent = get(url,"component_access_token"); | |||||
| return WxMaJscode2SessionResult.fromJson(responseContent); | |||||
| } | |||||
| @Override | |||||
| public List<WxOpenMaCodeTemplate> getTemplateDraftList() throws WxErrorException { | |||||
| String responseContent = get(GET_TEMPLATE_DRAFT_LIST_URL, "access_token"); | |||||
| JsonObject response = JSON_PARSER.parse(StringUtils.defaultString(responseContent, "{}")).getAsJsonObject(); | |||||
| boolean hasDraftList = response.has("draft_list"); | |||||
| if (hasDraftList) { | |||||
| return WxOpenGsonBuilder.create().fromJson(response.getAsJsonArray("draft_list"), | |||||
| new TypeToken<List<WxOpenMaCodeTemplate>>() { | |||||
| }.getType()); | |||||
| } else { | |||||
| return null; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public List<WxOpenMaCodeTemplate> getTemplateList() throws WxErrorException { | |||||
| String responseContent = get(GET_TEMPLATE_LIST_URL, "access_token"); | |||||
| JsonObject response = JSON_PARSER.parse(StringUtils.defaultString(responseContent, "{}")).getAsJsonObject(); | |||||
| boolean hasTemplateList = response.has("template_list"); | |||||
| if (hasTemplateList) { | |||||
| return WxOpenGsonBuilder.create().fromJson(response.getAsJsonArray("template_list"), | |||||
| new TypeToken<List<WxOpenMaCodeTemplate>>() { | |||||
| }.getType()); | |||||
| } else { | |||||
| return null; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void addToTemplate(long draftId) throws WxErrorException { | |||||
| JsonObject param = new JsonObject(); | |||||
| param.addProperty("draft_id", draftId); | |||||
| post(ADD_TO_TEMPLATE_URL, param.toString(), "access_token"); | |||||
| } | |||||
| @Override | |||||
| public void deleteTemplate(long templateId) throws WxErrorException { | |||||
| JsonObject param = new JsonObject(); | |||||
| param.addProperty("template_id", templateId); | |||||
| post(DELETE_TEMPLATE_URL, param.toString(), "access_token"); | |||||
| } | |||||
| @Override | |||||
| public WxOpenCreateResult createOpenAccount(String appId) throws WxErrorException { | |||||
| JsonObject param = new JsonObject(); | |||||
| param.addProperty("appid", appId); | |||||
| String json = postByAppAccessToken(appId, CREATE_OPEN_URL, param.toString(), "access_token"); | |||||
| return WxOpenCreateResult.fromJson(json); | |||||
| } | |||||
| private String excuteRequet(String uri,String tokenKey,String tokenValue,String postData,boolean isCompontAccessToken,RequestMethod method) throws WxErrorException { | |||||
| String uriWithComponentAccessToken = uri + (uri.contains("?") ? "&" : "?") + tokenKey + "=" + tokenValue; | |||||
| try { | |||||
| if (method == RequestMethod.POST) { | |||||
| return getTtOpenService().post(uriWithComponentAccessToken, postData); | |||||
| }else { | |||||
| return getTtOpenService().get(uriWithComponentAccessToken, null); | |||||
| } | |||||
| } catch (WxErrorException e) { | |||||
| WxError error = e.getError(); | |||||
| /* | |||||
| * 发生以下情况时尝试刷新access_token | |||||
| * 40001 获取access_token时AppSecret错误,或者access_token无效 | |||||
| * 42001 access_token超时 | |||||
| * 40014 不合法的access_token,请开发者认真比对access_token的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口 | |||||
| */ | |||||
| if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001 || error.getErrorCode() == 40014) { | |||||
| // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token | |||||
| Lock lock = this.getWxOpenConfigStorage().getComponentAccessTokenLock(); | |||||
| lock.lock(); | |||||
| try { | |||||
| if (isCompontAccessToken && StringUtils.equals(tokenValue, this.getWxOpenConfigStorage().getComponentAccessToken())) { | |||||
| this.getWxOpenConfigStorage().expireComponentAccessToken(); | |||||
| } | |||||
| } catch (Exception ex) { | |||||
| if (isCompontAccessToken) { | |||||
| this.getWxOpenConfigStorage().expireComponentAccessToken(); | |||||
| } | |||||
| } finally { | |||||
| lock.unlock(); | |||||
| } | |||||
| if (isCompontAccessToken && this.getWxOpenConfigStorage().autoRefreshToken()) { | |||||
| if (method == RequestMethod.POST) { | |||||
| return this.post(uri, postData, tokenKey); | |||||
| }else { | |||||
| return this.get(uri, tokenKey); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (error.getErrorCode() != 0) { | |||||
| throw new WxErrorException(error, e); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public Boolean bindOpenAccount(String appId, String openAppid) throws WxErrorException { | |||||
| JsonObject param = new JsonObject(); | |||||
| param.addProperty("appid", appId); | |||||
| param.addProperty("open_appid", openAppid); | |||||
| String json = postByAppAccessToken(appId, BIND_OPEN_URL, param.toString(), "access_token"); | |||||
| return WxOpenResult.fromJson(json).isSuccess(); | |||||
| } | |||||
| @Override | |||||
| public Boolean unbindOpenAccount(String appId, String openAppid) throws WxErrorException { | |||||
| JsonObject param = new JsonObject(); | |||||
| param.addProperty("appid", appId); | |||||
| param.addProperty("open_appid", openAppid); | |||||
| String json = postByAppAccessToken(appId, UNBIND_OPEN_URL, param.toString(), "access_token"); | |||||
| return WxOpenResult.fromJson(json).isSuccess(); | |||||
| } | |||||
| @Override | |||||
| public WxOpenGetResult getOpenAccount(String appId) throws WxErrorException { | |||||
| JsonObject param = new JsonObject(); | |||||
| param.addProperty("appid", appId); | |||||
| String json = postByAppAccessToken(appId, GET_OPEN_URL, param.toString(), "access_token"); | |||||
| return WxOpenGetResult.fromJson(json); | |||||
| } | |||||
| @Override | |||||
| public WxOpenResult fastRegisterWeapp(String name, String code, String codeType, String legalPersonaWechat, String legalPersonaName, String componentPhone) throws WxErrorException { | |||||
| JsonObject jsonObject = new JsonObject(); | |||||
| jsonObject.addProperty("name", name); | |||||
| jsonObject.addProperty("code", code); | |||||
| jsonObject.addProperty("code_type", codeType); | |||||
| jsonObject.addProperty("legal_persona_wechat", legalPersonaWechat); | |||||
| jsonObject.addProperty("legal_persona_name", legalPersonaName); | |||||
| jsonObject.addProperty("component_phone", componentPhone); | |||||
| String response = post(FAST_REGISTER_WEAPP_URL, jsonObject.toString(), "component_access_token"); | |||||
| return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| @Override | |||||
| public WxOpenResult fastRegisterWeappSearch(String name, String legalPersonaWechat, String legalPersonaName) throws WxErrorException { | |||||
| JsonObject jsonObject = new JsonObject(); | |||||
| jsonObject.addProperty("name", name); | |||||
| jsonObject.addProperty("legal_persona_wechat", legalPersonaWechat); | |||||
| jsonObject.addProperty("legal_persona_name", legalPersonaName); | |||||
| String response = post(FAST_REGISTER_WEAPP_SEARCH_URL, jsonObject.toString(), "component_access_token"); | |||||
| return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,643 @@ | |||||
| package com.iformall.service.toutiao.api.impl; | |||||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||||
| import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||||
| import com.google.gson.JsonArray; | |||||
| import com.google.gson.JsonObject; | |||||
| import com.iformall.service.toutiao.api.TtOpenComponentService; | |||||
| import com.iformall.service.toutiao.api.TtOpenMaService; | |||||
| import com.iformall.service.toutiao.miniapp.impl.TtMaServiceImpl; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.open.bean.ma.WxMaOpenCommitExtInfo; | |||||
| import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam; | |||||
| import me.chanjar.weixin.open.bean.message.WxOpenMaSubmitAuditMessage; | |||||
| import me.chanjar.weixin.open.bean.result.*; | |||||
| import me.chanjar.weixin.open.executor.MaQrCodeRequestExecutor; | |||||
| import java.io.File; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * @author <a href="https://github.com/007gzs">007</a> | |||||
| * <pre> | |||||
| * 增加开放平台代小程序管理服务能力 | |||||
| * 说明:这里让这个服务公开便于调用者模拟本地测试服务 | |||||
| * </pre> | |||||
| * @author yqx | |||||
| * @date 2018-09-12 | |||||
| */ | |||||
| public class TtOpenMaServiceImpl extends TtMaServiceImpl implements TtOpenMaService { | |||||
| private TtOpenComponentService ttOpenComponentService; | |||||
| private WxMaConfig wxMaConfig; | |||||
| private String appId; | |||||
| public TtOpenMaServiceImpl(TtOpenComponentService ttOpenComponentService, String appId, WxMaConfig wxMaConfig) { | |||||
| this.ttOpenComponentService = ttOpenComponentService; | |||||
| this.appId = appId; | |||||
| this.wxMaConfig = wxMaConfig; | |||||
| initHttp(); | |||||
| } | |||||
| @Override | |||||
| public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException { | |||||
| return ttOpenComponentService.miniappJscode2Session(appId, jsCode); | |||||
| } | |||||
| @Override | |||||
| public WxMaConfig getWxMaConfig() { | |||||
| return wxMaConfig; | |||||
| } | |||||
| @Override | |||||
| public String getAccessToken(boolean forceRefresh) throws WxErrorException { | |||||
| return ttOpenComponentService.getAuthorizerAccessToken(appId, forceRefresh); | |||||
| } | |||||
| /** | |||||
| * 获得小程序的域名配置信息 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaDomainResult getDomain() throws WxErrorException { | |||||
| return modifyDomain("get", null, null, null, null); | |||||
| } | |||||
| /** | |||||
| * 修改服务器域名 | |||||
| * | |||||
| * @param action delete删除, set覆盖, get获取 | |||||
| * @param requestdomainList | |||||
| * @param wsrequestdomainList | |||||
| * @param uploaddomainList | |||||
| * @param downloaddomainList | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaDomainResult modifyDomain(String action, List<String> requestdomainList, List<String> wsrequestdomainList, List<String> uploaddomainList, List<String> downloaddomainList) throws WxErrorException { | |||||
| // if (!"get".equals(action) && (requestdomainList == null || wsrequestdomainList == null || uploaddomainList == null || downloaddomainList == null)) { | |||||
| // throw new WxErrorException(WxError.builder().errorCode(44004).errorMsg("域名参数不能为空").build()); | |||||
| // } | |||||
| JsonObject requestJson = new JsonObject(); | |||||
| requestJson.addProperty("action", action); | |||||
| if (!"get".equals(action)) { | |||||
| requestJson.add("requestdomain", toJsonArray(requestdomainList)); | |||||
| requestJson.add("wsrequestdomain", toJsonArray(wsrequestdomainList)); | |||||
| requestJson.add("uploaddomain", toJsonArray(uploaddomainList)); | |||||
| requestJson.add("downloaddomain", toJsonArray(downloaddomainList)); | |||||
| } | |||||
| String response = post(API_MODIFY_DOMAIN, GSON.toJson(requestJson)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaDomainResult.class); | |||||
| } | |||||
| /** | |||||
| * 获取小程序的业务域名 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @Override | |||||
| public String getWebViewDomain() throws WxErrorException { | |||||
| return setWebViewDomain("get", null); | |||||
| } | |||||
| /** | |||||
| * 获取小程序的业务域名 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaWebDomainResult getWebViewDomainInfo() throws WxErrorException { | |||||
| return setWebViewDomainInfo("get", null); | |||||
| } | |||||
| /** | |||||
| * 设置小程序的业务域名 | |||||
| * | |||||
| * @param action add添加, delete删除, set覆盖 | |||||
| * @param domainList | |||||
| * @return | |||||
| */ | |||||
| @Override | |||||
| public String setWebViewDomain(String action, List<String> domainList) throws WxErrorException { | |||||
| JsonObject requestJson = new JsonObject(); | |||||
| requestJson.addProperty("action", action); | |||||
| if (!"get".equals(action)) { | |||||
| requestJson.add("webviewdomain", toJsonArray(domainList)); | |||||
| } | |||||
| String response = post(API_SET_WEBVIEW_DOMAIN, GSON.toJson(requestJson)); | |||||
| //TODO 转化为对象返回 | |||||
| return response; | |||||
| } | |||||
| /** | |||||
| * 设置小程序的业务域名 | |||||
| * | |||||
| * @param action add添加, delete删除, set覆盖 | |||||
| * @return | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaWebDomainResult setWebViewDomainInfo(String action, List<String> domainList) throws WxErrorException { | |||||
| String response = this.setWebViewDomain(action, domainList); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaWebDomainResult.class); | |||||
| } | |||||
| /** | |||||
| * 获取小程序的信息,GET请求 | |||||
| * <pre> | |||||
| * 注意:这里不能直接用小程序的access_token | |||||
| * //TODO 待调整 | |||||
| * </pre> | |||||
| * | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public String getAccountBasicInfo() throws WxErrorException { | |||||
| String response = get(API_GET_ACCOUNT_BASICINFO, ""); | |||||
| return response; | |||||
| } | |||||
| /** | |||||
| * 绑定小程序体验者 | |||||
| * | |||||
| * @param wechatid 体验者微信号(不是openid) | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaBindTesterResult bindTester(String wechatid) throws WxErrorException { | |||||
| JsonObject paramJson = new JsonObject(); | |||||
| paramJson.addProperty("wechatid", wechatid); | |||||
| String response = post(API_BIND_TESTER, GSON.toJson(paramJson)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaBindTesterResult.class); | |||||
| } | |||||
| /** | |||||
| * 解除绑定小程序体验者 | |||||
| * | |||||
| * @param wechatid 体验者微信号(不是openid) | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult unbindTester(String wechatid) throws WxErrorException { | |||||
| JsonObject paramJson = new JsonObject(); | |||||
| paramJson.addProperty("wechatid", wechatid); | |||||
| String response = post(API_UNBIND_TESTER, GSON.toJson(paramJson)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 解除绑定小程序体验者 | |||||
| * @param userstr 人员对应的唯一字符串, 可通过获取已绑定的体验者列表获取人员对应的字符串 | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult unbindTesterByUserstr(String userstr) throws WxErrorException { | |||||
| JsonObject paramJson = new JsonObject(); | |||||
| paramJson.addProperty("userstr", userstr); | |||||
| String response = post(API_UNBIND_TESTER, GSON.toJson(paramJson)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 获得体验者列表 | |||||
| * | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaTesterListResult getTesterList() throws WxErrorException { | |||||
| JsonObject paramJson = new JsonObject(); | |||||
| paramJson.addProperty("action", "get_experiencer"); | |||||
| String response = post(API_GET_TESTERLIST, GSON.toJson(paramJson)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaTesterListResult.class); | |||||
| } | |||||
| /** | |||||
| * 设置小程序隐私设置(是否可被搜索) | |||||
| * | |||||
| * @param status 1表示不可搜索,0表示可搜索 | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult changeWxaSearchStatus(Integer status) throws WxErrorException { | |||||
| JsonObject paramJson = new JsonObject(); | |||||
| paramJson.addProperty("status", status); | |||||
| String response = post(API_CHANGE_WXA_SEARCH_STATUS, GSON.toJson(paramJson)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 2. 查询小程序当前隐私设置(是否可被搜索) | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaSearchStatusResult getWxaSearchStatus() throws WxErrorException { | |||||
| String response = get(API_GET_WXA_SEARCH_STATUS, null); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaSearchStatusResult.class); | |||||
| } | |||||
| /** | |||||
| * 3.1 获取展示的公众号信息 | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaShowItemResult getShowWxaItem() throws WxErrorException { | |||||
| String response = get(API_GET_SHOW_WXA_ITEM, null); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaShowItemResult.class); | |||||
| } | |||||
| /** | |||||
| * 3.2 设置展示的公众号 | |||||
| * | |||||
| * @param flag 0 关闭,1 开启 | |||||
| * @param mpappid 如果开启,需要传新的公众号appid | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult updateShowwxaitem(Integer flag, String mpappid) throws WxErrorException { | |||||
| JsonObject paramJson = new JsonObject(); | |||||
| paramJson.addProperty("wxa_subscribe_biz_flag", flag); | |||||
| paramJson.addProperty("appid", mpappid); | |||||
| String response = post(API_UPDATE_SHOW_WXA_ITEM, GSON.toJson(paramJson)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 1、为授权的小程序帐号上传小程序代码 | |||||
| * | |||||
| * @param templateId 代码模板ID | |||||
| * @param userVersion 用户定义版本 | |||||
| * @param userDesc 用户定义版本描述 | |||||
| * @param extInfo 第三方自定义的配置 | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult codeCommit(Long templateId, String userVersion, String userDesc, WxMaOpenCommitExtInfo extInfo) throws WxErrorException { | |||||
| JsonObject params = new JsonObject(); | |||||
| params.addProperty("template_id", templateId); | |||||
| params.addProperty("user_version", userVersion); | |||||
| params.addProperty("user_desc", userDesc); | |||||
| //注意:ext_json必须是字符串类型 | |||||
| params.addProperty("ext_json", GSON.toJson(extInfo)); | |||||
| String response = post(API_CODE_COMMIT, GSON.toJson(params)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 获取体验小程序的体验二维码 | |||||
| * | |||||
| * @param pagePath | |||||
| * @param params | |||||
| * @return | |||||
| */ | |||||
| @Override | |||||
| public File getTestQrcode(String pagePath, Map<String, String> params) throws WxErrorException { | |||||
| WxMaQrcodeParam qrcodeParam = WxMaQrcodeParam.create(pagePath); | |||||
| qrcodeParam.addPageParam(params); | |||||
| return execute(MaQrCodeRequestExecutor.create(getRequestHttp()), API_TEST_QRCODE, qrcodeParam); | |||||
| } | |||||
| /** | |||||
| * 获取授权小程序帐号的可选类目 | |||||
| * <p> | |||||
| * 注意:该接口可获取已设置的二级类目及用于代码审核的可选三级类目。 | |||||
| * </p> | |||||
| * | |||||
| * @return WxOpenMaCategoryListResult | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaCategoryListResult getCategoryList() throws WxErrorException { | |||||
| String response = get(API_GET_CATEGORY, null); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaCategoryListResult.class); | |||||
| } | |||||
| /** | |||||
| * 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用) | |||||
| * | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaPageListResult getPageList() throws WxErrorException { | |||||
| String response = get(API_GET_PAGE, null); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaPageListResult.class); | |||||
| } | |||||
| /** | |||||
| * 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用) | |||||
| * | |||||
| * @param submitAuditMessage | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaSubmitAuditResult submitAudit(WxOpenMaSubmitAuditMessage submitAuditMessage) throws WxErrorException { | |||||
| String response = post(API_SUBMIT_AUDIT, GSON.toJson(submitAuditMessage)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaSubmitAuditResult.class); | |||||
| } | |||||
| /** | |||||
| * 7. 查询某个指定版本的审核状态(仅供第三方代小程序调用) | |||||
| * | |||||
| * @param auditid | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaQueryAuditResult getAuditStatus(Long auditid) throws WxErrorException { | |||||
| JsonObject params = new JsonObject(); | |||||
| params.addProperty("auditid", auditid); | |||||
| String response = post(API_GET_AUDIT_STATUS, GSON.toJson(params)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaQueryAuditResult.class); | |||||
| } | |||||
| /** | |||||
| * 8. 查询最新一次提交的审核状态(仅供第三方代小程序调用) | |||||
| * | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaQueryAuditResult getLatestAuditStatus() throws WxErrorException { | |||||
| String response = get(API_GET_LATEST_AUDIT_STATUS, null); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaQueryAuditResult.class); | |||||
| } | |||||
| /** | |||||
| * 9. 发布已通过审核的小程序(仅供第三方代小程序调用) | |||||
| * <p> | |||||
| * 请填写空的数据包,POST的json数据包为空即可。 | |||||
| * </p> | |||||
| * | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult releaesAudited() throws WxErrorException { | |||||
| JsonObject params = new JsonObject(); | |||||
| String response = post(API_RELEASE, GSON.toJson(params)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 10. 修改小程序线上代码的可见状态(仅供第三方代小程序调用) | |||||
| * | |||||
| * @param action 设置可访问状态,发布后默认可访问,close为不可见,open为可见 | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult changeVisitstatus(String action) throws WxErrorException { | |||||
| JsonObject params = new JsonObject(); | |||||
| params.addProperty("action", action); | |||||
| String response = post(API_CHANGE_VISITSTATUS, GSON.toJson(params)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 11. 小程序版本回退(仅供第三方代小程序调用) | |||||
| * | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult revertCodeReleaes() throws WxErrorException { | |||||
| String response = get(API_REVERT_CODE_RELEASE, null); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 15. 小程序审核撤回 | |||||
| * <p> | |||||
| * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。 | |||||
| * </p> | |||||
| * | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult undoCodeAudit() throws WxErrorException { | |||||
| String response = get(API_UNDO_CODE_AUDIT, null); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 12. 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用) | |||||
| * | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public String getSupportVersion() throws WxErrorException { | |||||
| JsonObject params = new JsonObject(); | |||||
| String response = post(API_GET_WEAPP_SUPPORT_VERSION, GSON.toJson(params)); | |||||
| return response; | |||||
| } | |||||
| /** | |||||
| * 12. 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用) | |||||
| * | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaWeappSupportVersionResult getSupportVersionInfo() throws WxErrorException { | |||||
| String response = this.getSupportVersion(); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaWeappSupportVersionResult.class); | |||||
| } | |||||
| /** | |||||
| * 13. 设置最低基础库版本(仅供第三方代小程序调用) | |||||
| * | |||||
| * @param version | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public String setSupportVersion(String version) throws WxErrorException { | |||||
| JsonObject params = new JsonObject(); | |||||
| params.addProperty("version", version); | |||||
| String response = post(API_SET_WEAPP_SUPPORT_VERSION, GSON.toJson(params)); | |||||
| return response; | |||||
| } | |||||
| /** | |||||
| * 13. 设置最低基础库版本(仅供第三方代小程序调用) | |||||
| * | |||||
| * @param version | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult setSupportVersionInfo(String version) throws WxErrorException { | |||||
| String response = this.setSupportVersion(version); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 16. 小程序分阶段发布 - 1)分阶段发布接口 | |||||
| * | |||||
| * @param grayPercentage 灰度的百分比,1到100的整数 | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult grayrelease(Integer grayPercentage) throws WxErrorException { | |||||
| JsonObject params = new JsonObject(); | |||||
| params.addProperty("gray_percentage", grayPercentage); | |||||
| String response = post(API_GRAY_RELEASE, GSON.toJson(params)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 16. 小程序分阶段发布 - 2)取消分阶段发布 | |||||
| * | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult revertgrayrelease() throws WxErrorException { | |||||
| String response = get(API_REVERT_GRAY_RELEASE, null); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 16. 小程序分阶段发布 - 3)查询当前分阶段发布详情 | |||||
| * | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaGrayReleasePlanResult getgrayreleaseplan() throws WxErrorException { | |||||
| String response = get(API_GET_GRAY_RELEASE_PLAN, null); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaGrayReleasePlanResult.class); | |||||
| } | |||||
| /** | |||||
| * 查询服务商的当月提审限额和加急次数(Quota) | |||||
| * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/code/query_quota.html | |||||
| */ | |||||
| @Override | |||||
| public WxOpenMaQueryQuotaResult queryQuota() throws WxErrorException { | |||||
| String response = get(API_QUERY_QUOTA, null); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenMaQueryQuotaResult.class); | |||||
| } | |||||
| /** | |||||
| * 加急审核申请 | |||||
| * 有加急次数的第三方可以通过该接口,对已经提审的小程序进行加急操作,加急后的小程序预计2-12小时内审完。 | |||||
| */ | |||||
| @Override | |||||
| public Boolean speedAudit(Long auditid) throws WxErrorException { | |||||
| JsonObject params = new JsonObject(); | |||||
| params.addProperty("auditid", auditid); | |||||
| String response = post(API_SPEED_AUDIT, GSON.toJson(params)); | |||||
| WxOpenResult result = WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| return result.isSuccess(); | |||||
| } | |||||
| /** | |||||
| * (1)增加或修改二维码规则 | |||||
| * @param wxQrcodeJumpRule | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult addQrcodeJump(WxQrcodeJumpRule wxQrcodeJumpRule) throws WxErrorException { | |||||
| String response = post(API_QRCODE_JUMP_ADD, GSON.toJson(wxQrcodeJumpRule)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * (2)获取已设置的二维码规则 | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxGetQrcodeJumpResult getQrcodeJump() throws WxErrorException { | |||||
| String response = post(API_QRCODE_JUMP_GET, "{}"); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxGetQrcodeJumpResult.class); | |||||
| } | |||||
| /** | |||||
| * (3)获取校验文件名称及内容 | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxDownlooadQrcodeJumpResult downloadQrcodeJump() throws WxErrorException { | |||||
| String response = post(API_QRCODE_JUMP_DOWNLOAD, "{}"); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxDownlooadQrcodeJumpResult.class); | |||||
| } | |||||
| /** | |||||
| * (4)删除已设置的二维码规则 | |||||
| * @param prefix | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult deleteQrcodeJump(String prefix) throws WxErrorException { | |||||
| JsonObject params = new JsonObject(); | |||||
| params.addProperty("prefix", prefix); | |||||
| String response = post(API_QRCODE_JUMP_DELETE, GSON.toJson(params)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * (5)发布已设置的二维码规则 | |||||
| * @param prefix | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| @Override | |||||
| public WxOpenResult publishQrcodeJump(String prefix) throws WxErrorException { | |||||
| JsonObject params = new JsonObject(); | |||||
| params.addProperty("prefix", prefix); | |||||
| String response = post(API_QRCODE_JUMP_PUBLISH, GSON.toJson(params)); | |||||
| return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); | |||||
| } | |||||
| /** | |||||
| * 将字符串对象转化为GsonArray对象 | |||||
| * | |||||
| * @param strList | |||||
| * @return | |||||
| */ | |||||
| private JsonArray toJsonArray(List<String> strList) { | |||||
| JsonArray jsonArray = new JsonArray(); | |||||
| if (strList != null && !strList.isEmpty()) { | |||||
| for (String str : strList) { | |||||
| jsonArray.add(str); | |||||
| } | |||||
| } | |||||
| return jsonArray; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,62 @@ | |||||
| package com.iformall.service.toutiao.api.impl; | |||||
| import com.iformall.service.toutiao.api.TtOpenComponentService; | |||||
| import com.iformall.service.toutiao.api.TtOpenService; | |||||
| import me.chanjar.weixin.common.WxType; | |||||
| import me.chanjar.weixin.common.error.WxError; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.common.util.http.RequestExecutor; | |||||
| import me.chanjar.weixin.common.util.http.RequestHttp; | |||||
| import me.chanjar.weixin.open.api.WxOpenConfigStorage; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import java.io.IOException; | |||||
| /** | |||||
| * @author | |||||
| */ | |||||
| public abstract class TtOpenServiceAbstractImpl<H, P> implements TtOpenService, RequestHttp<H, P> { | |||||
| private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||||
| private TtOpenComponentService ttOpenComponentService = new TtOpenComponentServiceImpl(this); | |||||
| private WxOpenConfigStorage wxOpenConfigStorage; | |||||
| @Override | |||||
| public TtOpenComponentService getTtOpenComponentService() { | |||||
| return ttOpenComponentService; | |||||
| } | |||||
| @Override | |||||
| public WxOpenConfigStorage getWxOpenConfigStorage() { | |||||
| return wxOpenConfigStorage; | |||||
| } | |||||
| @Override | |||||
| public void setWxOpenConfigStorage(WxOpenConfigStorage wxOpenConfigStorage) { | |||||
| this.wxOpenConfigStorage = wxOpenConfigStorage; | |||||
| this.initHttp(); | |||||
| } | |||||
| /** | |||||
| * 初始化 RequestHttp. | |||||
| */ | |||||
| public abstract void initHttp(); | |||||
| protected <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | |||||
| try { | |||||
| T result = executor.execute(uri, data, WxType.Open); | |||||
| this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uri, data, result); | |||||
| return result; | |||||
| } catch (WxErrorException e) { | |||||
| WxError error = e.getError(); | |||||
| if (error.getErrorCode() != 0) { | |||||
| this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uri, data, error); | |||||
| throw new WxErrorException(error, e); | |||||
| } | |||||
| return null; | |||||
| } catch (IOException e) { | |||||
| this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uri, data, e.getMessage()); | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,67 @@ | |||||
| package com.iformall.service.toutiao.api.impl; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.common.util.http.HttpType; | |||||
| import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||||
| import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||||
| import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||||
| import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; | |||||
| import me.chanjar.weixin.open.api.WxOpenConfigStorage; | |||||
| import org.apache.http.HttpHost; | |||||
| import org.apache.http.impl.client.CloseableHttpClient; | |||||
| /** | |||||
| * apache-http方式实现 | |||||
| * | |||||
| * @author <a href="https://github.com/007gzs">007</a> | |||||
| */ | |||||
| public class TtOpenServiceApacheHttpClientImpl extends TtOpenServiceAbstractImpl<CloseableHttpClient, HttpHost> { | |||||
| private CloseableHttpClient httpClient; | |||||
| private HttpHost httpProxy; | |||||
| @Override | |||||
| public void initHttp() { | |||||
| WxOpenConfigStorage configStorage = this.getWxOpenConfigStorage(); | |||||
| ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder(); | |||||
| if (null == apacheHttpClientBuilder) { | |||||
| apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); | |||||
| } | |||||
| apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) | |||||
| .httpProxyPort(configStorage.getHttpProxyPort()) | |||||
| .httpProxyUsername(configStorage.getHttpProxyUsername()) | |||||
| .httpProxyPassword(configStorage.getHttpProxyPassword()); | |||||
| if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { | |||||
| this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); | |||||
| } | |||||
| this.httpClient = apacheHttpClientBuilder.build(); | |||||
| } | |||||
| @Override | |||||
| public CloseableHttpClient getRequestHttpClient() { | |||||
| return httpClient; | |||||
| } | |||||
| @Override | |||||
| public HttpHost getRequestHttpProxy() { | |||||
| return httpProxy; | |||||
| } | |||||
| @Override | |||||
| public HttpType getRequestType() { | |||||
| return HttpType.APACHE_HTTP; | |||||
| } | |||||
| @Override | |||||
| public String get(String url, String queryParam) throws WxErrorException { | |||||
| return execute(SimpleGetRequestExecutor.create(this), url, queryParam); | |||||
| } | |||||
| @Override | |||||
| public String post(String url, String postData) throws WxErrorException { | |||||
| return execute(SimplePostRequestExecutor.create(this), url, postData); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,8 @@ | |||||
| package com.iformall.service.toutiao.api.impl; | |||||
| /** | |||||
| * @author | |||||
| */ | |||||
| public class TtOpenServiceImpl extends TtOpenServiceApacheHttpClientImpl { | |||||
| } | |||||
| @@ -0,0 +1,28 @@ | |||||
| package com.iformall.service.toutiao.miniapp; | |||||
| import com.iformall.service.toutiao.miniapp.bean.TtMaSubscribeMessage; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 消息发送接口 | |||||
| * </pre> | |||||
| * | |||||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
| */ | |||||
| public interface TtMaMsgService { | |||||
| String SUBSCRIBE_MSG_SEND_URL = "https://developer.toutiao.com/api/apps/subscribe_notification/developer/v1/notify"; | |||||
| /** | |||||
| * <pre> | |||||
| * 发送订阅消息 | |||||
| * https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/subscribe-notification/notify/ | |||||
| * </pre> | |||||
| * | |||||
| * @param subscribeMessage 订阅消息 | |||||
| * @throws WxErrorException . | |||||
| */ | |||||
| void sendSubscribeMsg(TtMaSubscribeMessage subscribeMessage) throws WxErrorException; | |||||
| } | |||||
| @@ -0,0 +1,39 @@ | |||||
| package com.iformall.service.toutiao.miniapp; | |||||
| import cn.binarywang.wx.miniapp.bean.AbstractWxMaQrcodeWrapper; | |||||
| import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||||
| import lombok.Data; | |||||
| import lombok.EqualsAndHashCode; | |||||
| import java.io.Serializable; | |||||
| /** | |||||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
| */ | |||||
| @Data | |||||
| @EqualsAndHashCode(callSuper = false) | |||||
| public class TtMaQrcode extends AbstractWxMaQrcodeWrapper implements Serializable { | |||||
| private static final long serialVersionUID = 5777119669111011584L; | |||||
| private String access_token; | |||||
| private String appname; | |||||
| private String path; | |||||
| private int width = 430; | |||||
| public TtMaQrcode(String appname, String path, int width) { | |||||
| this.appname = appname; | |||||
| this.path = path; | |||||
| this.width = width; | |||||
| } | |||||
| public static TtMaQrcode fromJson(String json) { | |||||
| return WxMaGsonBuilder.create().fromJson(json, TtMaQrcode.class); | |||||
| } | |||||
| @Override | |||||
| public String toString() { | |||||
| return WxMaGsonBuilder.create().toJson(this); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,31 @@ | |||||
| package com.iformall.service.toutiao.miniapp; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import java.io.File; | |||||
| /** | |||||
| * <pre> | |||||
| * 二维码相关操作接口. | |||||
| * | |||||
| * </pre> | |||||
| * | |||||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
| */ | |||||
| public interface TtMaQrcodeService { | |||||
| String GET_TTACODE_URL = "https://developer.toutiao.com/api/apps/qrcode"; | |||||
| /** | |||||
| * 接口A: 获取小程序码. | |||||
| * | |||||
| * @param path 不能为空,最大长度 128 字节 | |||||
| * @param width 默认430 二维码的宽度 | |||||
| * @return 文件对象 | |||||
| * @throws WxErrorException 异常 | |||||
| */ | |||||
| File createTtaCode(String appname, String path, int width) throws WxErrorException; | |||||
| } | |||||
| @@ -0,0 +1,132 @@ | |||||
| package com.iformall.service.toutiao.miniapp; | |||||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||||
| import me.chanjar.weixin.common.util.http.RequestExecutor; | |||||
| import me.chanjar.weixin.common.util.http.RequestHttp; | |||||
| /** | |||||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
| */ | |||||
| public interface TtMaService { | |||||
| /** | |||||
| * 获取access_token. | |||||
| */ | |||||
| String GET_ACCESS_TOKEN_URL = "https://developer.toutiao.com/api/apps/token?grant_type=client_credential&appid=%s&secret=%s"; | |||||
| String JSCODE_TO_SESSION_URL = "https://developer.toutiao.com/api/apps/jscode2session"; | |||||
| String ORDER_PUSH = "https://developer.toutiao.com/api/apps/order/v2/push"; | |||||
| /** | |||||
| * 获取登录后的session信息. | |||||
| * | |||||
| * @param jsCode 登录时获取的 code | |||||
| */ | |||||
| WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 验证消息的确来自微信服务器. | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN | |||||
| * </pre> | |||||
| */ | |||||
| boolean checkSignature(String timestamp, String nonce, String signature); | |||||
| /** | |||||
| * <pre> | |||||
| * 获取access_token,本方法线程安全. | |||||
| * 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限 | |||||
| * | |||||
| * 另:本service的所有方法都会在access_token过期是调用此方法 | |||||
| * | |||||
| * 程序员在非必要情况下尽量不要主动调用此方法 | |||||
| * | |||||
| * 详情请见: https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/interface-request-credential/get-access-token | |||||
| * </pre> | |||||
| * | |||||
| * @param forceRefresh 强制刷新 | |||||
| */ | |||||
| String getAccessToken(boolean forceRefresh) throws WxErrorException; | |||||
| /** | |||||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求. | |||||
| */ | |||||
| String get(String url, String queryParam) throws WxErrorException; | |||||
| /** | |||||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求. | |||||
| */ | |||||
| String post(String url, String postData) throws WxErrorException; | |||||
| /** | |||||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求. | |||||
| */ | |||||
| String post(String url, Object obj) throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * Service没有实现某个API的时候,可以用这个, | |||||
| * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。 | |||||
| * 可以参考,{@link MediaUploadRequestExecutor}的实现方法 | |||||
| * </pre> | |||||
| */ | |||||
| <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试. | |||||
| * 默认:1000ms | |||||
| * </pre> | |||||
| */ | |||||
| void setRetrySleepMillis(int retrySleepMillis); | |||||
| /** | |||||
| * <pre> | |||||
| * 设置当微信系统响应系统繁忙时,最大重试次数. | |||||
| * 默认:5次 | |||||
| * </pre> | |||||
| */ | |||||
| void setMaxRetryTimes(int maxRetryTimes); | |||||
| /** | |||||
| * 获取WxMaConfig 对象. | |||||
| * | |||||
| * @return WxMaConfig | |||||
| */ | |||||
| WxMaConfig getWxMaConfig(); | |||||
| /** | |||||
| * 注入 {@link WxMaConfig} 的实现. | |||||
| */ | |||||
| void setWxMaConfig(WxMaConfig wxConfigProvider); | |||||
| /** | |||||
| * 初始化http请求对象. | |||||
| */ | |||||
| void initHttp(); | |||||
| /** | |||||
| * 请求http请求相关信息. | |||||
| */ | |||||
| RequestHttp getRequestHttp(); | |||||
| /** | |||||
| * 返回用户相关接口方法的实现类对象,以方便调用其各个接口. | |||||
| * | |||||
| * @return TtMaUserService | |||||
| */ | |||||
| TtMaUserService getUserService(); | |||||
| /** | |||||
| * 返回用户相关接口方法的实现类对象,以方便调用其各个接口. | |||||
| * | |||||
| * @return TtMaUserService | |||||
| */ | |||||
| TtMaQrcodeService getQrcodeService(); | |||||
| TtMaMsgService getMsgService(); | |||||
| } | |||||
| @@ -0,0 +1,52 @@ | |||||
| package com.iformall.service.toutiao.miniapp; | |||||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||||
| import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | |||||
| import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| /** | |||||
| * 用户信息相关操作接口. | |||||
| * | |||||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
| */ | |||||
| public interface TtMaUserService { | |||||
| /** | |||||
| * 获取登录后的session信息. | |||||
| * | |||||
| * @param jsCode 登录时获取的 code | |||||
| * @return . | |||||
| * @throws WxErrorException . | |||||
| */ | |||||
| WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException; | |||||
| /** | |||||
| * 解密用户敏感数据. | |||||
| * | |||||
| * @param sessionKey 会话密钥 | |||||
| * @param encryptedData 消息密文 | |||||
| * @param ivStr 加密算法的初始向量 | |||||
| */ | |||||
| WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr); | |||||
| /** | |||||
| * 解密用户手机号信息. | |||||
| * | |||||
| * @param sessionKey 会话密钥 | |||||
| * @param encryptedData 消息密文 | |||||
| * @param ivStr 加密算法的初始向量 | |||||
| * @return . | |||||
| */ | |||||
| WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr); | |||||
| /** | |||||
| * 验证用户信息完整性. | |||||
| * | |||||
| * @param sessionKey 会话密钥 | |||||
| * @param rawData 微信用户基本信息 | |||||
| * @param signature 数据签名 | |||||
| * @return . | |||||
| */ | |||||
| boolean checkUserInfo(String sessionKey, String rawData, String signature); | |||||
| } | |||||
| @@ -0,0 +1,69 @@ | |||||
| package com.iformall.service.toutiao.miniapp; | |||||
| import cn.binarywang.wx.miniapp.bean.AbstractWxMaQrcodeWrapper; | |||||
| import me.chanjar.weixin.common.WxType; | |||||
| import me.chanjar.weixin.common.error.WxError; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.common.util.fs.FileUtils; | |||||
| import me.chanjar.weixin.common.util.http.RequestExecutor; | |||||
| import me.chanjar.weixin.common.util.http.RequestHttp; | |||||
| import me.chanjar.weixin.common.util.http.ResponseHandler; | |||||
| import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler; | |||||
| import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; | |||||
| import org.apache.http.Header; | |||||
| import org.apache.http.HttpHost; | |||||
| import org.apache.http.client.config.RequestConfig; | |||||
| import org.apache.http.client.methods.CloseableHttpResponse; | |||||
| import org.apache.http.client.methods.HttpPost; | |||||
| import org.apache.http.entity.ContentType; | |||||
| import org.apache.http.entity.StringEntity; | |||||
| import org.apache.http.impl.client.CloseableHttpClient; | |||||
| import java.io.File; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.util.UUID; | |||||
| /** | |||||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
| */ | |||||
| public class TtQrcodeRequestExecutor implements RequestExecutor<File, AbstractWxMaQrcodeWrapper> { | |||||
| protected RequestHttp<CloseableHttpClient, HttpHost> requestHttp; | |||||
| public TtQrcodeRequestExecutor(RequestHttp requestHttp) { | |||||
| this.requestHttp = requestHttp; | |||||
| } | |||||
| @Override | |||||
| public void execute(String uri, AbstractWxMaQrcodeWrapper data, ResponseHandler<File> handler, WxType wxType) throws WxErrorException, IOException { | |||||
| handler.handle(this.execute(uri, data, wxType)); | |||||
| } | |||||
| @Override | |||||
| public File execute(String uri, AbstractWxMaQrcodeWrapper qrcodeWrapper, WxType wxType) throws WxErrorException, IOException { | |||||
| HttpPost httpPost = new HttpPost(uri); | |||||
| if (requestHttp.getRequestHttpProxy() != null) { | |||||
| httpPost.setConfig( | |||||
| RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build() | |||||
| ); | |||||
| } | |||||
| httpPost.addHeader("Content-Type","application/json"); | |||||
| httpPost.setEntity(new StringEntity(qrcodeWrapper.toJson())); | |||||
| try (final CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); | |||||
| final InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response)) { | |||||
| Header[] contentTypeHeader = response.getHeaders("Content-Type"); | |||||
| if (contentTypeHeader != null && contentTypeHeader.length > 0 | |||||
| && ContentType.APPLICATION_JSON.getMimeType() | |||||
| .equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) { | |||||
| String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); | |||||
| throw new WxErrorException(WxError.fromJson(responseContent, wxType)); | |||||
| } | |||||
| return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg"); | |||||
| } finally { | |||||
| httpPost.releaseConnection(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,21 @@ | |||||
| package com.iformall.service.toutiao.miniapp.bean; | |||||
| import com.google.gson.Gson; | |||||
| import com.google.gson.GsonBuilder; | |||||
| /** | |||||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
| */ | |||||
| public class TtMaGsonBuilder { | |||||
| private static final GsonBuilder INSTANCE = new GsonBuilder(); | |||||
| static { | |||||
| INSTANCE.disableHtmlEscaping(); | |||||
| INSTANCE.registerTypeAdapter(TtMaSubscribeMessage.class, new TtMaSubscribeMessageGsonAdapter()); | |||||
| } | |||||
| public static Gson create() { | |||||
| return INSTANCE.create(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,70 @@ | |||||
| package com.iformall.service.toutiao.miniapp.bean; | |||||
| import com.google.gson.JsonObject; | |||||
| import lombok.*; | |||||
| import java.io.Serializable; | |||||
| /** | |||||
| * 订阅消息. | |||||
| * @author S | |||||
| */ | |||||
| @Getter | |||||
| @Setter | |||||
| @NoArgsConstructor | |||||
| @AllArgsConstructor | |||||
| @Builder | |||||
| public class TtMaSubscribeMessage implements Serializable { | |||||
| private static final long serialVersionUID = -5931206947637944325L; | |||||
| private String accessToken; | |||||
| private String appId; | |||||
| /** | |||||
| * 接收者(用户)的 openid. | |||||
| * <pre> | |||||
| * 参数:touser | |||||
| * 是否必填: 是 | |||||
| * 描述: 接收者(用户)的 openid | |||||
| * </pre> | |||||
| */ | |||||
| private String openId; | |||||
| /** | |||||
| * 所需下发的模板消息的id. | |||||
| * <pre> | |||||
| * 参数:template_id | |||||
| * 是否必填: 是 | |||||
| * 描述: 所需下发的模板消息的id | |||||
| * </pre> | |||||
| */ | |||||
| private String tplId; | |||||
| /** | |||||
| * 点击模板卡片后的跳转页面,仅限本小程序内的页面. | |||||
| * <pre> | |||||
| * 参数:page | |||||
| * 是否必填: 否 | |||||
| * 描述: 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。 | |||||
| * </pre> | |||||
| */ | |||||
| private String page; | |||||
| /** | |||||
| * 模板内容,不填则下发空模板. | |||||
| * <pre> | |||||
| * 参数:data | |||||
| * 是否必填: 是 | |||||
| * 描述: 模板内容,不填则下发空模板 | |||||
| * </pre> | |||||
| */ | |||||
| private JsonObject data; | |||||
| public String toJson() { | |||||
| return TtMaGsonBuilder.create().toJson(this); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,33 @@ | |||||
| package com.iformall.service.toutiao.miniapp.bean; | |||||
| import com.google.gson.JsonElement; | |||||
| import com.google.gson.JsonObject; | |||||
| import com.google.gson.JsonSerializationContext; | |||||
| import com.google.gson.JsonSerializer; | |||||
| import java.lang.reflect.Type; | |||||
| /** | |||||
| * . | |||||
| * | |||||
| * @author S | |||||
| */ | |||||
| public class TtMaSubscribeMessageGsonAdapter implements JsonSerializer<TtMaSubscribeMessage> { | |||||
| @Override | |||||
| public JsonElement serialize(TtMaSubscribeMessage message, Type typeOfSrc, JsonSerializationContext context) { | |||||
| JsonObject messageJson = new JsonObject(); | |||||
| messageJson.addProperty("access_token", message.getAccessToken()); | |||||
| messageJson.addProperty("app_id", message.getAppId()); | |||||
| messageJson.addProperty("tpl_id", message.getTplId()); | |||||
| messageJson.addProperty("open_id", message.getOpenId()); | |||||
| if (message.getPage() != null) { | |||||
| messageJson.addProperty("page", message.getPage()); | |||||
| } | |||||
| messageJson.add("data", message.getData()); | |||||
| return messageJson; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,30 @@ | |||||
| package com.iformall.service.toutiao.miniapp.impl; | |||||
| import com.google.gson.JsonObject; | |||||
| import com.google.gson.JsonParser; | |||||
| import com.iformall.service.toutiao.miniapp.TtMaMsgService; | |||||
| import com.iformall.service.toutiao.miniapp.TtMaService; | |||||
| import com.iformall.service.toutiao.miniapp.bean.TtMaSubscribeMessage; | |||||
| import lombok.AllArgsConstructor; | |||||
| import me.chanjar.weixin.common.WxType; | |||||
| import me.chanjar.weixin.common.error.WxError; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| /** | |||||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
| */ | |||||
| @AllArgsConstructor | |||||
| public class TtMaMsgServiceImpl implements TtMaMsgService { | |||||
| private static final JsonParser JSON_PARSER = new JsonParser(); | |||||
| private TtMaService ttMaService; | |||||
| @Override | |||||
| public void sendSubscribeMsg(TtMaSubscribeMessage subscribeMessage) throws WxErrorException { | |||||
| String responseContent = this.ttMaService.post(SUBSCRIBE_MSG_SEND_URL, subscribeMessage); | |||||
| JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | |||||
| if (jsonObject.get("err_no").getAsInt() != 0) { | |||||
| throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,26 @@ | |||||
| package com.iformall.service.toutiao.miniapp.impl; | |||||
| import com.iformall.service.toutiao.miniapp.TtMaQrcode; | |||||
| import com.iformall.service.toutiao.miniapp.TtMaQrcodeService; | |||||
| import com.iformall.service.toutiao.miniapp.TtMaService; | |||||
| import com.iformall.service.toutiao.miniapp.TtQrcodeRequestExecutor; | |||||
| import lombok.AllArgsConstructor; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import java.io.File; | |||||
| /** | |||||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
| */ | |||||
| @AllArgsConstructor | |||||
| public class TtMaQrcodeServiceImpl implements TtMaQrcodeService { | |||||
| private TtMaService ttMaService; | |||||
| @Override | |||||
| public File createTtaCode(String appname, String path, int width) throws WxErrorException { | |||||
| final TtQrcodeRequestExecutor executor = new TtQrcodeRequestExecutor(this.ttMaService.getRequestHttp()); | |||||
| return this.ttMaService.execute(executor, GET_TTACODE_URL, new TtMaQrcode(appname,path, width)); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,312 @@ | |||||
| package com.iformall.service.toutiao.miniapp.impl; | |||||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||||
| import com.google.common.base.Joiner; | |||||
| import com.google.gson.Gson; | |||||
| import com.google.gson.JsonParser; | |||||
| import com.iformall.service.toutiao.miniapp.TtMaMsgService; | |||||
| import com.iformall.service.toutiao.miniapp.TtMaQrcodeService; | |||||
| import com.iformall.service.toutiao.miniapp.TtMaService; | |||||
| import com.iformall.service.toutiao.miniapp.TtMaUserService; | |||||
| import com.iformall.service.toutiao.miniapp.bean.TtMaGsonBuilder; | |||||
| import lombok.extern.slf4j.Slf4j; | |||||
| import me.chanjar.weixin.common.WxType; | |||||
| import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
| import me.chanjar.weixin.common.error.WxError; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.common.util.DataUtils; | |||||
| import me.chanjar.weixin.common.util.crypto.SHA1; | |||||
| import me.chanjar.weixin.common.util.http.*; | |||||
| import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||||
| import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; | |||||
| import org.apache.http.HttpHost; | |||||
| import org.apache.http.client.config.RequestConfig; | |||||
| import org.apache.http.client.methods.CloseableHttpResponse; | |||||
| import org.apache.http.client.methods.HttpGet; | |||||
| import org.apache.http.impl.client.BasicResponseHandler; | |||||
| import org.apache.http.impl.client.CloseableHttpClient; | |||||
| import java.io.IOException; | |||||
| import java.lang.reflect.Field; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| import java.util.concurrent.locks.Lock; | |||||
| import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*; | |||||
| /** | |||||
| * @author | |||||
| */ | |||||
| @Slf4j | |||||
| public class TtMaServiceImpl implements TtMaService, RequestHttp<CloseableHttpClient, HttpHost> { | |||||
| private static final JsonParser JSON_PARSER = new JsonParser(); | |||||
| private CloseableHttpClient httpClient; | |||||
| private HttpHost httpProxy; | |||||
| private WxMaConfig wxMaConfig; | |||||
| private TtMaUserService userService = new TtMaUserServiceImpl(this); | |||||
| private TtMaQrcodeService qrcodeService = new TtMaQrcodeServiceImpl(this); | |||||
| private TtMaMsgService msgService = new TtMaMsgServiceImpl(this); | |||||
| private int retrySleepMillis = 1000; | |||||
| private int maxRetryTimes = 5; | |||||
| protected static final Gson GSON = new Gson(); | |||||
| @Override | |||||
| public CloseableHttpClient getRequestHttpClient() { | |||||
| return httpClient; | |||||
| } | |||||
| @Override | |||||
| public HttpHost getRequestHttpProxy() { | |||||
| return httpProxy; | |||||
| } | |||||
| @Override | |||||
| public HttpType getRequestType() { | |||||
| return HttpType.APACHE_HTTP; | |||||
| } | |||||
| @Override | |||||
| public void initHttp() { | |||||
| WxMaConfig configStorage = this.getWxMaConfig(); | |||||
| ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder(); | |||||
| if (null == apacheHttpClientBuilder) { | |||||
| apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); | |||||
| } | |||||
| apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) | |||||
| .httpProxyPort(configStorage.getHttpProxyPort()) | |||||
| .httpProxyUsername(configStorage.getHttpProxyUsername()) | |||||
| .httpProxyPassword(configStorage.getHttpProxyPassword()); | |||||
| if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { | |||||
| this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); | |||||
| } | |||||
| this.httpClient = apacheHttpClientBuilder.build(); | |||||
| } | |||||
| @Override | |||||
| public RequestHttp getRequestHttp() { | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public String getAccessToken(boolean forceRefresh) throws WxErrorException { | |||||
| if (!this.getWxMaConfig().isAccessTokenExpired() && !forceRefresh) { | |||||
| return this.getWxMaConfig().getAccessToken(); | |||||
| } | |||||
| Lock lock = this.getWxMaConfig().getAccessTokenLock(); | |||||
| lock.lock(); | |||||
| try { | |||||
| String url = String.format(this.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), | |||||
| this.getWxMaConfig().getSecret()); | |||||
| try { | |||||
| HttpGet httpGet = new HttpGet(url); | |||||
| if (this.getRequestHttpProxy() != null) { | |||||
| RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); | |||||
| httpGet.setConfig(config); | |||||
| } | |||||
| try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) { | |||||
| String resultContent = new BasicResponseHandler().handleResponse(response); | |||||
| WxError error = WxError.fromJson(resultContent, WxType.MiniApp); | |||||
| if (error.getErrorCode() != 0) { | |||||
| throw new WxErrorException(error); | |||||
| } | |||||
| WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
| this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
| return this.getWxMaConfig().getAccessToken(); | |||||
| } finally { | |||||
| httpGet.releaseConnection(); | |||||
| } | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } finally { | |||||
| lock.unlock(); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException { | |||||
| final WxMaConfig config = getWxMaConfig(); | |||||
| Map<String, String> params = new HashMap<>(8); | |||||
| params.put("appid", config.getAppid()); | |||||
| params.put("secret", config.getSecret()); | |||||
| params.put("code", jsCode); | |||||
| String result = get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); | |||||
| return WxMaJscode2SessionResult.fromJson(result); | |||||
| } | |||||
| @Override | |||||
| public boolean checkSignature(String timestamp, String nonce, String signature) { | |||||
| try { | |||||
| return SHA1.gen(this.getWxMaConfig().getToken(), timestamp, nonce).equals(signature); | |||||
| } catch (Exception e) { | |||||
| log.error("Checking signature failed, and the reason is :" + e.getMessage()); | |||||
| return false; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public String get(String url, String queryParam) throws WxErrorException { | |||||
| if (url.contains("access_token=")) { | |||||
| throw new IllegalArgumentException("uri参数中不允许有access_token: " + url); | |||||
| } | |||||
| String accessToken = getAccessToken(false); | |||||
| String uriWithAccessToken = url + (url.contains("?") ? "&" : "?") + "access_token=" + accessToken; | |||||
| return execute(SimpleGetRequestExecutor.create(this), uriWithAccessToken, queryParam); | |||||
| } | |||||
| @Override | |||||
| public String post(String url, String postData) throws WxErrorException { | |||||
| if (url.contains("access_token=")) { | |||||
| throw new IllegalArgumentException("uri参数中不允许有access_token: " + url); | |||||
| } | |||||
| String accessToken = getAccessToken(false); | |||||
| String uriWithAccessToken = url + (url.contains("?") ? "&" : "?") + "access_token=" + accessToken; | |||||
| return execute(SimplePostRequestExecutor.create(this), uriWithAccessToken, postData); | |||||
| } | |||||
| @Override | |||||
| public String post(String url, Object obj) throws WxErrorException { | |||||
| if (url.contains("access_token=")) { | |||||
| throw new IllegalArgumentException("uri参数中不允许有access_token: " + url); | |||||
| } | |||||
| try { | |||||
| String accessToken = getAccessToken(false); | |||||
| Class<?> aClass = obj.getClass(); | |||||
| if(!aClass.equals(String.class)){ | |||||
| if(aClass.equals(HashMap.class)){ | |||||
| ((HashMap) obj).put("access_token",accessToken); | |||||
| }else{ | |||||
| Field accessToken1 = aClass.getDeclaredField("accessToken"); | |||||
| accessToken1.setAccessible(true); | |||||
| accessToken1.set(obj,accessToken); | |||||
| } | |||||
| } | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| log.error("【请求参数】:{}添加accessToken失败" + obj.getClass()); | |||||
| } | |||||
| return this.execute(SimplePostRequestExecutor.create(this), url, TtMaGsonBuilder.create().toJson(obj)); | |||||
| } | |||||
| /** | |||||
| * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 | |||||
| */ | |||||
| @Override | |||||
| public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | |||||
| int retryTimes = 0; | |||||
| do { | |||||
| try { | |||||
| return this.executeInternal(executor, uri, data); | |||||
| } catch (WxErrorException e) { | |||||
| if (retryTimes + 1 > this.maxRetryTimes) { | |||||
| log.warn("重试达到最大次数【{}】", maxRetryTimes); | |||||
| //最后一次重试失败后,直接抛出异常,不再等待 | |||||
| throw new RuntimeException("微信服务端异常,超出重试次数"); | |||||
| } | |||||
| WxError error = e.getError(); | |||||
| // -1 系统繁忙, 1000ms后重试 | |||||
| if (error.getErrorCode() == -1) { | |||||
| int sleepMillis = this.retrySleepMillis * (1 << retryTimes); | |||||
| try { | |||||
| log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); | |||||
| Thread.sleep(sleepMillis); | |||||
| } catch (InterruptedException e1) { | |||||
| Thread.currentThread().interrupt(); | |||||
| } | |||||
| } else { | |||||
| throw e; | |||||
| } | |||||
| } | |||||
| } while (retryTimes++ < this.maxRetryTimes); | |||||
| log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||||
| throw new RuntimeException("微信服务端异常,超出重试次数"); | |||||
| } | |||||
| private <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | |||||
| E dataForLog = DataUtils.handleDataWithSecret(data); | |||||
| try { | |||||
| T result = executor.execute(uri, data, WxType.MiniApp); | |||||
| log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uri, data, result); | |||||
| return result; | |||||
| } catch (WxErrorException e) { | |||||
| WxError error = e.getError(); | |||||
| /* | |||||
| * 发生以下情况时尝试刷新access_token | |||||
| */ | |||||
| if (error.getErrorCode() == ERR_40001 | |||||
| || error.getErrorCode() == ERR_42001 | |||||
| || error.getErrorCode() == ERR_40014) { | |||||
| // 强制设置WxMaConfig的access token过期了,这样在下一次请求里就会刷新access token | |||||
| this.getWxMaConfig().expireAccessToken(); | |||||
| if (this.getWxMaConfig().autoRefreshToken()) { | |||||
| return this.execute(executor, uri, data); | |||||
| } | |||||
| } | |||||
| if (error.getErrorCode() != 0) { | |||||
| log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uri, dataForLog, error); | |||||
| throw new WxErrorException(error, e); | |||||
| } | |||||
| return null; | |||||
| } catch (IOException e) { | |||||
| log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uri, dataForLog, e.getMessage()); | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public WxMaConfig getWxMaConfig() { | |||||
| return this.wxMaConfig; | |||||
| } | |||||
| @Override | |||||
| public void setWxMaConfig(WxMaConfig wxConfigProvider) { | |||||
| this.wxMaConfig = wxConfigProvider; | |||||
| this.initHttp(); | |||||
| } | |||||
| @Override | |||||
| public void setRetrySleepMillis(int retrySleepMillis) { | |||||
| this.retrySleepMillis = retrySleepMillis; | |||||
| } | |||||
| @Override | |||||
| public void setMaxRetryTimes(int maxRetryTimes) { | |||||
| this.maxRetryTimes = maxRetryTimes; | |||||
| } | |||||
| @Override | |||||
| public TtMaUserService getUserService() { | |||||
| return this.userService; | |||||
| } | |||||
| @Override | |||||
| public TtMaQrcodeService getQrcodeService() { | |||||
| return this.qrcodeService; | |||||
| } | |||||
| @Override | |||||
| public TtMaMsgService getMsgService() { | |||||
| return this.msgService; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,41 @@ | |||||
| package com.iformall.service.toutiao.miniapp.impl; | |||||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||||
| import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | |||||
| import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | |||||
| import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; | |||||
| import com.iformall.service.toutiao.miniapp.TtMaService; | |||||
| import com.iformall.service.toutiao.miniapp.TtMaUserService; | |||||
| import lombok.AllArgsConstructor; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import org.apache.commons.codec.digest.DigestUtils; | |||||
| /** | |||||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
| */ | |||||
| @AllArgsConstructor | |||||
| public class TtMaUserServiceImpl implements TtMaUserService { | |||||
| private TtMaService service; | |||||
| @Override | |||||
| public WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException { | |||||
| return service.jsCode2SessionInfo(jsCode); | |||||
| } | |||||
| @Override | |||||
| public WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr) { | |||||
| return WxMaUserInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | |||||
| } | |||||
| @Override | |||||
| public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr) { | |||||
| return WxMaPhoneNumberInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | |||||
| } | |||||
| @Override | |||||
| public boolean checkUserInfo(String sessionKey, String rawData, String signature) { | |||||
| final String generatedSignature = DigestUtils.sha1Hex(rawData + sessionKey); | |||||
| return generatedSignature.equals(signature); | |||||
| } | |||||
| } | |||||
| @@ -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"); | |||||
| } | |||||
| } | |||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,131 @@ | |||||
| package com.iformall.controller; | |||||
| import com.iformall.common.ResultData; | |||||
| import com.iformall.domain.po.WxAuthorizerInfo; | |||||
| import com.iformall.domain.vo.WxWeappInfo; | |||||
| import com.iformall.enums.EnumAppPlat; | |||||
| import com.iformall.enums.EnumWxAuthorizationStatus; | |||||
| import com.iformall.service.WxAuthorizerInfoService; | |||||
| import com.iformall.service.toutiao.FmTtOpenService; | |||||
| import com.iformall.service.toutiao.api.bean.TtOpenAuthorizationInfo; | |||||
| import io.swagger.annotations.Api; | |||||
| import io.swagger.annotations.ApiOperation; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.stereotype.Controller; | |||||
| import org.springframework.web.bind.annotation.GetMapping; | |||||
| import org.springframework.web.bind.annotation.RequestMapping; | |||||
| import org.springframework.web.bind.annotation.RequestParam; | |||||
| import org.springframework.web.bind.annotation.ResponseBody; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import javax.servlet.http.HttpServletResponse; | |||||
| import java.io.IOException; | |||||
| import java.util.Date; | |||||
| import java.util.List; | |||||
| /** | |||||
| * Stormeye Wu | |||||
| */ | |||||
| @Controller | |||||
| @RequestMapping("/wt_auth") | |||||
| @Api(description = "微信第三方开放平台授权相关接口") | |||||
| public class TtAuthController { | |||||
| private final Logger logger = LoggerFactory.getLogger(getClass()); | |||||
| @Autowired | |||||
| protected FmTtOpenService openService; | |||||
| @Autowired | |||||
| private WxAuthorizerInfoService authorizerInfoService; | |||||
| @ApiOperation(value = "获取授权跳转页", notes = "") | |||||
| @GetMapping("/goto_auth_url_show") | |||||
| @ResponseBody | |||||
| public String gotoPreAuthUrlShow() { | |||||
| return "<html>" + | |||||
| "<body>" + | |||||
| "<a href='/T/wt_auth/goto_auth_url'>go</a>" + | |||||
| "</body>" + | |||||
| "</html>"; | |||||
| } | |||||
| @ApiOperation(value = "授权跳转页", notes = "") | |||||
| @GetMapping("/goto_auth_url") | |||||
| public void gotoPreAuthUrl(HttpServletRequest request, HttpServletResponse response) { | |||||
| String host = request.getHeader("host"); | |||||
| String url = "https://" + host + "/T/wt_auth/jump"; | |||||
| try { | |||||
| url = openService.getTtOpenComponentService().getPreAuthUrl(url); | |||||
| response.sendRedirect(url); | |||||
| } catch (WxErrorException | IOException e) { | |||||
| logger.error("gotoPreAuthUrl", e); | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| @ApiOperation(value = "授权", notes = "") | |||||
| @GetMapping("/jump") | |||||
| @ResponseBody | |||||
| public void jump(@RequestParam("auth_code") String authorizationCode) { | |||||
| try { | |||||
| TtOpenAuthorizationInfo queryAuthResult = openService.getTtOpenComponentService().getQueryAuth(authorizationCode); | |||||
| logger.info("getQueryAuth", queryAuthResult); | |||||
| // WxOpenAuthorizerInfoResult openAuthorizerInfoResult = openService.getTtOpenComponentService().getAuthorizerInfo( | |||||
| // queryAuthResult.getAuthorizerAppid()); | |||||
| // logger.info(openAuthorizerInfoResult.toString()); | |||||
| // save auth info | |||||
| authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo(){ | |||||
| { | |||||
| setAuthorizerAppid(queryAuthResult.getAuthorizerAppid()); | |||||
| setRefreshToken(queryAuthResult.getAuthorizerRefreshToken()); | |||||
| setAccessToken(queryAuthResult.getAuthorizerAccessToken()); | |||||
| setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + queryAuthResult.getExpiresIn()*1000))); | |||||
| setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode()); | |||||
| setAuthTime(new Date()); | |||||
| } | |||||
| }); | |||||
| } catch (WxErrorException e) { | |||||
| logger.error("gotoPreAuthUrl", e); | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| @ApiOperation(value = "刷新", notes = "") | |||||
| @GetMapping("/retrieve") | |||||
| @ResponseBody | |||||
| public ResultData retrieve() { | |||||
| WxWeappInfo weappInfo = new WxWeappInfo(); | |||||
| weappInfo.setPlat(EnumAppPlat.TOUTIAO.getCode()); | |||||
| List<WxWeappInfo> list = authorizerInfoService.getList(weappInfo); | |||||
| if(list != null && list.size() > 0){ | |||||
| for (WxWeappInfo info: list) { | |||||
| try { | |||||
| String code = openService.getTtOpenComponentService().retrieveCode(info.getAuthorizerAppid()); | |||||
| TtOpenAuthorizationInfo queryAuthResult = openService.getTtOpenComponentService().getQueryAuth(code); | |||||
| logger.info("getQueryAuth", queryAuthResult); | |||||
| // save auth info | |||||
| authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo(){ | |||||
| { | |||||
| setAuthorizerAppid(queryAuthResult.getAuthorizerAppid()); | |||||
| setRefreshToken(queryAuthResult.getAuthorizerRefreshToken()); | |||||
| setAccessToken(queryAuthResult.getAuthorizerAccessToken()); | |||||
| setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + queryAuthResult.getExpiresIn()*1000))); | |||||
| setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode()); | |||||
| setAuthTime(new Date()); | |||||
| } | |||||
| }); | |||||
| } catch (WxErrorException e) { | |||||
| logger.error(info.getAuthorizerAppid()+"找回授权码", e); | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| return new ResultData(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,388 @@ | |||||
| package com.iformall.controller; | |||||
| import com.google.gson.Gson; | |||||
| import com.google.gson.GsonBuilder; | |||||
| import com.iformall.common.Result; | |||||
| import com.iformall.common.ResultData; | |||||
| import com.iformall.controller.base.BaseController; | |||||
| import com.iformall.domain.po.WxAuthorizerInfo; | |||||
| import com.iformall.domain.po.WxComponentVerifyTicket; | |||||
| import com.iformall.enums.EnumWxAuthorizationStatus; | |||||
| import com.iformall.service.WxAuthorizerInfoService; | |||||
| import com.iformall.service.WxComponentVerifyTicketService; | |||||
| import com.iformall.service.toutiao.FmTtOpenService; | |||||
| import com.iformall.service.toutiao.api.bean.TtOpenTicket; | |||||
| import com.iformall.utils.DateUtils; | |||||
| import io.swagger.annotations.Api; | |||||
| import io.swagger.annotations.ApiOperation; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
| import me.chanjar.weixin.open.bean.WxOpenCreateResult; | |||||
| import me.chanjar.weixin.open.bean.WxOpenGetResult; | |||||
| import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage; | |||||
| import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult; | |||||
| import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerListResult; | |||||
| import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.stereotype.Controller; | |||||
| import org.springframework.web.bind.annotation.*; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import javax.servlet.http.HttpServletResponse; | |||||
| import java.io.ByteArrayOutputStream; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.io.PrintWriter; | |||||
| import java.nio.charset.Charset; | |||||
| import java.util.Date; | |||||
| import java.util.Map; | |||||
| @Controller | |||||
| @RequestMapping("/ttOpen") | |||||
| @Api(description = "头条第三方开放平台回调相关接口") | |||||
| public class TtCalllbackController extends BaseController { | |||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||||
| @Autowired | |||||
| protected FmTtOpenService openService; | |||||
| @Autowired | |||||
| private WxComponentVerifyTicketService componentVerifyTicketService; | |||||
| @Autowired | |||||
| private WxAuthorizerInfoService authorizerInfoService; | |||||
| @PostMapping(value = "/notify") | |||||
| @ApiOperation("接收ticket回调") | |||||
| @ResponseBody | |||||
| public String receiveTicket(HttpServletRequest request) throws IOException { | |||||
| logger.info("[" + getIpAddr() + "]接收微信回调 /ttOpen/notify"); | |||||
| String nonce = request.getParameter("Nonce"); | |||||
| String timestamp = request.getParameter("TimeStamp"); | |||||
| // String signature = request.getParameter("signature"); | |||||
| String encrypt = request.getParameter("Encrypt"); | |||||
| String msgSignature = request.getParameter("MsgSignature"); | |||||
| if (!openService.getTtOpenComponentService().checkSignature(timestamp, nonce, encrypt, msgSignature)) { | |||||
| throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); | |||||
| } | |||||
| // aes加密的消息, 解密 | |||||
| TtOpenTicket openTicket = openService.getTtOpenComponentService().decrypt(encrypt); | |||||
| // 处理 | |||||
| handleTicket(openTicket); | |||||
| return "success"; | |||||
| } | |||||
| /** | |||||
| * 处理ticket消息 | |||||
| * @param | |||||
| * @return | |||||
| */ | |||||
| private void handleTicket(TtOpenTicket ticket) { | |||||
| try { | |||||
| String out = openService.getTtOpenComponentService().route(ticket); | |||||
| this.logger.info("组装回复信息:{}", out); | |||||
| } catch (WxErrorException e) { | |||||
| this.logger.error("notify", e); | |||||
| } | |||||
| if (StringUtils.equalsIgnoreCase(ticket.getMsgType(), "Ticket")) { | |||||
| // 保存component_verify_ticket | |||||
| componentVerifyTicketService.saveOrUpdate(new WxComponentVerifyTicket() { | |||||
| { | |||||
| setComponentVerifyTicket(ticket.getTicket()); | |||||
| setCreateTime(DateUtils.stringToDate(ticket.getCreateTime(),"yyyy-MM-dd HH:mm:ss")); | |||||
| setComponentAppid(ticket.getComponentAppId()); | |||||
| setDeadline(new Date(Long.valueOf(System.currentTimeMillis() + (60 * 60 * 1000)))); | |||||
| } | |||||
| }); | |||||
| } | |||||
| } | |||||
| // /** | |||||
| // * 处理小程序审核消息 | |||||
| // * @param inMessage | |||||
| // * @return | |||||
| // */ | |||||
| // private boolean handleWeappAuditedMessage(WxOpenXmlMessage inMessage) { | |||||
| // // 审核通过 | |||||
| // try { | |||||
| // WxOpenQueryAuthResult queryAuthResult = openService.getWxOpenComponentService().getQueryAuth(inMessage.getAuthorizationCode()); | |||||
| // logger.info("getQueryAuth", queryAuthResult); | |||||
| // | |||||
| // WxOpenAuthorizerInfoResult openAuthorizerInfoResult = openService.getWxOpenComponentService().getAuthorizerInfo( | |||||
| // queryAuthResult.getAuthorizationInfo().getAuthorizerAppid()); | |||||
| // logger.info(openAuthorizerInfoResult.toString()); | |||||
| // // save auth info | |||||
| // authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() { | |||||
| // { | |||||
| // setAuthorizerAppid(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAppid()); | |||||
| // setAlias(openAuthorizerInfoResult.getAuthorizerInfo().getAlias()); | |||||
| // setHeadImg(openAuthorizerInfoResult.getAuthorizerInfo().getHeadImg()); | |||||
| // setQrcodeUrl(openAuthorizerInfoResult.getAuthorizerInfo().getQrcodeUrl()); | |||||
| // setRefreshToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerRefreshToken()); | |||||
| // setAccessToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAccessToken()); | |||||
| // setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + openAuthorizerInfoResult.getAuthorizationInfo().getExpiresIn()*1000))); | |||||
| // setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode()); | |||||
| // setAuthTime(new Date()); | |||||
| // } | |||||
| // }); | |||||
| // return true; | |||||
| // } catch (WxErrorException e) { | |||||
| // logger.error(e.getMessage()); | |||||
| // } | |||||
| // return false; | |||||
| // } | |||||
| @RequestMapping(value = "/{appId}/callback") | |||||
| @ApiOperation("消息回调") | |||||
| public void callback(@PathVariable("appId") String appId, HttpServletRequest request, HttpServletResponse response) throws IOException { | |||||
| logger.info("[" + getIpAddr() + "]接收微信APP回调 /{appId}/callback"); | |||||
| String timestamp = request.getParameter("timestamp"); | |||||
| String nonce = request.getParameter("nonce"); | |||||
| String signature = request.getParameter("signature"); | |||||
| String encType = request.getParameter("encrypt_type"); | |||||
| String openid = request.getParameter("openid"); | |||||
| String msgSignature = request.getParameter("msg_signature"); | |||||
| // 第三方平台一定接收的是加密的消息 | |||||
| if (!StringUtils.equalsIgnoreCase("aes", encType) | |||||
| || !openService.getTtOpenComponentService().checkSignature(timestamp, nonce,encType, signature)) { | |||||
| throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); | |||||
| } | |||||
| // String resultxml = getRequestXML(request); | |||||
| // logger.info("接收微信APP回调:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}]," | |||||
| // + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", | |||||
| // appId, openid, signature, encType, msgSignature, timestamp, nonce, resultxml); | |||||
| // String out = ""; | |||||
| // // aes加密的消息, 解密 | |||||
| // WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(resultxml, | |||||
| // openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature); | |||||
| // logger.info("接收微信APP回调,消息解密后内容为:\n{}", inMessage.toString()); | |||||
| // // 实际 消息 处理 | |||||
| // //handleCallbackMessage(appId, response, inMessage); | |||||
| // WxMpXmlOutMessage outMessage = openService.getWxOpenMessageRouter().route(inMessage, appId); | |||||
| // if(outMessage == null) { | |||||
| // PrintWriter printWriter = response.getWriter(); | |||||
| // wechatWriteSuccess(printWriter); | |||||
| // } | |||||
| PrintWriter printWriter = response.getWriter(); | |||||
| wechatWriteSuccess(printWriter); | |||||
| } | |||||
| private void wechatWriteSuccess(PrintWriter printWriter) { | |||||
| printWriter.print("success"); | |||||
| printWriter.flush(); | |||||
| printWriter.close(); | |||||
| } | |||||
| // /** | |||||
| // * 测试账号特殊处理 | |||||
| // * @param appId | |||||
| // * @param response | |||||
| // * @param inMessage | |||||
| // * @throws IOException | |||||
| // */ | |||||
| // private void handleWechatTest(@PathVariable("appId") String appId, HttpServletResponse response, WxMpXmlMessage inMessage) throws IOException { | |||||
| // String out; | |||||
| // try { | |||||
| // if (StringUtils.equals(inMessage.getMsgType(), "text")) { | |||||
| // if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) { | |||||
| // out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml( | |||||
| // WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback") | |||||
| // .fromUser(inMessage.getToUser()) | |||||
| // .toUser(inMessage.getFromUser()) | |||||
| // .build(), | |||||
| // openService.getWxOpenConfigStorage() | |||||
| // ); | |||||
| // } else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) { | |||||
| // String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api"; | |||||
| // WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build(); | |||||
| // openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage); | |||||
| // } | |||||
| // } else if (StringUtils.equals(inMessage.getMsgType(), "event")) { | |||||
| // WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build(); | |||||
| // openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage); | |||||
| // } | |||||
| // } catch (WxErrorException e) { | |||||
| // logger.error("callback", e); | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // private String getRequestXML(HttpServletRequest request) throws IOException { | |||||
| // InputStream inStream = request.getInputStream(); | |||||
| // ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); | |||||
| // byte[] buffer = new byte[1024]; | |||||
| // int len = 0; | |||||
| // while ((len = inStream.read(buffer)) != -1) { | |||||
| // outSteam.write(buffer, 0, len); | |||||
| // } | |||||
| // String resultxml = new String(outSteam.toByteArray(), Charset.forName("UTF-8")); | |||||
| // logger.info(resultxml); | |||||
| // | |||||
| // outSteam.close(); | |||||
| // inStream.close(); | |||||
| // return resultxml; | |||||
| // } | |||||
| // | |||||
| // private void getThreadCount( int i) { | |||||
| // ThreadGroup group = Thread.currentThread().getThreadGroup(); | |||||
| // ThreadGroup topGroup = group; | |||||
| // // 遍历线程组树,获取根线程组 | |||||
| // while (group != null) { | |||||
| // topGroup = group; | |||||
| // group = group.getParent(); | |||||
| // } | |||||
| // // 激活的线程数加倍 | |||||
| // int activeCount = topGroup.activeCount(); | |||||
| // // copy into a list that is the exact size | |||||
| // logger.info("Thread list size == " + i + " : " + activeCount); | |||||
| // | |||||
| // } | |||||
| // | |||||
| // | |||||
| // @GetMapping(value = "/createOpenPlatform") | |||||
| // @ApiOperation("创建微信开放平台") | |||||
| // public ResultData createOpenPlatform(@RequestParam(value = "appId") String appId) { | |||||
| // Gson gson = new GsonBuilder().create(); | |||||
| // try { | |||||
| // WxOpenCreateResult ret = openService.getWxOpenComponentService().createOpenAccount(appId); | |||||
| // logger.info(ret.toString()); | |||||
| // if (ret.isSuccess()) { | |||||
| // WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId); | |||||
| // if (authorizerInfo != null) { | |||||
| // authorizerInfo.setId(authorizerInfo.getId()); | |||||
| // authorizerInfo.setOpenAppid(ret.getOpenAppid()); | |||||
| // authorizerInfoService.updateOpenAppid(authorizerInfo); | |||||
| // } | |||||
| // } | |||||
| // return new ResultData(ret.toString()); | |||||
| // } catch (WxErrorException e) { | |||||
| // logger.error(e.getMessage()); | |||||
| // return new ResultData(Result.ERROR, e.getMessage()); | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // @GetMapping(value = "/bindOpenPlatform") | |||||
| // @ApiOperation("绑定微信开放平台") | |||||
| // public ResultData bindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) { | |||||
| // Gson gson = new GsonBuilder().create(); | |||||
| // try { | |||||
| // Boolean ret = openService.getWxOpenComponentService().bindOpenAccount(appId, openAppId); | |||||
| // if (ret) { | |||||
| // WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId); | |||||
| // if (authorizerInfo != null) { | |||||
| // authorizerInfo.setId(authorizerInfo.getId()); | |||||
| // authorizerInfo.setOpenAppid(openAppId); | |||||
| // authorizerInfoService.updateOpenAppid(authorizerInfo); | |||||
| // } | |||||
| // return new ResultData(); | |||||
| // } | |||||
| // return new ResultData(Result.ERROR, ret.toString()); | |||||
| // } catch (WxErrorException e) { | |||||
| // logger.error(e.getMessage()); | |||||
| // return new ResultData(Result.ERROR, e.getMessage()); | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // @GetMapping(value = "/unbindOpenPlatform") | |||||
| // @ApiOperation("解绑微信开放平台") | |||||
| // public ResultData unbindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) { | |||||
| // Gson gson = new GsonBuilder().create(); | |||||
| // try { | |||||
| // Boolean ret = openService.getWxOpenComponentService().unbindOpenAccount(appId, openAppId); | |||||
| // if (ret) { | |||||
| // WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo(); | |||||
| // authorizerInfo.setAuthorizerAppid(appId); | |||||
| // authorizerInfoService.removeOpenAppid(authorizerInfo); | |||||
| // return new ResultData(); | |||||
| // } | |||||
| // return new ResultData(Result.ERROR, ret.toString()); | |||||
| // } catch (WxErrorException e) { | |||||
| // logger.error(e.getMessage()); | |||||
| // return new ResultData(Result.ERROR, e.getMessage()); | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // @GetMapping(value = "/getOpenPlatform") | |||||
| // @ApiOperation("获取微信开放平台appId") | |||||
| // public ResultData getOpenPlatform(@RequestParam(value = "appId") String appId) { | |||||
| // Gson gson = new GsonBuilder().create(); | |||||
| // try { | |||||
| // WxOpenGetResult ret = openService.getWxOpenComponentService().getOpenAccount(appId); | |||||
| // logger.info(ret.toString()); | |||||
| // if (ret.isSuccess()) { | |||||
| // WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId); | |||||
| // if (authorizerInfo != null) { | |||||
| // if(StringUtils.isBlank(authorizerInfo.getOpenAppid()) && StringUtils.isNotBlank(ret.getOpenAppid())) { | |||||
| // authorizerInfo.setId(authorizerInfo.getId()); | |||||
| // authorizerInfo.setOpenAppid(ret.getOpenAppid()); | |||||
| // authorizerInfoService.updateOpenAppid(authorizerInfo); | |||||
| // } | |||||
| // } | |||||
| // return new ResultData(ret.getOpenAppid()); | |||||
| // } | |||||
| // return new ResultData(Result.ERROR, ret.toString()); | |||||
| // } catch (WxErrorException e) { | |||||
| // logger.error(e.getMessage()); | |||||
| // return new ResultData(Result.ERROR, e.getMessage()); | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // @GetMapping(value = "/clearQuota") | |||||
| // @ApiOperation("代公众号清零quota,每个公众号每个月有10次清零机会,包括在微信公众平台上的清零以及调用API进行清零") | |||||
| // public ResultData clearQuota(@RequestParam(value = "appId") String appId) { | |||||
| // try { | |||||
| // openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).clearQuota(appId); | |||||
| // return new ResultData(); | |||||
| // } catch (WxErrorException e) { | |||||
| // logger.error(e.getMessage()); | |||||
| // return new ResultData(Result.ERROR, e.getMessage()); | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // @GetMapping(value = "/getAuthorizerList") | |||||
| // @ApiOperation("拉取当前所有已授权的帐号基本信息") | |||||
| // public ResultData getAuthorizerList() { | |||||
| // try { | |||||
| // WxOpenAuthorizerListResult authorizerListResult = openService.getWxOpenComponentService().getAuthorizerList(0, 100); | |||||
| // logger.info("getAuthorizerList: " + authorizerListResult); | |||||
| // if(authorizerListResult.getTotalCount() > 0) { | |||||
| // for (Map<String, String> data : authorizerListResult.getList()) { | |||||
| // String appId = data.get("authorizer_appid"); | |||||
| // String refreshToken = data.get("refresh_token"); | |||||
| // Long authTime = Long.valueOf(data.get("auth_time")); | |||||
| // Date authDate = new Date(authTime*1000); | |||||
| // | |||||
| // if(StringUtils.isBlank(refreshToken)) { | |||||
| // continue; | |||||
| // } | |||||
| // | |||||
| // WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo(); | |||||
| // authorizerInfo.setAuthorizerAppid(appId); | |||||
| // authorizerInfo.setRefreshToken(refreshToken); | |||||
| // authorizerInfo.setAuthTime(authDate); | |||||
| // authorizerInfoService.updateAuthAppidInfo(authorizerInfo); | |||||
| // | |||||
| // openService.getWxOpenComponentService().getWxOpenConfigStorage().setAuthorizerRefreshToken(appId, refreshToken); | |||||
| // } | |||||
| // } | |||||
| // return new ResultData(authorizerListResult.toString()); | |||||
| // } catch (WxErrorException e) { | |||||
| // logger.error(e.getMessage()); | |||||
| // return new ResultData(Result.ERROR, e.getMessage()); | |||||
| // } | |||||
| // } | |||||
| } | |||||
| @@ -1,106 +0,0 @@ | |||||
| package com.iformall.controller; | |||||
| import com.iformall.domain.po.WxAuthorizerInfo; | |||||
| import com.iformall.enums.EnumWxAuthorizationStatus; | |||||
| import com.iformall.service.WxAuthorizerInfoService; | |||||
| import com.iformall.service.wechat.FmOpenService; | |||||
| import io.swagger.annotations.Api; | |||||
| import io.swagger.annotations.ApiOperation; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult; | |||||
| import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.stereotype.Controller; | |||||
| import org.springframework.web.bind.annotation.*; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import javax.servlet.http.HttpServletResponse; | |||||
| import java.io.IOException; | |||||
| import java.util.Date; | |||||
| /** | |||||
| * Stormeye Wu | |||||
| */ | |||||
| @Controller | |||||
| @RequestMapping("/wt_auth") | |||||
| @Api(description = "微信第三方开放平台授权相关接口") | |||||
| public class WechatAuthController { | |||||
| private final Logger logger = LoggerFactory.getLogger(getClass()); | |||||
| @Autowired | |||||
| private FmOpenService openService; | |||||
| @Autowired | |||||
| private WxAuthorizerInfoService authorizerInfoService; | |||||
| @ApiOperation(value = "获取授权跳转页", notes = "") | |||||
| @GetMapping("/goto_auth_url_show") | |||||
| @ResponseBody | |||||
| public String gotoPreAuthUrlShow() { | |||||
| return "<html>" + | |||||
| "<body>" + | |||||
| "<a href='/O/wt_auth/goto_auth_url'>go</a>" + | |||||
| "</body>" + | |||||
| "</html>"; | |||||
| } | |||||
| @ApiOperation(value = "授权跳转页", notes = "") | |||||
| @GetMapping("/goto_auth_url") | |||||
| public void gotoPreAuthUrl(HttpServletRequest request, HttpServletResponse response) { | |||||
| String host = request.getHeader("host"); | |||||
| String url = "https://" + host + "/O/wt_auth/jump"; | |||||
| try { | |||||
| url = openService.getWxOpenComponentService().getPreAuthUrl(url); | |||||
| response.sendRedirect(url); | |||||
| } catch (WxErrorException | IOException e) { | |||||
| logger.error("gotoPreAuthUrl", e); | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| @ApiOperation(value = "授权", notes = "") | |||||
| @GetMapping("/jump") | |||||
| @ResponseBody | |||||
| public void jump(@RequestParam("auth_code") String authorizationCode) { | |||||
| try { | |||||
| WxOpenQueryAuthResult queryAuthResult = openService.getWxOpenComponentService().getQueryAuth(authorizationCode); | |||||
| logger.info("getQueryAuth", queryAuthResult); | |||||
| WxOpenAuthorizerInfoResult openAuthorizerInfoResult = openService.getWxOpenComponentService().getAuthorizerInfo( | |||||
| queryAuthResult.getAuthorizationInfo().getAuthorizerAppid()); | |||||
| logger.info(openAuthorizerInfoResult.toString()); | |||||
| // save auth info | |||||
| authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo(){ | |||||
| { | |||||
| setAuthorizerAppid(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAppid()); | |||||
| setAlias(openAuthorizerInfoResult.getAuthorizerInfo().getAlias()); | |||||
| setHeadImg(openAuthorizerInfoResult.getAuthorizerInfo().getHeadImg()); | |||||
| setQrcodeUrl(openAuthorizerInfoResult.getAuthorizerInfo().getQrcodeUrl()); | |||||
| setRefreshToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerRefreshToken()); | |||||
| setAccessToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAccessToken()); | |||||
| setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + openAuthorizerInfoResult.getAuthorizationInfo().getExpiresIn()*1000))); | |||||
| setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode()); | |||||
| setAuthTime(new Date()); | |||||
| } | |||||
| }); | |||||
| } catch (WxErrorException e) { | |||||
| logger.error("gotoPreAuthUrl", e); | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| @ApiOperation(value = "手机跳转链接url", notes = "") | |||||
| @GetMapping("/getMobilePreAuthUrl") | |||||
| @ResponseBody | |||||
| public String getMobilePreAuthUrl(HttpServletRequest request, HttpServletResponse response) { | |||||
| String host = request.getHeader("host"); | |||||
| String url = "https://" + host + "/O/wt_auth/jump"; | |||||
| try { | |||||
| url = openService.getWxOpenComponentService().getMobilePreAuthUrl(url); | |||||
| return url; | |||||
| } catch (WxErrorException e) { | |||||
| logger.error("getMobilePreAuthUrl", e); | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,415 +0,0 @@ | |||||
| package com.iformall.controller; | |||||
| import com.alibaba.fastjson.JSON; | |||||
| import com.alibaba.fastjson.JSONArray; | |||||
| import com.alibaba.fastjson.JSONObject; | |||||
| import com.google.gson.Gson; | |||||
| import com.google.gson.GsonBuilder; | |||||
| import com.iformall.common.Result; | |||||
| import com.iformall.common.ResultData; | |||||
| import com.iformall.controller.base.BaseController; | |||||
| import com.iformall.domain.po.*; | |||||
| import com.iformall.enums.*; | |||||
| import com.iformall.service.*; | |||||
| import com.iformall.service.wechat.FmOpenService; | |||||
| import io.swagger.annotations.Api; | |||||
| import io.swagger.annotations.ApiOperation; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
| import me.chanjar.weixin.open.bean.WxOpenCreateResult; | |||||
| import me.chanjar.weixin.open.bean.WxOpenGetResult; | |||||
| import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage; | |||||
| import me.chanjar.weixin.open.bean.result.*; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.stereotype.Controller; | |||||
| import org.springframework.web.bind.annotation.*; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import javax.servlet.http.HttpServletResponse; | |||||
| import java.io.ByteArrayOutputStream; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.io.PrintWriter; | |||||
| import java.nio.charset.Charset; | |||||
| import java.util.Date; | |||||
| import java.util.Map; | |||||
| @Controller | |||||
| @RequestMapping("/wxOpen") | |||||
| @Api(description = "微信第三方开放平台回调相关接口") | |||||
| public class WechatCalllbackController extends BaseController { | |||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||||
| @Autowired | |||||
| protected FmOpenService openService; | |||||
| @Autowired | |||||
| private WxComponentVerifyTicketService componentVerifyTicketService; | |||||
| @Autowired | |||||
| private WxAuthorizerInfoService authorizerInfoService; | |||||
| @PostMapping(value = "/notify") | |||||
| @ApiOperation("接收ticket回调") | |||||
| @ResponseBody | |||||
| public String receiveTicket(HttpServletRequest request) throws IOException { | |||||
| logger.info("[" + getIpAddr() + "]接收微信回调 /wxOpen/notify"); | |||||
| String timestamp = request.getParameter("timestamp"); | |||||
| String nonce = request.getParameter("nonce"); | |||||
| String signature = request.getParameter("signature"); | |||||
| String encType = request.getParameter("encrypt_type"); | |||||
| String msgSignature = request.getParameter("msg_signature"); | |||||
| if (!StringUtils.equalsIgnoreCase("aes", encType) | |||||
| || !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) { | |||||
| throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); | |||||
| } | |||||
| String resultxml = getRequestXML(request); | |||||
| logger.info("\n接收微信回调:[signature=[{}], encType=[{}], msgSignature=[{}]," | |||||
| + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", | |||||
| signature, encType, msgSignature, timestamp, nonce, resultxml); | |||||
| // aes加密的消息, 解密 | |||||
| WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(resultxml, | |||||
| openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature); | |||||
| this.logger.info("接收微信回调,消息解密后内容为:{} ", inMessage.toString()); | |||||
| // 处理 | |||||
| handleTicket(inMessage); | |||||
| return "success"; | |||||
| } | |||||
| /** | |||||
| * 处理ticket消息 | |||||
| * @param inMessage | |||||
| * @return | |||||
| */ | |||||
| private void handleTicket(WxOpenXmlMessage inMessage) { | |||||
| try { | |||||
| String out = openService.getWxOpenComponentService().route(inMessage); | |||||
| this.logger.info("组装回复信息:{}", out); | |||||
| } catch (WxErrorException e) { | |||||
| this.logger.error("notify", e); | |||||
| } | |||||
| if (inMessage.getInfoType().equalsIgnoreCase(EnumWxAuthorizationInfoType.COMPONENT_VERIFY_TICKET.getMessage())) { | |||||
| // 保存component_verify_ticket | |||||
| componentVerifyTicketService.saveOrUpdate(new WxComponentVerifyTicket() { | |||||
| { | |||||
| setComponentVerifyTicket(inMessage.getComponentVerifyTicket()); | |||||
| setCreateTime(new Date(Long.valueOf(inMessage.getCreateTime() * 1000))); | |||||
| setComponentAppid(inMessage.getAppId()); | |||||
| setDeadline(new Date(Long.valueOf(System.currentTimeMillis() + (60 * 60 * 1000)))); | |||||
| } | |||||
| }); | |||||
| } else if (inMessage.getInfoType().equalsIgnoreCase(EnumWxAuthorizationInfoType.AUTHORIZED.getMessage()) || | |||||
| inMessage.getInfoType().equalsIgnoreCase(EnumWxAuthorizationInfoType.UPDATEAUTHORIZED.getMessage())) { | |||||
| // 审核通过 | |||||
| handleWeappAuditedMessage(inMessage); | |||||
| } else if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.UNAUTHORIZED.getMessage())) { | |||||
| // 授权取消 | |||||
| authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() { | |||||
| { | |||||
| setAuthorizerAppid(inMessage.getAuthorizerAppid()); | |||||
| setAuthorizationStatus(EnumWxAuthorizationStatus.UNAUTHORIZED.getCode()); | |||||
| setAuthTime(new Date()); | |||||
| } | |||||
| }); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 处理小程序审核消息 | |||||
| * @param inMessage | |||||
| * @return | |||||
| */ | |||||
| private boolean handleWeappAuditedMessage(WxOpenXmlMessage inMessage) { | |||||
| // 审核通过 | |||||
| try { | |||||
| WxOpenQueryAuthResult queryAuthResult = openService.getWxOpenComponentService().getQueryAuth(inMessage.getAuthorizationCode()); | |||||
| logger.info("getQueryAuth", queryAuthResult); | |||||
| WxOpenAuthorizerInfoResult openAuthorizerInfoResult = openService.getWxOpenComponentService().getAuthorizerInfo( | |||||
| queryAuthResult.getAuthorizationInfo().getAuthorizerAppid()); | |||||
| logger.info(openAuthorizerInfoResult.toString()); | |||||
| // save auth info | |||||
| authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() { | |||||
| { | |||||
| setAuthorizerAppid(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAppid()); | |||||
| setAlias(openAuthorizerInfoResult.getAuthorizerInfo().getAlias()); | |||||
| setHeadImg(openAuthorizerInfoResult.getAuthorizerInfo().getHeadImg()); | |||||
| setQrcodeUrl(openAuthorizerInfoResult.getAuthorizerInfo().getQrcodeUrl()); | |||||
| setRefreshToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerRefreshToken()); | |||||
| setAccessToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAccessToken()); | |||||
| setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + openAuthorizerInfoResult.getAuthorizationInfo().getExpiresIn()*1000))); | |||||
| setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode()); | |||||
| setAuthTime(new Date()); | |||||
| } | |||||
| }); | |||||
| return true; | |||||
| } catch (WxErrorException e) { | |||||
| logger.error(e.getMessage()); | |||||
| } | |||||
| return false; | |||||
| } | |||||
| @RequestMapping(value = "/{appId}/callback") | |||||
| @ApiOperation("消息回调") | |||||
| public void callback(@PathVariable("appId") String appId, HttpServletRequest request, HttpServletResponse response) throws IOException { | |||||
| logger.info("[" + getIpAddr() + "]接收微信APP回调 /{appId}/callback"); | |||||
| String timestamp = request.getParameter("timestamp"); | |||||
| String nonce = request.getParameter("nonce"); | |||||
| String signature = request.getParameter("signature"); | |||||
| String encType = request.getParameter("encrypt_type"); | |||||
| String openid = request.getParameter("openid"); | |||||
| String msgSignature = request.getParameter("msg_signature"); | |||||
| // 第三方平台一定接收的是加密的消息 | |||||
| if (!StringUtils.equalsIgnoreCase("aes", encType) | |||||
| || !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) { | |||||
| throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); | |||||
| } | |||||
| String resultxml = getRequestXML(request); | |||||
| logger.info("接收微信APP回调:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}]," | |||||
| + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", | |||||
| appId, openid, signature, encType, msgSignature, timestamp, nonce, resultxml); | |||||
| String out = ""; | |||||
| // aes加密的消息, 解密 | |||||
| WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(resultxml, | |||||
| openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature); | |||||
| logger.info("接收微信APP回调,消息解密后内容为:\n{}", inMessage.toString()); | |||||
| // 全网发布测试用例 | |||||
| if (StringUtils.equalsAnyIgnoreCase(appId, "wx570bc396a51b8ff8", "wxd101a85aa106f53e")) { | |||||
| handleWechatTest(appId, response, inMessage); | |||||
| PrintWriter printWriter = response.getWriter(); | |||||
| wechatWriteSuccess(printWriter); | |||||
| return; | |||||
| } | |||||
| // 实际 消息 处理 | |||||
| //handleCallbackMessage(appId, response, inMessage); | |||||
| WxMpXmlOutMessage outMessage = openService.getWxOpenMessageRouter().route(inMessage, appId); | |||||
| if(outMessage == null) { | |||||
| PrintWriter printWriter = response.getWriter(); | |||||
| wechatWriteSuccess(printWriter); | |||||
| } | |||||
| PrintWriter printWriter = response.getWriter(); | |||||
| wechatWriteSuccess(printWriter); | |||||
| } | |||||
| private void wechatWriteSuccess(PrintWriter printWriter) { | |||||
| printWriter.print("success"); | |||||
| printWriter.flush(); | |||||
| printWriter.close(); | |||||
| } | |||||
| /** | |||||
| * 测试账号特殊处理 | |||||
| * @param appId | |||||
| * @param response | |||||
| * @param inMessage | |||||
| * @throws IOException | |||||
| */ | |||||
| private void handleWechatTest(@PathVariable("appId") String appId, HttpServletResponse response, WxMpXmlMessage inMessage) throws IOException { | |||||
| String out; | |||||
| try { | |||||
| if (StringUtils.equals(inMessage.getMsgType(), "text")) { | |||||
| if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) { | |||||
| out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml( | |||||
| WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback") | |||||
| .fromUser(inMessage.getToUser()) | |||||
| .toUser(inMessage.getFromUser()) | |||||
| .build(), | |||||
| openService.getWxOpenConfigStorage() | |||||
| ); | |||||
| } else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) { | |||||
| String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api"; | |||||
| WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build(); | |||||
| openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage); | |||||
| } | |||||
| } else if (StringUtils.equals(inMessage.getMsgType(), "event")) { | |||||
| WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build(); | |||||
| openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage); | |||||
| } | |||||
| } catch (WxErrorException e) { | |||||
| logger.error("callback", e); | |||||
| } | |||||
| } | |||||
| private String getRequestXML(HttpServletRequest request) throws IOException { | |||||
| InputStream inStream = request.getInputStream(); | |||||
| ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); | |||||
| byte[] buffer = new byte[1024]; | |||||
| int len = 0; | |||||
| while ((len = inStream.read(buffer)) != -1) { | |||||
| outSteam.write(buffer, 0, len); | |||||
| } | |||||
| String resultxml = new String(outSteam.toByteArray(), Charset.forName("UTF-8")); | |||||
| logger.info(resultxml); | |||||
| outSteam.close(); | |||||
| inStream.close(); | |||||
| return resultxml; | |||||
| } | |||||
| private void getThreadCount( int i) { | |||||
| ThreadGroup group = Thread.currentThread().getThreadGroup(); | |||||
| ThreadGroup topGroup = group; | |||||
| // 遍历线程组树,获取根线程组 | |||||
| while (group != null) { | |||||
| topGroup = group; | |||||
| group = group.getParent(); | |||||
| } | |||||
| // 激活的线程数加倍 | |||||
| int activeCount = topGroup.activeCount(); | |||||
| // copy into a list that is the exact size | |||||
| logger.info("Thread list size == " + i + " : " + activeCount); | |||||
| } | |||||
| @GetMapping(value = "/createOpenPlatform") | |||||
| @ApiOperation("创建微信开放平台") | |||||
| public ResultData createOpenPlatform(@RequestParam(value = "appId") String appId) { | |||||
| Gson gson = new GsonBuilder().create(); | |||||
| try { | |||||
| WxOpenCreateResult ret = openService.getWxOpenComponentService().createOpenAccount(appId); | |||||
| logger.info(ret.toString()); | |||||
| if (ret.isSuccess()) { | |||||
| WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId); | |||||
| if (authorizerInfo != null) { | |||||
| authorizerInfo.setId(authorizerInfo.getId()); | |||||
| authorizerInfo.setOpenAppid(ret.getOpenAppid()); | |||||
| authorizerInfoService.updateOpenAppid(authorizerInfo); | |||||
| } | |||||
| } | |||||
| return new ResultData(ret.toString()); | |||||
| } catch (WxErrorException e) { | |||||
| logger.error(e.getMessage()); | |||||
| return new ResultData(Result.ERROR, e.getMessage()); | |||||
| } | |||||
| } | |||||
| @GetMapping(value = "/bindOpenPlatform") | |||||
| @ApiOperation("绑定微信开放平台") | |||||
| public ResultData bindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) { | |||||
| Gson gson = new GsonBuilder().create(); | |||||
| try { | |||||
| Boolean ret = openService.getWxOpenComponentService().bindOpenAccount(appId, openAppId); | |||||
| if (ret) { | |||||
| WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId); | |||||
| if (authorizerInfo != null) { | |||||
| authorizerInfo.setId(authorizerInfo.getId()); | |||||
| authorizerInfo.setOpenAppid(openAppId); | |||||
| authorizerInfoService.updateOpenAppid(authorizerInfo); | |||||
| } | |||||
| return new ResultData(); | |||||
| } | |||||
| return new ResultData(Result.ERROR, ret.toString()); | |||||
| } catch (WxErrorException e) { | |||||
| logger.error(e.getMessage()); | |||||
| return new ResultData(Result.ERROR, e.getMessage()); | |||||
| } | |||||
| } | |||||
| @GetMapping(value = "/unbindOpenPlatform") | |||||
| @ApiOperation("解绑微信开放平台") | |||||
| public ResultData unbindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) { | |||||
| Gson gson = new GsonBuilder().create(); | |||||
| try { | |||||
| Boolean ret = openService.getWxOpenComponentService().unbindOpenAccount(appId, openAppId); | |||||
| if (ret) { | |||||
| WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo(); | |||||
| authorizerInfo.setAuthorizerAppid(appId); | |||||
| authorizerInfoService.removeOpenAppid(authorizerInfo); | |||||
| return new ResultData(); | |||||
| } | |||||
| return new ResultData(Result.ERROR, ret.toString()); | |||||
| } catch (WxErrorException e) { | |||||
| logger.error(e.getMessage()); | |||||
| return new ResultData(Result.ERROR, e.getMessage()); | |||||
| } | |||||
| } | |||||
| @GetMapping(value = "/getOpenPlatform") | |||||
| @ApiOperation("获取微信开放平台appId") | |||||
| public ResultData getOpenPlatform(@RequestParam(value = "appId") String appId) { | |||||
| Gson gson = new GsonBuilder().create(); | |||||
| try { | |||||
| WxOpenGetResult ret = openService.getWxOpenComponentService().getOpenAccount(appId); | |||||
| logger.info(ret.toString()); | |||||
| if (ret.isSuccess()) { | |||||
| WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId); | |||||
| if (authorizerInfo != null) { | |||||
| if(StringUtils.isBlank(authorizerInfo.getOpenAppid()) && StringUtils.isNotBlank(ret.getOpenAppid())) { | |||||
| authorizerInfo.setId(authorizerInfo.getId()); | |||||
| authorizerInfo.setOpenAppid(ret.getOpenAppid()); | |||||
| authorizerInfoService.updateOpenAppid(authorizerInfo); | |||||
| } | |||||
| } | |||||
| return new ResultData(ret.getOpenAppid()); | |||||
| } | |||||
| return new ResultData(Result.ERROR, ret.toString()); | |||||
| } catch (WxErrorException e) { | |||||
| logger.error(e.getMessage()); | |||||
| return new ResultData(Result.ERROR, e.getMessage()); | |||||
| } | |||||
| } | |||||
| @GetMapping(value = "/clearQuota") | |||||
| @ApiOperation("代公众号清零quota,每个公众号每个月有10次清零机会,包括在微信公众平台上的清零以及调用API进行清零") | |||||
| public ResultData clearQuota(@RequestParam(value = "appId") String appId) { | |||||
| try { | |||||
| openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).clearQuota(appId); | |||||
| return new ResultData(); | |||||
| } catch (WxErrorException e) { | |||||
| logger.error(e.getMessage()); | |||||
| return new ResultData(Result.ERROR, e.getMessage()); | |||||
| } | |||||
| } | |||||
| @GetMapping(value = "/getAuthorizerList") | |||||
| @ApiOperation("拉取当前所有已授权的帐号基本信息") | |||||
| public ResultData getAuthorizerList() { | |||||
| try { | |||||
| WxOpenAuthorizerListResult authorizerListResult = openService.getWxOpenComponentService().getAuthorizerList(0, 100); | |||||
| logger.info("getAuthorizerList: " + authorizerListResult); | |||||
| if(authorizerListResult.getTotalCount() > 0) { | |||||
| for (Map<String, String> data : authorizerListResult.getList()) { | |||||
| String appId = data.get("authorizer_appid"); | |||||
| String refreshToken = data.get("refresh_token"); | |||||
| Long authTime = Long.valueOf(data.get("auth_time")); | |||||
| Date authDate = new Date(authTime*1000); | |||||
| if(StringUtils.isBlank(refreshToken)) { | |||||
| continue; | |||||
| } | |||||
| WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo(); | |||||
| authorizerInfo.setAuthorizerAppid(appId); | |||||
| authorizerInfo.setRefreshToken(refreshToken); | |||||
| authorizerInfo.setAuthTime(authDate); | |||||
| authorizerInfoService.updateAuthAppidInfo(authorizerInfo); | |||||
| openService.getWxOpenComponentService().getWxOpenConfigStorage().setAuthorizerRefreshToken(appId, refreshToken); | |||||
| } | |||||
| } | |||||
| return new ResultData(authorizerListResult.toString()); | |||||
| } catch (WxErrorException e) { | |||||
| logger.error(e.getMessage()); | |||||
| return new ResultData(Result.ERROR, e.getMessage()); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -98,6 +98,7 @@ public class WxWeappInfoController extends BaseController { | |||||
| weappInfo.setName(null); | weappInfo.setName(null); | ||||
| } | } | ||||
| } | } | ||||
| weappInfo.setPlat(EnumAppPlat.TOUTIAO.getCode()); | |||||
| weappInfo.setSortColumns(BaseEntity.SortField.TenantId_ASC,BaseEntity.SortField.Type_ASC,BaseEntity.SortField.Name_ASC); | weappInfo.setSortColumns(BaseEntity.SortField.TenantId_ASC,BaseEntity.SortField.Type_ASC,BaseEntity.SortField.Name_ASC); | ||||
| final PageInfo<WxWeappInfo> page = authorizerInfoService.listVoAsPage(weappInfo, pageNum, pageSize); | final PageInfo<WxWeappInfo> page = authorizerInfoService.listVoAsPage(weappInfo, pageNum, pageSize); | ||||
| return new ResultData(page); | return new ResultData(page); | ||||
| @@ -82,15 +82,11 @@ aws: | |||||
| # min-idle: 10 | # min-idle: 10 | ||||
| wechat: | wechat: | ||||
| web: | |||||
| appId: "wx091907dd0bfd3f6b" | |||||
| secret: "2a2ca10738998b9ef92c1fe8a4d366a6" | |||||
| url: "https://admintest.malls.iformall.com" | |||||
| open: | open: | ||||
| componentAppId: wxdfc8fb4e62d6b52b | |||||
| componentSecret: 98daa62b316dd6feabaad708327ce233 | |||||
| componentToken: formall2018 | |||||
| componentAesKey: htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN | |||||
| componentAppId: tt146862d58b42b88c | |||||
| componentSecret: d681a8873b81276dc93fd499bbf0bcbafb82b31c | |||||
| componentToken: iformallDouyinAuthorizeToken | |||||
| componentAesKey: iformallDouyinEntryKey202204082101028888888 | |||||
| redis: | redis: | ||||
| host: 101.200.130.134 | host: 101.200.130.134 | ||||
| port: 6379 | port: 6379 | ||||
| @@ -77,10 +77,10 @@ aws: | |||||
| wechat: | wechat: | ||||
| open: | 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: | redis: | ||||
| host: r-2zeaglwf13qqmnllj5.redis.rds.aliyuncs.com | host: r-2zeaglwf13qqmnllj5.redis.rds.aliyuncs.com | ||||
| port: 6379 | port: 6379 | ||||