@@ -6,10 +6,10 @@ | |||||
<modelVersion>4.0.0</modelVersion> | <modelVersion>4.0.0</modelVersion> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>3.1.0</version> | |||||
<version>3.2.0</version> | |||||
<packaging>pom</packaging> | <packaging>pom</packaging> | ||||
<name>Weixin Java Tools - Parent</name> | <name>Weixin Java Tools - Parent</name> | ||||
<description>微信公众号、企业号上级POM</description> | |||||
<description>微信开发Java SDK</description> | |||||
<url>https://github.com/wechat-group/weixin-java-tools</url> | <url>https://github.com/wechat-group/weixin-java-tools</url> | ||||
<licenses> | <licenses> | ||||
@@ -83,20 +83,6 @@ | |||||
<module name="EmptyLineSeparator"> | <module name="EmptyLineSeparator"> | ||||
<property name="allowNoEmptyLineBetweenFields" value="true"/> | <property name="allowNoEmptyLineBetweenFields" value="true"/> | ||||
</module> | </module> | ||||
<module name="SeparatorWrap"> | |||||
<property name="id" value="SeparatorWrapDot"/> | |||||
<property name="tokens" value="DOT"/> | |||||
<property name="option" value="nl"/> | |||||
</module> | |||||
<module name="SeparatorWrap"> | |||||
<property name="id" value="SeparatorWrapComma"/> | |||||
<property name="tokens" value="COMMA"/> | |||||
<property name="option" value="EOL"/> | |||||
</module> | |||||
<module name="SeparatorWrap"> | |||||
<property name="id" value="SeparatorWrapMethodRef"/> | |||||
<property name="option" value="nl"/> | |||||
</module> | |||||
<module name="PackageName"> | <module name="PackageName"> | ||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/> | <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/> | ||||
<message key="name.invalidPattern" | <message key="name.invalidPattern" | ||||
@@ -156,7 +142,7 @@ | |||||
<module name="VariableDeclarationUsageDistance"/> | <module name="VariableDeclarationUsageDistance"/> | ||||
<module name="CustomImportOrder"> | <module name="CustomImportOrder"> | ||||
<property name="customImportOrderRules" | <property name="customImportOrderRules" | ||||
value="THIRD_PARTY_PACKAGE###SPECIAL_IMPORTS###STANDARD_JAVA_PACKAGE###STATIC"/> | |||||
value="SPECIAL_IMPORTS###STANDARD_JAVA_PACKAGE###THIRD_PARTY_PACKAGE###STATIC"/> | |||||
<property name="specialImportsRegExp" value="^javax\."/> | <property name="specialImportsRegExp" value="^javax\."/> | ||||
<property name="standardPackageRegExp" value="^java\."/> | <property name="standardPackageRegExp" value="^java\."/> | ||||
<property name="sortImportsInGroupAlphabetically" value="true"/> | <property name="sortImportsInGroupAlphabetically" value="true"/> | ||||
@@ -7,12 +7,12 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>3.1.0</version> | |||||
<version>3.2.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-common</artifactId> | <artifactId>weixin-java-common</artifactId> | ||||
<name>Weixin Java Tools - Common</name> | <name>Weixin Java Tools - Common</name> | ||||
<description>微信公众号、企业号Java SDK Common</description> | |||||
<description>微信开发Java SDK公共模块</description> | |||||
<dependencies> | <dependencies> | ||||
<dependency> | <dependency> | ||||
@@ -6,7 +6,7 @@ import java.util.Map; | |||||
/** | /** | ||||
* 微信开发所使用到的常量类. | * 微信开发所使用到的常量类. | ||||
* | * | ||||
* @author chanjarster & binarywang | |||||
* @author Daniel Qian & binarywang | |||||
*/ | */ | ||||
public class WxConsts { | public class WxConsts { | ||||
/** | /** | ||||
@@ -82,7 +82,7 @@ public class WxConsts { | |||||
/** | /** | ||||
* 小程序卡片(要求小程序与公众号已关联) | * 小程序卡片(要求小程序与公众号已关联) | ||||
*/ | */ | ||||
public static final String MINIPROGRAMPAGE="miniprogrampage"; | |||||
public static final String MINIPROGRAMPAGE = "miniprogrampage"; | |||||
} | } | ||||
/** | /** | ||||
@@ -151,14 +151,106 @@ public class WxConsts { | |||||
public static final String CLICK = "CLICK"; | public static final String CLICK = "CLICK"; | ||||
public static final String VIEW = "VIEW"; | public static final String VIEW = "VIEW"; | ||||
public static final String MASS_SEND_JOB_FINISH = "MASSSENDJOBFINISH"; | public static final String MASS_SEND_JOB_FINISH = "MASSSENDJOBFINISH"; | ||||
/** | |||||
* 扫码推事件的事件推送 | |||||
*/ | |||||
public static final String SCANCODE_PUSH = "scancode_push"; | public static final String SCANCODE_PUSH = "scancode_push"; | ||||
/** | |||||
* 扫码推事件且弹出“消息接收中”提示框的事件推送. | |||||
*/ | |||||
public static final String SCANCODE_WAITMSG = "scancode_waitmsg"; | public static final String SCANCODE_WAITMSG = "scancode_waitmsg"; | ||||
/** | |||||
* 弹出系统拍照发图的事件推送. | |||||
*/ | |||||
public static final String PIC_SYSPHOTO = "pic_sysphoto"; | public static final String PIC_SYSPHOTO = "pic_sysphoto"; | ||||
/** | |||||
* 弹出拍照或者相册发图的事件推送. | |||||
*/ | |||||
public static final String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; | public static final String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; | ||||
/** | |||||
* 弹出微信相册发图器的事件推送. | |||||
*/ | |||||
public static final String PIC_WEIXIN = "pic_weixin"; | public static final String PIC_WEIXIN = "pic_weixin"; | ||||
/** | |||||
* 弹出地理位置选择器的事件推送. | |||||
*/ | |||||
public static final String LOCATION_SELECT = "location_select"; | public static final String LOCATION_SELECT = "location_select"; | ||||
public static final String TEMPLATE_SEND_JOB_FINISH = "TEMPLATESENDJOBFINISH"; | public static final String TEMPLATE_SEND_JOB_FINISH = "TEMPLATESENDJOBFINISH"; | ||||
public static final String ENTER_AGENT = "enter_agent"; | |||||
/** | |||||
* 微信小店 订单付款通知. | |||||
*/ | |||||
public static final String MERCHANT_ORDER = "merchant_order"; | |||||
/** | |||||
* 卡券事件:卡券通过审核 | |||||
*/ | |||||
public static final String CARD_PASS_CHECK = "card_pass_check"; | |||||
/** | |||||
* 卡券事件:卡券未通过审核 | |||||
*/ | |||||
public static final String CARD_NOT_PASS_CHECK = "card_not_pass_check"; | |||||
/** | |||||
* 卡券事件:用户领取卡券 | |||||
*/ | |||||
public static final String CARD_USER_GET_CARD = "user_get_card"; | |||||
/** | |||||
* 卡券事件:用户转赠卡券 | |||||
*/ | |||||
public static final String CARD_USER_GIFTING_CARD = "user_gifting_card"; | |||||
/** | |||||
* 卡券事件:用户核销卡券 | |||||
*/ | |||||
public static final String CARD_USER_CONSUME_CARD = "user_consume_card"; | |||||
/** | |||||
* 卡券事件:用户通过卡券的微信买单完成时推送 | |||||
*/ | |||||
public static final String CARD_USER_PAY_FROM_PAY_CELL = "user_pay_from_pay_cell"; | |||||
/** | |||||
* 卡券事件:用户提交会员卡开卡信息 | |||||
*/ | |||||
public static final String CARD_SUBMIT_MEMBERCARD_USER_INFO = "submit_membercard_user_info"; | |||||
/** | |||||
* 卡券事件:用户打开查看卡券 | |||||
*/ | |||||
public static final String CARD_USER_VIEW_CARD = "user_view_card"; | |||||
/** | |||||
* 卡券事件:用户删除卡券 | |||||
*/ | |||||
public static final String CARD_USER_DEL_CARD = "user_del_card"; | |||||
/** | |||||
* 卡券事件:用户在卡券里点击查看公众号进入会话时(需要用户已经关注公众号) | |||||
*/ | |||||
public static final String CARD_USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card"; | |||||
/** | |||||
* 卡券事件:当用户的会员卡积分余额发生变动时 | |||||
*/ | |||||
public static final String CARD_UPDATE_MEMBER_CARD = "update_member_card"; | |||||
/** | |||||
* 卡券事件:当某个card_id的初始库存数大于200且当前库存小于等于100时,用户尝试领券会触发发送事件给商户,事件每隔12h发送一次 | |||||
*/ | |||||
public static final String CARD_SKU_REMIND = "card_sku_remind"; | |||||
/** | |||||
* 卡券事件:当商户朋友的券券点发生变动时 | |||||
*/ | |||||
public static final String CARD_PAY_ORDER = "card_pay_order"; | |||||
} | } | ||||
/** | /** | ||||
@@ -230,10 +322,16 @@ public class WxConsts { | |||||
* 不弹出授权页面,直接跳转,只能获取用户openid. | * 不弹出授权页面,直接跳转,只能获取用户openid. | ||||
*/ | */ | ||||
public static final String SNSAPI_BASE = "snsapi_base"; | public static final String SNSAPI_BASE = "snsapi_base"; | ||||
/** | /** | ||||
* 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息. | * 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息. | ||||
*/ | */ | ||||
public static final String SNSAPI_USERINFO = "snsapi_userinfo"; | public static final String SNSAPI_USERINFO = "snsapi_userinfo"; | ||||
/** | |||||
* 手动授权,可获取成员的详细信息,包含手机、邮箱。只适用于企业微信或企业号. | |||||
*/ | |||||
public static final String SNSAPI_PRIVATEINFO = "snsapi_privateinfo"; | |||||
} | } | ||||
/** | /** | ||||
@@ -252,4 +350,5 @@ public class WxConsts { | |||||
public static final String IMAGE = "image"; | public static final String IMAGE = "image"; | ||||
public static final String VIDEO = "video"; | public static final String VIDEO = "video"; | ||||
} | } | ||||
} | } |
@@ -4,6 +4,8 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||||
/** | /** | ||||
* WxErrorException处理器. | * WxErrorException处理器. | ||||
* | |||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public interface WxErrorExceptionHandler { | public interface WxErrorExceptionHandler { | ||||
@@ -5,6 +5,8 @@ package me.chanjar.weixin.common.api; | |||||
* 消息重复检查器. | * 消息重复检查器. | ||||
* 微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次 | * 微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次 | ||||
* </pre> | * </pre> | ||||
* | |||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public interface WxMessageDuplicateChecker { | public interface WxMessageDuplicateChecker { | ||||
@@ -9,6 +9,8 @@ import java.util.concurrent.atomic.AtomicBoolean; | |||||
* 默认消息重复检查器. | * 默认消息重复检查器. | ||||
* 将每个消息id保存在内存里,每隔5秒清理已经过期的消息id,每个消息id的过期时间是15秒 | * 将每个消息id保存在内存里,每隔5秒清理已经过期的消息id,每个消息id的过期时间是15秒 | ||||
* </pre> | * </pre> | ||||
* | |||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public class WxMessageInMemoryDuplicateChecker implements WxMessageDuplicateChecker { | public class WxMessageInMemoryDuplicateChecker implements WxMessageDuplicateChecker { | ||||
@@ -1,10 +1,15 @@ | |||||
package me.chanjar.weixin.common.bean; | package me.chanjar.weixin.common.bean; | ||||
import java.io.Serializable; | |||||
import lombok.Data; | import lombok.Data; | ||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | import me.chanjar.weixin.common.util.json.WxGsonBuilder; | ||||
import java.io.Serializable; | |||||
/** | |||||
* access token. | |||||
* | |||||
* @author Daniel Qian | |||||
*/ | |||||
@Data | @Data | ||||
public class WxAccessToken implements Serializable { | public class WxAccessToken implements Serializable { | ||||
private static final long serialVersionUID = 8709719312922168909L; | private static final long serialVersionUID = 8709719312922168909L; | ||||
@@ -1,10 +1,12 @@ | |||||
package me.chanjar.weixin.common.bean; | package me.chanjar.weixin.common.bean; | ||||
import lombok.Data; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import java.io.Serializable; | import java.io.Serializable; | ||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import lombok.Data; | |||||
/** | /** | ||||
* 卡券Api签名. | * 卡券Api签名. | ||||
* | * | ||||
@@ -35,7 +37,6 @@ public class WxCardApiSignature implements Serializable { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
} | } |
@@ -1,13 +1,21 @@ | |||||
package me.chanjar.weixin.common.bean; | package me.chanjar.weixin.common.bean; | ||||
import lombok.Data; | |||||
import java.io.Serializable; | import java.io.Serializable; | ||||
import lombok.AllArgsConstructor; | |||||
import lombok.Builder; | |||||
import lombok.Data; | |||||
import lombok.NoArgsConstructor; | |||||
/** | /** | ||||
* jspai signature. | * jspai signature. | ||||
* | |||||
* @author Daniel Qian | |||||
*/ | */ | ||||
@Data | @Data | ||||
@Builder | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
public class WxJsapiSignature implements Serializable { | public class WxJsapiSignature implements Serializable { | ||||
private static final long serialVersionUID = -1116808193154384804L; | private static final long serialVersionUID = -1116808193154384804L; | ||||
@@ -1,9 +1,5 @@ | |||||
package me.chanjar.weixin.common.bean.menu; | package me.chanjar.weixin.common.bean.menu; | ||||
import lombok.Data; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.io.InputStreamReader; | import java.io.InputStreamReader; | ||||
import java.io.Serializable; | import java.io.Serializable; | ||||
@@ -11,6 +7,12 @@ import java.nio.charset.StandardCharsets; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import lombok.Data; | |||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
/** | /** | ||||
* 菜单(公众号和企业号共用的). | * 菜单(公众号和企业号共用的). | ||||
* | * | ||||
@@ -47,7 +49,7 @@ public class WxMenu implements Serializable { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
} | } |
@@ -1,13 +1,20 @@ | |||||
package me.chanjar.weixin.common.bean.menu; | package me.chanjar.weixin.common.bean.menu; | ||||
import com.google.gson.annotations.SerializedName; | |||||
import lombok.Data; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import java.io.Serializable; | import java.io.Serializable; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import com.google.gson.annotations.SerializedName; | |||||
import lombok.Data; | |||||
/** | |||||
* menu button. | |||||
* | |||||
* @author Daniel Qian | |||||
*/ | |||||
@Data | @Data | ||||
public class WxMenuButton implements Serializable { | public class WxMenuButton implements Serializable { | ||||
private static final long serialVersionUID = -1070939403109776555L; | private static final long serialVersionUID = -1070939403109776555L; | ||||
@@ -76,7 +83,7 @@ public class WxMenuButton implements Serializable { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
} | } |
@@ -1,11 +1,18 @@ | |||||
package me.chanjar.weixin.common.bean.menu; | package me.chanjar.weixin.common.bean.menu; | ||||
import java.io.Serializable; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import com.google.gson.annotations.SerializedName; | import com.google.gson.annotations.SerializedName; | ||||
import lombok.Data; | import lombok.Data; | ||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import java.io.Serializable; | |||||
/** | |||||
* menu rule. | |||||
* | |||||
* @author Daniel Qian | |||||
*/ | |||||
@Data | @Data | ||||
public class WxMenuRule implements Serializable { | public class WxMenuRule implements Serializable { | ||||
private static final long serialVersionUID = -4587181819499286670L; | private static final long serialVersionUID = -4587181819499286670L; | ||||
@@ -24,6 +31,6 @@ public class WxMenuRule implements Serializable { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
} | } |
@@ -1,15 +1,22 @@ | |||||
package me.chanjar.weixin.common.bean.result; | package me.chanjar.weixin.common.bean.result; | ||||
import java.io.Serializable; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import lombok.Data; | import lombok.Data; | ||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | import me.chanjar.weixin.common.util.json.WxGsonBuilder; | ||||
import java.io.Serializable; | |||||
/** | |||||
* | |||||
* @author Daniel Qian | |||||
*/ | |||||
@Data | @Data | ||||
public class WxMediaUploadResult implements Serializable { | public class WxMediaUploadResult implements Serializable { | ||||
private static final long serialVersionUID = 330834334738622341L; | private static final long serialVersionUID = 330834334738622341L; | ||||
private String url; | |||||
private String type; | private String type; | ||||
private String mediaId; | private String mediaId; | ||||
private String thumbMediaId; | private String thumbMediaId; | ||||
@@ -21,7 +28,7 @@ public class WxMediaUploadResult implements Serializable { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
} | } |
@@ -1,5 +1,9 @@ | |||||
package me.chanjar.weixin.common.session; | package me.chanjar.weixin.common.session; | ||||
/** | |||||
* | |||||
* @author Daniel Qian | |||||
*/ | |||||
public interface InternalSession { | public interface InternalSession { | ||||
/** | /** | ||||
@@ -1,5 +1,8 @@ | |||||
package me.chanjar.weixin.common.session; | package me.chanjar.weixin.common.session; | ||||
/** | |||||
* @author Daniel Qian | |||||
*/ | |||||
public interface InternalSessionManager { | public interface InternalSessionManager { | ||||
/** | /** | ||||
@@ -1,11 +1,18 @@ | |||||
package me.chanjar.weixin.common.session; | package me.chanjar.weixin.common.session; | ||||
import me.chanjar.weixin.common.util.res.StringManager; | |||||
import java.util.*; | |||||
import java.util.Collections; | |||||
import java.util.Enumeration; | |||||
import java.util.HashSet; | |||||
import java.util.Map; | |||||
import java.util.Set; | |||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
import java.util.concurrent.atomic.AtomicInteger; | import java.util.concurrent.atomic.AtomicInteger; | ||||
import me.chanjar.weixin.common.util.res.StringManager; | |||||
/** | |||||
* @author Daniel Qian | |||||
*/ | |||||
public class StandardSession implements WxSession, InternalSession { | public class StandardSession implements WxSession, InternalSession { | ||||
/** | /** | ||||
@@ -2,6 +2,9 @@ package me.chanjar.weixin.common.session; | |||||
import java.util.Enumeration; | import java.util.Enumeration; | ||||
/** | |||||
* @author Daniel Qian | |||||
*/ | |||||
public class StandardSessionFacade implements WxSession { | public class StandardSessionFacade implements WxSession { | ||||
/** | /** | ||||
@@ -1,15 +1,18 @@ | |||||
package me.chanjar.weixin.common.session; | package me.chanjar.weixin.common.session; | ||||
import me.chanjar.weixin.common.util.res.StringManager; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
import java.util.concurrent.atomic.AtomicBoolean; | import java.util.concurrent.atomic.AtomicBoolean; | ||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import me.chanjar.weixin.common.util.res.StringManager; | |||||
/** | /** | ||||
* 基于内存的session manager | |||||
* 基于内存的session manager. | |||||
* | |||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public class StandardSessionManager implements WxSessionManager, InternalSessionManager { | public class StandardSessionManager implements WxSessionManager, InternalSessionManager { | ||||
@@ -19,6 +19,8 @@ package me.chanjar.weixin.common.session; | |||||
/** | /** | ||||
* An exception that indicates the maximum number of active sessions has been | * An exception that indicates the maximum number of active sessions has been | ||||
* reached and the server is refusing to create any new sessions. | * reached and the server is refusing to create any new sessions. | ||||
* | |||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public class TooManyActiveSessionsException | public class TooManyActiveSessionsException | ||||
extends IllegalStateException { | extends IllegalStateException { | ||||
@@ -2,6 +2,9 @@ package me.chanjar.weixin.common.session; | |||||
import java.util.Enumeration; | import java.util.Enumeration; | ||||
/** | |||||
* @author Daniel Qian | |||||
*/ | |||||
public interface WxSession { | public interface WxSession { | ||||
Object getAttribute(String name); | Object getAttribute(String name); | ||||
@@ -1,5 +1,8 @@ | |||||
package me.chanjar.weixin.common.session; | package me.chanjar.weixin.common.session; | ||||
/** | |||||
* @author Daniel Qian | |||||
*/ | |||||
public interface WxSessionManager { | public interface WxSessionManager { | ||||
/** | /** | ||||
@@ -0,0 +1,40 @@ | |||||
package me.chanjar.weixin.common.util; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
import org.apache.commons.codec.binary.Hex; | |||||
import javax.crypto.Mac; | |||||
import javax.crypto.spec.SecretKeySpec; | |||||
import java.security.InvalidKeyException; | |||||
import java.security.NoSuchAlgorithmException; | |||||
/** | |||||
* <pre> | |||||
* 签名工具类 | |||||
* Created by BinaryWang on 2018/7/11. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
@Slf4j | |||||
public class SignUtils { | |||||
/** | |||||
* HmacSHA256 签名算法 | |||||
* | |||||
* @param message 签名数据 | |||||
* @param key 签名密钥 | |||||
*/ | |||||
public static String createHmacSha256Sign(String message, String key) { | |||||
try { | |||||
Mac sha256 = Mac.getInstance("HmacSHA256"); | |||||
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "HmacSHA256"); | |||||
sha256.init(secretKeySpec); | |||||
byte[] bytes = sha256.doFinal(message.getBytes()); | |||||
return Hex.encodeHexString(bytes).toUpperCase(); | |||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) { | |||||
SignUtils.log.error(e.getMessage(), e); | |||||
} | |||||
return null; | |||||
} | |||||
} |
@@ -1,64 +0,0 @@ | |||||
package me.chanjar.weixin.common.util; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
/** | |||||
* <pre> | |||||
* 自定义的ToString方法,用于产生去掉空值属性的字符串 | |||||
* Created by Binary Wang on 2016-10-27. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
public class ToStringUtils { | |||||
public static final ToStringStyle THE_STYLE = new SimpleMultiLineToStringStyle(); | |||||
/** | |||||
* 用于产生去掉空值属性并以换行符分割各属性键值的toString字符串 | |||||
* | |||||
* @param obj | |||||
*/ | |||||
public static String toSimpleString(Object obj) { | |||||
String toStringResult = ToStringBuilder.reflectionToString(obj, THE_STYLE); | |||||
String[] split = toStringResult.split(SimpleMultiLineToStringStyle.LINE_SEPARATOR); | |||||
StringBuilder result = new StringBuilder(); | |||||
for (String string : split) { | |||||
if (string.endsWith(SimpleMultiLineToStringStyle.NULL_TEXT)) { | |||||
continue; | |||||
} | |||||
result.append(string + SimpleMultiLineToStringStyle.LINE_SEPARATOR); | |||||
} | |||||
if (result.length() == 0) { | |||||
return ""; | |||||
} | |||||
//如果没有非空的属性,就输出 <all null properties> | |||||
if (StringUtils.countMatches(result, SimpleMultiLineToStringStyle.LINE_SEPARATOR) == 2) { | |||||
return result.toString().split(SimpleMultiLineToStringStyle.LINE_SEPARATOR)[0] | |||||
+ "<all null values>]"; | |||||
} | |||||
return result.deleteCharAt(result.length() - 1).toString(); | |||||
} | |||||
private static class SimpleMultiLineToStringStyle extends ToStringStyle { | |||||
private static final long serialVersionUID = 4645306494220335355L; | |||||
private static final String LINE_SEPARATOR = "\n"; | |||||
private static final String NULL_TEXT = "<null>"; | |||||
public SimpleMultiLineToStringStyle() { | |||||
super(); | |||||
this.setContentStart("["); | |||||
this.setFieldSeparator(LINE_SEPARATOR + " "); | |||||
this.setFieldSeparatorAtStart(true); | |||||
this.setContentEnd(LINE_SEPARATOR + "]"); | |||||
this.setNullText(NULL_TEXT); | |||||
this.setUseShortClassName(true); | |||||
this.setUseIdentityHashCode(false); | |||||
} | |||||
} | |||||
} |
@@ -1,6 +1,7 @@ | |||||
package me.chanjar.weixin.common.util.http.apache; | package me.chanjar.weixin.common.util.http.apache; | ||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
import org.apache.http.HttpHost; | |||||
import org.apache.http.annotation.NotThreadSafe; | import org.apache.http.annotation.NotThreadSafe; | ||||
import org.apache.http.auth.AuthScope; | import org.apache.http.auth.AuthScope; | ||||
import org.apache.http.auth.UsernamePasswordCredentials; | import org.apache.http.auth.UsernamePasswordCredentials; | ||||
@@ -236,9 +237,10 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { | |||||
if (StringUtils.isNotBlank(this.httpProxyHost) && StringUtils.isNotBlank(this.httpProxyUsername)) { | if (StringUtils.isNotBlank(this.httpProxyHost) && StringUtils.isNotBlank(this.httpProxyUsername)) { | ||||
// 使用代理服务器 需要用户认证的代理服务器 | // 使用代理服务器 需要用户认证的代理服务器 | ||||
CredentialsProvider provider = new BasicCredentialsProvider(); | CredentialsProvider provider = new BasicCredentialsProvider(); | ||||
provider.setCredentials(new AuthScope(this.httpProxyHost, this.httpProxyPort) | |||||
, new UsernamePasswordCredentials(this.httpProxyUsername, this.httpProxyPassword)); | |||||
provider.setCredentials(new AuthScope(this.httpProxyHost, this.httpProxyPort), | |||||
new UsernamePasswordCredentials(this.httpProxyUsername, this.httpProxyPassword)); | |||||
this.httpClientBuilder.setDefaultCredentialsProvider(provider); | this.httpClientBuilder.setDefaultCredentialsProvider(provider); | ||||
this.httpClientBuilder.setProxy(new HttpHost(this.httpProxyHost, this.httpProxyPort)); | |||||
} | } | ||||
if (StringUtils.isNotBlank(this.userAgent)) { | if (StringUtils.isNotBlank(this.userAgent)) { | ||||
@@ -1,6 +1,7 @@ | |||||
package me.chanjar.weixin.common.util.http.apache; | package me.chanjar.weixin.common.util.http.apache; | ||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
import org.apache.http.HttpHost; | |||||
import org.apache.http.annotation.NotThreadSafe; | import org.apache.http.annotation.NotThreadSafe; | ||||
import org.apache.http.auth.AuthScope; | import org.apache.http.auth.AuthScope; | ||||
import org.apache.http.auth.UsernamePasswordCredentials; | import org.apache.http.auth.UsernamePasswordCredentials; | ||||
@@ -226,29 +227,26 @@ public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder { | |||||
.setConnectionManager(connectionManager) | .setConnectionManager(connectionManager) | ||||
.setConnectionManagerShared(true) | .setConnectionManagerShared(true) | ||||
.setSSLSocketFactory(this.buildSSLConnectionSocketFactory()) | .setSSLSocketFactory(this.buildSSLConnectionSocketFactory()) | ||||
.setDefaultRequestConfig( | |||||
RequestConfig.custom() | |||||
.setSocketTimeout(this.soTimeout) | |||||
.setConnectTimeout(this.connectionTimeout) | |||||
.setConnectionRequestTimeout(this.connectionRequestTimeout) | |||||
.build() | |||||
) | |||||
.setRetryHandler(this.httpRequestRetryHandler); | |||||
if (StringUtils.isNotBlank(this.httpProxyHost) | |||||
&& StringUtils.isNotBlank(this.httpProxyUsername)) { | |||||
.setDefaultRequestConfig(RequestConfig.custom() | |||||
.setSocketTimeout(this.soTimeout) | |||||
.setConnectTimeout(this.connectionTimeout) | |||||
.setConnectionRequestTimeout(this.connectionRequestTimeout) | |||||
.build() | |||||
).setRetryHandler(this.httpRequestRetryHandler); | |||||
if (StringUtils.isNotBlank(this.httpProxyHost) && StringUtils.isNotBlank(this.httpProxyUsername)) { | |||||
// 使用代理服务器 需要用户认证的代理服务器 | // 使用代理服务器 需要用户认证的代理服务器 | ||||
CredentialsProvider provider = new BasicCredentialsProvider(); | CredentialsProvider provider = new BasicCredentialsProvider(); | ||||
provider.setCredentials( | |||||
new AuthScope(this.httpProxyHost, this.httpProxyPort), | |||||
new UsernamePasswordCredentials(this.httpProxyUsername, | |||||
this.httpProxyPassword)); | |||||
provider.setCredentials(new AuthScope(this.httpProxyHost, this.httpProxyPort), | |||||
new UsernamePasswordCredentials(this.httpProxyUsername, this.httpProxyPassword)); | |||||
httpClientBuilder.setDefaultCredentialsProvider(provider); | httpClientBuilder.setDefaultCredentialsProvider(provider); | ||||
httpClientBuilder.setProxy(new HttpHost(this.httpProxyHost, this.httpProxyPort)); | |||||
} | } | ||||
if (StringUtils.isNotBlank(this.userAgent)) { | if (StringUtils.isNotBlank(this.userAgent)) { | ||||
httpClientBuilder.setUserAgent(this.userAgent); | httpClientBuilder.setUserAgent(this.userAgent); | ||||
} | } | ||||
this.closeableHttpClient = httpClientBuilder.build(); | this.closeableHttpClient = httpClientBuilder.build(); | ||||
prepared.set(true); | prepared.set(true); | ||||
} | } | ||||
@@ -1,10 +1,14 @@ | |||||
package me.chanjar.weixin.common.util.json; | package me.chanjar.weixin.common.util.json; | ||||
import com.google.gson.*; | |||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||
import com.google.gson.JsonDeserializationContext; | |||||
import com.google.gson.JsonDeserializer; | |||||
import com.google.gson.JsonElement; | |||||
import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonParseException; | |||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
/** | /** | ||||
* @author Daniel Qian | * @author Daniel Qian | ||||
*/ | */ | ||||
@@ -12,22 +16,25 @@ public class WxMediaUploadResultAdapter implements JsonDeserializer<WxMediaUploa | |||||
@Override | @Override | ||||
public WxMediaUploadResult deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { | public WxMediaUploadResult deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { | ||||
WxMediaUploadResult uploadResult = new WxMediaUploadResult(); | |||||
JsonObject uploadResultJsonObject = json.getAsJsonObject(); | |||||
WxMediaUploadResult result = new WxMediaUploadResult(); | |||||
JsonObject jsonObject = json.getAsJsonObject(); | |||||
if (jsonObject.get("url") != null && !jsonObject.get("url").isJsonNull()) { | |||||
result.setUrl(GsonHelper.getAsString(jsonObject.get("url"))); | |||||
} | |||||
if (uploadResultJsonObject.get("type") != null && !uploadResultJsonObject.get("type").isJsonNull()) { | |||||
uploadResult.setType(GsonHelper.getAsString(uploadResultJsonObject.get("type"))); | |||||
if (jsonObject.get("type") != null && !jsonObject.get("type").isJsonNull()) { | |||||
result.setType(GsonHelper.getAsString(jsonObject.get("type"))); | |||||
} | } | ||||
if (uploadResultJsonObject.get("media_id") != null && !uploadResultJsonObject.get("media_id").isJsonNull()) { | |||||
uploadResult.setMediaId(GsonHelper.getAsString(uploadResultJsonObject.get("media_id"))); | |||||
if (jsonObject.get("media_id") != null && !jsonObject.get("media_id").isJsonNull()) { | |||||
result.setMediaId(GsonHelper.getAsString(jsonObject.get("media_id"))); | |||||
} | } | ||||
if (uploadResultJsonObject.get("thumb_media_id") != null && !uploadResultJsonObject.get("thumb_media_id").isJsonNull()) { | |||||
uploadResult.setThumbMediaId(GsonHelper.getAsString(uploadResultJsonObject.get("thumb_media_id"))); | |||||
if (jsonObject.get("thumb_media_id") != null && !jsonObject.get("thumb_media_id").isJsonNull()) { | |||||
result.setThumbMediaId(GsonHelper.getAsString(jsonObject.get("thumb_media_id"))); | |||||
} | } | ||||
if (uploadResultJsonObject.get("created_at") != null && !uploadResultJsonObject.get("created_at").isJsonNull()) { | |||||
uploadResult.setCreatedAt(GsonHelper.getAsPrimitiveLong(uploadResultJsonObject.get("created_at"))); | |||||
if (jsonObject.get("created_at") != null && !jsonObject.get("created_at").isJsonNull()) { | |||||
result.setCreatedAt(GsonHelper.getAsPrimitiveLong(jsonObject.get("created_at"))); | |||||
} | } | ||||
return uploadResult; | |||||
return result; | |||||
} | } | ||||
} | } |
@@ -7,7 +7,7 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>3.1.0</version> | |||||
<version>3.2.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-cp</artifactId> | <artifactId>weixin-java-cp</artifactId> | ||||
@@ -0,0 +1,129 @@ | |||||
package me.chanjar.weixin.cp; | |||||
/** | |||||
* <pre> | |||||
* 企业微信常量 | |||||
* Created by Binary Wang on 2018/8/25. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
public class WxCpConsts { | |||||
/** | |||||
* 企业微信端推送过来的事件类型. | |||||
* 参考文档:https://work.weixin.qq.com/api/doc#12974 | |||||
*/ | |||||
public static class EventType { | |||||
/** | |||||
* 成员关注事件. | |||||
*/ | |||||
public static final String SUBSCRIBE = "subscribe"; | |||||
/** | |||||
* 成员取消关注事件. | |||||
*/ | |||||
public static final String UNSUBSCRIBE = "unsubscribe"; | |||||
/** | |||||
* 进入应用事件. | |||||
*/ | |||||
public static final String ENTER_AGENT = "enter_agent"; | |||||
/** | |||||
* 上报地理位置. | |||||
*/ | |||||
public static final String LOCATION = "LOCATION"; | |||||
/** | |||||
* 异步任务完成事件推送. | |||||
*/ | |||||
public static final String BATCH_JOB_RESULT = "batch_job_result"; | |||||
/** | |||||
* 企业微信通讯录变更事件. | |||||
*/ | |||||
public static final String CHANGE_CONTACT = "change_contact"; | |||||
/** | |||||
* 点击菜单拉取消息的事件推送. | |||||
*/ | |||||
public static final String CLICK = "click"; | |||||
/** | |||||
* 点击菜单跳转链接的事件推送. | |||||
*/ | |||||
public static final String VIEW = "view"; | |||||
/** | |||||
* 扫码推事件的事件推送. | |||||
*/ | |||||
public static final String SCANCODE_PUSH = "scancode_push"; | |||||
/** | |||||
* 扫码推事件且弹出“消息接收中”提示框的事件推送. | |||||
*/ | |||||
public static final String SCANCODE_WAITMSG = "scancode_waitmsg"; | |||||
/** | |||||
* 弹出系统拍照发图的事件推送. | |||||
*/ | |||||
public static final String PIC_SYSPHOTO = "pic_sysphoto"; | |||||
/** | |||||
* 弹出拍照或者相册发图的事件推送. | |||||
*/ | |||||
public static final String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; | |||||
/** | |||||
* 弹出微信相册发图器的事件推送. | |||||
*/ | |||||
public static final String PIC_WEIXIN = "pic_weixin"; | |||||
/** | |||||
* 弹出地理位置选择器的事件推送. | |||||
*/ | |||||
public static final String LOCATION_SELECT = "location_select"; | |||||
} | |||||
/** | |||||
* 企业微信通讯录变更事件. | |||||
*/ | |||||
public static class ContactChangeType { | |||||
/** | |||||
* 新增成员事件. | |||||
*/ | |||||
public static final String CREATE_USER = "create_user"; | |||||
/** | |||||
* 更新成员事件. | |||||
*/ | |||||
public static final String UPDATE_USER = "update_user"; | |||||
/** | |||||
* 删除成员事件. | |||||
*/ | |||||
public static final String DELETE_USER = "delete_user"; | |||||
/** | |||||
* 新增部门事件. | |||||
*/ | |||||
public static final String CREATE_PARTY = "create_party"; | |||||
/** | |||||
* 更新部门事件. | |||||
*/ | |||||
public static final String UPDATE_PARTY = "update_party"; | |||||
/** | |||||
* 删除部门事件. | |||||
*/ | |||||
public static final String DELETE_PARTY = "delete_party"; | |||||
/** | |||||
* 标签成员变更事件. | |||||
*/ | |||||
public static final String UPDATE_TAG = "update_tag"; | |||||
} | |||||
} |
@@ -3,9 +3,12 @@ package me.chanjar.weixin.cp.api; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.bean.WxCpAgent; | import me.chanjar.weixin.cp.bean.WxCpAgent; | ||||
import java.util.List; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 管理企业号应用 | * 管理企业号应用 | ||||
* 文档地址:https://work.weixin.qq.com/api/doc#10087 | |||||
* Created by huansinho on 2018/4/13. | * Created by huansinho on 2018/4/13. | ||||
* </pre> | * </pre> | ||||
* | * | ||||
@@ -17,7 +20,7 @@ public interface WxCpAgentService { | |||||
* <pre> | * <pre> | ||||
* 获取企业号应用信息 | * 获取企业号应用信息 | ||||
* 该API用于获取企业号某个应用的基本信息,包括头像、昵称、帐号类型、认证类型、可见范围等信息 | * 该API用于获取企业号某个应用的基本信息,包括头像、昵称、帐号类型、认证类型、可见范围等信息 | ||||
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=获取企业号应用 | |||||
* 详情请见: https://work.weixin.qq.com/api/doc#10087 | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param agentId 企业应用的id | * @param agentId 企业应用的id | ||||
@@ -25,4 +28,25 @@ public interface WxCpAgentService { | |||||
*/ | */ | ||||
WxCpAgent get(Integer agentId) throws WxErrorException; | WxCpAgent get(Integer agentId) throws WxErrorException; | ||||
/** | |||||
* <pre> | |||||
* 设置应用. | |||||
* 仅企业可调用,可设置当前凭证对应的应用;第三方不可调用。 | |||||
* 详情请见: https://work.weixin.qq.com/api/doc#10088 | |||||
* </pre> | |||||
* | |||||
* @param agentInfo 应用信息 | |||||
*/ | |||||
void set(WxCpAgent agentInfo) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 获取应用列表. | |||||
* 企业仅可获取当前凭证对应的应用;第三方仅可获取被授权的应用。 | |||||
* 详情请见: https://work.weixin.qq.com/api/doc#11214 | |||||
* </pre> | |||||
* | |||||
*/ | |||||
List<WxCpAgent> list() throws WxErrorException; | |||||
} | } |
@@ -9,17 +9,20 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 媒体管理接口 | |||||
* 媒体管理接口. | |||||
* Created by BinaryWang on 2017/6/24. | * Created by BinaryWang on 2017/6/24. | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
public interface WxCpMediaService { | public interface WxCpMediaService { | ||||
String MEDIA_GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; | |||||
String MEDIA_UPLOAD_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type="; | |||||
String IMG_UPLOAD_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/uploadimg"; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 上传多媒体文件 | |||||
* 上传多媒体文件. | |||||
* 上传的多媒体文件有格式和大小限制,如下: | * 上传的多媒体文件有格式和大小限制,如下: | ||||
* 图片(image): 1M,支持JPG格式 | * 图片(image): 1M,支持JPG格式 | ||||
* 语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式 | * 语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式 | ||||
@@ -36,6 +39,8 @@ public interface WxCpMediaService { | |||||
throws WxErrorException, IOException; | throws WxErrorException, IOException; | ||||
/** | /** | ||||
* 上传多媒体文件. | |||||
* | |||||
* @param mediaType 媒体类型 | * @param mediaType 媒体类型 | ||||
* @param file 文件对象 | * @param file 文件对象 | ||||
* @see #upload(String, String, InputStream) | * @see #upload(String, String, InputStream) | ||||
@@ -44,7 +49,7 @@ public interface WxCpMediaService { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 下载多媒体文件 | |||||
* 下载多媒体文件. | |||||
* 根据微信文档,视频文件下载不了,会返回null | * 根据微信文档,视频文件下载不了,会返回null | ||||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件 | * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件 | ||||
* </pre> | * </pre> | ||||
@@ -54,4 +59,17 @@ public interface WxCpMediaService { | |||||
*/ | */ | ||||
File download(String mediaId) throws WxErrorException; | File download(String mediaId) throws WxErrorException; | ||||
/** | |||||
* <pre> | |||||
* 上传图片. | |||||
* 上传图片得到图片URL,该URL永久有效 | |||||
* 返回的图片URL,仅能用于图文消息(mpnews)正文中的图片展示;若用于非企业微信域名下的页面,图片将被屏蔽。 | |||||
* 每个企业每天最多可上传100张图片 | |||||
* 接口url格式:https://qyapi.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN | |||||
* </pre> | |||||
* | |||||
* @param file 上传的文件对象 | |||||
* @return 返回图片url | |||||
*/ | |||||
String uploadImg(File file) throws WxErrorException; | |||||
} | } |
@@ -34,6 +34,19 @@ public interface WxCpOAuth2Service { | |||||
*/ | */ | ||||
String buildAuthorizationUrl(String redirectUri, String state); | String buildAuthorizationUrl(String redirectUri, String state); | ||||
/** | |||||
* <pre> | |||||
* 构造oauth2授权的url连接 | |||||
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code | |||||
* </pre> | |||||
* | |||||
* @param redirectUri 跳转链接地址 | |||||
* @param state 状态码 | |||||
* @param scope 取值参考me.chanjar.weixin.common.api.WxConsts.OAuth2Scope类 | |||||
* @return url | |||||
*/ | |||||
String buildAuthorizationUrl(String redirectUri, String state, String scope); | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 用oauth2获取用户信息 | * 用oauth2获取用户信息 | ||||
@@ -51,16 +64,17 @@ public interface WxCpOAuth2Service { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 用oauth2获取用户信息 | |||||
* 根据code获取成员信息 | |||||
* http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息 | * http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息 | ||||
* https://work.weixin.qq.com/api/doc#10028/根据code获取成员信息 | |||||
* 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。 | * 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。 | ||||
* | * | ||||
* 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param agentId 企业号应用的id | * @param agentId 企业号应用的id | ||||
* @param code 微信oauth授权返回的代码 | |||||
* @return [userid, deviceid] | |||||
* @param code 通过成员授权获取到的code,最大为512字节。每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。 | |||||
* @return [UserId, DeviceId, OpenId, user_ticket, expires_in] | |||||
* @see #getUserInfo(String) | * @see #getUserInfo(String) | ||||
*/ | */ | ||||
String[] getUserInfo(Integer agentId, String code) throws WxErrorException; | String[] getUserInfo(Integer agentId, String code) throws WxErrorException; | ||||
@@ -77,7 +91,7 @@ public interface WxCpOAuth2Service { | |||||
* 需要有对应应用的使用权限,且成员必须在授权应用的可见范围内。 | * 需要有对应应用的使用权限,且成员必须在授权应用的可见范围内。 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param userTicket 成员票据 | |||||
* @param userTicket 成员票据 | |||||
*/ | */ | ||||
WxCpUserDetail getUserDetail(String userTicket) throws WxErrorException; | WxCpUserDetail getUserDetail(String userTicket) throws WxErrorException; | ||||
} | } |
@@ -1,11 +1,12 @@ | |||||
package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
import java.util.List; | |||||
import java.util.Map; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.bean.WxCpInviteResult; | import me.chanjar.weixin.cp.bean.WxCpInviteResult; | ||||
import me.chanjar.weixin.cp.bean.WxCpUser; | import me.chanjar.weixin.cp.bean.WxCpUser; | ||||
import java.util.List; | |||||
import java.util.Map; | |||||
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -18,7 +19,7 @@ import java.util.Map; | |||||
public interface WxCpUserService { | public interface WxCpUserService { | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 用在二次验证的时候 | |||||
* 用在二次验证的时候. | |||||
* 企业在员工验证成功后,调用本方法告诉企业号平台该员工关注成功。 | * 企业在员工验证成功后,调用本方法告诉企业号平台该员工关注成功。 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
@@ -28,7 +29,7 @@ public interface WxCpUserService { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 获取部门成员(详情) | |||||
* 获取部门成员(详情). | |||||
* | * | ||||
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98.28.E8.AF.A6.E6.83.85.29 | * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98.28.E8.AF.A6.E6.83.85.29 | ||||
* </pre> | * </pre> | ||||
@@ -41,7 +42,7 @@ public interface WxCpUserService { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 获取部门成员 | |||||
* 获取部门成员. | |||||
* | * | ||||
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98 | * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98 | ||||
* </pre> | * </pre> | ||||
@@ -53,14 +54,14 @@ public interface WxCpUserService { | |||||
List<WxCpUser> listSimpleByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | List<WxCpUser> listSimpleByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | ||||
/** | /** | ||||
* 新建用户 | |||||
* 新建用户. | |||||
* | * | ||||
* @param user 用户对象 | * @param user 用户对象 | ||||
*/ | */ | ||||
void create(WxCpUser user) throws WxErrorException; | void create(WxCpUser user) throws WxErrorException; | ||||
/** | /** | ||||
* 更新用户 | |||||
* 更新用户. | |||||
* | * | ||||
* @param user 用户对象 | * @param user 用户对象 | ||||
*/ | */ | ||||
@@ -68,7 +69,7 @@ public interface WxCpUserService { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 删除用户/批量删除成员 | |||||
* 删除用户/批量删除成员. | |||||
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98 | * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
@@ -77,7 +78,7 @@ public interface WxCpUserService { | |||||
void delete(String... userIds) throws WxErrorException; | void delete(String... userIds) throws WxErrorException; | ||||
/** | /** | ||||
* 获取用户 | |||||
* 获取用户. | |||||
* | * | ||||
* @param userid 用户id | * @param userid 用户id | ||||
*/ | */ | ||||
@@ -85,7 +86,7 @@ public interface WxCpUserService { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 邀请成员 | |||||
* 邀请成员. | |||||
* 企业可通过接口批量邀请成员使用企业微信,邀请后将通过短信或邮件下发通知。 | * 企业可通过接口批量邀请成员使用企业微信,邀请后将通过短信或邮件下发通知。 | ||||
* 请求方式:POST(HTTPS) | * 请求方式:POST(HTTPS) | ||||
* 请求地址: https://qyapi.weixin.qq.com/cgi-bin/batch/invite?access_token=ACCESS_TOKEN | * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/batch/invite?access_token=ACCESS_TOKEN | ||||
@@ -113,14 +114,14 @@ public interface WxCpUserService { | |||||
* @param userId 企业内的成员id | * @param userId 企业内的成员id | ||||
* @param agentId 非必填,整型,仅用于发红包。其它场景该参数不要填,如微信支付、企业转账、电子发票 | * @param agentId 非必填,整型,仅用于发红包。其它场景该参数不要填,如微信支付、企业转账、电子发票 | ||||
* @return map对象,可能包含以下值: | * @return map对象,可能包含以下值: | ||||
* - openid 企业微信成员userid对应的openid,若有传参agentid,则是针对该agentid的openid。否则是针对企业微信corpid的openid | |||||
* - appid 应用的appid,若请求包中不包含agentid则不返回appid。该appid在使用微信红包时会用到 | |||||
* - openid 企业微信成员userid对应的openid,若有传参agentid,则是针对该agentid的openid。否则是针对企业微信corpid的openid | |||||
* - appid 应用的appid,若请求包中不包含agentid则不返回appid。该appid在使用微信红包时会用到 | |||||
*/ | */ | ||||
Map<String, String> userId2Openid(String userId, Integer agentId) throws WxErrorException; | Map<String, String> userId2Openid(String userId, Integer agentId) throws WxErrorException; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* openid转userid | |||||
* openid转userid. | |||||
* | * | ||||
* 该接口主要应用于使用微信支付、微信红包和企业转账之后的结果查询。 | * 该接口主要应用于使用微信支付、微信红包和企业转账之后的结果查询。 | ||||
* 开发者需要知道某个结果事件的openid对应企业微信内成员的信息时,可以通过调用该接口进行转换查询。 | * 开发者需要知道某个结果事件的openid对应企业微信内成员的信息时,可以通过调用该接口进行转换查询。 | ||||
@@ -134,4 +135,17 @@ public interface WxCpUserService { | |||||
* @return userid 该openid在企业微信对应的成员userid | * @return userid 该openid在企业微信对应的成员userid | ||||
*/ | */ | ||||
String openid2UserId(String openid) throws WxErrorException; | String openid2UserId(String openid) throws WxErrorException; | ||||
/** | |||||
* 获取外部联系人详情. | |||||
* <pre> | |||||
* 企业可通过此接口,根据外部联系人的userid,拉取外部联系人详情。权限说明: | |||||
* 企业需要使用外部联系人管理secret所获取的accesstoken来调用 | |||||
* 第三方应用需拥有“企业客户”权限。 | |||||
* 第三方应用调用时,返回的跟进人follow_user仅包含应用可见范围之内的成员。 | |||||
* </pre> | |||||
* | |||||
* @param userId 外部联系人的userid | |||||
*/ | |||||
WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException; | |||||
} | } |
@@ -1,9 +1,17 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | package me.chanjar.weixin.cp.api.impl; | ||||
import com.google.gson.Gson; | |||||
import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonParser; | |||||
import com.google.gson.reflect.TypeToken; | |||||
import me.chanjar.weixin.common.error.WxError; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.api.WxCpAgentService; | import me.chanjar.weixin.cp.api.WxCpAgentService; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.bean.WxCpAgent; | import me.chanjar.weixin.cp.bean.WxCpAgent; | ||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
import java.util.List; | |||||
/** | /** | ||||
@@ -15,6 +23,8 @@ import me.chanjar.weixin.cp.bean.WxCpAgent; | |||||
* @author <a href="https://github.com/huansinho">huansinho</a> | * @author <a href="https://github.com/huansinho">huansinho</a> | ||||
*/ | */ | ||||
public class WxCpAgentServiceImpl implements WxCpAgentService { | public class WxCpAgentServiceImpl implements WxCpAgentService { | ||||
private static final JsonParser JSON_PARSER = new JsonParser(); | |||||
private WxCpService mainService; | private WxCpService mainService; | ||||
public WxCpAgentServiceImpl(WxCpService mainService) { | public WxCpAgentServiceImpl(WxCpService mainService) { | ||||
@@ -23,15 +33,36 @@ public class WxCpAgentServiceImpl implements WxCpAgentService { | |||||
@Override | @Override | ||||
public WxCpAgent get(Integer agentId) throws WxErrorException { | public WxCpAgent get(Integer agentId) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/agent/get"; | |||||
if (agentId != null) { | |||||
url += "?agentid=" + agentId; | |||||
} else { | |||||
if (agentId == null) { | |||||
throw new IllegalArgumentException("缺少agentid参数"); | throw new IllegalArgumentException("缺少agentid参数"); | ||||
} | } | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/agent/get?agentid=" + agentId; | |||||
String responseContent = this.mainService.get(url, null); | String responseContent = this.mainService.get(url, null); | ||||
return WxCpAgent.fromJson(responseContent); | return WxCpAgent.fromJson(responseContent); | ||||
} | } | ||||
@Override | |||||
public void set(WxCpAgent agentInfo) throws WxErrorException { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/agent/set"; | |||||
String responseContent = this.mainService.post(url, agentInfo.toJson()); | |||||
JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | |||||
if (jsonObject.get("errcode").getAsInt() != 0) { | |||||
throw new WxErrorException(WxError.fromJson(responseContent)); | |||||
} | |||||
} | |||||
@Override | |||||
public List<WxCpAgent> list() throws WxErrorException { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/agent/list"; | |||||
String responseContent = this.mainService.get(url, null); | |||||
JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | |||||
if (jsonObject.get("errcode").getAsInt() != 0) { | |||||
throw new WxErrorException(WxError.fromJson(responseContent)); | |||||
} | |||||
return WxCpGsonBuilder.create().fromJson(jsonObject.get("agentlist").toString(), new TypeToken<List<WxCpAgent>>() { | |||||
}.getType()); | |||||
} | |||||
} | } |
@@ -1,5 +1,10 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | package me.chanjar.weixin.cp.api.impl; | ||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.UUID; | |||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.fs.FileUtils; | import me.chanjar.weixin.common.util.fs.FileUtils; | ||||
@@ -8,17 +13,13 @@ import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||||
import me.chanjar.weixin.cp.api.WxCpMediaService; | import me.chanjar.weixin.cp.api.WxCpMediaService; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.UUID; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 媒体管理接口 | |||||
* 媒体管理接口. | |||||
* Created by Binary Wang on 2017-6-25. | * Created by Binary Wang on 2017-6-25. | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
* </pre> | * </pre> | ||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | */ | ||||
public class WxCpMediaServiceImpl implements WxCpMediaService { | public class WxCpMediaServiceImpl implements WxCpMediaService { | ||||
private WxCpService mainService; | private WxCpService mainService; | ||||
@@ -35,16 +36,22 @@ public class WxCpMediaServiceImpl implements WxCpMediaService { | |||||
@Override | @Override | ||||
public WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException { | public WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType; | |||||
return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), url, file); | |||||
return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), | |||||
MEDIA_UPLOAD_URL + mediaType, file); | |||||
} | } | ||||
@Override | @Override | ||||
public File download(String mediaId) throws WxErrorException { | public File download(String mediaId) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; | |||||
return this.mainService.execute( | return this.mainService.execute( | ||||
BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(), | BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(), | ||||
this.mainService.getWxCpConfigStorage().getTmpDirFile()), | this.mainService.getWxCpConfigStorage().getTmpDirFile()), | ||||
url, "media_id=" + mediaId); | |||||
MEDIA_GET_URL, "media_id=" + mediaId); | |||||
} | |||||
@Override | |||||
public String uploadImg(File file) throws WxErrorException { | |||||
final WxMediaUploadResult result = this.mainService | |||||
.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), IMG_UPLOAD_URL, file); | |||||
return result.getUrl(); | |||||
} | } | ||||
} | } |
@@ -4,6 +4,7 @@ import com.google.gson.GsonBuilder; | |||||
import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||
import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
import me.chanjar.weixin.common.api.WxConsts; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.http.URIUtil; | import me.chanjar.weixin.common.util.http.URIUtil; | ||||
import me.chanjar.weixin.common.util.json.GsonHelper; | import me.chanjar.weixin.common.util.json.GsonHelper; | ||||
@@ -16,6 +17,8 @@ import me.chanjar.weixin.cp.bean.WxCpUserDetail; | |||||
* | * | ||||
* Created by Binary Wang on 2017-6-25. | * Created by Binary Wang on 2017-6-25. | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
* | |||||
* @author Binary Wang | |||||
* </pre> | * </pre> | ||||
*/ | */ | ||||
public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { | public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { | ||||
@@ -35,16 +38,27 @@ public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { | |||||
@Override | @Override | ||||
public String buildAuthorizationUrl(String redirectUri, String state) { | public String buildAuthorizationUrl(String redirectUri, String state) { | ||||
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?"; | |||||
url += "appid=" + this.mainService.getWxCpConfigStorage().getCorpId(); | |||||
url += "&redirect_uri=" + URIUtil.encodeURIComponent(redirectUri); | |||||
url += "&response_type=code"; | |||||
url += "&scope=snsapi_base"; | |||||
return this.buildAuthorizationUrl(redirectUri, state, WxConsts.OAuth2Scope.SNSAPI_BASE); | |||||
} | |||||
@Override | |||||
public String buildAuthorizationUrl(String redirectUri, String state, String scope) { | |||||
StringBuilder url = new StringBuilder("https://open.weixin.qq.com/connect/oauth2/authorize?"); | |||||
url.append("appid=").append(this.mainService.getWxCpConfigStorage().getCorpId()); | |||||
url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectUri)); | |||||
url.append("&response_type=code"); | |||||
url.append("&scope=").append(scope); | |||||
if (WxConsts.OAuth2Scope.SNSAPI_PRIVATEINFO.equals(scope) | |||||
|| WxConsts.OAuth2Scope.SNSAPI_USERINFO.equals(scope)) { | |||||
url.append("&agentid=").append(this.mainService.getWxCpConfigStorage().getAgentId()); | |||||
} | |||||
if (state != null) { | if (state != null) { | ||||
url += "&state=" + state; | |||||
url.append("&state=").append(state); | |||||
} | } | ||||
url += "#wechat_redirect"; | |||||
return url; | |||||
url.append("#wechat_redirect"); | |||||
return url.toString(); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -61,7 +75,10 @@ public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { | |||||
JsonObject jo = je.getAsJsonObject(); | JsonObject jo = je.getAsJsonObject(); | ||||
return new String[]{GsonHelper.getString(jo, "UserId"), | return new String[]{GsonHelper.getString(jo, "UserId"), | ||||
GsonHelper.getString(jo, "DeviceId"), | GsonHelper.getString(jo, "DeviceId"), | ||||
GsonHelper.getString(jo, "OpenId")}; | |||||
GsonHelper.getString(jo, "OpenId"), | |||||
GsonHelper.getString(jo, "user_ticket"), | |||||
GsonHelper.getString(jo, "expires_in") | |||||
}; | |||||
} | } | ||||
@Override | @Override | ||||
@@ -1,18 +1,23 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | package me.chanjar.weixin.cp.api.impl; | ||||
import java.util.List; | |||||
import java.util.Map; | |||||
import com.google.common.collect.Maps; | import com.google.common.collect.Maps; | ||||
import com.google.gson.*; | |||||
import com.google.gson.JsonArray; | |||||
import com.google.gson.JsonElement; | |||||
import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonParser; | |||||
import com.google.gson.JsonPrimitive; | |||||
import com.google.gson.reflect.TypeToken; | import com.google.gson.reflect.TypeToken; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.api.WxCpUserService; | import me.chanjar.weixin.cp.api.WxCpUserService; | ||||
import me.chanjar.weixin.cp.bean.WxCpInviteResult; | import me.chanjar.weixin.cp.bean.WxCpInviteResult; | ||||
import me.chanjar.weixin.cp.bean.WxCpUser; | import me.chanjar.weixin.cp.bean.WxCpUser; | ||||
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import java.util.List; | |||||
import java.util.Map; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* Created by BinaryWang on 2017/6/24. | * Created by BinaryWang on 2017/6/24. | ||||
@@ -178,4 +183,11 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return tmpJsonElement.getAsJsonObject().get("userid").getAsString(); | return tmpJsonElement.getAsJsonObject().get("userid").getAsString(); | ||||
} | } | ||||
@Override | |||||
public WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/crm/get_external_contact?external_userid=" + userId; | |||||
String responseContent = this.mainService.get(url, null); | |||||
return WxCpUserExternalContactInfo.fromJson(responseContent); | |||||
} | |||||
} | } |
@@ -1,7 +1,10 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import com.google.gson.annotations.SerializedName; | import com.google.gson.annotations.SerializedName; | ||||
import lombok.AllArgsConstructor; | |||||
import lombok.Builder; | |||||
import lombok.Data; | import lombok.Data; | ||||
import lombok.NoArgsConstructor; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import java.io.Serializable; | import java.io.Serializable; | ||||
@@ -16,16 +19,20 @@ import java.util.List; | |||||
* @author <a href="https://github.com/huansinho">huansinho</a> | * @author <a href="https://github.com/huansinho">huansinho</a> | ||||
*/ | */ | ||||
@Data | @Data | ||||
@Builder | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
public class WxCpAgent implements Serializable { | public class WxCpAgent implements Serializable { | ||||
private static final long serialVersionUID = 5002894979081127234L; | |||||
@SerializedName("errcode") | @SerializedName("errcode") | ||||
private Integer errcode; | |||||
private Integer errCode; | |||||
@SerializedName("errmsg") | @SerializedName("errmsg") | ||||
private String errmsg; | |||||
private String errMsg; | |||||
@SerializedName("agentid") | @SerializedName("agentid") | ||||
private Integer agentid; | |||||
private Integer agentId; | |||||
@SerializedName("name") | @SerializedName("name") | ||||
private String name; | private String name; | ||||
@@ -33,14 +40,17 @@ public class WxCpAgent implements Serializable { | |||||
@SerializedName("square_logo_url") | @SerializedName("square_logo_url") | ||||
private String squareLogoUrl; | private String squareLogoUrl; | ||||
@SerializedName("logo_mediaid") | |||||
private String logoMediaId; | |||||
@SerializedName("description") | @SerializedName("description") | ||||
private String description; | private String description; | ||||
@SerializedName("allow_userinfos") | @SerializedName("allow_userinfos") | ||||
private Users allowUserinfos; | |||||
private Users allowUserInfos; | |||||
@SerializedName("allow_partys") | @SerializedName("allow_partys") | ||||
private Partys allowPartys; | |||||
private Parties allowParties; | |||||
@SerializedName("allow_tags") | @SerializedName("allow_tags") | ||||
private Tags allowTags; | private Tags allowTags; | ||||
@@ -55,7 +65,7 @@ public class WxCpAgent implements Serializable { | |||||
private Integer reportLocationFlag; | private Integer reportLocationFlag; | ||||
@SerializedName("isreportenter") | @SerializedName("isreportenter") | ||||
private Integer isreportenter; | |||||
private Integer isReportEnter; | |||||
@SerializedName("home_url") | @SerializedName("home_url") | ||||
private String homeUrl; | private String homeUrl; | ||||
@@ -71,26 +81,25 @@ public class WxCpAgent implements Serializable { | |||||
@Data | @Data | ||||
public static class Users implements Serializable { | public static class Users implements Serializable { | ||||
@SerializedName("user") | @SerializedName("user") | ||||
private List<User> user; | |||||
private List<User> users; | |||||
} | } | ||||
@Data | @Data | ||||
public class User implements Serializable { | public class User implements Serializable { | ||||
@SerializedName("userid") | @SerializedName("userid") | ||||
private String userid; | |||||
private String userId; | |||||
} | } | ||||
@Data | @Data | ||||
public class Partys { | |||||
public class Parties { | |||||
@SerializedName("partyid") | @SerializedName("partyid") | ||||
private List<Integer> partyids = null; | |||||
private List<Integer> partyIds = null; | |||||
} | } | ||||
@Data | @Data | ||||
public class Tags { | public class Tags { | ||||
@SerializedName("tagid") | @SerializedName("tagid") | ||||
private List<Integer> tagids = null; | |||||
private List<Integer> tagIds = null; | |||||
} | } | ||||
} | } |
@@ -1,15 +1,17 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import java.io.Serializable; | |||||
import java.util.Collections; | |||||
import java.util.List; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import com.google.common.base.Splitter; | import com.google.common.base.Splitter; | ||||
import com.google.gson.annotations.SerializedName; | import com.google.gson.annotations.SerializedName; | ||||
import lombok.Data; | import lombok.Data; | ||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import org.apache.commons.lang3.StringUtils; | |||||
import java.io.Serializable; | |||||
import java.util.Collections; | |||||
import java.util.List; | |||||
/** | /** | ||||
* 邀请成员的结果对象类. | * 邀请成员的结果对象类. | ||||
@@ -23,7 +25,7 @@ public class WxCpInviteResult implements Serializable { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
public static WxCpInviteResult fromJson(String json) { | public static WxCpInviteResult fromJson(String json) { | ||||
@@ -1,15 +1,17 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import java.io.Serializable; | |||||
import java.util.Collections; | |||||
import java.util.List; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import com.google.common.base.Splitter; | import com.google.common.base.Splitter; | ||||
import com.google.gson.annotations.SerializedName; | import com.google.gson.annotations.SerializedName; | ||||
import lombok.Data; | import lombok.Data; | ||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import org.apache.commons.lang3.StringUtils; | |||||
import java.io.Serializable; | |||||
import java.util.Collections; | |||||
import java.util.List; | |||||
/** | /** | ||||
* 消息发送结果对象类. | * 消息发送结果对象类. | ||||
@@ -23,7 +25,7 @@ public class WxCpMessageSendResult implements Serializable { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
public static WxCpMessageSendResult fromJson(String json) { | public static WxCpMessageSendResult fromJson(String json) { | ||||
@@ -1,15 +1,17 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import java.io.Serializable; | |||||
import java.util.Collections; | |||||
import java.util.List; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import com.google.common.base.Splitter; | import com.google.common.base.Splitter; | ||||
import com.google.gson.annotations.SerializedName; | import com.google.gson.annotations.SerializedName; | ||||
import lombok.Data; | import lombok.Data; | ||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import org.apache.commons.lang3.StringUtils; | |||||
import java.io.Serializable; | |||||
import java.util.Collections; | |||||
import java.util.List; | |||||
/** | /** | ||||
* 为标签添加或移除用户结果对象类. | * 为标签添加或移除用户结果对象类. | ||||
@@ -23,7 +25,7 @@ public class WxCpTagAddOrRemoveUsersResult implements Serializable { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
public static WxCpTagAddOrRemoveUsersResult fromJson(String json) { | public static WxCpTagAddOrRemoveUsersResult fromJson(String json) { | ||||
@@ -1,13 +1,15 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import lombok.AllArgsConstructor; | |||||
import lombok.Data; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
import java.io.Serializable; | import java.io.Serializable; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
import lombok.AllArgsConstructor; | |||||
import lombok.Builder; | |||||
import lombok.Data; | |||||
import lombok.NoArgsConstructor; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
/** | /** | ||||
* 微信用户信息. | * 微信用户信息. | ||||
* | * | ||||
@@ -24,6 +26,7 @@ public class WxCpUser implements Serializable { | |||||
private Gender gender; | private Gender gender; | ||||
private String email; | private String email; | ||||
private String avatar; | private String avatar; | ||||
private String avatarMediaId; | |||||
private Integer status; | private Integer status; | ||||
private Integer enable; | private Integer enable; | ||||
private Integer isLeader; | private Integer isLeader; | ||||
@@ -33,6 +36,14 @@ public class WxCpUser implements Serializable { | |||||
private String telephone; | private String telephone; | ||||
private String qrCode; | private String qrCode; | ||||
private Boolean toInvite; | private Boolean toInvite; | ||||
/** | |||||
* 成员对外信息. | |||||
*/ | |||||
private List<ExternalAttribute> externalAttrs = new ArrayList<>(); | |||||
public void addExternalAttr(ExternalAttribute externalAttr) { | |||||
this.externalAttrs.add(externalAttr); | |||||
} | |||||
public void addExtAttr(String name, String value) { | public void addExtAttr(String name, String value) { | ||||
this.extAttrs.add(new Attr(name, value)); | this.extAttrs.add(new Attr(name, value)); | ||||
@@ -53,4 +64,39 @@ public class WxCpUser implements Serializable { | |||||
private String value; | private String value; | ||||
} | } | ||||
@Data | |||||
@Builder | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
public static class ExternalAttribute { | |||||
/** | |||||
* 属性类型: 0-本文 1-网页 2-小程序. | |||||
*/ | |||||
private int type; | |||||
/** | |||||
* 属性名称: 需要先确保在管理端有创建改属性,否则会忽略. | |||||
*/ | |||||
private String name; | |||||
/** | |||||
* 文本属性内容,长度限制12个UTF8字符. | |||||
*/ | |||||
private String value; | |||||
/** | |||||
* 网页的url,必须包含http或者https头. | |||||
*/ | |||||
private String url; | |||||
/** | |||||
* 小程序的展示标题,长度限制12个UTF8字符. | |||||
* 或者 网页的展示标题,长度限制12个UTF8字符 | |||||
*/ | |||||
private String title; | |||||
/** | |||||
* 小程序appid,必须是有在本企业安装授权的小程序,否则会被忽略. | |||||
*/ | |||||
private String appid; | |||||
/** | |||||
* 小程序的页面路径. | |||||
*/ | |||||
private String pagePath; | |||||
} | |||||
} | } |
@@ -13,12 +13,42 @@ import lombok.Data; | |||||
*/ | */ | ||||
@Data | @Data | ||||
public class WxCpUserDetail { | public class WxCpUserDetail { | ||||
/** | |||||
* 成员UserID | |||||
*/ | |||||
@SerializedName("userid") | @SerializedName("userid") | ||||
private String userId; | private String userId; | ||||
/** | |||||
* 成员姓名 | |||||
*/ | |||||
private String name; | private String name; | ||||
/** | |||||
* 成员手机号,仅在用户同意snsapi_privateinfo授权时返回 | |||||
*/ | |||||
private String mobile; | private String mobile; | ||||
/** | |||||
* 性别。0表示未定义,1表示男性,2表示女性 | |||||
*/ | |||||
private String gender; | private String gender; | ||||
/** | |||||
* 成员邮箱,仅在用户同意snsapi_privateinfo授权时返回 | |||||
*/ | |||||
private String email; | private String email; | ||||
@SerializedName("qrCode") | |||||
/** | |||||
* 头像url。注:如果要获取小图将url最后的”/0”改成”/100”即可。仅在用户同意snsapi_privateinfo授权时返回 | |||||
*/ | |||||
private String avatar; | |||||
/** | |||||
* 员工个人二维码(扫描可添加为外部联系人),仅在用户同意snsapi_privateinfo授权时返回 | |||||
*/ | |||||
@SerializedName("qr_code") | |||||
private String qrCode; | private String qrCode; | ||||
} | } |
@@ -0,0 +1,126 @@ | |||||
package me.chanjar.weixin.cp.bean; | |||||
import java.util.List; | |||||
import com.google.gson.annotations.SerializedName; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Builder; | |||||
import lombok.Data; | |||||
import lombok.Getter; | |||||
import lombok.NoArgsConstructor; | |||||
import lombok.Setter; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
/** | |||||
* <pre> | |||||
* 外部联系人详情 | |||||
* Created by Binary Wang on 2018/9/16. | |||||
* 参考文档:https://work.weixin.qq.com/api/doc#13878 | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
@Getter | |||||
@Setter | |||||
public class WxCpUserExternalContactInfo { | |||||
@SerializedName("external_contact") | |||||
private ExternalContact externalContact; | |||||
@SerializedName("follow_user") | |||||
private List<FollowedUser> followedUsers; | |||||
@Getter | |||||
@Setter | |||||
public static class ExternalContact { | |||||
@SerializedName("external_userid") | |||||
private String externalUserId; | |||||
@SerializedName("position") | |||||
private String position; | |||||
@SerializedName("name") | |||||
private String name; | |||||
@SerializedName("avatar") | |||||
private String avatar; | |||||
@SerializedName("corp_name") | |||||
private String corpName; | |||||
@SerializedName("corp_full_name") | |||||
private String corpFullName; | |||||
@SerializedName("type") | |||||
private Integer type; | |||||
@SerializedName("gender") | |||||
private Integer gender; | |||||
@SerializedName("unionid") | |||||
private String unionId; | |||||
@SerializedName("external_profile") | |||||
private ExternalProfile externalProfile; | |||||
} | |||||
@Setter | |||||
@Getter | |||||
public static class ExternalProfile { | |||||
@SerializedName("external_attr") | |||||
private List<ExternalAttribute> externalAttrs; | |||||
} | |||||
@Data | |||||
@Builder | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
public static class ExternalAttribute { | |||||
@Setter | |||||
@Getter | |||||
public static class Text { | |||||
private String value; | |||||
} | |||||
@Setter | |||||
@Getter | |||||
public static class Web { | |||||
private String title; | |||||
private String url; | |||||
} | |||||
@Setter | |||||
@Getter | |||||
public static class MiniProgram { | |||||
@SerializedName("pagepath") | |||||
private String pagePath; | |||||
private String appid; | |||||
private String title; | |||||
} | |||||
private int type; | |||||
private String name; | |||||
private Text text; | |||||
private Web web; | |||||
@SerializedName("miniprogram") | |||||
private MiniProgram miniProgram; | |||||
} | |||||
@Setter | |||||
@Getter | |||||
public static class FollowedUser { | |||||
@SerializedName("userid") | |||||
private String userId; | |||||
private String remark; | |||||
private String description; | |||||
@SerializedName("createtime") | |||||
private Long createTime; | |||||
} | |||||
public static WxCpUserExternalContactInfo fromJson(String json) { | |||||
return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalContactInfo.class); | |||||
} | |||||
} |
@@ -1,35 +1,38 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.Serializable; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import org.apache.commons.io.IOUtils; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; | ||||
import lombok.Data; | import lombok.Data; | ||||
import lombok.extern.slf4j.Slf4j; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | ||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpConfigStorage; | ||||
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | ||||
import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | ||||
import org.apache.commons.io.IOUtils; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.Serializable; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 微信推送过来的消息,也是同步回复给用户的消息,xml格式 | * 微信推送过来的消息,也是同步回复给用户的消息,xml格式 | ||||
* 相关字段的解释看微信开发者文档: | * 相关字段的解释看微信开发者文档: | ||||
* http://mp.weixin.qq.com/wiki/index.php?title=接收普通消息 | |||||
* http://mp.weixin.qq.com/wiki/index.php?title=接收事件推送 | |||||
* http://mp.weixin.qq.com/wiki/index.php?title=接收语音识别结果 | |||||
* https://work.weixin.qq.com/api/doc#12973 | |||||
* https://work.weixin.qq.com/api/doc#12974 | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @author Daniel Qian | * @author Daniel Qian | ||||
*/ | */ | ||||
@XStreamAlias("xml") | |||||
@Data | @Data | ||||
@Slf4j | |||||
@XStreamAlias("xml") | |||||
public class WxCpXmlMessage implements Serializable { | public class WxCpXmlMessage implements Serializable { | ||||
private static final long serialVersionUID = -1042994982179476410L; | private static final long serialVersionUID = -1042994982179476410L; | ||||
@@ -51,6 +54,24 @@ public class WxCpXmlMessage implements Serializable { | |||||
@XStreamAlias("CreateTime") | @XStreamAlias("CreateTime") | ||||
private Long createTime; | private Long createTime; | ||||
/** | |||||
* <pre> | |||||
* 当接受用户消息时,可能会获得以下值: | |||||
* {@link WxConsts.XmlMsgType#TEXT} | |||||
* {@link WxConsts.XmlMsgType#IMAGE} | |||||
* {@link WxConsts.XmlMsgType#VOICE} | |||||
* {@link WxConsts.XmlMsgType#VIDEO} | |||||
* {@link WxConsts.XmlMsgType#LOCATION} | |||||
* {@link WxConsts.XmlMsgType#LINK} | |||||
* {@link WxConsts.XmlMsgType#EVENT} | |||||
* 当发送消息的时候使用: | |||||
* {@link WxConsts.XmlMsgType#TEXT} | |||||
* {@link WxConsts.XmlMsgType#IMAGE} | |||||
* {@link WxConsts.XmlMsgType#VOICE} | |||||
* {@link WxConsts.XmlMsgType#VIDEO} | |||||
* {@link WxConsts.XmlMsgType#NEWS} | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("MsgType") | @XStreamAlias("MsgType") | ||||
@XStreamConverter(value = XStreamCDataConverter.class) | @XStreamConverter(value = XStreamCDataConverter.class) | ||||
private String msgType; | private String msgType; | ||||
@@ -128,15 +149,172 @@ public class WxCpXmlMessage implements Serializable { | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | @XStreamConverter(value = XStreamCDataConverter.class) | ||||
private String recognition; | private String recognition; | ||||
/** | |||||
* 通讯录变更事件. | |||||
* 请参考常量 me.chanjar.weixin.cp.WxCpConsts.ContactChangeType | |||||
*/ | |||||
@XStreamAlias("ChangeType") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String changeType; | |||||
/** | |||||
* 变更信息的成员UserID. | |||||
*/ | |||||
@XStreamAlias("UserID") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String userId; | |||||
/** | |||||
* 新的UserID,变更时推送(userid由系统生成时可更改一次). | |||||
*/ | |||||
@XStreamAlias("NewUserID") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String newUserId; | |||||
/** | |||||
* 成员名称. | |||||
* 或者部门名称 | |||||
*/ | |||||
@XStreamAlias("Name") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String name; | |||||
/** | |||||
* 成员部门列表. | |||||
*/ | |||||
@XStreamAlias("Department") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String department; | |||||
/** | |||||
* 手机号码. | |||||
*/ | |||||
@XStreamAlias("Mobile") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String mobile; | |||||
/** | |||||
* 职位信息。长度为0~64个字节. | |||||
*/ | |||||
@XStreamAlias("Position") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String position; | |||||
/** | |||||
* 性别,1表示男性,2表示女性. | |||||
*/ | |||||
@XStreamAlias("Gender") | |||||
private Integer gender; | |||||
/** | |||||
* 邮箱. | |||||
*/ | |||||
@XStreamAlias("Email") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String email; | |||||
/** | |||||
* 头像url。注:如果要获取小图将url最后的”/0”改成”/100”即可. | |||||
*/ | |||||
@XStreamAlias("Avatar") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String avatar; | |||||
/** | |||||
* 英文名. | |||||
*/ | |||||
@XStreamAlias("EnglishName") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String englishName; | |||||
/** | |||||
* 上级字段,标识是否为上级。0表示普通成员,1表示上级. | |||||
*/ | |||||
@XStreamAlias("IsLeader") | |||||
private Integer isLeader; | |||||
/** | |||||
* 座机. | |||||
*/ | |||||
@XStreamAlias("Telephone") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String telephone; | |||||
/** | |||||
* 扩展属性. | |||||
*/ | |||||
@XStreamAlias("ExtAttr") | |||||
private ExtAttr extAttrs = new ExtAttr(); | |||||
/** | |||||
* 部门Id. | |||||
*/ | |||||
@XStreamAlias("Id") | |||||
private Integer id; | |||||
/** | |||||
* 父部门id. | |||||
*/ | |||||
@XStreamAlias("ParentId") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String parentId; | |||||
/** | |||||
* 部门排序. | |||||
*/ | |||||
@XStreamAlias("Order") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String order; | |||||
/** | |||||
* 标签Id. | |||||
*/ | |||||
@XStreamAlias("TagId") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String tagId; | |||||
/** | |||||
* 标签中新增的成员userid列表,用逗号分隔. | |||||
*/ | |||||
@XStreamAlias("AddUserItems") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String addUserItems; | |||||
/** | |||||
* 标签中删除的成员userid列表,用逗号分隔. | |||||
*/ | |||||
@XStreamAlias("DelUserItems") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String delUserItems; | |||||
/** | |||||
* 标签中新增的部门id列表,用逗号分隔. | |||||
*/ | |||||
@XStreamAlias("AddPartyItems") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String addPartyItems; | |||||
/** | |||||
* 标签中删除的部门id列表,用逗号分隔. | |||||
*/ | |||||
@XStreamAlias("DelPartyItems") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String delPartyItems; | |||||
/////////////////////////////////////// | /////////////////////////////////////// | ||||
// 群发消息返回的结果 | // 群发消息返回的结果 | ||||
/////////////////////////////////////// | /////////////////////////////////////// | ||||
/** | /** | ||||
* 群发的结果. | |||||
* 多个时间共用字段. | |||||
* 1. 群发的结果. | |||||
* 2. 通讯录变更事件 | |||||
* 激活状态:1=已激活 2=已禁用 4=未激活 已激活代表已激活企业微信或已关注微工作台(原企业号). | |||||
*/ | */ | ||||
@XStreamAlias("Status") | @XStreamAlias("Status") | ||||
@XStreamConverter(value = XStreamCDataConverter.class) | @XStreamConverter(value = XStreamCDataConverter.class) | ||||
private String status; | private String status; | ||||
/** | /** | ||||
* group_id下粉丝数;或者openid_list中的粉丝数. | * group_id下粉丝数;或者openid_list中的粉丝数. | ||||
*/ | */ | ||||
@@ -181,19 +359,16 @@ public class WxCpXmlMessage implements Serializable { | |||||
/** | /** | ||||
* 从加密字符串转换. | * 从加密字符串转换. | ||||
*/ | */ | ||||
public static WxCpXmlMessage fromEncryptedXml( | |||||
String encryptedXml, | |||||
WxCpConfigStorage wxCpConfigStorage, | |||||
String timestamp, String nonce, String msgSignature) { | |||||
public static WxCpXmlMessage fromEncryptedXml(String encryptedXml, WxCpConfigStorage wxCpConfigStorage, | |||||
String timestamp, String nonce, String msgSignature) { | |||||
WxCpCryptUtil cryptUtil = new WxCpCryptUtil(wxCpConfigStorage); | WxCpCryptUtil cryptUtil = new WxCpCryptUtil(wxCpConfigStorage); | ||||
String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); | String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); | ||||
log.debug("解密后的原始xml消息内容:{}", plainText); | |||||
return fromXml(plainText); | return fromXml(plainText); | ||||
} | } | ||||
public static WxCpXmlMessage fromEncryptedXml( | |||||
InputStream is, | |||||
WxCpConfigStorage wxCpConfigStorage, | |||||
String timestamp, String nonce, String msgSignature) { | |||||
public static WxCpXmlMessage fromEncryptedXml(InputStream is, WxCpConfigStorage wxCpConfigStorage, | |||||
String timestamp, String nonce, String msgSignature) { | |||||
try { | try { | ||||
return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxCpConfigStorage, timestamp, nonce, msgSignature); | return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxCpConfigStorage, timestamp, nonce, msgSignature); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
@@ -201,39 +376,9 @@ public class WxCpXmlMessage implements Serializable { | |||||
} | } | ||||
} | } | ||||
/** | |||||
* <pre> | |||||
* 当接受用户消息时,可能会获得以下值: | |||||
* {@link WxConsts.XmlMsgType#TEXT} | |||||
* {@link WxConsts.XmlMsgType#IMAGE} | |||||
* {@link WxConsts.XmlMsgType#VOICE} | |||||
* {@link WxConsts.XmlMsgType#VIDEO} | |||||
* {@link WxConsts.XmlMsgType#LOCATION} | |||||
* {@link WxConsts.XmlMsgType#LINK} | |||||
* {@link WxConsts.XmlMsgType#EVENT} | |||||
* </pre> | |||||
*/ | |||||
public String getMsgType() { | |||||
return this.msgType; | |||||
} | |||||
/** | |||||
* <pre> | |||||
* 当发送消息的时候使用: | |||||
* {@link WxConsts.XmlMsgType#TEXT} | |||||
* {@link WxConsts.XmlMsgType#IMAGE} | |||||
* {@link WxConsts.XmlMsgType#VOICE} | |||||
* {@link WxConsts.XmlMsgType#VIDEO} | |||||
* {@link WxConsts.XmlMsgType#NEWS} | |||||
* </pre> | |||||
*/ | |||||
public void setMsgType(String msgType) { | |||||
this.msgType = msgType; | |||||
} | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
@Data | @Data | ||||
@@ -255,6 +400,23 @@ public class WxCpXmlMessage implements Serializable { | |||||
private String scanResult; | private String scanResult; | ||||
} | } | ||||
@Data | |||||
public static class ExtAttr { | |||||
@XStreamAlias("Item") | |||||
protected final List<Item> items = new ArrayList<>(); | |||||
@Data | |||||
public static class Item { | |||||
@XStreamAlias("Name") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String name; | |||||
@XStreamAlias("Value") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String value; | |||||
} | |||||
} | |||||
@Data | @Data | ||||
@XStreamAlias("SendPicsInfo") | @XStreamAlias("SendPicsInfo") | ||||
public static class SendPicsInfo { | public static class SendPicsInfo { | ||||
@@ -1,16 +1,26 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import java.io.Serializable; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; | ||||
import lombok.Data; | import lombok.Data; | ||||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | ||||
import me.chanjar.weixin.cp.bean.outxmlbuilder.ImageBuilder; | |||||
import me.chanjar.weixin.cp.bean.outxmlbuilder.NewsBuilder; | |||||
import me.chanjar.weixin.cp.bean.outxmlbuilder.TextBuilder; | |||||
import me.chanjar.weixin.cp.bean.outxmlbuilder.VideoBuilder; | |||||
import me.chanjar.weixin.cp.bean.outxmlbuilder.VoiceBuilder; | |||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpConfigStorage; | ||||
import me.chanjar.weixin.cp.bean.outxmlbuilder.*; | |||||
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | ||||
import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | ||||
import java.io.Serializable; | |||||
/** | |||||
* 被动回复消息. | |||||
* https://work.weixin.qq.com/api/doc#12975 | |||||
* | |||||
* @author Daniel Qian | |||||
*/ | |||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
@Data | @Data | ||||
public abstract class WxCpXmlOutMessage implements Serializable { | public abstract class WxCpXmlOutMessage implements Serializable { | ||||
@@ -1,11 +1,13 @@ | |||||
package me.chanjar.weixin.cp.config; | package me.chanjar.weixin.cp.config; | ||||
import java.io.File; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | ||||
import java.io.File; | |||||
/** | /** | ||||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | ||||
* | * | ||||
@@ -201,7 +203,7 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -43,17 +43,24 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||||
private volatile File tmpDirFile; | private volatile File tmpDirFile; | ||||
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | ||||
public WxCpJedisConfigStorage(JedisPool jedisPool) { | |||||
this.jedisPool = jedisPool; | |||||
} | |||||
public WxCpJedisConfigStorage(String host, int port) { | public WxCpJedisConfigStorage(String host, int port) { | ||||
this.jedisPool = new JedisPool(host, port); | |||||
jedisPool = new JedisPool(host, port); | |||||
} | } | ||||
public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port) { | public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port) { | ||||
this.jedisPool = new JedisPool(poolConfig, host, port); | |||||
jedisPool = new JedisPool(poolConfig, host, port); | |||||
} | |||||
public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port, int timeout, String password) { | |||||
jedisPool = new JedisPool(poolConfig, host, port, timeout, password); | |||||
} | } | ||||
public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port, int timeout, final String password) { | |||||
this.jedisPool = new JedisPool(poolConfig, host, port, timeout, password); | |||||
public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port, int timeout, String password, int database) { | |||||
jedisPool = new JedisPool(poolConfig, host, port, timeout, password, database); | |||||
} | } | ||||
/** | /** | ||||
@@ -10,6 +10,9 @@ import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
import me.chanjar.weixin.cp.bean.WxCpTag; | import me.chanjar.weixin.cp.bean.WxCpTag; | ||||
import me.chanjar.weixin.cp.bean.WxCpUser; | import me.chanjar.weixin.cp.bean.WxCpUser; | ||||
/** | |||||
* @author Daniel Qian | |||||
*/ | |||||
public class WxCpGsonBuilder { | public class WxCpGsonBuilder { | ||||
public static final GsonBuilder INSTANCE = new GsonBuilder(); | public static final GsonBuilder INSTANCE = new GsonBuilder(); | ||||
@@ -8,21 +8,30 @@ | |||||
*/ | */ | ||||
package me.chanjar.weixin.cp.util.json; | package me.chanjar.weixin.cp.util.json; | ||||
import com.google.gson.*; | |||||
import java.lang.reflect.Type; | |||||
import com.google.gson.JsonArray; | |||||
import com.google.gson.JsonDeserializationContext; | |||||
import com.google.gson.JsonDeserializer; | |||||
import com.google.gson.JsonElement; | |||||
import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonParseException; | |||||
import com.google.gson.JsonPrimitive; | |||||
import com.google.gson.JsonSerializationContext; | |||||
import com.google.gson.JsonSerializer; | |||||
import me.chanjar.weixin.common.util.json.GsonHelper; | import me.chanjar.weixin.common.util.json.GsonHelper; | ||||
import me.chanjar.weixin.cp.bean.Gender; | import me.chanjar.weixin.cp.bean.Gender; | ||||
import me.chanjar.weixin.cp.bean.WxCpUser; | import me.chanjar.weixin.cp.bean.WxCpUser; | ||||
import java.lang.reflect.Type; | |||||
/** | /** | ||||
* @author Daniel Qian | * @author Daniel Qian | ||||
*/ | */ | ||||
public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSerializer<WxCpUser> { | public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSerializer<WxCpUser> { | ||||
private static final String EXTERNAL_PROFILE = "external_profile"; | |||||
private static final String EXTERNAL_ATTR = "external_attr"; | |||||
@Override | @Override | ||||
public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) | |||||
throws JsonParseException { | |||||
public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { | |||||
JsonObject o = json.getAsJsonObject(); | JsonObject o = json.getAsJsonObject(); | ||||
WxCpUser user = new WxCpUser(); | WxCpUser user = new WxCpUser(); | ||||
@@ -43,6 +52,7 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri | |||||
user.setGender(Gender.fromCode(GsonHelper.getString(o, "gender"))); | user.setGender(Gender.fromCode(GsonHelper.getString(o, "gender"))); | ||||
user.setEmail(GsonHelper.getString(o, "email")); | user.setEmail(GsonHelper.getString(o, "email")); | ||||
user.setAvatar(GsonHelper.getString(o, "avatar")); | user.setAvatar(GsonHelper.getString(o, "avatar")); | ||||
user.setAvatarMediaId(GsonHelper.getString(o, "avatar_mediaid")); | |||||
user.setStatus(GsonHelper.getInteger(o, "status")); | user.setStatus(GsonHelper.getInteger(o, "status")); | ||||
user.setEnable(GsonHelper.getInteger(o, "enable")); | user.setEnable(GsonHelper.getInteger(o, "enable")); | ||||
user.setIsLeader(GsonHelper.getInteger(o, "isleader")); | user.setIsLeader(GsonHelper.getInteger(o, "isleader")); | ||||
@@ -62,6 +72,53 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri | |||||
user.getExtAttrs().add(attr); | user.getExtAttrs().add(attr); | ||||
} | } | ||||
} | } | ||||
if (GsonHelper.isNotNull(o.get(EXTERNAL_PROFILE))) { | |||||
JsonArray attrJsonElements = o.get(EXTERNAL_PROFILE).getAsJsonObject().get(EXTERNAL_ATTR).getAsJsonArray(); | |||||
for (JsonElement element : attrJsonElements) { | |||||
final Integer type = GsonHelper.getInteger(element.getAsJsonObject(), "type"); | |||||
final String name = GsonHelper.getString(element.getAsJsonObject(), "name"); | |||||
switch (type) { | |||||
case 0: { | |||||
user.getExternalAttrs() | |||||
.add(WxCpUser.ExternalAttribute.builder() | |||||
.type(type) | |||||
.name(name) | |||||
.value(GsonHelper.getString(element.getAsJsonObject().get("text").getAsJsonObject(), "value")) | |||||
.build() | |||||
); | |||||
break; | |||||
} | |||||
case 1: { | |||||
final JsonObject web = element.getAsJsonObject().get("web").getAsJsonObject(); | |||||
user.getExternalAttrs() | |||||
.add(WxCpUser.ExternalAttribute.builder() | |||||
.type(type) | |||||
.name(name) | |||||
.url(GsonHelper.getString(web, "url")) | |||||
.title(GsonHelper.getString(web, "title")) | |||||
.build() | |||||
); | |||||
break; | |||||
} | |||||
case 2: { | |||||
final JsonObject miniprogram = element.getAsJsonObject().get("miniprogram").getAsJsonObject(); | |||||
user.getExternalAttrs() | |||||
.add(WxCpUser.ExternalAttribute.builder() | |||||
.type(type) | |||||
.name(name) | |||||
.appid(GsonHelper.getString(miniprogram, "appid")) | |||||
.pagePath(GsonHelper.getString(miniprogram, "pagepath")) | |||||
.title(GsonHelper.getString(miniprogram, "title")) | |||||
.build() | |||||
); | |||||
break; | |||||
} | |||||
default://ignored | |||||
} | |||||
} | |||||
} | |||||
return user; | return user; | ||||
} | } | ||||
@@ -96,6 +153,9 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri | |||||
if (user.getAvatar() != null) { | if (user.getAvatar() != null) { | ||||
o.addProperty("avatar", user.getAvatar()); | o.addProperty("avatar", user.getAvatar()); | ||||
} | } | ||||
if (user.getAvatarMediaId() != null) { | |||||
o.addProperty("avatar_mediaid", user.getAvatarMediaId()); | |||||
} | |||||
if (user.getStatus() != null) { | if (user.getStatus() != null) { | ||||
o.addProperty("status", user.getStatus()); | o.addProperty("status", user.getStatus()); | ||||
} | } | ||||
@@ -133,6 +193,45 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri | |||||
attrsJson.add("attrs", attrsJsonArray); | attrsJson.add("attrs", attrsJsonArray); | ||||
o.add("extattr", attrsJson); | o.add("extattr", attrsJson); | ||||
} | } | ||||
if (user.getExternalAttrs().size() > 0) { | |||||
JsonArray attrsJsonArray = new JsonArray(); | |||||
for (WxCpUser.ExternalAttribute attr : user.getExternalAttrs()) { | |||||
JsonObject attrJson = new JsonObject(); | |||||
attrJson.addProperty("type",attr.getType()); | |||||
attrJson.addProperty("name", attr.getName()); | |||||
switch (attr.getType()) { | |||||
case 0: { | |||||
JsonObject text = new JsonObject(); | |||||
text.addProperty("value", attr.getValue()); | |||||
attrJson.add("text", text); | |||||
break; | |||||
} | |||||
case 1: { | |||||
JsonObject web = new JsonObject(); | |||||
web.addProperty("url", attr.getUrl()); | |||||
web.addProperty("title", attr.getTitle()); | |||||
attrJson.add("web", web); | |||||
break; | |||||
} | |||||
case 2: { | |||||
JsonObject miniprogram = new JsonObject(); | |||||
miniprogram.addProperty("appid", attr.getAppid()); | |||||
miniprogram.addProperty("pagepath", attr.getPagePath()); | |||||
miniprogram.addProperty("title", attr.getTitle()); | |||||
attrJson.add("miniprogram", miniprogram); | |||||
break; | |||||
} | |||||
default://忽略 | |||||
} | |||||
attrsJsonArray.add(attrJson); | |||||
} | |||||
JsonObject attrsJson = new JsonObject(); | |||||
attrsJson.add(EXTERNAL_ATTR, attrsJsonArray); | |||||
o.add(EXTERNAL_PROFILE, attrsJson); | |||||
} | |||||
return o; | return o; | ||||
} | } | ||||
@@ -1,23 +1,21 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | package me.chanjar.weixin.cp.api.impl; | ||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
import me.chanjar.weixin.common.api.WxConsts; | |||||
import me.chanjar.weixin.common.bean.menu.WxMenu; | |||||
import me.chanjar.weixin.common.bean.menu.WxMenuButton; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.cp.api.ApiTestModule; | import me.chanjar.weixin.cp.api.ApiTestModule; | ||||
import me.chanjar.weixin.cp.api.WxCpAgentService; | import me.chanjar.weixin.cp.api.WxCpAgentService; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.bean.WxCpAgent; | import me.chanjar.weixin.cp.bean.WxCpAgent; | ||||
import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage; | |||||
import org.mockito.Mock; | |||||
import org.testng.Assert; | import org.testng.Assert; | ||||
import org.testng.annotations.DataProvider; | |||||
import org.testng.annotations.Guice; | import org.testng.annotations.Guice; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
import java.util.List; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.when; | |||||
import static org.testng.Assert.assertEquals; | import static org.testng.Assert.assertEquals; | ||||
import static org.testng.Assert.assertNotNull; | |||||
import static org.mockito.Mockito.*; | |||||
/** | /** | ||||
@@ -28,24 +26,64 @@ import static org.mockito.Mockito.*; | |||||
* | * | ||||
* @author <a href="https://github.com/huansinho">huansinho</a> | * @author <a href="https://github.com/huansinho">huansinho</a> | ||||
*/ | */ | ||||
@Guice(modules = ApiTestModule.class) | |||||
public class WxCpAgentServiceImplTest { | public class WxCpAgentServiceImplTest { | ||||
protected WxCpService wxService = mock(WxCpService.class); | |||||
@Inject | |||||
private WxCpService wxCpService; | |||||
@Test | @Test | ||||
public void testGet() throws Exception { | public void testGet() throws Exception { | ||||
String returnJson = "{\"errcode\": 0,\"errmsg\": \"ok\",\"agentid\": 9,\"name\": \"测试应用\",\"square_logo_url\": \"http://wx.qlogo.cn/mmhead/alksjf;lasdjf;lasjfuodiuj3rj2o34j/0\",\"description\": \"这是一个企业号应用\",\"allow_userinfos\": {\"user\": [{\"userid\": \"0009854\"}, {\"userid\": \"1723\"}, {\"userid\": \"5625\"}]},\"allow_partys\": {\"partyid\": [42762742]},\"allow_tags\": {\"tagid\": [23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7]},\"close\": 0,\"redirect_domain\": \"weixin.com.cn\",\"report_location_flag\": 0,\"isreportenter\": 0,\"home_url\": \"\"}"; | |||||
when(wxService.get("https://qyapi.weixin.qq.com/cgi-bin/agent/get?agentid=9", null)).thenReturn(returnJson); | |||||
when(wxService.getAgentService()).thenReturn(new WxCpAgentServiceImpl(wxService)); | |||||
final Integer agentId = this.wxCpService.getWxCpConfigStorage().getAgentId(); | |||||
WxCpAgent wxCpAgent = this.wxCpService.getAgentService().get(agentId); | |||||
assertThat(wxCpAgent.getAgentId()).isEqualTo(agentId); | |||||
assertThat(wxCpAgent.getAllowUserInfos().getUsers().toArray()).isNotEmpty(); | |||||
assertThat(wxCpAgent.getAllowParties().getPartyIds().toArray()).isNotEmpty(); | |||||
assertThat(wxCpAgent.getAllowTags().getTagIds().toArray()).isNotEmpty(); | |||||
} | |||||
@Test | |||||
public void testSet() throws WxErrorException { | |||||
final Integer agentId = this.wxCpService.getWxCpConfigStorage().getAgentId(); | |||||
this.wxCpService.getAgentService().set(WxCpAgent.builder() | |||||
.description("abcddd") | |||||
.logoMediaId("aaaaaaaaaaaaaa") | |||||
.agentId(agentId) | |||||
.build()); | |||||
} | |||||
@Test | |||||
public void testList() throws WxErrorException { | |||||
List<WxCpAgent> list = this.wxCpService.getAgentService().list(); | |||||
assertThat(list).isNotEmpty(); | |||||
assertThat(list.get(0).getAgentId()).isNotNull(); | |||||
assertThat(list.get(0).getName()).isNotEmpty(); | |||||
assertThat(list.get(0).getSquareLogoUrl()).isNotEmpty(); | |||||
} | |||||
public static class MockTest { | |||||
private WxCpService wxService = mock(WxCpService.class); | |||||
@Test | |||||
public void testGet() throws Exception { | |||||
String returnJson = "{\"errcode\": 0,\"errmsg\": \"ok\",\"agentid\": 9,\"name\": \"测试应用\",\"square_logo_url\": \"http://wx.qlogo.cn/mmhead/alksjf;lasdjf;lasjfuodiuj3rj2o34j/0\",\"description\": \"这是一个企业号应用\",\"allow_userinfos\": {\"user\": [{\"userid\": \"0009854\"}, {\"userid\": \"1723\"}, {\"userid\": \"5625\"}]},\"allow_partys\": {\"partyid\": [42762742]},\"allow_tags\": {\"tagid\": [23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7]},\"close\": 0,\"redirect_domain\": \"weixin.com.cn\",\"report_location_flag\": 0,\"isreportenter\": 0,\"home_url\": \"\"}"; | |||||
when(wxService.get("https://qyapi.weixin.qq.com/cgi-bin/agent/get?agentid=9", null)).thenReturn(returnJson); | |||||
when(wxService.getAgentService()).thenReturn(new WxCpAgentServiceImpl(wxService)); | |||||
WxCpAgentService wxAgentService = this.wxService.getAgentService(); | |||||
WxCpAgent wxCpAgent = wxAgentService.get(9); | |||||
WxCpAgentService wxAgentService = this.wxService.getAgentService(); | |||||
WxCpAgent wxCpAgent = wxAgentService.get(9); | |||||
assertEquals(9, wxCpAgent.getAgentId().intValue()); | |||||
Assert.assertEquals(9, wxCpAgent.getAgentid().intValue()); | |||||
assertEquals(new Integer[]{42762742}, wxCpAgent.getAllowParties().getPartyIds().toArray()); | |||||
Assert.assertEquals(new Integer[]{42762742}, wxCpAgent.getAllowPartys().getPartyids().toArray()); | |||||
assertEquals(new Integer[]{23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7}, wxCpAgent.getAllowTags().getTagIds().toArray()); | |||||
Assert.assertEquals(new Integer[]{23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7}, wxCpAgent.getAllowTags().getTagids().toArray()); | |||||
} | |||||
} | } | ||||
@@ -1,5 +1,14 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | package me.chanjar.weixin.cp.api.impl; | ||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.net.URL; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import org.testng.annotations.*; | |||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
@@ -7,22 +16,14 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.cp.api.ApiTestModule; | import me.chanjar.weixin.cp.api.ApiTestModule; | ||||
import me.chanjar.weixin.cp.api.TestConstants; | import me.chanjar.weixin.cp.api.TestConstants; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import org.testng.annotations.*; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.testng.Assert.*; | import static org.testng.Assert.*; | ||||
/** | /** | ||||
* <pre> | |||||
* | |||||
* Created by Binary Wang on 2017-6-25. | * Created by Binary Wang on 2017-6-25. | ||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
* </pre> | |||||
*/ | */ | ||||
@Guice(modules = ApiTestModule.class) | @Guice(modules = ApiTestModule.class) | ||||
public class WxCpMediaServiceImplTest { | public class WxCpMediaServiceImplTest { | ||||
@@ -35,8 +36,8 @@ public class WxCpMediaServiceImplTest { | |||||
public Object[][] mediaData() { | public Object[][] mediaData() { | ||||
return new Object[][]{ | return new Object[][]{ | ||||
new Object[]{WxConsts.MediaFileType.IMAGE, TestConstants.FILE_JPG, "mm.jpeg"}, | new Object[]{WxConsts.MediaFileType.IMAGE, TestConstants.FILE_JPG, "mm.jpeg"}, | ||||
new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_MP3, "mm.mp3"}, | |||||
new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_AMR, "mm.amr"},//{"errcode":301017,"errmsg":"voice file only support amr like myvoice.amr"} | |||||
new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_MP3, "mm.mp3"},//{"errcode":301017,"errmsg":"voice file only support amr like myvoice.amr"} | |||||
new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_AMR, "mm.amr"}, | |||||
new Object[]{WxConsts.MediaFileType.VIDEO, TestConstants.FILE_MP4, "mm.mp4"}, | new Object[]{WxConsts.MediaFileType.VIDEO, TestConstants.FILE_MP4, "mm.mp4"}, | ||||
new Object[]{WxConsts.MediaFileType.FILE, TestConstants.FILE_JPG, "mm.jpeg"} | new Object[]{WxConsts.MediaFileType.FILE, TestConstants.FILE_JPG, "mm.jpeg"} | ||||
}; | }; | ||||
@@ -75,4 +76,10 @@ public class WxCpMediaServiceImplTest { | |||||
System.out.println(file); | System.out.println(file); | ||||
} | } | ||||
@Test | |||||
public void testUploadImg() throws WxErrorException { | |||||
URL url = ClassLoader.getSystemResource("mm.jpeg"); | |||||
String res = this.wxService.getMediaService().uploadImg(new File(url.getFile())); | |||||
assertThat(res).isNotEmpty(); | |||||
} | |||||
} | } |
@@ -1,22 +1,23 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | package me.chanjar.weixin.cp.api.impl; | ||||
import java.util.List; | |||||
import java.util.Map; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import org.testng.annotations.*; | |||||
import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.cp.api.ApiTestModule; | import me.chanjar.weixin.cp.api.ApiTestModule; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.bean.Gender; | import me.chanjar.weixin.cp.bean.Gender; | ||||
import me.chanjar.weixin.cp.bean.WxCpInviteResult; | import me.chanjar.weixin.cp.bean.WxCpInviteResult; | ||||
import me.chanjar.weixin.cp.bean.WxCpUser; | import me.chanjar.weixin.cp.bean.WxCpUser; | ||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import org.testng.annotations.Guice; | |||||
import org.testng.annotations.Test; | |||||
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import static org.testng.Assert.assertNotEquals; | |||||
import static org.testng.Assert.assertNotNull; | |||||
import static org.testng.Assert.*; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -109,4 +110,11 @@ public class WxCpUserServiceImplTest { | |||||
System.out.println(result); | System.out.println(result); | ||||
assertNotNull(result); | assertNotNull(result); | ||||
} | } | ||||
@Test | |||||
public void testGetExternalContact() throws WxErrorException { | |||||
WxCpUserExternalContactInfo result = this.wxCpService.getUserService().getExternalContact(userId); | |||||
System.out.println(result); | |||||
assertNotNull(result); | |||||
} | |||||
} | } |
@@ -14,11 +14,11 @@ public class WxCpAgentTest { | |||||
WxCpAgent wxCpAgent = WxCpAgent.fromJson(json); | WxCpAgent wxCpAgent = WxCpAgent.fromJson(json); | ||||
Assert.assertEquals(9, wxCpAgent.getAgentid().intValue()); | |||||
Assert.assertEquals(9, wxCpAgent.getAgentId().intValue()); | |||||
Assert.assertEquals(new Integer[]{42762742}, wxCpAgent.getAllowPartys().getPartyids().toArray()); | |||||
Assert.assertEquals(new Integer[]{42762742}, wxCpAgent.getAllowParties().getPartyIds().toArray()); | |||||
Assert.assertEquals(new Integer[]{23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7}, wxCpAgent.getAllowTags().getTagids().toArray()); | |||||
Assert.assertEquals(new Integer[]{23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7}, wxCpAgent.getAllowTags().getTagIds().toArray()); | |||||
} | } | ||||
@@ -0,0 +1,129 @@ | |||||
package me.chanjar.weixin.cp.bean; | |||||
import java.util.List; | |||||
import org.testng.annotations.*; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
/** | |||||
* <pre> | |||||
* | |||||
* Created by Binary Wang on 2018/9/16. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
public class WxCpUserExternalContactInfoTest { | |||||
@Test | |||||
public void testFromJson() { | |||||
final String json = "{\n" + | |||||
" \"errcode\": 0,\n" + | |||||
" \"errmsg\": \"ok\",\n" + | |||||
" \"external_contact\": {\n" + | |||||
" \"external_userid\": \"woAJ2GCAAAXtWyujaWJHDDGi0mACH71w\",\n" + | |||||
" \"name\": \"李四\",\n" + | |||||
" \"position\": \"Mangaer\",\n" + | |||||
" \"avatar\": \"http://p.qlogo.cn/bizmail/IcsdgagqefergqerhewSdage/0\",\n" + | |||||
" \"corp_name\": \"腾讯\",\n" + | |||||
" \"corp_full_name\": \"腾讯科技有限公司\",\n" + | |||||
" \"type\": 2,\n" + | |||||
" \"gender\": 1,\n" + | |||||
" \"unionid\": \"ozynqsulJFCZ2z1aYeS8h-nuasdfR1\",\n" + | |||||
" \"external_profile\": {\n" + | |||||
" \"external_attr\": [\n" + | |||||
" {\n" + | |||||
" \"type\": 0,\n" + | |||||
" \"name\": \"文本名称\",\n" + | |||||
" \"text\": {\n" + | |||||
" \"value\": \"文本\"\n" + | |||||
" }\n" + | |||||
" },\n" + | |||||
" {\n" + | |||||
" \"type\": 1,\n" + | |||||
" \"name\": \"网页名称\",\n" + | |||||
" \"web\": {\n" + | |||||
" \"url\": \"http://www.test.com\",\n" + | |||||
" \"title\": \"标题\"\n" + | |||||
" }\n" + | |||||
" },\n" + | |||||
" {\n" + | |||||
" \"type\": 2,\n" + | |||||
" \"name\": \"测试app\",\n" + | |||||
" \"miniprogram\": {\n" + | |||||
" \"appid\": \"wx8bd80126147df384\",\n" + | |||||
" \"pagepath\": \"/index\",\n" + | |||||
" \"title\": \"my miniprogram\"\n" + | |||||
" }\n" + | |||||
" }\n" + | |||||
" ]\n" + | |||||
" }\n" + | |||||
" },\n" + | |||||
" \"follow_user\": [\n" + | |||||
" {\n" + | |||||
" \"userid\": \"rocky\",\n" + | |||||
" \"remark\": \"李部长\",\n" + | |||||
" \"description\": \"对接采购事物\",\n" + | |||||
" \"createtime\": 1525779812\n" + | |||||
" },\n" + | |||||
" {\n" + | |||||
" \"userid\": \"tommy\",\n" + | |||||
" \"remark\": \"李总\",\n" + | |||||
" \"description\": \"采购问题咨询\",\n" + | |||||
" \"createtime\": 1525881637\n" + | |||||
" }\n" + | |||||
" ]\n" + | |||||
"}"; | |||||
final WxCpUserExternalContactInfo contactInfo = WxCpUserExternalContactInfo.fromJson(json); | |||||
assertThat(contactInfo).isNotNull(); | |||||
assertThat(contactInfo.getExternalContact()).isNotNull(); | |||||
assertThat(contactInfo.getExternalContact().getExternalUserId()).isEqualTo("woAJ2GCAAAXtWyujaWJHDDGi0mACH71w"); | |||||
assertThat(contactInfo.getExternalContact().getPosition()).isEqualTo("Mangaer"); | |||||
assertThat(contactInfo.getExternalContact().getAvatar()).isEqualTo("http://p.qlogo.cn/bizmail/IcsdgagqefergqerhewSdage/0"); | |||||
assertThat(contactInfo.getExternalContact().getCorpName()).isEqualTo("腾讯"); | |||||
assertThat(contactInfo.getExternalContact().getCorpFullName()).isEqualTo("腾讯科技有限公司"); | |||||
assertThat(contactInfo.getExternalContact().getType()).isEqualTo(2); | |||||
assertThat(contactInfo.getExternalContact().getGender()).isEqualTo(1); | |||||
assertThat(contactInfo.getExternalContact().getUnionId()).isEqualTo("ozynqsulJFCZ2z1aYeS8h-nuasdfR1"); | |||||
assertThat(contactInfo.getExternalContact().getName()).isEqualTo("李四"); | |||||
assertThat(contactInfo.getExternalContact().getExternalProfile()).isNotNull(); | |||||
final List<WxCpUserExternalContactInfo.ExternalAttribute> externalAttrs = contactInfo.getExternalContact().getExternalProfile().getExternalAttrs(); | |||||
assertThat(externalAttrs).isNotEmpty(); | |||||
final WxCpUserExternalContactInfo.ExternalAttribute externalAttr1 = externalAttrs.get(0); | |||||
assertThat(externalAttr1.getType()).isEqualTo(0); | |||||
assertThat(externalAttr1.getName()).isEqualTo("文本名称"); | |||||
assertThat(externalAttr1.getText().getValue()).isEqualTo("文本"); | |||||
final WxCpUserExternalContactInfo.ExternalAttribute externalAttr2 = externalAttrs.get(1); | |||||
assertThat(externalAttr2.getType()).isEqualTo(1); | |||||
assertThat(externalAttr2.getName()).isEqualTo("网页名称"); | |||||
assertThat(externalAttr2.getWeb().getUrl()).isEqualTo("http://www.test.com"); | |||||
assertThat(externalAttr2.getWeb().getTitle()).isEqualTo("标题"); | |||||
final WxCpUserExternalContactInfo.ExternalAttribute externalAttr3 = externalAttrs.get(2); | |||||
assertThat(externalAttr3.getType()).isEqualTo(2); | |||||
assertThat(externalAttr3.getName()).isEqualTo("测试app"); | |||||
assertThat(externalAttr3.getMiniProgram().getAppid()).isEqualTo("wx8bd80126147df384"); | |||||
assertThat(externalAttr3.getMiniProgram().getPagePath()).isEqualTo("/index"); | |||||
assertThat(externalAttr3.getMiniProgram().getTitle()).isEqualTo("my miniprogram"); | |||||
List<WxCpUserExternalContactInfo.FollowedUser> followedUsers = contactInfo.getFollowedUsers(); | |||||
assertThat(followedUsers).isNotEmpty(); | |||||
assertThat(followedUsers.get(0).getUserId()).isEqualTo("rocky"); | |||||
assertThat(followedUsers.get(0).getRemark()).isEqualTo("李部长"); | |||||
assertThat(followedUsers.get(0).getDescription()).isEqualTo("对接采购事物"); | |||||
assertThat(followedUsers.get(0).getCreateTime()).isEqualTo(1525779812); | |||||
assertThat(followedUsers.get(1).getUserId()).isEqualTo("tommy"); | |||||
assertThat(followedUsers.get(1).getRemark()).isEqualTo("李总"); | |||||
assertThat(followedUsers.get(1).getDescription()).isEqualTo("采购问题咨询"); | |||||
assertThat(followedUsers.get(1).getCreateTime()).isEqualTo(1525881637); | |||||
} | |||||
} |
@@ -1,6 +1,16 @@ | |||||
package me.chanjar.weixin.cp.demo; | package me.chanjar.weixin.cp.demo; | ||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.Map; | |||||
import org.eclipse.jetty.server.Server; | |||||
import org.eclipse.jetty.servlet.ServletHandler; | |||||
import org.eclipse.jetty.servlet.ServletHolder; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.cp.WxCpConsts; | |||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; | import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; | ||||
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | ||||
@@ -9,13 +19,6 @@ import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; | |||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpConfigStorage; | ||||
import me.chanjar.weixin.cp.message.WxCpMessageHandler; | import me.chanjar.weixin.cp.message.WxCpMessageHandler; | ||||
import me.chanjar.weixin.cp.message.WxCpMessageRouter; | import me.chanjar.weixin.cp.message.WxCpMessageRouter; | ||||
import org.eclipse.jetty.server.Server; | |||||
import org.eclipse.jetty.servlet.ServletHandler; | |||||
import org.eclipse.jetty.servlet.ServletHolder; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.Map; | |||||
public class WxCpDemoServer { | public class WxCpDemoServer { | ||||
@@ -78,9 +81,26 @@ public class WxCpDemoServer { | |||||
}; | }; | ||||
wxCpMessageRouter = new WxCpMessageRouter(wxCpService); | wxCpMessageRouter = new WxCpMessageRouter(wxCpService); | ||||
wxCpMessageRouter.rule().async(false).content("哈哈") // 拦截内容为“哈哈”的消息 | |||||
.handler(handler).end().rule().async(false).content("oauth") | |||||
.handler(oauth2handler).end(); | |||||
wxCpMessageRouter.rule() | |||||
.async(false) | |||||
.content("哈哈") // 拦截内容为“哈哈”的消息 | |||||
.handler(handler) | |||||
.end() | |||||
.rule() | |||||
.async(false) | |||||
.content("oauth") | |||||
.handler(oauth2handler) | |||||
.end() | |||||
.rule() | |||||
.event(WxCpConsts.EventType.CHANGE_CONTACT) | |||||
.handler(new WxCpMessageHandler() { | |||||
@Override | |||||
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService, WxSessionManager sessionManager) throws WxErrorException { | |||||
System.out.println("通讯录发生变更"); | |||||
return null; | |||||
} | |||||
}) | |||||
.end(); | |||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,124 @@ | |||||
package me.chanjar.weixin.cp.util.json; | |||||
import org.testng.annotations.*; | |||||
import me.chanjar.weixin.cp.bean.WxCpUser; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
/** | |||||
* <pre> | |||||
* | |||||
* Created by Binary Wang on 2018/9/16. | |||||
* </pre> | |||||
* | |||||
* @author BinaryWang | |||||
*/ | |||||
public class WxCpUserGsonAdapterTest { | |||||
@Test | |||||
public void testDeserialize() { | |||||
final String userJson = "{\n" + | |||||
" \"errcode\": 0,\n" + | |||||
" \"errmsg\": \"ok\",\n" + | |||||
" \"userid\": \"zhangsan\",\n" + | |||||
" \"name\": \"李四\",\n" + | |||||
" \"department\": [1, 2],\n" + | |||||
" \"order\": [1, 2],\n" + | |||||
" \"position\": \"后台工程师\",\n" + | |||||
" \"mobile\": \"15913215421\",\n" + | |||||
" \"gender\": \"1\",\n" + | |||||
" \"email\": \"zhangsan@gzdev.com\",\n" + | |||||
" \"isleader\": 1,\n" + | |||||
" \"avatar\": \"http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3WJ6DSZUfiakYe37PKnQhBIeOQBO4czqrnZDS79FH5Wm5m4X69TBicnHFlhiafvDwklOpZeXYQQ2icg/0\",\n" + | |||||
" \"telephone\": \"020-123456\",\n" + | |||||
" \"enable\": 1,\n" + | |||||
" \"alias\": \"jackzhang\",\n" + | |||||
" \"extattr\": {\n" + | |||||
" \"attrs\": [{\n" + | |||||
" \"name\": \"爱好\",\n" + | |||||
" \"value\": \"旅游\"\n" + | |||||
" }, {\n" + | |||||
" \"name\": \"卡号\",\n" + | |||||
" \"value\": \"1234567234\"\n" + | |||||
" }]\n" + | |||||
" },\n" + | |||||
" \"status\": 1,\n" + | |||||
" \"qr_code\": \"https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=xxx\",\n" + | |||||
" \"external_profile\": {\n" + | |||||
" \"external_attr\": [{\n" + | |||||
" \"type\": 0,\n" + | |||||
" \"name\": \"文本名称\",\n" + | |||||
" \"text\": {\n" + | |||||
" \"value\": \"文本\"\n" + | |||||
" }\n" + | |||||
" },\n" + | |||||
" {\n" + | |||||
" \"type\": 1,\n" + | |||||
" \"name\": \"网页名称\",\n" + | |||||
" \"web\": {\n" + | |||||
" \"url\": \"http://www.test.com\",\n" + | |||||
" \"title\": \"标题\"\n" + | |||||
" }\n" + | |||||
" },\n" + | |||||
" {\n" + | |||||
" \"type\": 2,\n" + | |||||
" \"name\": \"测试app\",\n" + | |||||
" \"miniprogram\": {\n" + | |||||
" \"appid\": \"wx8bd80126147df384\",\n" + | |||||
" \"pagepath\": \"/index\",\n" + | |||||
" \"title\": \"my miniprogram\"\n" + | |||||
" }\n" + | |||||
" }\n" + | |||||
" ]\n" + | |||||
" }\n" + | |||||
"}"; | |||||
final WxCpUser user = WxCpUser.fromJson(userJson); | |||||
assertThat(user).isNotNull(); | |||||
assertThat(user.getExternalAttrs()).isNotEmpty(); | |||||
final WxCpUser.ExternalAttribute externalAttr1 = user.getExternalAttrs().get(0); | |||||
assertThat(externalAttr1.getType()).isEqualTo(0); | |||||
assertThat(externalAttr1.getName()).isEqualTo("文本名称"); | |||||
assertThat(externalAttr1.getValue()).isEqualTo("文本"); | |||||
final WxCpUser.ExternalAttribute externalAttr2 = user.getExternalAttrs().get(1); | |||||
assertThat(externalAttr2.getType()).isEqualTo(1); | |||||
assertThat(externalAttr2.getName()).isEqualTo("网页名称"); | |||||
assertThat(externalAttr2.getUrl()).isEqualTo("http://www.test.com"); | |||||
assertThat(externalAttr2.getTitle()).isEqualTo("标题"); | |||||
final WxCpUser.ExternalAttribute externalAttr3 = user.getExternalAttrs().get(2); | |||||
assertThat(externalAttr3.getType()).isEqualTo(2); | |||||
assertThat(externalAttr3.getName()).isEqualTo("测试app"); | |||||
assertThat(externalAttr3.getAppid()).isEqualTo("wx8bd80126147df384"); | |||||
assertThat(externalAttr3.getPagePath()).isEqualTo("/index"); | |||||
assertThat(externalAttr3.getTitle()).isEqualTo("my miniprogram"); | |||||
} | |||||
@Test | |||||
public void testSerialize() { | |||||
WxCpUser user = new WxCpUser(); | |||||
user.addExternalAttr(WxCpUser.ExternalAttribute.builder() | |||||
.type(0) | |||||
.name("文本名称") | |||||
.value("文本") | |||||
.build()); | |||||
user.addExternalAttr(WxCpUser.ExternalAttribute.builder() | |||||
.type(1) | |||||
.name("网页名称") | |||||
.url("http://www.test.com") | |||||
.title("标题") | |||||
.build()); | |||||
user.addExternalAttr(WxCpUser.ExternalAttribute.builder() | |||||
.type(2) | |||||
.name("测试app") | |||||
.appid("wx8bd80126147df384") | |||||
.pagePath("/index") | |||||
.title("my miniprogram") | |||||
.build()); | |||||
assertThat(user.toJson()).isEqualTo("{\"external_profile\":{\"external_attr\":[{\"type\":0,\"name\":\"文本名称\",\"text\":{\"value\":\"文本\"}},{\"type\":1,\"name\":\"网页名称\",\"web\":{\"url\":\"http://www.test.com\",\"title\":\"标题\"}},{\"type\":2,\"name\":\"测试app\",\"miniprogram\":{\"appid\":\"wx8bd80126147df384\",\"pagepath\":\"/index\",\"title\":\"my miniprogram\"}}]}}"); | |||||
} | |||||
} |
@@ -7,7 +7,7 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>3.1.0</version> | |||||
<version>3.2.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-miniapp</artifactId> | <artifactId>weixin-java-miniapp</artifactId> | ||||
<name>Weixin Java Tools - MiniApp</name> | <name>Weixin Java Tools - MiniApp</name> | ||||
@@ -1,5 +1,7 @@ | |||||
package cn.binarywang.wx.miniapp.api; | package cn.binarywang.wx.miniapp.api; | ||||
import java.util.List; | |||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCategory; | import cn.binarywang.wx.miniapp.bean.code.WxMaCategory; | ||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeAuditStatus; | import cn.binarywang.wx.miniapp.bean.code.WxMaCodeAuditStatus; | ||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest; | import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest; | ||||
@@ -7,8 +9,6 @@ import cn.binarywang.wx.miniapp.bean.code.WxMaCodeSubmitAuditRequest; | |||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution; | import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import java.util.List; | |||||
/** | /** | ||||
* 小程序代码管理相关 API(大部分只能是第三方平台调用) | * 小程序代码管理相关 API(大部分只能是第三方平台调用) | ||||
* 文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN | * 文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN | ||||
@@ -44,11 +44,15 @@ public interface WxMaCodeService { | |||||
/** | /** | ||||
* 获取体验小程序的体验二维码 | * 获取体验小程序的体验二维码 | ||||
* 文档地址: | |||||
* https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN | |||||
* | * | ||||
* @param path 指定体验版二维码跳转到某个具体页面(如果不需要的话,则不需要填path参数,可在路径后以“?参数”方式传入参数) | |||||
* 具体的路径加参数需要urlencode(方法内部处理),比如page/index?action=1编码后得到page%2Findex%3Faction%3D1 | |||||
* @return 二维码 bytes | * @return 二维码 bytes | ||||
* @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档 | * @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档 | ||||
*/ | */ | ||||
byte[] getQrCode() throws WxErrorException; | |||||
byte[] getQrCode(String path) throws WxErrorException; | |||||
/** | /** | ||||
* 获取授权小程序帐号的可选类目 | * 获取授权小程序帐号的可选类目 | ||||
@@ -0,0 +1,48 @@ | |||||
package cn.binarywang.wx.miniapp.api; | |||||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* jsapi相关接口 | |||||
* Created by BinaryWang on 2018/8/5. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
public interface WxMaJsapiService { | |||||
/** | |||||
* 获得jsapi_ticket的url | |||||
*/ | |||||
String GET_JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; | |||||
/** | |||||
* 获得jsapi_ticket,不强制刷新jsapi_ticket | |||||
* | |||||
* @see #getJsapiTicket(boolean) | |||||
*/ | |||||
String getJsapiTicket() throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 获得jsapi_ticket | |||||
* 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干 | |||||
* | |||||
* 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN | |||||
* </pre> | |||||
* | |||||
* @param forceRefresh 强制刷新 | |||||
*/ | |||||
String getJsapiTicket(boolean forceRefresh) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 创建调用jsapi时所需要的签名 | |||||
* | |||||
* 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN | |||||
* </pre> | |||||
*/ | |||||
WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; | |||||
} |
@@ -2,6 +2,7 @@ package cn.binarywang.wx.miniapp.api; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
/** | /** | ||||
@@ -14,6 +15,7 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||||
public interface WxMaMsgService { | public interface WxMaMsgService { | ||||
String KEFU_MESSAGE_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; | String KEFU_MESSAGE_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; | ||||
String TEMPLATE_MSG_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send"; | String TEMPLATE_MSG_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send"; | ||||
String UNIFORM_MSG_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send"; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -32,4 +34,14 @@ public interface WxMaMsgService { | |||||
* </pre> | * </pre> | ||||
*/ | */ | ||||
void sendTemplateMsg(WxMaTemplateMessage templateMessage) throws WxErrorException; | void sendTemplateMsg(WxMaTemplateMessage templateMessage) throws WxErrorException; | ||||
/** | |||||
* <pre> | |||||
* 下发小程序和公众号统一的服务消息 | |||||
* 详情请见: <a href="https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html">下发小程序和公众号统一的服务消息</a> | |||||
* 接口url格式:https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN | |||||
* </pre> | |||||
*/ | |||||
void sendUniformMsg(WxMaUniformMessage uniformMessage) throws WxErrorException; | |||||
} | } |
@@ -1,5 +1,7 @@ | |||||
package cn.binarywang.wx.miniapp.api; | package cn.binarywang.wx.miniapp.api; | ||||
import java.io.File; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | ||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
@@ -17,12 +19,25 @@ public interface WxMaService { | |||||
String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; | String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; | ||||
String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; | String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; | ||||
String IMG_SEC_CHECK_URL = "https://api.weixin.qq.com/wxa/img_sec_check"; | |||||
/** | |||||
* <pre> | |||||
* 校验一张图片是否含有违法违规内容. | |||||
* 应用场景举例:1)图片智能鉴黄:涉及拍照的工具类应用(如美拍,识图类应用)用户拍照上传检测;电商类商品上架图片检测;媒体类用户文章里的图片检测等;2)敏感人脸识别:用户头像;媒体类用户文章里的图片检测;社交类用户上传的图片检测等。频率限制:单个 appId 调用上限为 1000 次/分钟,100,000 次/天 | |||||
* 详情请见: https://developers.weixin.qq.com/miniprogram/dev/api/imgSecCheck.html | |||||
* </pre> | |||||
*/ | |||||
boolean imgSecCheck(File file) throws WxErrorException; | |||||
/** | /** | ||||
* 获取登录后的session信息 | |||||
* 获取登录后的session信息. | |||||
* | * | ||||
* @param jsCode 登录时获取的 code | * @param jsCode 登录时获取的 code | ||||
*/ | */ | ||||
WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException; | WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 验证消息的确来自微信服务器. | * 验证消息的确来自微信服务器. | ||||
@@ -131,6 +146,7 @@ public interface WxMaService { | |||||
/** | /** | ||||
* 返回模板配置相关接口方法的实现类对象, 以方便调用其各个接口. | * 返回模板配置相关接口方法的实现类对象, 以方便调用其各个接口. | ||||
* | |||||
* @return WxMaTemplateService | * @return WxMaTemplateService | ||||
*/ | */ | ||||
WxMaTemplateService getTemplateService(); | WxMaTemplateService getTemplateService(); | ||||
@@ -149,6 +165,13 @@ public interface WxMaService { | |||||
*/ | */ | ||||
WxMaCodeService getCodeService(); | WxMaCodeService getCodeService(); | ||||
/** | |||||
* 返回jsapi操作相关的 API服务类对象 | |||||
* | |||||
* @return WxMaJsapiService | |||||
*/ | |||||
WxMaJsapiService getJsapiService(); | |||||
/** | /** | ||||
* 小程序修改服务器地址、成员管理 API | * 小程序修改服务器地址、成员管理 API | ||||
* | * | ||||
@@ -5,6 +5,8 @@ import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import java.util.Map; | |||||
/** | /** | ||||
* 用户信息相关操作接口. | * 用户信息相关操作接口. | ||||
* | * | ||||
@@ -28,6 +30,16 @@ public interface WxMaUserService { | |||||
*/ | */ | ||||
WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr); | WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr); | ||||
/** | |||||
* 上报用户数据后台接口. | |||||
* <p>小游戏可以通过本接口上报key-value数据到用户的CloudStorage。</p> | |||||
* 文档参考https://developers.weixin.qq.com/minigame/dev/document/open-api/data/setUserStorage.html | |||||
* @param kvMap 要上报的数据 | |||||
* @param sessionKey 通过wx.login 获得的登录态 | |||||
* @param openid | |||||
*/ | |||||
void setUserStorage(Map<String, String> kvMap, String sessionKey, String openid) throws WxErrorException; | |||||
/** | /** | ||||
* 解密用户手机号信息. | * 解密用户手机号信息. | ||||
* | * | ||||
@@ -1,5 +1,15 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | package cn.binarywang.wx.miniapp.api.impl; | ||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.net.URLEncoder; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.nio.file.Files; | |||||
import java.nio.file.Path; | |||||
import java.util.List; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import cn.binarywang.wx.miniapp.api.WxMaCodeService; | import cn.binarywang.wx.miniapp.api.WxMaCodeService; | ||||
import cn.binarywang.wx.miniapp.api.WxMaService; | import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCategory; | import cn.binarywang.wx.miniapp.bean.code.WxMaCategory; | ||||
@@ -17,12 +27,6 @@ import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; | |||||
import me.chanjar.weixin.common.util.http.RequestExecutor; | import me.chanjar.weixin.common.util.http.RequestExecutor; | ||||
import me.chanjar.weixin.common.util.json.GsonHelper; | import me.chanjar.weixin.common.util.json.GsonHelper; | ||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.nio.file.Files; | |||||
import java.nio.file.Path; | |||||
import java.util.List; | |||||
/** | /** | ||||
* @author <a href="https://github.com/charmingoh">Charming</a> | * @author <a href="https://github.com/charmingoh">Charming</a> | ||||
* @since 2018-04-26 20:00 | * @since 2018-04-26 20:00 | ||||
@@ -41,13 +45,19 @@ public class WxMaCodeServiceImpl implements WxMaCodeService { | |||||
} | } | ||||
@Override | @Override | ||||
public byte[] getQrCode() throws WxErrorException { | |||||
public byte[] getQrCode(String path) throws WxErrorException { | |||||
String appId = this.wxMaService.getWxMaConfig().getAppid(); | String appId = this.wxMaService.getWxMaConfig().getAppid(); | ||||
Path qrCodeFilePath = null; | Path qrCodeFilePath = null; | ||||
try { | try { | ||||
RequestExecutor<File, String> executor = BaseMediaDownloadRequestExecutor | RequestExecutor<File, String> executor = BaseMediaDownloadRequestExecutor | ||||
.create(this.wxMaService.getRequestHttp(), Files.createTempDirectory("weixin-java-tools-ma-" + appId).toFile()); | .create(this.wxMaService.getRequestHttp(), Files.createTempDirectory("weixin-java-tools-ma-" + appId).toFile()); | ||||
qrCodeFilePath = this.wxMaService.execute(executor, GET_QRCODE_URL, null).toPath(); | |||||
final StringBuilder url = new StringBuilder(GET_QRCODE_URL); | |||||
if (StringUtils.isNotBlank(path)) { | |||||
url.append("?path=").append(URLEncoder.encode(path, StandardCharsets.UTF_8.name())); | |||||
} | |||||
qrCodeFilePath = this.wxMaService.execute(executor, url.toString(), null).toPath(); | |||||
return Files.readAllBytes(qrCodeFilePath); | return Files.readAllBytes(qrCodeFilePath); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new WxErrorException(WxError.builder().errorMsg(e.getMessage()).build(), e); | throw new WxErrorException(WxError.builder().errorMsg(e.getMessage()).build(), e); | ||||
@@ -0,0 +1,75 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | |||||
import cn.binarywang.wx.miniapp.api.WxMaJsapiService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||||
import com.google.gson.JsonElement; | |||||
import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonParser; | |||||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.common.util.RandomUtils; | |||||
import me.chanjar.weixin.common.util.crypto.SHA1; | |||||
import java.util.concurrent.locks.Lock; | |||||
/** | |||||
* <pre> | |||||
* Created by BinaryWang on 2018/8/5. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
public class WxMaJsapiServiceImpl implements WxMaJsapiService { | |||||
private static final JsonParser JSON_PARSER = new JsonParser(); | |||||
private WxMaService wxMaService; | |||||
public WxMaJsapiServiceImpl(WxMaService wxMaService) { | |||||
this.wxMaService = wxMaService; | |||||
} | |||||
@Override | |||||
public String getJsapiTicket() throws WxErrorException { | |||||
return getJsapiTicket(false); | |||||
} | |||||
@Override | |||||
public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { | |||||
Lock lock = this.wxMaService.getWxMaConfig().getJsapiTicketLock(); | |||||
try { | |||||
lock.lock(); | |||||
if (forceRefresh) { | |||||
this.wxMaService.getWxMaConfig().expireJsapiTicket(); | |||||
} | |||||
if (this.wxMaService.getWxMaConfig().isJsapiTicketExpired()) { | |||||
String responseContent = this.wxMaService.get(GET_JSAPI_TICKET_URL, null); | |||||
JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); | |||||
JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); | |||||
String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); | |||||
int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | |||||
this.wxMaService.getWxMaConfig().updateJsapiTicket(jsapiTicket, expiresInSeconds); | |||||
} | |||||
} finally { | |||||
lock.unlock(); | |||||
} | |||||
return this.wxMaService.getWxMaConfig().getJsapiTicket(); | |||||
} | |||||
@Override | |||||
public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { | |||||
long timestamp = System.currentTimeMillis() / 1000; | |||||
String randomStr = RandomUtils.getRandomStr(); | |||||
String jsapiTicket = getJsapiTicket(false); | |||||
String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, | |||||
"noncestr=" + randomStr, "timestamp=" + timestamp, "url=" + url); | |||||
return WxJsapiSignature | |||||
.builder() | |||||
.appId(this.wxMaService.getWxMaConfig().getAppid()) | |||||
.timestamp(timestamp) | |||||
.nonceStr(randomStr) | |||||
.url(url) | |||||
.signature(signature) | |||||
.build(); | |||||
} | |||||
} |
@@ -4,6 +4,7 @@ import cn.binarywang.wx.miniapp.api.WxMaMsgService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaService; | import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; | |||||
import cn.binarywang.wx.miniapp.constant.WxMaConstants; | import cn.binarywang.wx.miniapp.constant.WxMaConstants; | ||||
import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
@@ -36,4 +37,13 @@ public class WxMaMsgServiceImpl implements WxMaMsgService { | |||||
} | } | ||||
} | } | ||||
@Override | |||||
public void sendUniformMsg(WxMaUniformMessage uniformMessage) throws WxErrorException { | |||||
String responseContent = this.wxMaService.post(UNIFORM_MSG_SEND_URL, uniformMessage.toJson()); | |||||
JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | |||||
if (jsonObject.get(WxMaConstants.ERRCODE).getAsInt() != 0) { | |||||
throw new WxErrorException(WxError.fromJson(responseContent)); | |||||
} | |||||
} | |||||
} | } |
@@ -1,7 +1,23 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | package cn.binarywang.wx.miniapp.api.impl; | ||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import java.util.concurrent.locks.Lock; | |||||
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 org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import cn.binarywang.wx.miniapp.api.WxMaAnalysisService; | import cn.binarywang.wx.miniapp.api.WxMaAnalysisService; | ||||
import cn.binarywang.wx.miniapp.api.WxMaCodeService; | import cn.binarywang.wx.miniapp.api.WxMaCodeService; | ||||
import cn.binarywang.wx.miniapp.api.WxMaJsapiService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaMediaService; | import cn.binarywang.wx.miniapp.api.WxMaMediaService; | ||||
import cn.binarywang.wx.miniapp.api.WxMaMsgService; | import cn.binarywang.wx.miniapp.api.WxMaMsgService; | ||||
import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; | import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; | ||||
@@ -12,31 +28,21 @@ import cn.binarywang.wx.miniapp.api.WxMaUserService; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | ||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
import com.google.common.base.Joiner; | import com.google.common.base.Joiner; | ||||
import com.google.gson.Gson; | |||||
import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
import me.chanjar.weixin.common.error.WxError; | import me.chanjar.weixin.common.error.WxError; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.DataUtils; | import me.chanjar.weixin.common.util.DataUtils; | ||||
import me.chanjar.weixin.common.util.crypto.SHA1; | import me.chanjar.weixin.common.util.crypto.SHA1; | ||||
import me.chanjar.weixin.common.util.http.HttpType; | import me.chanjar.weixin.common.util.http.HttpType; | ||||
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||||
import me.chanjar.weixin.common.util.http.RequestExecutor; | import me.chanjar.weixin.common.util.http.RequestExecutor; | ||||
import me.chanjar.weixin.common.util.http.RequestHttp; | import me.chanjar.weixin.common.util.http.RequestHttp; | ||||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | ||||
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | 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.ApacheHttpClientBuilder; | ||||
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; | 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 org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.io.IOException; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import java.util.concurrent.locks.Lock; | |||||
import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*; | import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*; | ||||
@@ -58,10 +64,13 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||||
private WxMaAnalysisService analysisService = new WxMaAnalysisServiceImpl(this); | private WxMaAnalysisService analysisService = new WxMaAnalysisServiceImpl(this); | ||||
private WxMaCodeService codeService = new WxMaCodeServiceImpl(this); | private WxMaCodeService codeService = new WxMaCodeServiceImpl(this); | ||||
private WxMaSettingService settingService = new WxMaSettingServiceImpl(this); | private WxMaSettingService settingService = new WxMaSettingServiceImpl(this); | ||||
private WxMaJsapiService jsapiService = new WxMaJsapiServiceImpl(this); | |||||
private int retrySleepMillis = 1000; | private int retrySleepMillis = 1000; | ||||
private int maxRetryTimes = 5; | private int maxRetryTimes = 5; | ||||
protected static final Gson GSON = new Gson(); | |||||
@Override | @Override | ||||
public CloseableHttpClient getRequestHttpClient() { | public CloseableHttpClient getRequestHttpClient() { | ||||
return httpClient; | return httpClient; | ||||
@@ -140,6 +149,13 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||||
return this.getWxMaConfig().getAccessToken(); | return this.getWxMaConfig().getAccessToken(); | ||||
} | } | ||||
@Override | |||||
public boolean imgSecCheck(File file) throws WxErrorException { | |||||
//这里只是借用MediaUploadRequestExecutor,并不使用其返回值WxMediaUploadResult | |||||
WxMediaUploadResult result = this.execute(MediaUploadRequestExecutor.create(this.getRequestHttp()), IMG_SEC_CHECK_URL, file); | |||||
return result != null; | |||||
} | |||||
@Override | @Override | ||||
public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException { | public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException { | ||||
final WxMaConfig config = getWxMaConfig(); | final WxMaConfig config = getWxMaConfig(); | ||||
@@ -310,6 +326,11 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||||
return this.codeService; | return this.codeService; | ||||
} | } | ||||
@Override | |||||
public WxMaJsapiService getJsapiService() { | |||||
return this.jsapiService; | |||||
} | |||||
@Override | @Override | ||||
public WxMaSettingService getSettingService() { | public WxMaSettingService getSettingService() { | ||||
return this.settingService; | return this.settingService; | ||||
@@ -1,14 +1,25 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | package cn.binarywang.wx.miniapp.api.impl; | ||||
import org.apache.commons.codec.digest.DigestUtils; | |||||
import cn.binarywang.wx.miniapp.api.WxMaService; | import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
import cn.binarywang.wx.miniapp.api.WxMaUserService; | import cn.binarywang.wx.miniapp.api.WxMaUserService; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | ||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||||
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; | import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; | ||||
import com.google.gson.JsonArray; | |||||
import com.google.gson.JsonObject; | |||||
import me.chanjar.weixin.common.error.WxError; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.SignUtils; | |||||
import org.apache.commons.codec.binary.Hex; | |||||
import org.apache.commons.codec.digest.DigestUtils; | |||||
import javax.crypto.Mac; | |||||
import javax.crypto.spec.SecretKeySpec; | |||||
import java.security.InvalidKeyException; | |||||
import java.security.NoSuchAlgorithmException; | |||||
import java.util.Map; | |||||
/** | /** | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
@@ -30,6 +41,30 @@ public class WxMaUserServiceImpl implements WxMaUserService { | |||||
return WxMaUserInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | return WxMaUserInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | ||||
} | } | ||||
@Override | |||||
public void setUserStorage(Map<String, String> kvMap, String sessionKey, String openid) throws WxErrorException { | |||||
final WxMaConfig config = this.service.getWxMaConfig(); | |||||
JsonObject param = new JsonObject(); | |||||
JsonArray array = new JsonArray(); | |||||
for (Map.Entry<String, String> e : kvMap.entrySet()) { | |||||
JsonObject jsonObject = new JsonObject(); | |||||
jsonObject.addProperty("key", e.getKey()); | |||||
jsonObject.addProperty("value", e.getValue()); | |||||
array.add(jsonObject); | |||||
} | |||||
param.add("kv_list", array); | |||||
String params = param.toString(); | |||||
String signature = SignUtils.createHmacSha256Sign(params, sessionKey); | |||||
String url = String.format("https://api.weixin.qq.com/wxa/set_user_storage" + | |||||
"?appid=%s&signature=%s&openid=%s&sig_method=%s", | |||||
config.getAppid(), signature, openid, "hmac_sha256"); | |||||
String result = this.service.post(url, params); | |||||
WxError error = WxError.fromJson(result); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
} | |||||
@Override | @Override | ||||
public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr) { | public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr) { | ||||
return WxMaPhoneNumberInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | return WxMaPhoneNumberInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | ||||
@@ -1,5 +1,14 @@ | |||||
package cn.binarywang.wx.miniapp.bean; | package cn.binarywang.wx.miniapp.bean; | ||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.Serializable; | |||||
import java.nio.charset.StandardCharsets; | |||||
import org.apache.commons.io.IOUtils; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; | import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; | ||||
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | ||||
@@ -8,14 +17,7 @@ import com.google.gson.annotations.SerializedName; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; | ||||
import lombok.Data; | import lombok.Data; | ||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | ||||
import org.apache.commons.io.IOUtils; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.Serializable; | |||||
import java.nio.charset.StandardCharsets; | |||||
/** | /** | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
@@ -166,7 +168,7 @@ public class WxMaMessage implements Serializable { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
public String toJson() { | public String toJson() { | ||||
@@ -0,0 +1,32 @@ | |||||
package cn.binarywang.wx.miniapp.bean; | |||||
import lombok.Data; | |||||
import lombok.NoArgsConstructor; | |||||
/** | |||||
* <pre> | |||||
* | |||||
* Created by Binary Wang on 2018/9/23. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
@Data | |||||
@NoArgsConstructor | |||||
public class WxMaTemplateData { | |||||
private String name; | |||||
private String value; | |||||
private String color; | |||||
public WxMaTemplateData(String name, String value) { | |||||
this.name = name; | |||||
this.value = value; | |||||
} | |||||
public WxMaTemplateData(String name, String value, String color) { | |||||
this.name = name; | |||||
this.value = value; | |||||
this.color = color; | |||||
} | |||||
} |
@@ -1,12 +1,16 @@ | |||||
package cn.binarywang.wx.miniapp.bean; | package cn.binarywang.wx.miniapp.bean; | ||||
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||||
import lombok.*; | |||||
import java.io.Serializable; | import java.io.Serializable; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Builder; | |||||
import lombok.Getter; | |||||
import lombok.NoArgsConstructor; | |||||
import lombok.Setter; | |||||
/** | /** | ||||
* 模板消息. | * 模板消息. | ||||
* 参考 https://mp.weixin.qq.com/debug/wxadoc/dev/api/notice.html#接口说明 模板消息部分 | * 参考 https://mp.weixin.qq.com/debug/wxadoc/dev/api/notice.html#接口说明 模板消息部分 | ||||
@@ -69,7 +73,7 @@ public class WxMaTemplateMessage implements Serializable { | |||||
* 描述: 模板内容,不填则下发空模板 | * 描述: 模板内容,不填则下发空模板 | ||||
* </pre> | * </pre> | ||||
*/ | */ | ||||
private List<Data> data; | |||||
private List<WxMaTemplateData> data; | |||||
/** | /** | ||||
* 模板内容字体的颜色,不填默认黑色. | * 模板内容字体的颜色,不填默认黑色. | ||||
@@ -91,7 +95,7 @@ public class WxMaTemplateMessage implements Serializable { | |||||
*/ | */ | ||||
private String emphasisKeyword; | private String emphasisKeyword; | ||||
public WxMaTemplateMessage addData(Data datum) { | |||||
public WxMaTemplateMessage addData(WxMaTemplateData datum) { | |||||
if (this.data == null) { | if (this.data == null) { | ||||
this.data = new ArrayList<>(); | this.data = new ArrayList<>(); | ||||
} | } | ||||
@@ -104,23 +108,4 @@ public class WxMaTemplateMessage implements Serializable { | |||||
return WxMaGsonBuilder.create().toJson(this); | return WxMaGsonBuilder.create().toJson(this); | ||||
} | } | ||||
@lombok.Data | |||||
public static class Data { | |||||
private String name; | |||||
private String value; | |||||
private String color; | |||||
public Data(String name, String value) { | |||||
this.name = name; | |||||
this.value = value; | |||||
} | |||||
public Data(String name, String value, String color) { | |||||
this.name = name; | |||||
this.value = value; | |||||
this.color = color; | |||||
} | |||||
} | |||||
} | } |
@@ -0,0 +1,108 @@ | |||||
package cn.binarywang.wx.miniapp.bean; | |||||
import java.io.Serializable; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Builder; | |||||
import lombok.Data; | |||||
import lombok.Getter; | |||||
import lombok.NoArgsConstructor; | |||||
import lombok.Setter; | |||||
/** | |||||
* 模板消息. | |||||
* 参考 https://mp.weixin.qq.com/debug/wxadoc/dev/api/notice.html#接口说明 模板消息部分 | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
@Getter | |||||
@Setter | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
@Builder | |||||
public class WxMaUniformMessage implements Serializable { | |||||
private static final long serialVersionUID = 5063374783759519418L; | |||||
/** | |||||
* 是否发送公众号模版消息,否则发送小程序模版消息. | |||||
*/ | |||||
private boolean isMpTemplateMsg; | |||||
/** | |||||
* 用户openid. | |||||
* 可以是小程序的openid,也可以是mp_template_msg.appid对应的公众号的openid | |||||
*/ | |||||
private String toUser; | |||||
/** | |||||
* 公众号appid,要求与小程序有绑定且同主体. | |||||
*/ | |||||
private String appid; | |||||
/** | |||||
* 公众号或小程序模板ID. | |||||
*/ | |||||
private String templateId; | |||||
/** | |||||
* 公众号模板消息所要跳转的url. | |||||
*/ | |||||
private String url; | |||||
/** | |||||
* 小程序页面路径. | |||||
*/ | |||||
private String page; | |||||
/** | |||||
* 小程序模板消息formid. | |||||
*/ | |||||
private String formId; | |||||
/** | |||||
* 公众号模板消息所要跳转的小程序,小程序的必须与公众号具有绑定关系. | |||||
*/ | |||||
private MiniProgram miniProgram; | |||||
/** | |||||
* 小程序模板数据. | |||||
*/ | |||||
private List<WxMaTemplateData> data; | |||||
/** | |||||
* 模板需要放大的关键词,不填则默认无放大. | |||||
*/ | |||||
private String emphasisKeyword; | |||||
public WxMaUniformMessage addData(WxMaTemplateData datum) { | |||||
if (this.data == null) { | |||||
this.data = new ArrayList<>(); | |||||
} | |||||
this.data.add(datum); | |||||
return this; | |||||
} | |||||
public String toJson() { | |||||
return WxMaGsonBuilder.create().toJson(this); | |||||
} | |||||
@Data | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
public static class MiniProgram implements Serializable { | |||||
private static final long serialVersionUID = -7945254706501974849L; | |||||
private String appid; | |||||
private String pagePath; | |||||
/** | |||||
* 是否使用path,否则使用pagepath. | |||||
* 加入此字段是基于微信官方接口变化多端的考虑 | |||||
*/ | |||||
private boolean usePath = false; | |||||
} | |||||
} |
@@ -1,14 +1,15 @@ | |||||
package cn.binarywang.wx.miniapp.bean.code; | package cn.binarywang.wx.miniapp.bean.code; | ||||
import lombok.Builder; | |||||
import lombok.Data; | |||||
import java.io.Serializable; | import java.io.Serializable; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import lombok.Builder; | |||||
import lombok.Data; | |||||
/** | /** | ||||
* 上传代码需要用到的第三方自定义的配置 | * 上传代码需要用到的第三方自定义的配置 | ||||
* 详细文档,参考:https://developers.weixin.qq.com/miniprogram/dev/framework/config.html | |||||
* | * | ||||
* @author <a href="https://github.com/charmingoh">Charming</a> | * @author <a href="https://github.com/charmingoh">Charming</a> | ||||
* @since 2018-04-26 19:44 | * @since 2018-04-26 19:44 | ||||
@@ -18,34 +19,34 @@ import java.util.Map; | |||||
public class WxMaCodeExtConfig implements Serializable { | public class WxMaCodeExtConfig implements Serializable { | ||||
private static final long serialVersionUID = -7666911367458178753L; | private static final long serialVersionUID = -7666911367458178753L; | ||||
/** | /** | ||||
* 配置 ext.json 是否生效 | |||||
* 配置 ext.json 是否生效. | |||||
* 必填:是 | * 必填:是 | ||||
*/ | */ | ||||
private boolean extEnable; | private boolean extEnable; | ||||
/** | /** | ||||
* 配置 extAppid | |||||
* 配置 extAppid. | |||||
* 必填:是 | * 必填:是 | ||||
*/ | */ | ||||
private String extAppid; | private String extAppid; | ||||
/** | /** | ||||
* 开发自定义的数据字段 | |||||
* 开发自定义的数据字段. | |||||
* 必填:否 | * 必填:否 | ||||
*/ | */ | ||||
private Object ext; | private Object ext; | ||||
/** | /** | ||||
* 单独设置每个页面的 json | |||||
* 单独设置每个页面的 json. | |||||
* 必填:否 | * 必填:否 | ||||
* key: page 名称,如 pages/logs/logs | * key: page 名称,如 pages/logs/logs | ||||
* value: page 配置 | * value: page 配置 | ||||
*/ | */ | ||||
private Map<String, PageConfig> extPages; | private Map<String, PageConfig> extPages; | ||||
/** | /** | ||||
* 是否直接提交到待审核列表 | |||||
* 是否直接提交到待审核列表. | |||||
* 必填:否 | * 必填:否 | ||||
*/ | */ | ||||
private Boolean directCommit; | private Boolean directCommit; | ||||
/** | /** | ||||
* 设置页面路径(同 app.json 相同的字段,填写会覆盖 app.json) | |||||
* 设置页面路径(同 app.json 相同的字段,填写会覆盖 app.json). | |||||
* 必填:否 | * 必填:否 | ||||
*/ | */ | ||||
private List<String> pages; | private List<String> pages; | ||||
@@ -64,6 +65,11 @@ public class WxMaCodeExtConfig implements Serializable { | |||||
* 必填:否 | * 必填:否 | ||||
*/ | */ | ||||
private Boolean debug; | private Boolean debug; | ||||
/** | |||||
* 底部 tab 栏的表现. | |||||
* 必填:否 | |||||
*/ | |||||
private TabBar tabBar; | |||||
/** | /** | ||||
* page.json 配置,页面配置 | * page.json 配置,页面配置 | ||||
@@ -73,125 +79,125 @@ public class WxMaCodeExtConfig implements Serializable { | |||||
@Builder | @Builder | ||||
public static class PageConfig { | public static class PageConfig { | ||||
/** | /** | ||||
* 导航栏背景颜色,如"#000000" HexColor | |||||
* 导航栏背景颜色,如"#000000" HexColor. | |||||
* 默认:#000000 | * 默认:#000000 | ||||
*/ | */ | ||||
private String navigationBarBackgroundColor; | private String navigationBarBackgroundColor; | ||||
/** | /** | ||||
* 导航栏标题颜色,仅支持 black/white | |||||
* 导航栏标题颜色,仅支持 black/white. | |||||
* 默认:white | * 默认:white | ||||
*/ | */ | ||||
private String navigationBarTextStyle; | private String navigationBarTextStyle; | ||||
/** | /** | ||||
* 导航栏标题文字内容 | |||||
* 导航栏标题文字内容. | |||||
*/ | */ | ||||
private String navigationBarTitleText; | private String navigationBarTitleText; | ||||
/** | /** | ||||
* 窗口的背景色 HexColor | |||||
* 窗口的背景色 HexColor. | |||||
* 默认:#ffffff | * 默认:#ffffff | ||||
*/ | */ | ||||
private String backgroundColor; | private String backgroundColor; | ||||
/** | /** | ||||
* 下拉背景字体、loading 图的样式,仅支持 dark/light | |||||
* 下拉背景字体、loading 图的样式,仅支持 dark/light. | |||||
* 默认:dark | * 默认:dark | ||||
*/ | */ | ||||
private String backgroundTextStyle; | private String backgroundTextStyle; | ||||
/** | /** | ||||
* 是否开启下拉刷新,详见页面相关事件处理函数 | |||||
* 是否开启下拉刷新,详见页面相关事件处理函数. | |||||
* 默认:false | * 默认:false | ||||
*/ | */ | ||||
private String enablePullDownRefresh; | private String enablePullDownRefresh; | ||||
/** | /** | ||||
* 设置为 true 则页面整体不能上下滚动;只在 page.json 中有效,无法在 app.json 中设置该项 | |||||
* 设置为 true 则页面整体不能上下滚动;只在 page.json 中有效,无法在 app.json 中设置该项. | |||||
* 默认:false | * 默认:false | ||||
*/ | */ | ||||
private Boolean disableScroll; | private Boolean disableScroll; | ||||
/** | /** | ||||
* 页面上拉触底事件触发时距页面底部距离,单位为px | |||||
* 页面上拉触底事件触发时距页面底部距离,单位为px. | |||||
* 默认:50 | * 默认:50 | ||||
*/ | */ | ||||
private Integer onReachBottomDistance; | private Integer onReachBottomDistance; | ||||
} | } | ||||
/** | /** | ||||
* tabBar 配置 | |||||
* tabBar 配置. | |||||
*/ | */ | ||||
@Data | @Data | ||||
@Builder | @Builder | ||||
public static class TabBar { | public static class TabBar { | ||||
/** | /** | ||||
* HexColor, tab 上的文字默认颜色 | |||||
* HexColor, tab 上的文字默认颜色. | |||||
*/ | */ | ||||
private String color; | private String color; | ||||
/** | /** | ||||
* HexColor, tab 上的文字选中时的颜色 | |||||
* HexColor, tab 上的文字选中时的颜色. | |||||
*/ | */ | ||||
private String selectedColor; | private String selectedColor; | ||||
/** | /** | ||||
* HexColor, tab 的背景色 | |||||
* HexColor, tab 的背景色. | |||||
*/ | */ | ||||
private String backgroundColor; | private String backgroundColor; | ||||
/** | /** | ||||
* tabbar 上边框的颜色,仅支持 black/white | |||||
* tabbar 上边框的颜色,仅支持 black/white. | |||||
*/ | */ | ||||
private String borderStyle; | private String borderStyle; | ||||
/** | /** | ||||
* tab 的列表,最少2个、最多5个 tab | |||||
* tab 的列表,最少2个、最多5个 tab. | |||||
*/ | */ | ||||
private List<Item> list; | private List<Item> list; | ||||
/** | /** | ||||
* 可选值 bottom、top | |||||
* 可选值 bottom、top. | |||||
*/ | */ | ||||
private String position; | private String position; | ||||
/** | /** | ||||
* list item | |||||
* list item. | |||||
*/ | */ | ||||
@Data | @Data | ||||
@Builder | @Builder | ||||
public static class Item { | public static class Item { | ||||
/** | /** | ||||
* 是 页面路径,必须在 pages 中先定义 | |||||
* 页面路径,必须在 pages 中先定义. | |||||
*/ | */ | ||||
private String pagePath; | private String pagePath; | ||||
/** | /** | ||||
* tab 上按钮文字 | |||||
* tab 上按钮文字. | |||||
*/ | */ | ||||
private String text; | private String text; | ||||
/** | /** | ||||
* 图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效,不支持网络图片 | |||||
* 图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效,不支持网络图片. | |||||
*/ | */ | ||||
private String iconPath; | private String iconPath; | ||||
/** | /** | ||||
* 选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效 | |||||
* 选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效. | |||||
*/ | */ | ||||
private String selectedIconPath; | private String selectedIconPath; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* 各种网络请求的超时时间 | |||||
* 各种网络请求的超时时间. | |||||
*/ | */ | ||||
@Data | @Data | ||||
@Builder | @Builder | ||||
public static class NetworkTimeout { | public static class NetworkTimeout { | ||||
/** | /** | ||||
* wx.request的超时时间,单位毫秒,默认为:60000 | |||||
* wx.request的超时时间,单位毫秒,默认为:60000. | |||||
* 必填:否 | * 必填:否 | ||||
*/ | */ | ||||
private Integer request; | private Integer request; | ||||
/** | /** | ||||
* wx.connectSocket的超时时间,单位毫秒,默认为:60000 | |||||
* wx.connectSocket的超时时间,单位毫秒,默认为:60000. | |||||
* 必填:否 | * 必填:否 | ||||
*/ | */ | ||||
private Integer connectSocket; | private Integer connectSocket; | ||||
/** | /** | ||||
* wx.uploadFile的超时时间,单位毫秒,默认为:60000 | |||||
* wx.uploadFile的超时时间,单位毫秒,默认为:60000. | |||||
* 必填:否 | * 必填:否 | ||||
*/ | */ | ||||
private Integer uploadFile; | private Integer uploadFile; | ||||
/** | /** | ||||
* wx.downloadFile的超时时间,单位毫秒,默认为:60000 | |||||
* wx.downloadFile的超时时间,单位毫秒,默认为:60000. | |||||
* 必填:否 | * 必填:否 | ||||
*/ | */ | ||||
private Integer downloadFile; | private Integer downloadFile; | ||||
@@ -38,6 +38,25 @@ public interface WxMaConfig { | |||||
*/ | */ | ||||
void updateAccessToken(String accessToken, int expiresInSeconds); | void updateAccessToken(String accessToken, int expiresInSeconds); | ||||
String getJsapiTicket(); | |||||
Lock getJsapiTicketLock(); | |||||
boolean isJsapiTicketExpired(); | |||||
/** | |||||
* 强制将jsapi ticket过期掉 | |||||
*/ | |||||
void expireJsapiTicket(); | |||||
/** | |||||
* 应该是线程安全的 | |||||
* | |||||
* @param jsapiTicket 新的jsapi ticket值 | |||||
* @param expiresInSeconds 过期时间,以秒为单位 | |||||
*/ | |||||
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | |||||
String getAppid(); | String getAppid(); | ||||
String getSecret(); | String getSecret(); | ||||
@@ -1,13 +1,15 @@ | |||||
package cn.binarywang.wx.miniapp.config; | package cn.binarywang.wx.miniapp.config; | ||||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||||
import java.io.File; | import java.io.File; | ||||
import java.util.concurrent.locks.Lock; | import java.util.concurrent.locks.Lock; | ||||
import java.util.concurrent.locks.ReentrantLock; | import java.util.concurrent.locks.ReentrantLock; | ||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||||
/** | /** | ||||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | ||||
* | * | ||||
@@ -27,7 +29,11 @@ public class WxMaInMemoryConfig implements WxMaConfig { | |||||
protected volatile String httpProxyUsername; | protected volatile String httpProxyUsername; | ||||
protected volatile String httpProxyPassword; | protected volatile String httpProxyPassword; | ||||
protected volatile String jsapiTicket; | |||||
protected volatile long jsapiTicketExpiresTime; | |||||
protected Lock accessTokenLock = new ReentrantLock(); | protected Lock accessTokenLock = new ReentrantLock(); | ||||
protected Lock jsapiTicketLock = new ReentrantLock(); | |||||
/** | /** | ||||
* 临时文件目录 | * 临时文件目录 | ||||
@@ -70,6 +76,33 @@ public class WxMaInMemoryConfig implements WxMaConfig { | |||||
this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | ||||
} | } | ||||
@Override | |||||
public String getJsapiTicket() { | |||||
return this.jsapiTicket; | |||||
} | |||||
@Override | |||||
public Lock getJsapiTicketLock() { | |||||
return this.jsapiTicketLock; | |||||
} | |||||
@Override | |||||
public boolean isJsapiTicketExpired() { | |||||
return System.currentTimeMillis() > this.jsapiTicketExpiresTime; | |||||
} | |||||
@Override | |||||
public void expireJsapiTicket() { | |||||
this.jsapiTicketExpiresTime = 0; | |||||
} | |||||
@Override | |||||
public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | |||||
this.jsapiTicket = jsapiTicket; | |||||
// 预留200秒的时间 | |||||
this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||||
} | |||||
@Override | @Override | ||||
public void expireAccessToken() { | public void expireAccessToken() { | ||||
this.expiresTime = 0; | this.expiresTime = 0; | ||||
@@ -158,7 +191,7 @@ public class WxMaInMemoryConfig implements WxMaConfig { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -1,6 +1,7 @@ | |||||
package cn.binarywang.wx.miniapp.util.json; | package cn.binarywang.wx.miniapp.util.json; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; | |||||
import cn.binarywang.wx.miniapp.bean.analysis.WxMaRetainInfo; | import cn.binarywang.wx.miniapp.bean.analysis.WxMaRetainInfo; | ||||
import cn.binarywang.wx.miniapp.bean.analysis.WxMaUserPortrait; | import cn.binarywang.wx.miniapp.bean.analysis.WxMaUserPortrait; | ||||
import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitDistribution; | import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitDistribution; | ||||
@@ -18,6 +19,7 @@ public class WxMaGsonBuilder { | |||||
static { | static { | ||||
INSTANCE.disableHtmlEscaping(); | INSTANCE.disableHtmlEscaping(); | ||||
INSTANCE.registerTypeAdapter(WxMaTemplateMessage.class, new WxMaTemplateMessageGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMaTemplateMessage.class, new WxMaTemplateMessageGsonAdapter()); | ||||
INSTANCE.registerTypeAdapter(WxMaUniformMessage.class, new WxMaUniformMessageGsonAdapter()); | |||||
INSTANCE.registerTypeAdapter(WxMaCodeCommitRequest.class, new WxMaCodeCommitRequestGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMaCodeCommitRequest.class, new WxMaCodeCommitRequestGsonAdapter()); | ||||
INSTANCE.registerTypeAdapter(WxMaCodeVersionDistribution.class, new WxMaCodeVersionDistributionGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMaCodeVersionDistribution.class, new WxMaCodeVersionDistributionGsonAdapter()); | ||||
INSTANCE.registerTypeAdapter(WxMaVisitDistribution.class, new WxMaVisitDistributionGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMaVisitDistribution.class, new WxMaVisitDistributionGsonAdapter()); | ||||
@@ -1,13 +1,14 @@ | |||||
package cn.binarywang.wx.miniapp.util.json; | package cn.binarywang.wx.miniapp.util.json; | ||||
import java.lang.reflect.Type; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateData; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | ||||
import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||
import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
import com.google.gson.JsonSerializationContext; | import com.google.gson.JsonSerializationContext; | ||||
import com.google.gson.JsonSerializer; | import com.google.gson.JsonSerializer; | ||||
import java.lang.reflect.Type; | |||||
/** | /** | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
@@ -26,10 +27,6 @@ public class WxMaTemplateMessageGsonAdapter implements JsonSerializer<WxMaTempla | |||||
messageJson.addProperty("form_id", message.getFormId()); | messageJson.addProperty("form_id", message.getFormId()); | ||||
} | } | ||||
if (message.getPage() != null) { | |||||
messageJson.addProperty("page", message.getPage()); | |||||
} | |||||
if (message.getColor() != null) { | if (message.getColor() != null) { | ||||
messageJson.addProperty("color", message.getColor()); | messageJson.addProperty("color", message.getColor()); | ||||
} | } | ||||
@@ -45,7 +42,7 @@ public class WxMaTemplateMessageGsonAdapter implements JsonSerializer<WxMaTempla | |||||
return messageJson; | return messageJson; | ||||
} | } | ||||
for (WxMaTemplateMessage.Data datum : message.getData()) { | |||||
for (WxMaTemplateData datum : message.getData()) { | |||||
JsonObject dataJson = new JsonObject(); | JsonObject dataJson = new JsonObject(); | ||||
dataJson.addProperty("value", datum.getValue()); | dataJson.addProperty("value", datum.getValue()); | ||||
if (datum.getColor() != null) { | if (datum.getColor() != null) { | ||||
@@ -0,0 +1,98 @@ | |||||
package cn.binarywang.wx.miniapp.util.json; | |||||
import java.lang.reflect.Type; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateData; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; | |||||
import com.google.gson.JsonElement; | |||||
import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonSerializationContext; | |||||
import com.google.gson.JsonSerializer; | |||||
/** | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
public class WxMaUniformMessageGsonAdapter implements JsonSerializer<WxMaUniformMessage> { | |||||
@Override | |||||
public JsonElement serialize(WxMaUniformMessage message, Type typeOfSrc, JsonSerializationContext context) { | |||||
JsonObject messageJson = new JsonObject(); | |||||
messageJson.addProperty("touser", message.getToUser()); | |||||
if (message.isMpTemplateMsg()) { | |||||
JsonObject msg = new JsonObject(); | |||||
if (message.getAppid() != null) { | |||||
msg.addProperty("appid", message.getAppid()); | |||||
} | |||||
msg.addProperty("template_id", message.getTemplateId()); | |||||
if (message.getUrl() != null) { | |||||
msg.addProperty("url", message.getUrl()); | |||||
} | |||||
final WxMaUniformMessage.MiniProgram miniProgram = message.getMiniProgram(); | |||||
if (miniProgram != null) { | |||||
JsonObject miniProgramJson = new JsonObject(); | |||||
miniProgramJson.addProperty("appid", miniProgram.getAppid()); | |||||
if (miniProgram.isUsePath()) { | |||||
miniProgramJson.addProperty("path", miniProgram.getPagePath()); | |||||
} else { | |||||
miniProgramJson.addProperty("pagepath", miniProgram.getPagePath()); | |||||
} | |||||
msg.add("miniprogram", miniProgramJson); | |||||
} | |||||
if (message.getData() != null) { | |||||
JsonObject data = new JsonObject(); | |||||
for (WxMaTemplateData templateData : message.getData()) { | |||||
JsonObject dataJson = new JsonObject(); | |||||
dataJson.addProperty("value", templateData.getValue()); | |||||
if (templateData.getColor() != null) { | |||||
dataJson.addProperty("color", templateData.getColor()); | |||||
} | |||||
data.add(templateData.getName(), dataJson); | |||||
} | |||||
msg.add("data", data); | |||||
} | |||||
messageJson.add("mp_template_msg", msg); | |||||
return messageJson; | |||||
} | |||||
//小程序模版消息 | |||||
JsonObject msg = new JsonObject(); | |||||
msg.addProperty("template_id", message.getTemplateId()); | |||||
if (message.getPage() != null) { | |||||
msg.addProperty("page", message.getPage()); | |||||
} | |||||
if (message.getFormId() != null) { | |||||
msg.addProperty("form_id", message.getFormId()); | |||||
} | |||||
JsonObject data = new JsonObject(); | |||||
msg.add("data", data); | |||||
if (message.getData() != null) { | |||||
for (WxMaTemplateData templateData : message.getData()) { | |||||
JsonObject dataJson = new JsonObject(); | |||||
dataJson.addProperty("value", templateData.getValue()); | |||||
if (templateData.getColor() != null) { | |||||
dataJson.addProperty("color", templateData.getColor()); | |||||
} | |||||
data.add(templateData.getName(), dataJson); | |||||
} | |||||
} | |||||
if (message.getEmphasisKeyword() != null) { | |||||
msg.addProperty("emphasis_keyword", message.getEmphasisKeyword()); | |||||
} | |||||
messageJson.add("weapp_template_msg", msg); | |||||
return messageJson; | |||||
} | |||||
} |
@@ -1,5 +1,12 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | package cn.binarywang.wx.miniapp.api.impl; | ||||
import java.util.Arrays; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import org.testng.annotations.*; | |||||
import cn.binarywang.wx.miniapp.api.WxMaCodeService; | import cn.binarywang.wx.miniapp.api.WxMaCodeService; | ||||
import cn.binarywang.wx.miniapp.api.WxMaService; | import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCategory; | import cn.binarywang.wx.miniapp.bean.code.WxMaCategory; | ||||
@@ -11,16 +18,8 @@ import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution; | |||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
import cn.binarywang.wx.miniapp.test.ApiTestModule; | import cn.binarywang.wx.miniapp.test.ApiTestModule; | ||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
import org.testng.annotations.Guice; | |||||
import org.testng.annotations.Test; | |||||
import java.util.Arrays; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import static org.testng.Assert.assertNotNull; | |||||
import static org.testng.Assert.assertTrue; | |||||
import static org.testng.Assert.*; | |||||
/** | /** | ||||
* @author <a href="https://github.com/charmingoh">Charming</a> | * @author <a href="https://github.com/charmingoh">Charming</a> | ||||
@@ -77,7 +76,7 @@ public class WxMaCodeServiceImplTest { | |||||
@Test | @Test | ||||
public void testGetQrCode() throws Exception { | public void testGetQrCode() throws Exception { | ||||
byte[] qrCode = wxService.getCodeService().getQrCode(); | |||||
byte[] qrCode = wxService.getCodeService().getQrCode(null); | |||||
assertTrue(qrCode.length > 0); | assertTrue(qrCode.length > 0); | ||||
} | } | ||||
@@ -0,0 +1,45 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | |||||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||||
import cn.binarywang.wx.miniapp.test.ApiTestModule; | |||||
import com.google.inject.Inject; | |||||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import org.testng.annotations.Guice; | |||||
import org.testng.annotations.Test; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
/** | |||||
* <pre> | |||||
* Created by BinaryWang on 2018/8/5. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
@Test | |||||
@Guice(modules = ApiTestModule.class) | |||||
public class WxMaJsapiServiceImplTest { | |||||
@Inject | |||||
private WxMaService wxService; | |||||
@Inject | |||||
private WxMaConfig wxMaConfig; | |||||
@Test | |||||
public void testGetJsapiTicket() throws WxErrorException { | |||||
assertThat(this.wxService.getJsapiService().getJsapiTicket()).isNotBlank(); | |||||
} | |||||
@Test | |||||
public void testGetJsapiTicket1() throws WxErrorException { | |||||
assertThat(this.wxService.getJsapiService().getJsapiTicket(true)).isNotBlank(); | |||||
} | |||||
@Test | |||||
public void testCreateJsapiSignature() throws WxErrorException { | |||||
final WxJsapiSignature jsapiSignature = this.wxService.getJsapiService().createJsapiSignature("http://www.qq.com"); | |||||
System.out.println(jsapiSignature); | |||||
assertThat(jsapiSignature).isNotNull(); | |||||
} | |||||
} |
@@ -1,17 +1,20 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | package cn.binarywang.wx.miniapp.api.impl; | ||||
import java.text.SimpleDateFormat; | |||||
import java.util.Date; | |||||
import org.testng.annotations.*; | |||||
import cn.binarywang.wx.miniapp.api.WxMaService; | import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateData; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; | |||||
import cn.binarywang.wx.miniapp.test.ApiTestModule; | import cn.binarywang.wx.miniapp.test.ApiTestModule; | ||||
import cn.binarywang.wx.miniapp.test.TestConfig; | import cn.binarywang.wx.miniapp.test.TestConfig; | ||||
import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import org.testng.annotations.*; | |||||
import java.text.SimpleDateFormat; | |||||
import java.util.Date; | |||||
/** | /** | ||||
* 测试消息相关接口 | * 测试消息相关接口 | ||||
@@ -45,15 +48,33 @@ public class WxMaMsgServiceImplTest { | |||||
.formId("FORMID") | .formId("FORMID") | ||||
.page("index") | .page("index") | ||||
.data(Lists.newArrayList( | .data(Lists.newArrayList( | ||||
new WxMaTemplateMessage.Data("keyword1", "339208499", "#173177"), | |||||
new WxMaTemplateMessage.Data("keyword2", dateFormat.format(new Date()), "#173177"), | |||||
new WxMaTemplateMessage.Data("keyword3", "粤海喜来登酒店", "#173177"), | |||||
new WxMaTemplateMessage.Data("keyword4", "广州市天河区天河路208号", "#173177"))) | |||||
new WxMaTemplateData("keyword1", "339208499", "#173177"), | |||||
new WxMaTemplateData("keyword2", dateFormat.format(new Date()), "#173177"), | |||||
new WxMaTemplateData("keyword3", "粤海喜来登酒店", "#173177"), | |||||
new WxMaTemplateData("keyword4", "广州市天河区天河路208号", "#173177"))) | |||||
.templateId(config.getTemplateId()) | .templateId(config.getTemplateId()) | ||||
.emphasisKeyword("keyword1.DATA") | .emphasisKeyword("keyword1.DATA") | ||||
.build(); | .build(); | ||||
//templateMessage.addData( new WxMaTemplateMessage.Data("keyword1", "339208499", "#173177")); | |||||
//templateMessage.addData( new WxMaTemplateData("keyword1", "339208499", "#173177")); | |||||
this.wxService.getMsgService().sendTemplateMsg(templateMessage); | this.wxService.getMsgService().sendTemplateMsg(templateMessage); | ||||
} | } | ||||
@Test | |||||
public void testSendUniformMsg() throws WxErrorException { | |||||
TestConfig config = (TestConfig) this.wxService.getWxMaConfig(); | |||||
WxMaUniformMessage message = WxMaUniformMessage.builder() | |||||
.isMpTemplateMsg(false) | |||||
.toUser(config.getOpenid()) | |||||
.page("page/page/index") | |||||
.templateId("TEMPLATE_ID") | |||||
.formId("FORMID") | |||||
.emphasisKeyword("keyword1.DATA") | |||||
.build(); | |||||
message.addData(new WxMaTemplateData("keyword1", "339208499")) | |||||
.addData(new WxMaTemplateData("keyword2", "2015年01月05日 12:30")) | |||||
.addData(new WxMaTemplateData("keyword3", "腾讯微信总部")) | |||||
.addData(new WxMaTemplateData("keyword4", "广州市海珠区新港中路397号")); | |||||
this.wxService.getMsgService().sendUniformMsg(message); | |||||
} | |||||
} | } |
@@ -1,16 +1,17 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | package cn.binarywang.wx.miniapp.api.impl; | ||||
import java.io.File; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.testng.annotations.*; | |||||
import cn.binarywang.wx.miniapp.api.WxMaService; | import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
import cn.binarywang.wx.miniapp.test.ApiTestModule; | import cn.binarywang.wx.miniapp.test.ApiTestModule; | ||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.testng.annotations.Guice; | |||||
import org.testng.annotations.Test; | |||||
import static org.testng.Assert.assertNotEquals; | |||||
import static org.testng.Assert.assertTrue; | |||||
import static org.testng.Assert.*; | |||||
/** | /** | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
@@ -32,4 +33,9 @@ public class WxMaServiceImplTest { | |||||
assertTrue(StringUtils.isNotBlank(after)); | assertTrue(StringUtils.isNotBlank(after)); | ||||
} | } | ||||
@Test | |||||
public void testImgSecCheck() throws WxErrorException { | |||||
boolean result = this.wxService.imgSecCheck(new File(ClassLoader.getSystemResource("tmp.png").getFile())); | |||||
assertTrue(result); | |||||
} | |||||
} | } |
@@ -1,5 +1,9 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | package cn.binarywang.wx.miniapp.api.impl; | ||||
import cn.binarywang.wx.miniapp.test.TestConfig; | |||||
import com.google.common.collect.ImmutableMap; | |||||
import jdk.nashorn.internal.ir.annotations.Immutable; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import org.testng.annotations.*; | import org.testng.annotations.*; | ||||
import cn.binarywang.wx.miniapp.api.WxMaService; | import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
@@ -8,6 +12,8 @@ import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | |||||
import cn.binarywang.wx.miniapp.test.ApiTestModule; | import cn.binarywang.wx.miniapp.test.ApiTestModule; | ||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
import javax.management.ImmutableDescriptor; | |||||
import static org.testng.Assert.*; | import static org.testng.Assert.*; | ||||
/** | /** | ||||
@@ -54,4 +60,17 @@ public class WxMaUserServiceImplTest { | |||||
assertNotNull(phoneNoInfo); | assertNotNull(phoneNoInfo); | ||||
System.out.println(phoneNoInfo.toString()); | System.out.println(phoneNoInfo.toString()); | ||||
} | } | ||||
@Test | |||||
public void testGetSessionInfo() { | |||||
} | |||||
/** | |||||
* TODO 测试数据有问题,需要替换为正确的数据 | |||||
*/ | |||||
@Test | |||||
public void testSetUserStorage() throws WxErrorException { | |||||
this.wxService.getUserService().setUserStorage(ImmutableMap.of("1","2"), | |||||
"r7BXXKkLb8qrSNn05n0qiA",((TestConfig)this.wxService.getWxMaConfig()).getOpenid()); | |||||
} | |||||
} | } |
@@ -1,9 +1,10 @@ | |||||
package cn.binarywang.wx.miniapp.bean; | package cn.binarywang.wx.miniapp.bean; | ||||
import org.testng.annotations.*; | |||||
import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||
import org.testng.annotations.Test; | |||||
import static org.testng.AssertJUnit.assertEquals; | |||||
import static org.testng.AssertJUnit.*; | |||||
/** | /** | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
@@ -17,10 +18,10 @@ public class WxMaTemplateMessageTest { | |||||
.formId("FORMID") | .formId("FORMID") | ||||
.page("index") | .page("index") | ||||
.data(Lists.newArrayList( | .data(Lists.newArrayList( | ||||
new WxMaTemplateMessage.Data("keyword1", "339208499", "#173177"), | |||||
new WxMaTemplateMessage.Data("keyword2", "2015年01月05日12:30", "#173177"), | |||||
new WxMaTemplateMessage.Data("keyword3", "粤海喜来登酒店", "#173177"), | |||||
new WxMaTemplateMessage.Data("keyword4", "广州市天河区天河路208号", "#173177"))) | |||||
new WxMaTemplateData("keyword1", "339208499", "#173177"), | |||||
new WxMaTemplateData("keyword2", "2015年01月05日12:30", "#173177"), | |||||
new WxMaTemplateData("keyword3", "粤海喜来登酒店", "#173177"), | |||||
new WxMaTemplateData("keyword4", "广州市天河区天河路208号", "#173177"))) | |||||
.templateId("TEMPLATE_ID") | .templateId("TEMPLATE_ID") | ||||
.emphasisKeyword("keyword1.DATA") | .emphasisKeyword("keyword1.DATA") | ||||
.build(); | .build(); | ||||
@@ -1,9 +1,20 @@ | |||||
package cn.binarywang.wx.miniapp.demo; | package cn.binarywang.wx.miniapp.demo; | ||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.Map; | |||||
import java.util.concurrent.locks.ReentrantLock; | |||||
import org.eclipse.jetty.server.Server; | |||||
import org.eclipse.jetty.servlet.ServletHandler; | |||||
import org.eclipse.jetty.servlet.ServletHolder; | |||||
import cn.binarywang.wx.miniapp.api.WxMaService; | import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; | import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaMessage; | import cn.binarywang.wx.miniapp.bean.WxMaMessage; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateData; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | ||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
import cn.binarywang.wx.miniapp.constant.WxMaConstants; | import cn.binarywang.wx.miniapp.constant.WxMaConstants; | ||||
@@ -14,15 +25,6 @@ import com.google.common.collect.Lists; | |||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import org.eclipse.jetty.server.Server; | |||||
import org.eclipse.jetty.servlet.ServletHandler; | |||||
import org.eclipse.jetty.servlet.ServletHolder; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.Map; | |||||
import java.util.concurrent.locks.ReentrantLock; | |||||
/** | /** | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
@@ -96,7 +98,7 @@ public class WxMaDemoServer { | |||||
throws WxErrorException { | throws WxErrorException { | ||||
service.getMsgService().sendTemplateMsg(WxMaTemplateMessage.builder() | service.getMsgService().sendTemplateMsg(WxMaTemplateMessage.builder() | ||||
.templateId(templateId).data(Lists.newArrayList( | .templateId(templateId).data(Lists.newArrayList( | ||||
new WxMaTemplateMessage.Data("keyword1", "339208499", "#173177"))) | |||||
new WxMaTemplateData("keyword1", "339208499", "#173177"))) | |||||
.toUser(wxMessage.getFromUser()) | .toUser(wxMessage.getFromUser()) | ||||
.formId("自己替换可用的formid") | .formId("自己替换可用的formid") | ||||
.build()); | .build()); | ||||
@@ -54,6 +54,7 @@ public class TestConfig extends WxMaInMemoryConfig { | |||||
this.templateId = templateId; | this.templateId = templateId; | ||||
} | } | ||||
@Override | |||||
public void setAccessTokenLock(Lock lock) { | public void setAccessTokenLock(Lock lock) { | ||||
super.accessTokenLock = lock; | super.accessTokenLock = lock; | ||||
} | } | ||||
@@ -0,0 +1,112 @@ | |||||
package cn.binarywang.wx.miniapp.util.json; | |||||
import org.testng.annotations.*; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateData; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; | |||||
import com.google.gson.JsonParser; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
/** | |||||
* <pre> | |||||
* | |||||
* Created by Binary Wang on 2018/9/23. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
public class WxMaUniformMessageGsonAdapterTest { | |||||
@Test | |||||
public void testSerialize_mp() { | |||||
WxMaUniformMessage message = WxMaUniformMessage.builder() | |||||
.isMpTemplateMsg(true) | |||||
.toUser("OPENID") | |||||
.appid("APPID") | |||||
.templateId("TEMPLATE_ID") | |||||
.url("http://weixin.qq.com/download") | |||||
.miniProgram(new WxMaUniformMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar", false)) | |||||
.build(); | |||||
message.addData(new WxMaTemplateData("first", "恭喜你购买成功!", "#173177")) | |||||
.addData(new WxMaTemplateData("keyword1", "巧克力", "#173177")) | |||||
.addData(new WxMaTemplateData("keyword2", "39.8元", "#173177")) | |||||
.addData(new WxMaTemplateData("keyword3", "2014年9月22日", "#173177")) | |||||
.addData(new WxMaTemplateData("remark", "欢迎再次购买!", "#173177")); | |||||
assertThat(message.toJson()).isEqualTo(new JsonParser().parse("{\n" + | |||||
" \"touser\":\"OPENID\",\n" + | |||||
" \"mp_template_msg\":{\n" + | |||||
" \"appid\":\"APPID\",\n" + | |||||
" \"template_id\":\"TEMPLATE_ID\",\n" + | |||||
" \"url\":\"http://weixin.qq.com/download\",\n" + | |||||
" \"miniprogram\":{\n" + | |||||
" \"appid\":\"xiaochengxuappid12345\",\n" + | |||||
" \"pagepath\":\"index?foo=bar\"\n" + | |||||
" },\n" + | |||||
" \"data\":{\n" + | |||||
" \"first\":{\n" + | |||||
" \"value\":\"恭喜你购买成功!\",\n" + | |||||
" \"color\":\"#173177\"\n" + | |||||
" },\n" + | |||||
" \"keyword1\":{\n" + | |||||
" \"value\":\"巧克力\",\n" + | |||||
" \"color\":\"#173177\"\n" + | |||||
" },\n" + | |||||
" \"keyword2\":{\n" + | |||||
" \"value\":\"39.8元\",\n" + | |||||
" \"color\":\"#173177\"\n" + | |||||
" },\n" + | |||||
" \"keyword3\":{\n" + | |||||
" \"value\":\"2014年9月22日\",\n" + | |||||
" \"color\":\"#173177\"\n" + | |||||
" },\n" + | |||||
" \"remark\":{\n" + | |||||
" \"value\":\"欢迎再次购买!\",\n" + | |||||
" \"color\":\"#173177\"\n" + | |||||
" }\n" + | |||||
" }\n" + | |||||
" }\n" + | |||||
"}").getAsJsonObject().toString()); | |||||
} | |||||
@Test | |||||
public void testSerialize_ma() { | |||||
WxMaUniformMessage message = WxMaUniformMessage.builder() | |||||
.isMpTemplateMsg(false) | |||||
.toUser("OPENID") | |||||
.page("page/page/index") | |||||
.templateId("TEMPLATE_ID") | |||||
.formId("FORMID") | |||||
.emphasisKeyword("keyword1.DATA") | |||||
.build(); | |||||
message.addData(new WxMaTemplateData("keyword1", "339208499")) | |||||
.addData(new WxMaTemplateData("keyword2", "2015年01月05日 12:30")) | |||||
.addData(new WxMaTemplateData("keyword3", "腾讯微信总部")) | |||||
.addData(new WxMaTemplateData("keyword4", "广州市海珠区新港中路397号")); | |||||
assertThat(message.toJson()).isEqualTo(new JsonParser().parse("{\n" + | |||||
" \"touser\":\"OPENID\",\n" + | |||||
" \"weapp_template_msg\":{\n" + | |||||
" \"template_id\":\"TEMPLATE_ID\",\n" + | |||||
" \"page\":\"page/page/index\",\n" + | |||||
" \"form_id\":\"FORMID\",\n" + | |||||
" \"data\":{\n" + | |||||
" \"keyword1\":{\n" + | |||||
" \"value\":\"339208499\"\n" + | |||||
" },\n" + | |||||
" \"keyword2\":{\n" + | |||||
" \"value\":\"2015年01月05日 12:30\"\n" + | |||||
" },\n" + | |||||
" \"keyword3\":{\n" + | |||||
" \"value\":\"腾讯微信总部\"\n" + | |||||
" },\n" + | |||||
" \"keyword4\":{\n" + | |||||
" \"value\":\"广州市海珠区新港中路397号\"\n" + | |||||
" }\n" + | |||||
" },\n" + | |||||
" \"emphasis_keyword\":\"keyword1.DATA\"\n" + | |||||
" }\n" + | |||||
"}").getAsJsonObject().toString()); | |||||
} | |||||
} |
@@ -7,7 +7,7 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>3.1.0</version> | |||||
<version>3.2.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-mp</artifactId> | <artifactId>weixin-java-mp</artifactId> | ||||
<name>Weixin Java Tools - MP</name> | <name>Weixin Java Tools - MP</name> | ||||
@@ -2,12 +2,16 @@ package me.chanjar.weixin.mp.api; | |||||
import me.chanjar.weixin.common.bean.WxCardApiSignature; | import me.chanjar.weixin.common.bean.WxCardApiSignature; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateRequest; | |||||
import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateResult; | |||||
import me.chanjar.weixin.mp.bean.card.WxMpCardQrcodeCreateResult; | |||||
import me.chanjar.weixin.mp.bean.result.WxMpCardResult; | import me.chanjar.weixin.mp.bean.result.WxMpCardResult; | ||||
/** | /** | ||||
* 卡券相关接口 | * 卡券相关接口 | ||||
* | * | ||||
* @author YuJian(mgcnrx11@hotmail.com) on 01/11/2016 | |||||
* @author YuJian(mgcnrx11 @ hotmail.com) on 01/11/2016 | |||||
* @author yuanqixun 2018-08-29 | |||||
*/ | */ | ||||
public interface WxMpCardService { | public interface WxMpCardService { | ||||
String CARD_GET = "https://api.weixin.qq.com/card/get"; | String CARD_GET = "https://api.weixin.qq.com/card/get"; | ||||
@@ -16,6 +20,13 @@ public interface WxMpCardService { | |||||
String CARD_CODE_GET = "https://api.weixin.qq.com/card/code/get"; | String CARD_CODE_GET = "https://api.weixin.qq.com/card/code/get"; | ||||
String CARD_CODE_CONSUME = "https://api.weixin.qq.com/card/code/consume"; | String CARD_CODE_CONSUME = "https://api.weixin.qq.com/card/code/consume"; | ||||
String CARD_CODE_MARK = "https://api.weixin.qq.com/card/code/mark"; | String CARD_CODE_MARK = "https://api.weixin.qq.com/card/code/mark"; | ||||
String CARD_TEST_WHITELIST = "https://api.weixin.qq.com/card/testwhitelist/set"; | |||||
String CARD_QRCODE_CREAET = "https://api.weixin.qq.com/card/qrcode/create"; | |||||
String CARD_LANDING_PAGE_CREAET = "https://api.weixin.qq.com/card/landingpage/create"; | |||||
/** | |||||
* 将用户的卡券设置为失效状态 | |||||
*/ | |||||
String CARD_CODE_UNAVAILABLE = "https://api.weixin.qq.com/card/code/unavailable"; | |||||
/** | /** | ||||
* 得到WxMpService | * 得到WxMpService | ||||
@@ -122,4 +133,53 @@ public interface WxMpCardService { | |||||
* <br> 可由 com.google.gson.JsonParser#parse 等方法直接取JSON串中的某个字段。 | * <br> 可由 com.google.gson.JsonParser#parse 等方法直接取JSON串中的某个字段。 | ||||
*/ | */ | ||||
String getCardDetail(String cardId) throws WxErrorException; | String getCardDetail(String cardId) throws WxErrorException; | ||||
/** | |||||
* 添加测试白名单 | |||||
* | |||||
* @param openid 用户的openid | |||||
* @return | |||||
*/ | |||||
String addTestWhiteList(String openid) throws WxErrorException; | |||||
/** | |||||
* 创建卡券二维码 | |||||
* | |||||
* @param cardId 卡券编号 | |||||
* @param outerStr 二维码标识 | |||||
* @return WxMpCardQrcodeCreateResult | |||||
*/ | |||||
WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerStr) throws WxErrorException; | |||||
/** | |||||
* 创建卡券二维码 | |||||
* | |||||
* @param cardId 卡券编号 | |||||
* @param outerStr 二维码标识 | |||||
* @param expiresIn 失效时间,单位秒,不填默认365天 | |||||
* @return WxMpCardQrcodeCreateResult | |||||
*/ | |||||
WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerStr, int expiresIn) throws WxErrorException; | |||||
/** | |||||
* 创建卡券货架 | |||||
* | |||||
* @param createRequest 货架创建参数 | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | |||||
WxMpCardLandingPageCreateResult createLandingPage(WxMpCardLandingPageCreateRequest createRequest) throws WxErrorException; | |||||
/** | |||||
* 将用户的卡券设置为失效状态 | |||||
* 详见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025272&anchor=9 | |||||
* | |||||
* @param cardId 卡券编号 | |||||
* @param code 用户会员卡号 | |||||
* @param reason 设置为失效的原因 | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | |||||
String unavailableCardCode(String cardId, String code, String reason) throws WxErrorException; | |||||
} | } |
@@ -1,13 +1,15 @@ | |||||
package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||||
import java.io.File; | import java.io.File; | ||||
import java.util.concurrent.locks.Lock; | import java.util.concurrent.locks.Lock; | ||||
import java.util.concurrent.locks.ReentrantLock; | import java.util.concurrent.locks.ReentrantLock; | ||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||||
/** | /** | ||||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | ||||
* | * | ||||
@@ -248,7 +250,7 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -1,16 +1,21 @@ | |||||
package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
import java.io.File; | |||||
import java.util.Date; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | ||||
import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; | import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; | ||||
import me.chanjar.weixin.mp.bean.kefu.result.*; | |||||
import java.io.File; | |||||
import java.util.Date; | |||||
import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfList; | |||||
import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfMsgList; | |||||
import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfOnlineList; | |||||
import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionGetResult; | |||||
import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionList; | |||||
import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionWaitCaseList; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 客服接口 , | |||||
* 客服接口. | |||||
* 注意:命名采用kefu拼音的原因是:其英文CustomerService如果再加上Service后缀显得有点啰嗦,如果不加又显得表意不完整。 | * 注意:命名采用kefu拼音的原因是:其英文CustomerService如果再加上Service后缀显得有点啰嗦,如果不加又显得表意不完整。 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
@@ -30,7 +35,8 @@ public interface WxMpKefuService { | |||||
String KFSESSION_GET_SESSION = "https://api.weixin.qq.com/customservice/kfsession/getsession?openid=%s"; | String KFSESSION_GET_SESSION = "https://api.weixin.qq.com/customservice/kfsession/getsession?openid=%s"; | ||||
String KFSESSION_GET_SESSION_LIST = "https://api.weixin.qq.com/customservice/kfsession/getsessionlist?kf_account=%s"; | String KFSESSION_GET_SESSION_LIST = "https://api.weixin.qq.com/customservice/kfsession/getsessionlist?kf_account=%s"; | ||||
String KFSESSION_GET_WAIT_CASE = "https://api.weixin.qq.com/customservice/kfsession/getwaitcase"; | String KFSESSION_GET_WAIT_CASE = "https://api.weixin.qq.com/customservice/kfsession/getwaitcase"; | ||||
String MSGRECORD_GET_MSG_LIST = "https://api.weixin.qq.com/customservice/msgrecord/getmsglist"; | |||||
String MSG_RECORD_LIST = "https://api.weixin.qq.com/customservice/msgrecord/getmsglist"; | |||||
String CUSTOM_TYPING = "https://api.weixin.qq.com/cgi-bin/message/custom/typing"; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -38,6 +44,8 @@ public interface WxMpKefuService { | |||||
* 详情请见: <a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140547&token=&lang=zh_CN">发送客服消息</a> | * 详情请见: <a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140547&token=&lang=zh_CN">发送客服消息</a> | ||||
* 接口url格式:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN | * 接口url格式:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
boolean sendKefuMessage(WxMpKefuMessage message) throws WxErrorException; | boolean sendKefuMessage(WxMpKefuMessage message) throws WxErrorException; | ||||
@@ -49,6 +57,8 @@ public interface WxMpKefuService { | |||||
* 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | ||||
* 接口url格式:https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token=ACCESS_TOKEN | * 接口url格式:https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token=ACCESS_TOKEN | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
WxMpKfList kfList() throws WxErrorException; | WxMpKfList kfList() throws WxErrorException; | ||||
@@ -58,6 +68,8 @@ public interface WxMpKefuService { | |||||
* 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | ||||
* 接口url格式:https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist?access_token=ACCESS_TOKEN | * 接口url格式:https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist?access_token=ACCESS_TOKEN | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
WxMpKfOnlineList kfOnlineList() throws WxErrorException; | WxMpKfOnlineList kfOnlineList() throws WxErrorException; | ||||
@@ -67,6 +79,8 @@ public interface WxMpKefuService { | |||||
* 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | ||||
* 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN | * 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
boolean kfAccountAdd(WxMpKfAccountRequest request) throws WxErrorException; | boolean kfAccountAdd(WxMpKfAccountRequest request) throws WxErrorException; | ||||
@@ -85,6 +99,8 @@ public interface WxMpKefuService { | |||||
* 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | ||||
* 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/inviteworker?access_token=ACCESS_TOKEN | * 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/inviteworker?access_token=ACCESS_TOKEN | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
boolean kfAccountInviteWorker(WxMpKfAccountRequest request) throws WxErrorException; | boolean kfAccountInviteWorker(WxMpKfAccountRequest request) throws WxErrorException; | ||||
@@ -94,8 +110,10 @@ public interface WxMpKefuService { | |||||
* 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | ||||
* 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT | * 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) throws WxErrorException; | |||||
boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -103,6 +121,8 @@ public interface WxMpKefuService { | |||||
* 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | ||||
* 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT | * 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
boolean kfAccountDel(String kfAccount) throws WxErrorException; | boolean kfAccountDel(String kfAccount) throws WxErrorException; | ||||
@@ -115,6 +135,8 @@ public interface WxMpKefuService { | |||||
* 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制接口</a> | * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制接口</a> | ||||
* 接口url格式: https://api.weixin.qq.com/customservice/kfsession/create?access_token=ACCESS_TOKEN | * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/create?access_token=ACCESS_TOKEN | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
boolean kfSessionCreate(String openid, String kfAccount) throws WxErrorException; | boolean kfSessionCreate(String openid, String kfAccount) throws WxErrorException; | ||||
@@ -125,6 +147,8 @@ public interface WxMpKefuService { | |||||
* 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制接口</a> | * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制接口</a> | ||||
* 接口url格式: https://api.weixin.qq.com/customservice/kfsession/close?access_token=ACCESS_TOKEN | * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/close?access_token=ACCESS_TOKEN | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
boolean kfSessionClose(String openid, String kfAccount) throws WxErrorException; | boolean kfSessionClose(String openid, String kfAccount) throws WxErrorException; | ||||
@@ -135,6 +159,8 @@ public interface WxMpKefuService { | |||||
* 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制接口</a> | * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制接口</a> | ||||
* 接口url格式: https://api.weixin.qq.com/customservice/kfsession/getsession?access_token=ACCESS_TOKEN&openid=OPENID | * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/getsession?access_token=ACCESS_TOKEN&openid=OPENID | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
WxMpKfSessionGetResult kfSessionGet(String openid) throws WxErrorException; | WxMpKfSessionGetResult kfSessionGet(String openid) throws WxErrorException; | ||||
@@ -145,6 +171,8 @@ public interface WxMpKefuService { | |||||
* 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制</a> | * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制</a> | ||||
* 接口url格式: https://api.weixin.qq.com/customservice/kfsession/getsessionlist?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT | * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/getsessionlist?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
WxMpKfSessionList kfSessionList(String kfAccount) throws WxErrorException; | WxMpKfSessionList kfSessionList(String kfAccount) throws WxErrorException; | ||||
@@ -155,6 +183,8 @@ public interface WxMpKefuService { | |||||
* 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制</a> | * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制</a> | ||||
* 接口url格式: https://api.weixin.qq.com/customservice/kfsession/getwaitcase?access_token=ACCESS_TOKEN | * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/getwaitcase?access_token=ACCESS_TOKEN | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
WxMpKfSessionWaitCaseList kfSessionGetWaitCase() throws WxErrorException; | WxMpKfSessionWaitCaseList kfSessionGetWaitCase() throws WxErrorException; | ||||
@@ -173,7 +203,7 @@ public interface WxMpKefuService { | |||||
* @param msgId 消息id顺序从小到大,从1开始 | * @param msgId 消息id顺序从小到大,从1开始 | ||||
* @param number 每次获取条数,最多10000条 | * @param number 每次获取条数,最多10000条 | ||||
* @return 聊天记录对象 | * @return 聊天记录对象 | ||||
* @throws WxErrorException | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
WxMpKfMsgList kfMsgList(Date startTime, Date endTime, Long msgId, Integer number) throws WxErrorException; | WxMpKfMsgList kfMsgList(Date startTime, Date endTime, Long msgId, Integer number) throws WxErrorException; | ||||
@@ -188,8 +218,27 @@ public interface WxMpKefuService { | |||||
* @param startTime 起始时间 | * @param startTime 起始时间 | ||||
* @param endTime 结束时间 | * @param endTime 结束时间 | ||||
* @return 聊天记录对象 | * @return 聊天记录对象 | ||||
* @throws WxErrorException | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
WxMpKfMsgList kfMsgList(Date startTime, Date endTime) throws WxErrorException; | WxMpKfMsgList kfMsgList(Date startTime, Date endTime) throws WxErrorException; | ||||
/** | |||||
* <pre> | |||||
* 客服输入状态 | |||||
* 开发者可通过调用“客服输入状态”接口,返回客服当前输入状态给用户。 | |||||
* 此接口需要客服消息接口权限。 | |||||
* 如果不满足发送客服消息的触发条件,则无法下发输入状态。 | |||||
* 下发输入状态,需要客服之前30秒内跟用户有过消息交互。 | |||||
* 在输入状态中(持续15s),不可重复下发输入态。 | |||||
* 在输入状态中,如果向用户下发消息,会同时取消输入状态。 | |||||
* | |||||
* 详情请见:<a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140547">客服输入状态</a> | |||||
* 接口url格式:https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN | |||||
* </pre> | |||||
* | |||||
* @param openid 用户id | |||||
* @param command "Typing":对用户下发“正在输入"状态 "CancelTyping":取消对用户的”正在输入"状态 | |||||
* @throws WxErrorException 异常 | |||||
*/ | |||||
boolean sendKfTypingState(String openid, String command) throws WxErrorException; | |||||
} | } |
@@ -1,12 +1,20 @@ | |||||
package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.mp.bean.material.*; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.mp.bean.material.WxMediaImgUploadResult; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterial; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialArticleUpdate; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialCountResult; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialFileBatchGetResult; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialNewsBatchGetResult; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialVideoInfoResult; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* Created by Binary Wang on 2016/7/21. | * Created by Binary Wang on 2016/7/21. | ||||
@@ -1,29 +1,57 @@ | |||||
package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardActivatedMessage; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateMessage; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult; | |||||
import me.chanjar.weixin.mp.bean.card.MemberCardActivateUserFormRequest; | |||||
import me.chanjar.weixin.mp.bean.card.MemberCardActivateUserFormResult; | |||||
import me.chanjar.weixin.mp.bean.card.WxMpCardCreateResult; | |||||
import me.chanjar.weixin.mp.bean.membercard.*; | |||||
/** | /** | ||||
* 会员卡相关接口 | * 会员卡相关接口 | ||||
* | * | ||||
* @author YuJian(mgcnrx11@gmail.com) | |||||
* @author YuJian(mgcnrx11 @ gmail.com) | |||||
* @author yuanqixun | |||||
* @version 2017/7/8 | * @version 2017/7/8 | ||||
* @date 2018-08-30 | |||||
*/ | */ | ||||
public interface WxMpMemberCardService { | public interface WxMpMemberCardService { | ||||
String MEMBER_CARD_CREAET = "https://api.weixin.qq.com/card/create"; | |||||
String MEMBER_CARD_ACTIVATE = "https://api.weixin.qq.com/card/membercard/activate"; | |||||
String MEMBER_CARD_USER_INFO_GET = "https://api.weixin.qq.com/card/membercard/userinfo/get"; | |||||
String MEMBER_CARD_UPDATE_USER = "https://api.weixin.qq.com/card/membercard/updateuser"; | |||||
/** | |||||
* 会员卡激活之微信开卡接口(wx_activate=true情况调用) | |||||
*/ | |||||
String MEMBER_CARD_ACTIVATEUSERFORM = "https://api.weixin.qq.com/card/membercard/activateuserform/set"; | |||||
/** | /** | ||||
* 得到WxMpService | * 得到WxMpService | ||||
*/ | */ | ||||
WxMpService getWxMpService(); | WxMpService getWxMpService(); | ||||
/** | |||||
* 会员卡创建接口 | |||||
* | |||||
* @param createJson | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | |||||
WxMpCardCreateResult createMemberCard(String createJson) throws WxErrorException; | |||||
/** | |||||
* 会员卡创建接口 | |||||
* | |||||
* @param createMessageMessage | |||||
* @return WxMpCardCreateResult | |||||
* @throws WxErrorException | |||||
*/ | |||||
WxMpCardCreateResult createMemberCard(WxMpMemberCardCreateMessage createMessageMessage) throws WxErrorException; | |||||
/** | /** | ||||
* 会员卡激活接口 | * 会员卡激活接口 | ||||
* | * | ||||
* @param activatedMessage 激活所需参数 | * @param activatedMessage 激活所需参数 | ||||
* @return 调用返回的JSON字符串。 | |||||
* @return 返回json字符串 | |||||
* @throws WxErrorException 接口调用失败抛出的异常 | * @throws WxErrorException 接口调用失败抛出的异常 | ||||
*/ | */ | ||||
String activateMemberCard(WxMpMemberCardActivatedMessage activatedMessage) throws WxErrorException; | String activateMemberCard(WxMpMemberCardActivatedMessage activatedMessage) throws WxErrorException; | ||||
@@ -32,7 +60,7 @@ public interface WxMpMemberCardService { | |||||
* 拉取会员信息接口 | * 拉取会员信息接口 | ||||
* | * | ||||
* @param cardId 会员卡的CardId,微信分配 | * @param cardId 会员卡的CardId,微信分配 | ||||
* @param code 领取会员的会员卡Code | |||||
* @param code 领取会员的会员卡Code | |||||
* @return 会员信息的结果对象 | * @return 会员信息的结果对象 | ||||
* @throws WxErrorException 接口调用失败抛出的异常 | * @throws WxErrorException 接口调用失败抛出的异常 | ||||
*/ | */ | ||||
@@ -40,7 +68,7 @@ public interface WxMpMemberCardService { | |||||
/** | /** | ||||
* 当会员持卡消费后,支持开发者调用该接口更新会员信息。会员卡交易后的每次信息变更需通过该接口通知微信,便于后续消息通知及其他扩展功能。 | * 当会员持卡消费后,支持开发者调用该接口更新会员信息。会员卡交易后的每次信息变更需通过该接口通知微信,便于后续消息通知及其他扩展功能。 | ||||
* | |||||
* <p> | |||||
* 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。同时传入add_bonus和bonus时 | * 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。同时传入add_bonus和bonus时 | ||||
* add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。 | * add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。 | ||||
* 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。 | * 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。 | ||||
@@ -50,4 +78,14 @@ public interface WxMpMemberCardService { | |||||
* @throws WxErrorException 接口调用失败抛出的异常 | * @throws WxErrorException 接口调用失败抛出的异常 | ||||
*/ | */ | ||||
WxMpMemberCardUpdateResult updateUserMemberCard(WxMpMemberCardUpdateMessage updateUserMessage) throws WxErrorException; | WxMpMemberCardUpdateResult updateUserMemberCard(WxMpMemberCardUpdateMessage updateUserMessage) throws WxErrorException; | ||||
/** | |||||
* 设置会员卡激活的字段(会员卡设置:wx_activate=true 时需要) | |||||
* | |||||
* @param userFormRequest | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | |||||
MemberCardActivateUserFormResult setActivateUserForm(MemberCardActivateUserFormRequest userFormRequest) throws WxErrorException; | |||||
} | } |
@@ -1,9 +1,6 @@ | |||||
package me.chanjar.weixin.mp.api.impl; | package me.chanjar.weixin.mp.api.impl; | ||||
import com.google.gson.JsonElement; | |||||
import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonParser; | |||||
import com.google.gson.JsonPrimitive; | |||||
import com.google.gson.*; | |||||
import com.google.gson.reflect.TypeToken; | import com.google.gson.reflect.TypeToken; | ||||
import me.chanjar.weixin.common.bean.WxCardApiSignature; | import me.chanjar.weixin.common.bean.WxCardApiSignature; | ||||
import me.chanjar.weixin.common.error.WxError; | import me.chanjar.weixin.common.error.WxError; | ||||
@@ -13,8 +10,12 @@ import me.chanjar.weixin.common.util.crypto.SHA1; | |||||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | ||||
import me.chanjar.weixin.mp.api.WxMpCardService; | import me.chanjar.weixin.mp.api.WxMpCardService; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateRequest; | |||||
import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateResult; | |||||
import me.chanjar.weixin.mp.bean.card.WxMpCardQrcodeCreateResult; | |||||
import me.chanjar.weixin.mp.bean.result.WxMpCardResult; | import me.chanjar.weixin.mp.bean.result.WxMpCardResult; | ||||
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
@@ -30,6 +31,8 @@ public class WxMpCardServiceImpl implements WxMpCardService { | |||||
private WxMpService wxMpService; | private WxMpService wxMpService; | ||||
private static final Gson GSON = new Gson(); | |||||
public WxMpCardServiceImpl(WxMpService wxMpService) { | public WxMpCardServiceImpl(WxMpService wxMpService) { | ||||
this.wxMpService = wxMpService; | this.wxMpService = wxMpService; | ||||
} | } | ||||
@@ -236,4 +239,90 @@ public class WxMpCardServiceImpl implements WxMpCardService { | |||||
return responseContent; | return responseContent; | ||||
} | } | ||||
/** | |||||
* 添加测试白名单 | |||||
* | |||||
* @param openid 用户的openid | |||||
* @return | |||||
*/ | |||||
public String addTestWhiteList(String openid) throws WxErrorException { | |||||
JsonArray array = new JsonArray(); | |||||
array.add(openid); | |||||
JsonObject jsonObject = new JsonObject(); | |||||
jsonObject.add("openid", array); | |||||
String respone = this.wxMpService.post(CARD_TEST_WHITELIST, GSON.toJson(jsonObject)); | |||||
return respone; | |||||
} | |||||
/** | |||||
* 创建卡券二维码 | |||||
* | |||||
* @param cardId | |||||
* @param outerStr | |||||
* @return | |||||
*/ | |||||
public WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerStr) throws WxErrorException { | |||||
return createQrcodeCard(cardId, outerStr, 0); | |||||
} | |||||
/** | |||||
* 创建卡券二维码 | |||||
* | |||||
* @param cardId 卡券编号 | |||||
* @param outerStr 二维码标识 | |||||
* @param expiresIn 失效时间,单位秒,不填默认365天 | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | |||||
public WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerStr, int expiresIn) throws WxErrorException { | |||||
JsonObject jsonObject = new JsonObject(); | |||||
jsonObject.addProperty("action_name", "QR_CARD"); | |||||
if (expiresIn > 0) { | |||||
jsonObject.addProperty("expire_seconds", expiresIn); | |||||
} | |||||
JsonObject actionInfoJson = new JsonObject(); | |||||
JsonObject cardJson = new JsonObject(); | |||||
cardJson.addProperty("card_id", cardId); | |||||
cardJson.addProperty("outer_str", outerStr); | |||||
actionInfoJson.add("card", cardJson); | |||||
jsonObject.add("action_info", actionInfoJson); | |||||
String response = this.wxMpService.post(CARD_QRCODE_CREAET, GSON.toJson(jsonObject)); | |||||
return WxMpCardQrcodeCreateResult.fromJson(response); | |||||
} | |||||
/** | |||||
* 创建卡券货架接口 | |||||
* | |||||
* @param request | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | |||||
@Override | |||||
public WxMpCardLandingPageCreateResult createLandingPage(WxMpCardLandingPageCreateRequest request) throws WxErrorException { | |||||
String response = this.wxMpService.post(CARD_LANDING_PAGE_CREAET, GSON.toJson(request)); | |||||
return WxMpCardLandingPageCreateResult.fromJson(response); | |||||
} | |||||
/** | |||||
* 将用户的卡券设置为失效状态 | |||||
* 详见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025272&anchor=9 | |||||
* | |||||
* @param cardId 卡券编号 | |||||
* @param code 用户会员卡号 | |||||
* @param reason 设置为失效的原因 | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | |||||
@Override | |||||
public String unavailableCardCode(String cardId, String code, String reason) throws WxErrorException { | |||||
if (StringUtils.isAnyBlank(cardId, code, reason)) | |||||
throw new WxErrorException(WxError.builder().errorCode(41012).errorMsg("参数不完整").build()); | |||||
JsonObject jsonRequest = new JsonObject(); | |||||
jsonRequest.addProperty("card_id", cardId); | |||||
jsonRequest.addProperty("code", code); | |||||
jsonRequest.addProperty("reason", reason); | |||||
String response = this.wxMpService.post(CARD_CODE_UNAVAILABLE, GSON.toJson(jsonRequest)); | |||||
return response; | |||||
} | |||||
} | } |
@@ -1,8 +1,14 @@ | |||||
package me.chanjar.weixin.mp.api.impl; | package me.chanjar.weixin.mp.api.impl; | ||||
import java.io.File; | |||||
import java.util.Date; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
import me.chanjar.weixin.common.error.WxError; | |||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
import me.chanjar.weixin.common.error.WxError; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | ||||
import me.chanjar.weixin.mp.api.WxMpKefuService; | import me.chanjar.weixin.mp.api.WxMpKefuService; | ||||
@@ -10,12 +16,12 @@ import me.chanjar.weixin.mp.api.WxMpService; | |||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | ||||
import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; | import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; | ||||
import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfSessionRequest; | import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfSessionRequest; | ||||
import me.chanjar.weixin.mp.bean.kefu.result.*; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.io.File; | |||||
import java.util.Date; | |||||
import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfList; | |||||
import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfMsgList; | |||||
import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfOnlineList; | |||||
import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionGetResult; | |||||
import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionList; | |||||
import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionWaitCaseList; | |||||
/** | /** | ||||
* @author Binary Wang | * @author Binary Wang | ||||
@@ -120,12 +126,12 @@ public class WxMpKefuServiceImpl implements WxMpKefuService { | |||||
} | } | ||||
JsonObject param = new JsonObject(); | JsonObject param = new JsonObject(); | ||||
param.addProperty("starttime", startTime.getTime() / 1000); //starttime 起始时间,unix时间戳 | |||||
param.addProperty("endtime", endTime.getTime() / 1000); //endtime 结束时间,unix时间戳,每次查询时段不能超过24小时 | |||||
param.addProperty("msgid", msgId); //msgid 消息id顺序从小到大,从1开始 | |||||
param.addProperty("number", number); //number 每次获取条数,最多10000条 | |||||
param.addProperty("starttime", startTime.getTime() / 1000); | |||||
param.addProperty("endtime", endTime.getTime() / 1000); | |||||
param.addProperty("msgid", msgId); | |||||
param.addProperty("number", number); | |||||
String responseContent = this.wxMpService.post(MSGRECORD_GET_MSG_LIST, param.toString()); | |||||
String responseContent = this.wxMpService.post(MSG_RECORD_LIST, param.toString()); | |||||
return WxMpKfMsgList.fromJson(responseContent); | return WxMpKfMsgList.fromJson(responseContent); | ||||
} | } | ||||
@@ -149,4 +155,13 @@ public class WxMpKefuServiceImpl implements WxMpKefuService { | |||||
return result; | return result; | ||||
} | } | ||||
@Override | |||||
public boolean sendKfTypingState(String openid, String command) throws WxErrorException { | |||||
JsonObject params = new JsonObject(); | |||||
params.addProperty("touser", openid); | |||||
params.addProperty("command", command); | |||||
String responseContent = this.wxMpService.post(CUSTOM_TYPING, params.toString()); | |||||
return responseContent != null; | |||||
} | |||||
} | } |
@@ -1,9 +1,16 @@ | |||||
package me.chanjar.weixin.mp.api.impl; | package me.chanjar.weixin.mp.api.impl; | ||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import java.util.UUID; | |||||
import me.chanjar.weixin.common.WxType; | import me.chanjar.weixin.common.WxType; | ||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.common.error.WxError; | |||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
import me.chanjar.weixin.common.error.WxError; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.fs.FileUtils; | import me.chanjar.weixin.common.util.fs.FileUtils; | ||||
import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; | import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; | ||||
@@ -11,17 +18,22 @@ import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | import me.chanjar.weixin.common.util.json.WxGsonBuilder; | ||||
import me.chanjar.weixin.mp.api.WxMpMaterialService; | import me.chanjar.weixin.mp.api.WxMpMaterialService; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
import me.chanjar.weixin.mp.bean.material.*; | |||||
import me.chanjar.weixin.mp.util.requestexecuter.material.*; | |||||
import me.chanjar.weixin.mp.util.requestexecuter.media.MediaImgUploadRequestExecutor; | |||||
import me.chanjar.weixin.mp.bean.material.WxMediaImgUploadResult; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterial; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialArticleUpdate; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialCountResult; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialFileBatchGetResult; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialNewsBatchGetResult; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult; | |||||
import me.chanjar.weixin.mp.bean.material.WxMpMaterialVideoInfoResult; | |||||
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import java.util.UUID; | |||||
import me.chanjar.weixin.mp.util.requestexecuter.material.MaterialDeleteRequestExecutor; | |||||
import me.chanjar.weixin.mp.util.requestexecuter.material.MaterialNewsInfoRequestExecutor; | |||||
import me.chanjar.weixin.mp.util.requestexecuter.material.MaterialUploadRequestExecutor; | |||||
import me.chanjar.weixin.mp.util.requestexecuter.material.MaterialVideoInfoRequestExecutor; | |||||
import me.chanjar.weixin.mp.util.requestexecuter.material.MaterialVoiceAndImageDownloadRequestExecutor; | |||||
import me.chanjar.weixin.mp.util.requestexecuter.media.MediaImgUploadRequestExecutor; | |||||
/** | /** | ||||
* Created by Binary Wang on 2016/7/21. | * Created by Binary Wang on 2016/7/21. | ||||
@@ -6,30 +6,29 @@ import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
import com.google.gson.reflect.TypeToken; | import com.google.gson.reflect.TypeToken; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
import me.chanjar.weixin.mp.api.WxMpMemberCardService; | import me.chanjar.weixin.mp.api.WxMpMemberCardService; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardActivatedMessage; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateMessage; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult; | |||||
import me.chanjar.weixin.mp.bean.card.*; | |||||
import me.chanjar.weixin.mp.bean.card.enums.BusinessServiceType; | |||||
import me.chanjar.weixin.mp.bean.card.enums.CardColor; | |||||
import me.chanjar.weixin.mp.bean.card.enums.DateInfoType; | |||||
import me.chanjar.weixin.mp.bean.membercard.*; | |||||
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
/** | /** | ||||
* 会员卡相关接口的实现类 | * 会员卡相关接口的实现类 | ||||
* | * | ||||
* @author YuJian(mgcnrx11@gmail.com) | |||||
* @author YuJian(mgcnrx11 @ gmail.com) | |||||
* @version 2017/7/8 | * @version 2017/7/8 | ||||
*/ | */ | ||||
public class WxMpMemberCardServiceImpl implements WxMpMemberCardService { | public class WxMpMemberCardServiceImpl implements WxMpMemberCardService { | ||||
private final Logger log = LoggerFactory.getLogger(WxMpMemberCardServiceImpl.class); | private final Logger log = LoggerFactory.getLogger(WxMpMemberCardServiceImpl.class); | ||||
private static final String MEMBER_CARD_ACTIVATE = "https://api.weixin.qq.com/card/membercard/activate"; | |||||
private static final String MEMBER_CARD_USER_INFO_GET = "https://api.weixin.qq.com/card/membercard/userinfo/get"; | |||||
private static final String MEMBER_CARD_UPDATE_USER = "https://api.weixin.qq.com/card/membercard/updateuser"; | |||||
private WxMpService wxMpService; | private WxMpService wxMpService; | ||||
private static final Gson GSON = new Gson(); | private static final Gson GSON = new Gson(); | ||||
@@ -46,11 +45,176 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService { | |||||
return this.wxMpService; | return this.wxMpService; | ||||
} | } | ||||
/** | |||||
* 会员卡创建接口 | |||||
* | |||||
* @param createJson 创建json | |||||
* @return 调用返回的JSON字符串。 | |||||
* @throws WxErrorException 接口调用失败抛出的异常 | |||||
*/ | |||||
@Override | |||||
public WxMpCardCreateResult createMemberCard(String createJson) throws WxErrorException { | |||||
WxMpMemberCardCreateMessage createMessage = WxGsonBuilder.create().fromJson(createJson, WxMpMemberCardCreateMessage.class); | |||||
return createMemberCard(createMessage); | |||||
} | |||||
/** | |||||
* 会员卡创建接口 | |||||
* | |||||
* @param createMessageMessage 创建所需参数 | |||||
* @return WxMpCardCreateResult。 | |||||
* @throws WxErrorException 接口调用失败抛出的异常 | |||||
*/ | |||||
@Override | |||||
public WxMpCardCreateResult createMemberCard(WxMpMemberCardCreateMessage createMessageMessage) throws WxErrorException { | |||||
//校验请求对象合法性 | |||||
WxMpCardCreateResult validResult = validCheck(createMessageMessage); | |||||
if (!validResult.isSuccess()) | |||||
return validResult; | |||||
String response = this.wxMpService.post(MEMBER_CARD_CREAET, GSON.toJson(createMessageMessage)); | |||||
return WxMpCardCreateResult.fromJson(response); | |||||
} | |||||
private WxMpCardCreateResult validCheck(WxMpMemberCardCreateMessage createMessageMessage) throws WxErrorException { | |||||
if (createMessageMessage == null) { | |||||
return WxMpCardCreateResult.failure("对象不能为空"); | |||||
} | |||||
MemberCardCreateRequest cardCreateRequest = createMessageMessage.getCardCreateRequest(); | |||||
if (createMessageMessage == null) { | |||||
return WxMpCardCreateResult.failure("会员卡对象不能为空"); | |||||
} | |||||
String cardType = cardCreateRequest.getCardType(); | |||||
if (!StringUtils.equals(cardType, "MEMBER_CARD")) { | |||||
return WxMpCardCreateResult.failure("卡券类型必须等于MEMBER_CARD"); | |||||
} | |||||
MemberCard memberCard = cardCreateRequest.getMemberCard(); | |||||
if (StringUtils.isEmpty(memberCard.getPrerogative())) { | |||||
return WxMpCardCreateResult.failure("会员卡特权说明不能为空:prerogative"); | |||||
} | |||||
//卡片激活规则 | |||||
if (!memberCard.isAutoActivate() && !memberCard.isWxActivate() && StringUtils.isEmpty(memberCard.getActivateUrl())) { | |||||
return WxMpCardCreateResult.failure("会员卡激活方式为接口激活,activate_url不能为空"); | |||||
} | |||||
//积分支持 | |||||
// if(memberCard.isSupplyBonus() && StringUtils.isEmpty(memberCard.getBonusUrl())){ | |||||
// return WxMpCardCreateResult.failure("会员卡支持积分,bonus_url不能为空"); | |||||
// } | |||||
// if(memberCard.isSupplyBonus() && memberCard.getBonusRule() == null){ | |||||
// return WxMpCardCreateResult.failure("会员卡支持积分,bonus_rule不能为空"); | |||||
// } | |||||
BaseInfo baseInfo = memberCard.getBaseInfo(); | |||||
if (baseInfo == null) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息对象base_info不能为空"); | |||||
} | |||||
if (StringUtils.isBlank(baseInfo.getLogoUrl())) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的商户logo:logo_url不能为空"); | |||||
} | |||||
if (StringUtils.isBlank(baseInfo.getCodeType())) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的条码类型:code_type不能为空"); | |||||
} | |||||
if (StringUtils.isBlank(baseInfo.getBrandName())) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的商户名字:brand_name不能为空"); | |||||
} | |||||
if (StringUtils.length(baseInfo.getBrandName()) > 12) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的商户名字:brand_name长度不能大于12个汉字"); | |||||
} | |||||
if (StringUtils.isBlank(baseInfo.getTitle())) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的卡券名称:title不能为空"); | |||||
} | |||||
if (StringUtils.length(baseInfo.getTitle()) > 9) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的卡券名称:title长度不能大于9个汉字"); | |||||
} | |||||
if (StringUtils.isBlank(baseInfo.getColor())) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的卡颜色:color不能为空"); | |||||
} | |||||
CardColor cardColor = null; | |||||
try { | |||||
cardColor = CardColor.valueOf(baseInfo.getColor()); | |||||
} catch (IllegalArgumentException ex) { | |||||
} | |||||
if (cardColor == null) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的卡颜色:" + baseInfo.getColor() + "不支持"); | |||||
} | |||||
if (StringUtils.isBlank(baseInfo.getNotice())) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的使用提醒:notice不能为空"); | |||||
} | |||||
if (StringUtils.isBlank(baseInfo.getDescription())) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的使用说明:description不能为空"); | |||||
} | |||||
if (baseInfo.getSku() == null) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的商品信息:sku不能为空"); | |||||
} | |||||
DateInfo dateInfo = baseInfo.getDateInfo(); | |||||
if (dateInfo == null) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的使用日期:date_info不能为空"); | |||||
} | |||||
DateInfoType dateInfoType = null; | |||||
try { | |||||
dateInfoType = DateInfoType.valueOf(dateInfo.getType()); | |||||
} catch (IllegalArgumentException ex) { | |||||
} | |||||
if (dateInfoType == null) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的使用日期类型:" + dateInfo.getType() + "不合法"); | |||||
} | |||||
//固定时长 | |||||
if (dateInfoType == DateInfoType.DATE_TYPE_FIX_TERM && (dateInfo.getFixedTerm() == null || dateInfo.getFixedBeginTerm() == null)) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的使用日期为:" + dateInfoType.getDescription() + ",fixedTerm和fixedBeginTerm不能为空"); | |||||
} | |||||
//固定期限 | |||||
if (dateInfoType == DateInfoType.DATE_TYPE_FIX_TIME_RANGE && (dateInfo.getBeginTimestamp() == null || dateInfo.getEndTimestamp() == null)) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的使用日期为:" + dateInfoType.getDescription() + ",beginTimestamp 和 endTimestamp 不能为空"); | |||||
} | |||||
if (dateInfoType == DateInfoType.DATE_TYPE_FIX_TIME_RANGE && (dateInfo.getBeginTimestamp() < System.currentTimeMillis() || dateInfo.getEndTimestamp() < System.currentTimeMillis() || dateInfo.getBeginTimestamp() > dateInfo.getEndTimestamp())) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的使用日期为:" + dateInfoType.getDescription() + ",beginTimestamp和endTimestamp的值不合法,请检查"); | |||||
} | |||||
if (!baseInfo.isUseAllLocations() && StringUtils.isBlank(baseInfo.getLocationIdList())) { | |||||
return WxMpCardCreateResult.failure("会员卡基本信息的门店使用范围选择指定门店,门店列表:locationIdList不能为空"); | |||||
} | |||||
//校验高级信息 | |||||
AdvancedInfo advancedInfo = memberCard.getAdvancedInfo(); | |||||
if (advancedInfo != null) { | |||||
if (advancedInfo.getBusinessServiceList() != null) { | |||||
for (String bs : advancedInfo.getBusinessServiceList()) { | |||||
BusinessServiceType businessServiceType = null; | |||||
try { | |||||
businessServiceType = BusinessServiceType.valueOf(bs); | |||||
} catch (IllegalArgumentException ex) { | |||||
return WxMpCardCreateResult.failure("会员卡高级信息的商户服务:" + bs + " 不合法"); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return WxMpCardCreateResult.success(); | |||||
} | |||||
/** | /** | ||||
* 会员卡激活接口 | * 会员卡激活接口 | ||||
* | * | ||||
* @param activatedMessage 激活所需参数 | * @param activatedMessage 激活所需参数 | ||||
* @return 调用返回的JSON字符串。 | |||||
* @return WxMpCardCreateResult。 | |||||
* @throws WxErrorException 接口调用失败抛出的异常 | * @throws WxErrorException 接口调用失败抛出的异常 | ||||
*/ | */ | ||||
@Override | @Override | ||||
@@ -70,9 +234,10 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService { | |||||
public WxMpMemberCardUserInfoResult getUserInfo(String cardId, String code) throws WxErrorException { | public WxMpMemberCardUserInfoResult getUserInfo(String cardId, String code) throws WxErrorException { | ||||
JsonObject jsonObject = new JsonObject(); | JsonObject jsonObject = new JsonObject(); | ||||
jsonObject.addProperty("card_id", cardId); | jsonObject.addProperty("card_id", cardId); | ||||
jsonObject.addProperty("code",code); | |||||
jsonObject.addProperty("code", code); | |||||
String responseContent = this.getWxMpService().post(MEMBER_CARD_USER_INFO_GET, jsonObject.toString()); | String responseContent = this.getWxMpService().post(MEMBER_CARD_USER_INFO_GET, jsonObject.toString()); | ||||
log.debug("{}",responseContent); | |||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, | return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, | ||||
new TypeToken<WxMpMemberCardUserInfoResult>() { | new TypeToken<WxMpMemberCardUserInfoResult>() { | ||||
@@ -81,7 +246,7 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService { | |||||
/** | /** | ||||
* 当会员持卡消费后,支持开发者调用该接口更新会员信息。会员卡交易后的每次信息变更需通过该接口通知微信,便于后续消息通知及其他扩展功能。 | * 当会员持卡消费后,支持开发者调用该接口更新会员信息。会员卡交易后的每次信息变更需通过该接口通知微信,便于后续消息通知及其他扩展功能。 | ||||
* | |||||
* <p> | |||||
* 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。同时传入add_bonus和bonus时 | * 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。同时传入add_bonus和bonus时 | ||||
* add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。 | * add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。 | ||||
* 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。 | * 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。 | ||||
@@ -101,4 +266,18 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService { | |||||
new TypeToken<WxMpMemberCardUpdateResult>() { | new TypeToken<WxMpMemberCardUpdateResult>() { | ||||
}.getType()); | }.getType()); | ||||
} | } | ||||
/** | |||||
* 设置会员卡激活的字段(会员卡设置:wx_activate=true 时需要) | |||||
* | |||||
* @param userFormRequest | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | |||||
@Override | |||||
public MemberCardActivateUserFormResult setActivateUserForm(MemberCardActivateUserFormRequest userFormRequest) throws WxErrorException { | |||||
String responseContent = this.getWxMpService().post(MEMBER_CARD_ACTIVATEUSERFORM, GSON.toJson(userFormRequest)); | |||||
return MemberCardActivateUserFormResult.fromJson(responseContent); | |||||
} | |||||
} | } |