Merge branch 'master' of github.com:foxinmy/weixin4j

This commit is contained in:
jinyu 2017-08-14 23:34:54 +08:00
commit db0f4b3014
4 changed files with 2247 additions and 2254 deletions

View File

@ -57,8 +57,7 @@ public class WeixinComponentProxy {
* token管理 * token管理
*/ */
public WeixinComponentProxy(CacheStorager<Token> cacheStorager) { public WeixinComponentProxy(CacheStorager<Token> cacheStorager) {
this(JSON.parseObject(Weixin4jConfigUtil.getValue("account"), this(JSON.parseObject(Weixin4jConfigUtil.getValue("account"), WeixinMpAccount.class), cacheStorager);
WeixinMpAccount.class), cacheStorager);
} }
/** /**
@ -69,26 +68,20 @@ public class WeixinComponentProxy {
* @param cacheStorager * @param cacheStorager
* token管理 * token管理
*/ */
public WeixinComponentProxy(WeixinMpAccount weixinMpAccount, public WeixinComponentProxy(WeixinMpAccount weixinMpAccount, CacheStorager<Token> cacheStorager) {
CacheStorager<Token> cacheStorager) {
if (weixinMpAccount == null) { if (weixinMpAccount == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException("weixinMpAccount must not be empty");
"weixinMpAccount must not be empty");
} }
if (cacheStorager == null) { if (cacheStorager == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException("cacheStorager must not be empty");
"cacheStorager must not be empty");
} }
this.weixinMpAccount = weixinMpAccount; this.weixinMpAccount = weixinMpAccount;
this.componentMap = new HashMap<String, ComponentApi>(weixinMpAccount this.componentMap = new HashMap<String, ComponentApi>(weixinMpAccount.getComponents().size());
.getComponents().size());
for (WeixinAccount component : weixinMpAccount.getComponents()) { for (WeixinAccount component : weixinMpAccount.getComponents()) {
this.componentMap.put(component.getId(), new ComponentApi( this.componentMap.put(component.getId(),
new TicketManager(component.getId(), component.getSecret(), new ComponentApi(new TicketManager(component.getId(), component.getSecret(), cacheStorager)));
cacheStorager)));
} }
this.componentMap.put(null, componentMap.get(weixinMpAccount this.componentMap.put(null, componentMap.get(weixinMpAccount.getComponents().get(0).getId()));
.getComponents().get(0).getId()));
} }
/** /**
@ -134,8 +127,7 @@ public class WeixinComponentProxy {
* @see com.foxinmy.weixin4j.mp.api.ComponentApi#getPreCodeManager() * @see com.foxinmy.weixin4j.mp.api.ComponentApi#getPreCodeManager()
* @throws WeixinException * @throws WeixinException
*/ */
public String getPreComponentTicket(String componentId) public String getPreComponentTicket(String componentId) throws WeixinException {
throws WeixinException {
ComponentApi component = component(componentId); ComponentApi component = component(componentId);
Token token = component.getTicketManager().getTicket(); Token token = component.getTicketManager().getTicket();
if (token == null || StringUtil.isBlank(token.getAccessToken())) { if (token == null || StringUtil.isBlank(token.getAccessToken())) {
@ -153,16 +145,14 @@ public class WeixinComponentProxy {
* 组件ticket内容 * 组件ticket内容
* @throws WeixinException * @throws WeixinException
*/ */
public void cacheComponentTicket(String componentId, String componentTicket) public void cacheComponentTicket(String componentId, String componentTicket) throws WeixinException {
throws WeixinException { component(componentId).getTicketManager().cachingTicket(componentTicket);
component(componentId).getTicketManager()
.cachingTicket(componentTicket);
} }
/** /**
* 应用组件授权 <font color="red">需先缓存ticket</font> <li> * 应用组件授权 <font color="red">需先缓存ticket</font>
* redirectUri默认填写weixin4j.properties#component.oauth.redirect.uri <li> * <li>redirectUri默认填写weixin4j.properties#component.oauth.redirect.uri
* state默认填写state * <li>state默认填写state
* *
* @param componentId * @param componentId
* 组件ID * 组件ID
@ -170,17 +160,14 @@ public class WeixinComponentProxy {
* @return 请求授权的URL * @return 请求授权的URL
* @throws WeixinException * @throws WeixinException
*/ */
public String getComponentAuthorizationURL(String componentId) public String getComponentAuthorizationURL(String componentId) throws WeixinException {
throws WeixinException { String redirectUri = Weixin4jConfigUtil.getValue("component.oauth.redirect.uri");
String redirectUri = Weixin4jConfigUtil
.getValue("component.oauth.redirect.uri");
return getComponentAuthorizationURL(componentId, redirectUri, "state"); return getComponentAuthorizationURL(componentId, redirectUri, "state");
} }
/** /**
* 应用组件授权 <font * 应用组件授权 <font color="red">需先缓存ticket在授权完成之后需要调用ComponentApi#
* color="red">需先缓存ticket在授权完成之后需要调用ComponentApi#exchangeAuthInfo方法 * exchangeAuthorizerToken方法 ,否则无法缓存token相关导致后续的组件接口调用失败</font>
* ,否则无法缓存token相关导致后续的组件接口调用失败</font>
* *
* @param componentId * @param componentId
* 组件ID * 组件ID
@ -192,17 +179,17 @@ public class WeixinComponentProxy {
* @see com.foxinmy.weixin4j.mp.api.ComponentApi * @see com.foxinmy.weixin4j.mp.api.ComponentApi
* @see com.foxinmy.weixin4j.mp.api.ComponentApi#getTicketManager() * @see com.foxinmy.weixin4j.mp.api.ComponentApi#getTicketManager()
* @see com.foxinmy.weixin4j.mp.api.ComponentApi#getPreCodeManager() * @see com.foxinmy.weixin4j.mp.api.ComponentApi#getPreCodeManager()
* @see com.foxinmy.weixin4j.mp.api.ComponentApi#exchangeAuthInfo(String) * @see com.foxinmy.weixin4j.mp.api.ComponentApi#exchangeAuthorizerToken(String)
* @see <a * @see <a href=
* href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1453779503&token=&lang=zh_CN">应用组件授权</a> * "https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1453779503&token=&lang=zh_CN">
* 应用组件授权</a>
* @return 请求授权的URL * @return 请求授权的URL
* @throws WeixinException * @throws WeixinException
*/ */
public String getComponentAuthorizationURL(String componentId, public String getComponentAuthorizationURL(String componentId, String redirectUri, String state)
String redirectUri, String state) throws WeixinException { throws WeixinException {
try { try {
return String.format(URLConsts.COMPONENT_OAUTH_URL, componentId, return String.format(URLConsts.COMPONENT_OAUTH_URL, componentId, getPreComponentTicket(componentId),
getPreComponentTicket(componentId),
URLEncoder.encode(redirectUri, Consts.UTF_8.name()), state); URLEncoder.encode(redirectUri, Consts.UTF_8.name()), state);
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
; ;
@ -210,5 +197,20 @@ public class WeixinComponentProxy {
return ""; return "";
} }
/**
* 创建WeixinProxy对象
*
* @param componentId
* 组件ID
* @param authAppId
* 已授权的appid
* @see com.foxinmy.weixin4j.mp.WeixinProxy
* @return
*/
public WeixinProxy getWeixinProxy(String componentId, String authAppId) {
return new WeixinProxy(component(componentId).getRefreshTokenManager(authAppId),
component(componentId).getTokenManager());
}
public final static String VERSION = "1.7.7"; public final static String VERSION = "1.7.7";
} }

View File

@ -188,6 +188,23 @@ public class WeixinProxy {
this(weixinAccount, new WeixinTokenCreator(weixinAccount.getId(), weixinAccount.getSecret()), cacheStorager); this(weixinAccount, new WeixinTokenCreator(weixinAccount.getId(), weixinAccount.getSecret()), cacheStorager);
} }
/**
* 第三方组件方式创建微信接口实现(永久刷新令牌机制)
*
* @param perTicketManager
* 第三方组件永久刷新token
* @param componentTokenManager
* 第三方组件凭证token
* @see com.foxinmy.weixin4j.mp.api.ComponentApi
* @see com.foxinmy.weixin4j.mp.api.ComponentApi#getPerCodeManager(String)
* @see com.foxinmy.weixin4j.mp.api.ComponentApi#getTokenManager
*/
public WeixinProxy(PerTicketManager perTicketManager, TokenManager componentTokenManager) {
this(new WeixinAccount(perTicketManager.getThirdId(), perTicketManager.getThirdSecret()),
new WeixinTokenComponentCreator(perTicketManager, componentTokenManager),
perTicketManager.getCacheStorager());
}
/** /**
* 微信接口实现 * 微信接口实现
* *
@ -264,8 +281,7 @@ public class WeixinProxy {
* @return * @return
*/ */
public TokenManager getTicketManager(TicketType ticketType) { public TokenManager getTicketManager(TicketType ticketType) {
return new TokenManager(new WeixinTicketCreator(ticketType, this.tokenManager), return new TokenManager(new WeixinTicketCreator(ticketType, this.tokenManager), this.cacheStorager);
this.cacheStorager);
} }
/** /**
@ -860,7 +876,8 @@ public class WeixinProxy {
* @throws WeixinException * @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.MassApi * @see com.foxinmy.weixin4j.mp.api.MassApi
* @see <a href= * @see <a href=
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">根据标签群发</a> * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">
* 根据标签群发</a>
*/ */
public String[] massToAll(MassTuple tuple) throws WeixinException { public String[] massToAll(MassTuple tuple) throws WeixinException {
return massApi.massToAll(tuple); return massApi.massToAll(tuple);
@ -879,7 +896,8 @@ public class WeixinProxy {
* @see {@link TagApi#listTags()} * @see {@link TagApi#listTags()}
* @see com.foxinmy.weixin4j.mp.api.MassApi * @see com.foxinmy.weixin4j.mp.api.MassApi
* @see <a href= * @see <a href=
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">根据标签群发</a> * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">
* 根据标签群发</a>
*/ */
public String[] massByTagId(MassTuple tuple, int tagId) throws WeixinException { public String[] massByTagId(MassTuple tuple, int tagId) throws WeixinException {
return massApi.massByTagId(tuple, tagId); return massApi.massByTagId(tuple, tagId);
@ -896,7 +914,8 @@ public class WeixinProxy {
* 图文消息被判定为转载时是否继续群发 * 图文消息被判定为转载时是否继续群发
* @return 第一个元素为消息发送任务的ID,第二个元素为消息的数据ID该字段只有在群发图文消息时才会出现 * @return 第一个元素为消息发送任务的ID,第二个元素为消息的数据ID该字段只有在群发图文消息时才会出现
* @see <a href= * @see <a href=
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">根据标签群发</a> * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">
* 根据标签群发</a>
* @see {@link #massByTagId(Tuple,int)} * @see {@link #massByTagId(Tuple,int)}
* @see com.foxinmy.weixin4j.tuple.MpArticle * @see com.foxinmy.weixin4j.tuple.MpArticle
* @see com.foxinmy.weixin4j.mp.api.MassApi * @see com.foxinmy.weixin4j.mp.api.MassApi
@ -917,7 +936,8 @@ public class WeixinProxy {
* @return 第一个元素为消息发送任务的ID,第二个元素为消息的数据ID该字段只有在群发图文消息时才会出现,可以用于在图文分析数据接口中 * @return 第一个元素为消息发送任务的ID,第二个元素为消息的数据ID该字段只有在群发图文消息时才会出现,可以用于在图文分析数据接口中
* @throws WeixinException * @throws WeixinException
* @see <a href= * @see <a href=
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">根据openid群发</a> * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">
* 根据openid群发</a>
* @see {@link UserApi#getUser(String)} * @see {@link UserApi#getUser(String)}
* @see com.foxinmy.weixin4j.mp.api.MassApi * @see com.foxinmy.weixin4j.mp.api.MassApi
*/ */
@ -936,7 +956,8 @@ public class WeixinProxy {
* openId列表 * openId列表
* @return 第一个元素为消息发送任务的ID,第二个元素为消息的数据ID该字段只有在群发图文消息时才会出现,可以用于在图文分析数据接口中. * @return 第一个元素为消息发送任务的ID,第二个元素为消息的数据ID该字段只有在群发图文消息时才会出现,可以用于在图文分析数据接口中.
* @see <a href= * @see <a href=
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">根据openid群发</a> * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">
* 根据openid群发</a>
* @see {@link #massByOpenIds(Tuple,String...)} * @see {@link #massByOpenIds(Tuple,String...)}
* @see com.foxinmy.weixin4j.tuple.MpArticle * @see com.foxinmy.weixin4j.tuple.MpArticle
* @see com.foxinmy.weixin4j.mp.api.MassApi * @see com.foxinmy.weixin4j.mp.api.MassApi
@ -954,7 +975,8 @@ public class WeixinProxy {
* 发送出去的消息ID * 发送出去的消息ID
* @throws WeixinException * @throws WeixinException
* @see <a href= * @see <a href=
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">删除群发</a> * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">
* 删除群发</a>
* @see #deleteMassNews(String, int) * @see #deleteMassNews(String, int)
* @see com.foxinmy.weixin4j.mp.api.MassApi * @see com.foxinmy.weixin4j.mp.api.MassApi
*/ */
@ -974,7 +996,8 @@ public class WeixinProxy {
* 要删除的文章在图文消息中的位置第一篇编号为1该字段不填或填0会删除全部文章 * 要删除的文章在图文消息中的位置第一篇编号为1该字段不填或填0会删除全部文章
* @throws WeixinException * @throws WeixinException
* @see <a href= * @see <a href=
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">删除群发</a> * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">
* 删除群发</a>
* @see {@link #massByTagId(Tuple, int)} * @see {@link #massByTagId(Tuple, int)}
* @see {@link #massByOpenIds(Tuple, String...) * @see {@link #massByOpenIds(Tuple, String...)
* @see com.foxinmy.weixin4j.mp.api.MassApi * @see com.foxinmy.weixin4j.mp.api.MassApi
@ -1591,7 +1614,8 @@ public class WeixinProxy {
* out of limit" }错误返回码。 * out of limit" }错误返回码。
* *
* @see <a href= * @see <a href=
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433744592&token=&lang=zh_CN">接口清零</a> * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433744592&token=&lang=zh_CN">
* 接口清零</a>
* @see com.foxinmy.weixin4j.mp.api.HelperApi * @see com.foxinmy.weixin4j.mp.api.HelperApi
* @return 操作结果 * @return 操作结果
* @throws WeixinException * @throws WeixinException
@ -1729,7 +1753,8 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.mp.api.TagApi * @see com.foxinmy.weixin4j.mp.api.TagApi
* @see com.foxinmy.weixin4j.mp.model.Tag * @see com.foxinmy.weixin4j.mp.model.Tag
* @see <a href= * @see <a href=
* "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">创建标签</a> * "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">
* 创建标签</a>
*/ */
public Tag createTag(String name) throws WeixinException { public Tag createTag(String name) throws WeixinException {
return tagApi.createTag(name); return tagApi.createTag(name);
@ -1743,7 +1768,8 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.mp.api.TagApi * @see com.foxinmy.weixin4j.mp.api.TagApi
* @see com.foxinmy.weixin4j.mp.model.Tag * @see com.foxinmy.weixin4j.mp.model.Tag
* @see <a href= * @see <a href=
* "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">获取标签</a> * "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">
* 获取标签</a>
*/ */
public List<Tag> listTags() throws WeixinException { public List<Tag> listTags() throws WeixinException {
return tagApi.listTags(); return tagApi.listTags();
@ -1759,7 +1785,8 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.mp.api.TagApi * @see com.foxinmy.weixin4j.mp.api.TagApi
* @see com.foxinmy.weixin4j.mp.model.Tag * @see com.foxinmy.weixin4j.mp.model.Tag
* @see <a href= * @see <a href=
* "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">更新标签</a> * "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">
* 更新标签</a>
*/ */
public ApiResult updateTag(Tag tag) throws WeixinException { public ApiResult updateTag(Tag tag) throws WeixinException {
return tagApi.updateTag(tag); return tagApi.updateTag(tag);
@ -1774,7 +1801,8 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.mp.api.TagApi * @see com.foxinmy.weixin4j.mp.api.TagApi
* @throws WeixinException * @throws WeixinException
* @see <a href= * @see <a href=
* "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">删除标签</a> * "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">
* 删除标签</a>
*/ */
public ApiResult deleteTag(int tagId) throws WeixinException { public ApiResult deleteTag(int tagId) throws WeixinException {
return tagApi.deleteTag(tagId); return tagApi.deleteTag(tagId);
@ -1791,7 +1819,8 @@ public class WeixinProxy {
* @throws WeixinException * @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.TagApi * @see com.foxinmy.weixin4j.mp.api.TagApi
* @see <a href= * @see <a href=
* "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">批量为用户打标签</a> * "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">
* 批量为用户打标签</a>
*/ */
public ApiResult taggingUsers(int tagId, String... openIds) throws WeixinException { public ApiResult taggingUsers(int tagId, String... openIds) throws WeixinException {
return tagApi.taggingUsers(tagId, openIds); return tagApi.taggingUsers(tagId, openIds);
@ -1808,7 +1837,8 @@ public class WeixinProxy {
* @throws WeixinException * @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.TagApi * @see com.foxinmy.weixin4j.mp.api.TagApi
* @see <a href= * @see <a href=
* "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">批量为用户取消标签</a> * "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">
* 批量为用户取消标签</a>
*/ */
public ApiResult untaggingUsers(int tagId, String... openIds) throws WeixinException { public ApiResult untaggingUsers(int tagId, String... openIds) throws WeixinException {
return tagApi.untaggingUsers(tagId, openIds); return tagApi.untaggingUsers(tagId, openIds);
@ -1825,7 +1855,8 @@ public class WeixinProxy {
* @throws WeixinException * @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.TagApi * @see com.foxinmy.weixin4j.mp.api.TagApi
* @see <a href= * @see <a href=
* "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">获取标签下粉丝列表</a> * "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">
* 获取标签下粉丝列表</a>
*/ */
public Following getTagFollowingOpenIds(int tagId, String nextOpenId) throws WeixinException { public Following getTagFollowingOpenIds(int tagId, String nextOpenId) throws WeixinException {
return tagApi.getTagFollowingOpenIds(tagId, nextOpenId); return tagApi.getTagFollowingOpenIds(tagId, nextOpenId);
@ -1842,7 +1873,8 @@ public class WeixinProxy {
* @throws WeixinException * @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.TagApi * @see com.foxinmy.weixin4j.mp.api.TagApi
* @see <a href= * @see <a href=
* "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">获取标签下粉丝列表</a> * "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">
* 获取标签下粉丝列表</a>
*/ */
public Following getTagFollowing(int tagId, String nextOpenId) throws WeixinException { public Following getTagFollowing(int tagId, String nextOpenId) throws WeixinException {
return tagApi.getTagFollowing(tagId, nextOpenId); return tagApi.getTagFollowing(tagId, nextOpenId);
@ -1858,7 +1890,8 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.mp.api.TagApi * @see com.foxinmy.weixin4j.mp.api.TagApi
* @see #getTagFollowingOpenIds(int,String) * @see #getTagFollowingOpenIds(int,String)
* @see <a href= * @see <a href=
* "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">获取标签下粉丝列表</a> * "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">
* 获取标签下粉丝列表</a>
*/ */
public List<String> getAllTagFollowingOpenIds(int tagId) throws WeixinException { public List<String> getAllTagFollowingOpenIds(int tagId) throws WeixinException {
return tagApi.getAllTagFollowingOpenIds(tagId); return tagApi.getAllTagFollowingOpenIds(tagId);
@ -1874,7 +1907,8 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.mp.api.TagApi * @see com.foxinmy.weixin4j.mp.api.TagApi
* @see #getTagFollowing(int,String) * @see #getTagFollowing(int,String)
* @see <a href= * @see <a href=
* "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">获取标签下粉丝列表</a> * "http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140837&token=&lang=zh_CN">
* 获取标签下粉丝列表</a>
*/ */
public List<User> getAllTagFollowing(int tagId) throws WeixinException { public List<User> getAllTagFollowing(int tagId) throws WeixinException {
return tagApi.getAllTagFollowing(tagId); return tagApi.getAllTagFollowing(tagId);
@ -1942,7 +1976,8 @@ public class WeixinProxy {
* @return 操作结果 * @return 操作结果
* @see com.foxinmy.weixin4j.mp.api.TagApi * @see com.foxinmy.weixin4j.mp.api.TagApi
* @see <a href= * @see <a href=
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1471422259_pJMWA&token=&lang=zh_CN">黑名单操作</a> * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1471422259_pJMWA&token=&lang=zh_CN">
* 黑名单操作</a>
* @throws WeixinException * @throws WeixinException
*/ */
public ApiResult batchBlacklist(boolean blacklist, String... openIds) throws WeixinException { public ApiResult batchBlacklist(boolean blacklist, String... openIds) throws WeixinException {
@ -1962,7 +1997,8 @@ public class WeixinProxy {
* @param cardCoupon * @param cardCoupon
* 卡券对象 * 卡券对象
* @see <a href= * @see <a href=
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025056&token=&lang=zh_CN">创建卡券</a> * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025056&token=&lang=zh_CN">
* 创建卡券</a>
* @see CardCoupons * @see CardCoupons
* @see MediaApi#uploadImage(java.io.InputStream, String) * @see MediaApi#uploadImage(java.io.InputStream, String)
* @see com.foxinmy.weixin4j.mp.api.CardApi * @see com.foxinmy.weixin4j.mp.api.CardApi
@ -2019,7 +2055,8 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.model.qr.QRParameter * @see com.foxinmy.weixin4j.model.qr.QRParameter
* @see com.foxinmy.weixin4j.mp.api.CardApi * @see com.foxinmy.weixin4j.mp.api.CardApi
* @see <a href= * @see <a href=
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025062&token=&lang=zh_CN">投放卡券</a> * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025062&token=&lang=zh_CN">
* 投放卡券</a>
* @throws WeixinException * @throws WeixinException
*/ */
public QRResult createCardQR(Integer expireSeconds, CardQR... cardQRs) throws WeixinException { public QRResult createCardQR(Integer expireSeconds, CardQR... cardQRs) throws WeixinException {

View File

@ -13,6 +13,7 @@ import com.foxinmy.weixin4j.http.weixin.ApiResult;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.mp.component.WeixinComponentPreCodeCreator; import com.foxinmy.weixin4j.mp.component.WeixinComponentPreCodeCreator;
import com.foxinmy.weixin4j.mp.component.WeixinComponentTokenCreator; import com.foxinmy.weixin4j.mp.component.WeixinComponentTokenCreator;
import com.foxinmy.weixin4j.mp.component.WeixinTokenComponentCreator;
import com.foxinmy.weixin4j.mp.model.AuthorizerOption; import com.foxinmy.weixin4j.mp.model.AuthorizerOption;
import com.foxinmy.weixin4j.mp.model.AuthorizerOption.AuthorizerOptionName; import com.foxinmy.weixin4j.mp.model.AuthorizerOption.AuthorizerOptionName;
import com.foxinmy.weixin4j.mp.model.ComponentAuthorizer; import com.foxinmy.weixin4j.mp.model.ComponentAuthorizer;
@ -20,6 +21,7 @@ import com.foxinmy.weixin4j.mp.model.ComponentAuthorizerToken;
import com.foxinmy.weixin4j.mp.model.OauthToken; import com.foxinmy.weixin4j.mp.model.OauthToken;
import com.foxinmy.weixin4j.token.PerTicketManager; import com.foxinmy.weixin4j.token.PerTicketManager;
import com.foxinmy.weixin4j.token.TicketManager; import com.foxinmy.weixin4j.token.TicketManager;
import com.foxinmy.weixin4j.token.TokenCreator;
import com.foxinmy.weixin4j.token.TokenManager; import com.foxinmy.weixin4j.token.TokenManager;
import com.foxinmy.weixin4j.util.Consts; import com.foxinmy.weixin4j.util.Consts;
import com.foxinmy.weixin4j.util.Weixin4jConfigUtil; import com.foxinmy.weixin4j.util.Weixin4jConfigUtil;
@ -32,7 +34,8 @@ import com.foxinmy.weixin4j.util.Weixin4jConfigUtil;
* @date 2015年6月17日 * @date 2015年6月17日
* @since JDK 1.6 * @since JDK 1.6
* @see <a href= * @see <a href=
* "https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1453779503&token=&lang=zh_CN">第三方应用组件概述</a> * "https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1453779503&token=&lang=zh_CN">
* 第三方应用组件概述</a>
*/ */
public class ComponentApi extends MpApi { public class ComponentApi extends MpApi {
/** /**
@ -55,11 +58,10 @@ public class ComponentApi extends MpApi {
*/ */
public ComponentApi(TicketManager ticketManager) { public ComponentApi(TicketManager ticketManager) {
this.ticketManager = ticketManager; this.ticketManager = ticketManager;
this.tokenManager = new TokenManager(new WeixinComponentTokenCreator( this.tokenManager = new TokenManager(new WeixinComponentTokenCreator(ticketManager),
ticketManager), ticketManager.getCacheStorager()); ticketManager.getCacheStorager());
this.preCodeManager = new TokenManager( this.preCodeManager = new TokenManager(
new WeixinComponentPreCodeCreator(tokenManager, new WeixinComponentPreCodeCreator(tokenManager, ticketManager.getThirdId()),
ticketManager.getThirdId()),
ticketManager.getCacheStorager()); ticketManager.getCacheStorager());
} }
@ -99,28 +101,26 @@ public class ComponentApi extends MpApi {
* @return 应用组件的perticket管理 * @return 应用组件的perticket管理
*/ */
public PerTicketManager getRefreshTokenManager(String authAppId) { public PerTicketManager getRefreshTokenManager(String authAppId) {
return new PerTicketManager(authAppId, ticketManager.getThirdId(), return new PerTicketManager(authAppId, ticketManager.getThirdId(), ticketManager.getThirdSecret(),
ticketManager.getThirdSecret(),
ticketManager.getCacheStorager()); ticketManager.getCacheStorager());
} }
/** /**
* 第三方组件代替授权公众号发起网页授权获取code <li> * 第三方组件代替授权公众号发起网页授权获取code
* redirectUri默认填写weixin4j.properties#component.user.oauth.redirect.uri <li> * <li>redirectUri默认填写weixin4j.properties#component.user.oauth.redirect.uri
* scope默认填写snsapi_base <li> * <li>scope默认填写snsapi_base
* state默认填写state * <li>state默认填写state
* *
* @param authAppId * @param authAppId
* 公众号的appid * 公众号的appid
* @see #getAuthorizationURL(String, String, String, String) * @see #getAuthorizationURL(String, String, String, String)
* @see <a * @see <a href=
* href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318590&token=&lang=zh_CN">第三方组件代替授权公众号发起网页授权</a> * "https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318590&token=&lang=zh_CN">
* 第三方组件代替授权公众号发起网页授权</a>
*/ */
public String getUserAuthorizationURL(String authAppId) { public String getUserAuthorizationURL(String authAppId) {
String redirectUri = Weixin4jConfigUtil String redirectUri = Weixin4jConfigUtil.getValue("component.user.oauth.redirect.uri");
.getValue("component.user.oauth.redirect.uri"); return getUserAuthorizationURL(authAppId, redirectUri, "snsapi_base", "state");
return getUserAuthorizationURL(authAppId, redirectUri, "snsapi_base",
"state");
} }
/** /**
@ -135,16 +135,15 @@ public class ComponentApi extends MpApi {
* @param state * @param state
* 重定向后会带上state参数开发者可以填写任意参数值最多128字节 * 重定向后会带上state参数开发者可以填写任意参数值最多128字节
* @return oauth授权URL * @return oauth授权URL
* @see <a * @see <a href=
* href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318590&token=&lang=zh_CN">第三方组件代替授权公众号发起网页授权</a> * "https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318590&token=&lang=zh_CN">
* 第三方组件代替授权公众号发起网页授权</a>
*/ */
public String getUserAuthorizationURL(String authAppId, String redirectUri, public String getUserAuthorizationURL(String authAppId, String redirectUri, String scope, String state) {
String scope, String state) {
String sns_component_user_auth_uri = getRequestUri("sns_component_user_auth_uri"); String sns_component_user_auth_uri = getRequestUri("sns_component_user_auth_uri");
try { try {
return String.format(sns_component_user_auth_uri, authAppId, return String.format(sns_component_user_auth_uri, authAppId,
URLEncoder.encode(redirectUri, Consts.UTF_8.name()), scope, URLEncoder.encode(redirectUri, Consts.UTF_8.name()), scope, state, this.ticketManager.getThirdId());
state, this.ticketManager.getThirdId());
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
; ;
} }
@ -163,16 +162,13 @@ public class ComponentApi extends MpApi {
* @see com.foxinmy.weixin4j.mp.model.OauthToken * @see com.foxinmy.weixin4j.mp.model.OauthToken
* @throws WeixinException * @throws WeixinException
*/ */
public OauthToken getAuthorizationToken(String authAppId, String code) public OauthToken getAuthorizationToken(String authAppId, String code) throws WeixinException {
throws WeixinException {
String sns_component_user_token_uri = getRequestUri("sns_component_user_token_uri"); String sns_component_user_token_uri = getRequestUri("sns_component_user_token_uri");
String accessToken = tokenManager.getAccessToken(); String accessToken = tokenManager.getAccessToken();
WeixinResponse response = weixinExecutor.get(String.format( WeixinResponse response = weixinExecutor.get(
sns_component_user_token_uri, authAppId, code, String.format(sns_component_user_token_uri, authAppId, code, ticketManager.getThirdId(), accessToken));
ticketManager.getThirdId(), accessToken));
JSONObject result = response.getAsJson(); JSONObject result = response.getAsJson();
OauthToken token = new OauthToken(result.getString("access_token"), OauthToken token = new OauthToken(result.getString("access_token"), result.getLongValue("expires_in") * 1000l);
result.getLongValue("expires_in") * 1000l);
token.setOpenId(result.getString("openid")); token.setOpenId(result.getString("openid"));
token.setScope(result.getString("scope")); token.setScope(result.getString("scope"));
token.setRefreshToken(result.getString("refresh_token")); token.setRefreshToken(result.getString("refresh_token"));
@ -192,16 +188,13 @@ public class ComponentApi extends MpApi {
* @see com.foxinmy.weixin4j.mp.model.OauthToken * @see com.foxinmy.weixin4j.mp.model.OauthToken
* @throws WeixinException * @throws WeixinException
*/ */
public OauthToken refreshAuthorizationToken(String authAppId, public OauthToken refreshAuthorizationToken(String authAppId, String refreshToken) throws WeixinException {
String refreshToken) throws WeixinException {
String sns_component_token_refresh_uri = getRequestUri("sns_component_token_refresh_uri"); String sns_component_token_refresh_uri = getRequestUri("sns_component_token_refresh_uri");
String accessToken = tokenManager.getAccessToken(); String accessToken = tokenManager.getAccessToken();
WeixinResponse response = weixinExecutor.get(String.format( WeixinResponse response = weixinExecutor.get(String.format(sns_component_token_refresh_uri, authAppId,
sns_component_token_refresh_uri, authAppId,
ticketManager.getThirdId(), accessToken, refreshToken)); ticketManager.getThirdId(), accessToken, refreshToken));
JSONObject result = response.getAsJson(); JSONObject result = response.getAsJson();
OauthToken token = new OauthToken(result.getString("access_token"), OauthToken token = new OauthToken(result.getString("access_token"), result.getLongValue("expires_in") * 1000l);
result.getLongValue("expires_in") * 1000l);
token.setOpenId(result.getString("openid")); token.setOpenId(result.getString("openid"));
token.setScope(result.getString("scope")); token.setScope(result.getString("scope"));
token.setRefreshToken(result.getString("refresh_token")); token.setRefreshToken(result.getString("refresh_token"));
@ -222,61 +215,31 @@ public class ComponentApi extends MpApi {
* @see com.foxinmy.weixin4j.mp.model.ComponentAuthorizerToken * @see com.foxinmy.weixin4j.mp.model.ComponentAuthorizerToken
* @throws WeixinException * @throws WeixinException
*/ */
public ComponentAuthorizerToken exchangeAuthorizerToken(String authCode) public ComponentAuthorizerToken exchangeAuthorizerToken(String authCode) throws WeixinException {
throws WeixinException {
String component_exchange_authorizer_uri = getRequestUri("component_query_authorization_uri"); String component_exchange_authorizer_uri = getRequestUri("component_query_authorization_uri");
JSONObject obj = new JSONObject(); JSONObject obj = new JSONObject();
obj.put("component_appid", ticketManager.getThirdId()); obj.put("component_appid", ticketManager.getThirdId());
obj.put("authorization_code", authCode); obj.put("authorization_code", authCode);
WeixinResponse response = weixinExecutor.post( WeixinResponse response = weixinExecutor.post(
String.format(component_exchange_authorizer_uri, String.format(component_exchange_authorizer_uri, tokenManager.getAccessToken()), obj.toJSONString());
tokenManager.getAccessToken()), obj.toJSONString()); JSONObject authObj = response.getAsJson().getJSONObject("authorization_info");
obj = response.getAsJson();
JSONObject authObj = obj.getJSONObject("authorization_info");
JSONArray privilegesObj = authObj.getJSONArray("func_info"); JSONArray privilegesObj = authObj.getJSONArray("func_info");
List<Integer> privileges = new ArrayList<Integer>(privilegesObj.size()); List<Integer> privileges = new ArrayList<Integer>(privilegesObj.size());
for (int i = 0; i < privilegesObj.size(); i++) { for (int i = 0; i < privilegesObj.size(); i++) {
privileges.add(privilegesObj.getJSONObject(i) privileges.add(privilegesObj.getJSONObject(i).getJSONObject("funcscope_category").getInteger("id"));
.getJSONObject("funcscope_category").getInteger("id"));
} }
ComponentAuthorizerToken token = new ComponentAuthorizerToken( ComponentAuthorizerToken token = new ComponentAuthorizerToken(authObj.getString("authorizer_access_token"),
authObj.getString("authorizer_access_token"), authObj.getLongValue("expires_in") * 1000l);
authObj.getLongValue("expires_in"));
token.setRefreshToken(authObj.getString("authorizer_refresh_token")); token.setRefreshToken(authObj.getString("authorizer_refresh_token"));
token.setPrivileges(privileges); token.setPrivileges(privileges);
token.setAppId(authObj.getString("authorizer_appid")); token.setAppId(authObj.getString("authorizer_appid"));
return token; // 微信授权公众号的永久刷新令牌
} PerTicketManager perTicketManager = getRefreshTokenManager(token.getAppId());
// 缓存微信公众号的access_token
/** TokenCreator tokenCreator = new WeixinTokenComponentCreator(perTicketManager, tokenManager);
* 获取刷新授权公众号或小程序的接口调用凭据令牌 ticketManager.getCacheStorager().caching(tokenCreator.key(), token);
* // 缓存微信公众号的永久授权码(refresh_token)
* @param authAppId perTicketManager.cachingTicket(token.getRefreshToken());
* 授权方appid
* @param authRefreshToken
* 授权方的刷新令牌刷新令牌主要用于第三方平台获取和刷新已授权用户的access_token只会在授权时刻提供请妥善保存
* 一旦丢失只能让用户重新授权才能再次拿到新的刷新令牌
* @return 第三方组件授权信息
* @see {@link exchangeAuthInfo(String)}
* @see com.foxinmy.weixin4j.mp.model.ComponentAuthorizerToken
* @throws WeixinException
*/
public ComponentAuthorizerToken refreshAuthorizerToken(String authAppId,
String authRefreshToken) throws WeixinException {
String component_refresh_authorizer_token_uri = getRequestUri("component_refresh_authorizer_token_uri");
JSONObject obj = new JSONObject();
obj.put("component_appid", ticketManager.getThirdId());
obj.put("authorizer_appid", authAppId);
obj.put("authorizer_refresh_token", authRefreshToken);
WeixinResponse response = weixinExecutor.post(String.format(
component_refresh_authorizer_token_uri,
tokenManager.getAccessToken()), obj.toJSONString());
obj = response.getAsJson();
ComponentAuthorizerToken token = new ComponentAuthorizerToken(
obj.getString("authorizer_access_token"),
obj.getLongValue("expires_in"));
token.setRefreshToken(obj.getString("authorizer_refresh_token"));
token.setAppId(authAppId);
return token; return token;
} }
@ -291,29 +254,23 @@ public class ComponentApi extends MpApi {
* @see com.foxinmy.weixin4j.mp.model.ComponentAuthorizer * @see com.foxinmy.weixin4j.mp.model.ComponentAuthorizer
* @throws WeixinException * @throws WeixinException
*/ */
public ComponentAuthorizer getAuthorizerInfo(String authAppId) public ComponentAuthorizer getAuthorizerInfo(String authAppId) throws WeixinException {
throws WeixinException {
String component_get_authorizer_uri = getRequestUri("component_get_authorizer_uri"); String component_get_authorizer_uri = getRequestUri("component_get_authorizer_uri");
JSONObject obj = new JSONObject(); JSONObject obj = new JSONObject();
obj.put("component_appid", ticketManager.getThirdId()); obj.put("component_appid", ticketManager.getThirdId());
obj.put("authorizer_appid", authAppId); obj.put("authorizer_appid", authAppId);
WeixinResponse response = weixinExecutor.post( WeixinResponse response = weixinExecutor
String.format(component_get_authorizer_uri, .post(String.format(component_get_authorizer_uri, tokenManager.getAccessToken()), obj.toJSONString());
tokenManager.getAccessToken()), obj.toJSONString());
obj = response.getAsJson(); obj = response.getAsJson();
JSONObject auth = obj.getJSONObject("authorizer_info"); JSONObject auth = obj.getJSONObject("authorizer_info");
ComponentAuthorizer authorizer = JSON.toJavaObject(auth, ComponentAuthorizer authorizer = JSON.toJavaObject(auth, ComponentAuthorizer.class);
ComponentAuthorizer.class); authorizer.setServiceType(auth.getJSONObject("service_type_info").getIntValue("id"));
authorizer.setServiceType(auth.getJSONObject("service_type_info") authorizer.setVerifyType(auth.getJSONObject("verify_type_info").getIntValue("id"));
.getIntValue("id"));
authorizer.setVerifyType(auth.getJSONObject("verify_type_info")
.getIntValue("id"));
auth = obj.getJSONObject("authorization_info"); auth = obj.getJSONObject("authorization_info");
JSONArray privilegesObj = auth.getJSONArray("func_info"); JSONArray privilegesObj = auth.getJSONArray("func_info");
List<Integer> privileges = new ArrayList<Integer>(privilegesObj.size()); List<Integer> privileges = new ArrayList<Integer>(privilegesObj.size());
for (int i = 0; i < privilegesObj.size(); i++) { for (int i = 0; i < privilegesObj.size(); i++) {
privileges.add(privilegesObj.getJSONObject(i) privileges.add(privilegesObj.getJSONObject(i).getJSONObject("funcscope_category").getInteger("id"));
.getJSONObject("funcscope_category").getInteger("id"));
} }
authorizer.setPrivileges(privileges); authorizer.setPrivileges(privileges);
authorizer.setAppId(auth.getString("appid")); authorizer.setAppId(auth.getString("appid"));
@ -331,16 +288,15 @@ public class ComponentApi extends MpApi {
* @see com.foxinmy.weixin4j.mp.model.AuthorizerOption * @see com.foxinmy.weixin4j.mp.model.AuthorizerOption
* @throws WeixinException * @throws WeixinException
*/ */
public AuthorizerOption getAuthorizerOption(String authAppId, public AuthorizerOption getAuthorizerOption(String authAppId, AuthorizerOptionName optionName)
AuthorizerOptionName optionName) throws WeixinException { throws WeixinException {
String component_get_authorizer_option_uri = getRequestUri("component_get_authorizer_option_uri"); String component_get_authorizer_option_uri = getRequestUri("component_get_authorizer_option_uri");
JSONObject obj = new JSONObject(); JSONObject obj = new JSONObject();
obj.put("component_appid", ticketManager.getThirdId()); obj.put("component_appid", ticketManager.getThirdId());
obj.put("authorizer_appid", authAppId); obj.put("authorizer_appid", authAppId);
obj.put("option_name", optionName.name()); obj.put("option_name", optionName.name());
WeixinResponse response = weixinExecutor.post( WeixinResponse response = weixinExecutor.post(
String.format(component_get_authorizer_option_uri, String.format(component_get_authorizer_option_uri, tokenManager.getAccessToken()), obj.toJSONString());
tokenManager.getAccessToken()), obj.toJSONString());
int optionValue = response.getAsJson().getIntValue("option_value"); int optionValue = response.getAsJson().getIntValue("option_value");
return AuthorizerOption.parse(optionName, optionValue); return AuthorizerOption.parse(optionName, optionValue);
} }
@ -354,8 +310,7 @@ public class ComponentApi extends MpApi {
* @see com.foxinmy.weixin4j.mp.model.AuthorizerOption * @see com.foxinmy.weixin4j.mp.model.AuthorizerOption
* @throws WeixinException * @throws WeixinException
*/ */
public ApiResult setAuthorizerOption(String authAppId, public ApiResult setAuthorizerOption(String authAppId, AuthorizerOption option) throws WeixinException {
AuthorizerOption option) throws WeixinException {
String component_set_authorizer_option_uri = getRequestUri("component_set_authorizer_option_uri"); String component_set_authorizer_option_uri = getRequestUri("component_set_authorizer_option_uri");
JSONObject obj = new JSONObject(); JSONObject obj = new JSONObject();
obj.put("component_appid", ticketManager.getThirdId()); obj.put("component_appid", ticketManager.getThirdId());
@ -363,8 +318,7 @@ public class ComponentApi extends MpApi {
obj.put("option_name", option.getName()); obj.put("option_name", option.getName());
obj.put("option_value", option.getValue()); obj.put("option_value", option.getValue());
WeixinResponse response = weixinExecutor.post( WeixinResponse response = weixinExecutor.post(
String.format(component_set_authorizer_option_uri, String.format(component_set_authorizer_option_uri, tokenManager.getAccessToken()), obj.toJSONString());
tokenManager.getAccessToken()), obj.toJSONString());
return response.getAsResult(); return response.getAsResult();
} }
} }

View File

@ -55,7 +55,7 @@ public class WeixinTokenComponentCreator extends TokenCreator {
String.format(URLConsts.TOKEN_COMPONENT_URL, componentTokenManager.getAccessToken()), String.format(URLConsts.TOKEN_COMPONENT_URL, componentTokenManager.getAccessToken()),
obj.toJSONString()); obj.toJSONString());
obj = response.getAsJson(); obj = response.getAsJson();
return new Token(obj.getString("access_token"), obj.getLongValue("expires_in") * 1000l); perTicketManager.cachingTicket(obj.getString("authorizer_refresh_token"));
return new Token(obj.getString("authorizer_access_token"), obj.getLongValue("expires_in") * 1000l);
} }
} }