修复媒体消息转换错误bug & 新增创建卡券二维码接口
This commit is contained in:
parent
1c2c966ea2
commit
4c8e33dfac
18
CHANGE.md
18
CHANGE.md
@ -737,4 +737,20 @@
|
|||||||
|
|
||||||
+ weixin4j-base:主要调整退款相关类与官网一致
|
+ weixin4j-base:主要调整退款相关类与官网一致
|
||||||
|
|
||||||
+ weixin4j-base:获取cache时加锁处理(via 风车车)
|
+ weixin4j-base:获取cache时加锁处理(via 风车车)
|
||||||
|
|
||||||
|
* 2016-08-05
|
||||||
|
|
||||||
|
+ weixin4j-base:model包拆分media/paging
|
||||||
|
|
||||||
|
+ weixin4j-base:type包拆分card/mch
|
||||||
|
|
||||||
|
+ weixin4j-base:新增card卡券相关类
|
||||||
|
|
||||||
|
+ weixin4j-mp:新增CardApi:创建卡券接口
|
||||||
|
|
||||||
|
* 2016-08-09
|
||||||
|
|
||||||
|
+ weixin4j-base:修复媒体消息转换错误bug
|
||||||
|
|
||||||
|
+ weixin4j-mp:新增创建卡券二维码接口
|
||||||
@ -160,7 +160,7 @@
|
|||||||
|
|
||||||
* 2016-08-05
|
* 2016-08-05
|
||||||
|
|
||||||
+ model包拆分media/paing
|
+ model包拆分media/paging
|
||||||
|
|
||||||
+ type包拆分card/mch
|
+ type包拆分card/mch
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package com.foxinmy.weixin4j.http;
|
package com.foxinmy.weixin4j.http;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.util.StringUtil;
|
import com.foxinmy.weixin4j.util.StringUtil;
|
||||||
@ -35,12 +37,14 @@ public class MimeType implements Serializable {
|
|||||||
public static final MimeType TEXT_XML;
|
public static final MimeType TEXT_XML;
|
||||||
public static final MimeType TEXT_JSON;
|
public static final MimeType TEXT_JSON;
|
||||||
|
|
||||||
|
public static final List<MimeType> STREAM_MIMETYPES;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
APPLICATION_FORM_URLENCODED = valueOf("application/x-www-form-urlencoded");
|
APPLICATION_FORM_URLENCODED = valueOf("application/x-www-form-urlencoded");
|
||||||
APPLICATION_JSON = valueOf("application/json");
|
APPLICATION_JSON = valueOf("application/json");
|
||||||
APPLICATION_OCTET_STREAM = valueOf("application/octet-stream");
|
APPLICATION_OCTET_STREAM = valueOf("application/octet-stream");
|
||||||
APPLICATION_XML = valueOf("application/xml");
|
APPLICATION_XML = valueOf("application/xml");
|
||||||
MULTIPART_FORM_DATA = MimeType.valueOf("multipart/form-data");
|
MULTIPART_FORM_DATA = valueOf("multipart/form-data");
|
||||||
TEXT_HTML = valueOf("text/html");
|
TEXT_HTML = valueOf("text/html");
|
||||||
TEXT_PLAIN = valueOf("text/plain");
|
TEXT_PLAIN = valueOf("text/plain");
|
||||||
IMAGE_JPG = valueOf("image/jpg");
|
IMAGE_JPG = valueOf("image/jpg");
|
||||||
@ -48,6 +52,12 @@ public class MimeType implements Serializable {
|
|||||||
VIDEO_MPEG4 = valueOf("video/mpeg4");
|
VIDEO_MPEG4 = valueOf("video/mpeg4");
|
||||||
TEXT_XML = valueOf("text/xml");
|
TEXT_XML = valueOf("text/xml");
|
||||||
TEXT_JSON = valueOf("text/json");
|
TEXT_JSON = valueOf("text/json");
|
||||||
|
|
||||||
|
STREAM_MIMETYPES = new ArrayList<MimeType>(4);
|
||||||
|
STREAM_MIMETYPES.add(APPLICATION_OCTET_STREAM);
|
||||||
|
STREAM_MIMETYPES.add(valueOf("image/*"));
|
||||||
|
STREAM_MIMETYPES.add(valueOf("audio/*"));
|
||||||
|
STREAM_MIMETYPES.add(valueOf("video/*"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MimeType(String type) {
|
public MimeType(String type) {
|
||||||
@ -72,28 +82,33 @@ public class MimeType implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isWildcardSubtype() {
|
public boolean isWildcardSubtype() {
|
||||||
return WILDCARD_TYPE.equals(getSubType()) || getSubType().startsWith("*+");
|
return WILDCARD_TYPE.equals(getSubType())
|
||||||
|
|| getSubType().startsWith("*+");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MimeType valueOf(String value) {
|
public static MimeType valueOf(String value) {
|
||||||
if (StringUtil.isBlank(value)) {
|
if (StringUtil.isBlank(value)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String mimeType = StringUtil.tokenizeToStringArray(value, ";")[0].trim().toLowerCase(Locale.ENGLISH);
|
String mimeType = StringUtil.tokenizeToStringArray(value, ";")[0]
|
||||||
|
.trim().toLowerCase(Locale.ENGLISH);
|
||||||
if (WILDCARD_TYPE.equals(mimeType)) {
|
if (WILDCARD_TYPE.equals(mimeType)) {
|
||||||
mimeType = "*/*";
|
mimeType = "*/*";
|
||||||
}
|
}
|
||||||
int subIndex = mimeType.indexOf('/');
|
int subIndex = mimeType.indexOf('/');
|
||||||
if (subIndex == -1) {
|
if (subIndex == -1) {
|
||||||
throw new IllegalArgumentException(mimeType + ":does not contain '/'");
|
throw new IllegalArgumentException(mimeType
|
||||||
|
+ ":does not contain '/'");
|
||||||
}
|
}
|
||||||
if (subIndex == mimeType.length() - 1) {
|
if (subIndex == mimeType.length() - 1) {
|
||||||
throw new IllegalArgumentException(mimeType + ":does not contain subtype after '/'");
|
throw new IllegalArgumentException(mimeType
|
||||||
|
+ ":does not contain subtype after '/'");
|
||||||
}
|
}
|
||||||
String type = mimeType.substring(0, subIndex);
|
String type = mimeType.substring(0, subIndex);
|
||||||
String subType = mimeType.substring(subIndex + 1, mimeType.length());
|
String subType = mimeType.substring(subIndex + 1, mimeType.length());
|
||||||
if (WILDCARD_TYPE.equals(type) && !WILDCARD_TYPE.equals(subType)) {
|
if (WILDCARD_TYPE.equals(type) && !WILDCARD_TYPE.equals(subType)) {
|
||||||
throw new IllegalArgumentException(mimeType + ":wildcard type is legal only in '*/*' (all mime types)");
|
throw new IllegalArgumentException(mimeType
|
||||||
|
+ ":wildcard type is legal only in '*/*' (all mime types)");
|
||||||
}
|
}
|
||||||
return new MimeType(type, subType);
|
return new MimeType(type, subType);
|
||||||
}
|
}
|
||||||
@ -121,10 +136,14 @@ public class MimeType implements Serializable {
|
|||||||
// application/*+xml includes application/soap+xml
|
// application/*+xml includes application/soap+xml
|
||||||
int otherPlusIdx = other.getSubType().indexOf('+');
|
int otherPlusIdx = other.getSubType().indexOf('+');
|
||||||
if (otherPlusIdx != -1) {
|
if (otherPlusIdx != -1) {
|
||||||
String thisSubtypeNoSuffix = getSubType().substring(0, thisPlusIdx);
|
String thisSubtypeNoSuffix = getSubType().substring(0,
|
||||||
String thisSubtypeSuffix = getSubType().substring(thisPlusIdx + 1);
|
thisPlusIdx);
|
||||||
String otherSubtypeSuffix = other.getSubType().substring(otherPlusIdx + 1);
|
String thisSubtypeSuffix = getSubType().substring(
|
||||||
if (thisSubtypeSuffix.equals(otherSubtypeSuffix) && WILDCARD_TYPE.equals(thisSubtypeNoSuffix)) {
|
thisPlusIdx + 1);
|
||||||
|
String otherSubtypeSuffix = other.getSubType()
|
||||||
|
.substring(otherPlusIdx + 1);
|
||||||
|
if (thisSubtypeSuffix.equals(otherSubtypeSuffix)
|
||||||
|
&& WILDCARD_TYPE.equals(thisSubtypeNoSuffix)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,7 +167,8 @@ public class MimeType implements Serializable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MimeType otherType = (MimeType) other;
|
MimeType otherType = (MimeType) other;
|
||||||
return this.type.equalsIgnoreCase(otherType.type) && this.subType.equalsIgnoreCase(otherType.subType);
|
return this.type.equalsIgnoreCase(otherType.type)
|
||||||
|
&& this.subType.equalsIgnoreCase(otherType.subType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import com.foxinmy.weixin4j.http.HttpMethod;
|
|||||||
import com.foxinmy.weixin4j.http.HttpParams;
|
import com.foxinmy.weixin4j.http.HttpParams;
|
||||||
import com.foxinmy.weixin4j.http.HttpRequest;
|
import com.foxinmy.weixin4j.http.HttpRequest;
|
||||||
import com.foxinmy.weixin4j.http.HttpResponse;
|
import com.foxinmy.weixin4j.http.HttpResponse;
|
||||||
|
import com.foxinmy.weixin4j.http.MimeType;
|
||||||
import com.foxinmy.weixin4j.http.URLParameter;
|
import com.foxinmy.weixin4j.http.URLParameter;
|
||||||
import com.foxinmy.weixin4j.http.apache.FormBodyPart;
|
import com.foxinmy.weixin4j.http.apache.FormBodyPart;
|
||||||
import com.foxinmy.weixin4j.http.apache.HttpMultipartMode;
|
import com.foxinmy.weixin4j.http.apache.HttpMultipartMode;
|
||||||
@ -129,8 +130,6 @@ public class WeixinRequestExecutor {
|
|||||||
+ request.getURI().toString());
|
+ request.getURI().toString());
|
||||||
HttpResponse httpResponse = httpClient.execute(request);
|
HttpResponse httpResponse = httpClient.execute(request);
|
||||||
WeixinResponse response = new WeixinResponse(httpResponse);
|
WeixinResponse response = new WeixinResponse(httpResponse);
|
||||||
logger.info("weixin response << " + httpResponse.getProtocol()
|
|
||||||
+ httpResponse.getStatus() + ":" + response.getAsString());
|
|
||||||
handleResponse(response);
|
handleResponse(response);
|
||||||
return response;
|
return response;
|
||||||
} catch (HttpClientException e) {
|
} catch (HttpClientException e) {
|
||||||
@ -138,6 +137,24 @@ public class WeixinRequestExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应内容是否为流
|
||||||
|
*
|
||||||
|
* @param response
|
||||||
|
* 微信响应
|
||||||
|
* @return true/false
|
||||||
|
*/
|
||||||
|
private boolean hasStreamMimeType(WeixinResponse response) {
|
||||||
|
MimeType responseMimeType = MimeType.valueOf(response.getHeaders()
|
||||||
|
.getContentType());
|
||||||
|
for (MimeType streamMimeType : MimeType.STREAM_MIMETYPES) {
|
||||||
|
if (streamMimeType.includes(responseMimeType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* handle the weixin response
|
* handle the weixin response
|
||||||
*
|
*
|
||||||
@ -147,6 +164,16 @@ public class WeixinRequestExecutor {
|
|||||||
*/
|
*/
|
||||||
protected void handleResponse(WeixinResponse response)
|
protected void handleResponse(WeixinResponse response)
|
||||||
throws WeixinException {
|
throws WeixinException {
|
||||||
|
boolean hasStreamMimeType = hasStreamMimeType(response);
|
||||||
|
logger.info("weixin response << "
|
||||||
|
+ response.getProtocol()
|
||||||
|
+ response.getStatus()
|
||||||
|
+ ":"
|
||||||
|
+ (hasStreamMimeType ? response.getHeaders().getContentType()
|
||||||
|
: response.getAsString()));
|
||||||
|
if (hasStreamMimeType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ApiResult result = response.getAsResult();
|
ApiResult result = response.getAsResult();
|
||||||
if (!SUCCESS_CODE.contains(String.format(",%s,", result.getReturnCode()
|
if (!SUCCESS_CODE.contains(String.format(",%s,", result.getReturnCode()
|
||||||
.toLowerCase()))) {
|
.toLowerCase()))) {
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import java.io.InputStream;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.alibaba.fastjson.TypeReference;
|
import com.alibaba.fastjson.TypeReference;
|
||||||
import com.foxinmy.weixin4j.http.HttpHeaders;
|
import com.foxinmy.weixin4j.http.HttpHeaders;
|
||||||
@ -34,6 +33,8 @@ public class WeixinResponse implements HttpResponse {
|
|||||||
};
|
};
|
||||||
private final TypeReference<XmlResult> XMLRESULT_CLAZZ = new TypeReference<XmlResult>() {
|
private final TypeReference<XmlResult> XMLRESULT_CLAZZ = new TypeReference<XmlResult>() {
|
||||||
};
|
};
|
||||||
|
private final TypeReference<JSONObject> JSONOBJECT_CLAZZ = new TypeReference<JSONObject>() {
|
||||||
|
};
|
||||||
|
|
||||||
static {
|
static {
|
||||||
messageConverters.add(new JsonMessageConverter());
|
messageConverters.add(new JsonMessageConverter());
|
||||||
@ -56,7 +57,7 @@ public class WeixinResponse implements HttpResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public JSONObject getAsJson() {
|
public JSONObject getAsJson() {
|
||||||
return JSON.parseObject(getAsString());
|
return getAsObject(JSONOBJECT_CLAZZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmlResult getAsXml() {
|
public XmlResult getAsXml() {
|
||||||
@ -71,7 +72,8 @@ public class WeixinResponse implements HttpResponse {
|
|||||||
try {
|
try {
|
||||||
return messageConverter.convert(clazz, response);
|
return messageConverter.convert(clazz, response);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("IO error on convert to " + typeReference, e);
|
throw new RuntimeException("IO error on convert to "
|
||||||
|
+ typeReference, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,7 +63,7 @@ public class Button implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个菜单
|
* 创建一个具有子菜单的菜单
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
* 菜单名
|
* 菜单名
|
||||||
@ -76,7 +76,7 @@ public class Button implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个菜单
|
* 创建一个普通菜单
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
* 菜单名
|
* 菜单名
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package com.foxinmy.weixin4j.card;
|
package com.foxinmy.weixin4j.model.card;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
import com.foxinmy.weixin4j.type.card.CardType;
|
import com.foxinmy.weixin4j.type.card.CardType;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.foxinmy.weixin4j.card;
|
package com.foxinmy.weixin4j.model.card;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 卡券构造器
|
* 卡券构造器
|
||||||
@ -0,0 +1,156 @@
|
|||||||
|
package com.foxinmy.weixin4j.model.card;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 卡券二维码参数
|
||||||
|
*
|
||||||
|
* @className CardQR
|
||||||
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
|
* @date 2016年8月9日
|
||||||
|
* @since JDK 1.6
|
||||||
|
* @see Builder
|
||||||
|
*/
|
||||||
|
public class CardQR implements Serializable {
|
||||||
|
private static final long serialVersionUID = -2810577326677518511L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 卡券ID
|
||||||
|
*/
|
||||||
|
@JSONField(name = "card_id")
|
||||||
|
private String cardId;
|
||||||
|
/**
|
||||||
|
* 卡券Code码,use_custom_code字段为true的卡券必须填写,非自定义code和导入code模式的卡券不必填写。
|
||||||
|
*/
|
||||||
|
@JSONField(name = "code")
|
||||||
|
private String cardCode;
|
||||||
|
/**
|
||||||
|
* 指定领取者的openid,只有该用户能领取。bind_openid字段为true的卡券必须填写,非指定openid不必填写。
|
||||||
|
*/
|
||||||
|
@JSONField(name = "openid")
|
||||||
|
private String openId;
|
||||||
|
/**
|
||||||
|
* 领取场景值,用于领取渠道的数据统计,默认值为0,字段类型为整型,长度限制为60位数字。用户领取卡券后触发的事件推送中会带上此自定义场景值。
|
||||||
|
*/
|
||||||
|
@JSONField(name = "outer_str")
|
||||||
|
private String sceneValue;
|
||||||
|
/**
|
||||||
|
* 指定下发二维码,生成的二维码随机分配一个code,领取后不可再次扫描。填写true或false。默认false,注意填写该字段时,
|
||||||
|
* 卡券须通过审核且库存不为0。
|
||||||
|
*/
|
||||||
|
@JSONField(name = "is_unique_code")
|
||||||
|
private boolean isUniqueCode;
|
||||||
|
|
||||||
|
private CardQR(Builder builder) {
|
||||||
|
this.cardId = builder.cardId;
|
||||||
|
this.cardCode = builder.cardCode;
|
||||||
|
this.isUniqueCode = builder.isUniqueCode;
|
||||||
|
this.openId = builder.openId;
|
||||||
|
this.sceneValue = builder.sceneValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCardId() {
|
||||||
|
return cardId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCardCode() {
|
||||||
|
return cardCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOpenId() {
|
||||||
|
return openId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSceneValue() {
|
||||||
|
return sceneValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUniqueCode() {
|
||||||
|
return isUniqueCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "CardQR [cardId=" + cardId + ", cardCode=" + cardCode
|
||||||
|
+ ", openId=" + openId + ", sceneValue=" + sceneValue
|
||||||
|
+ ", isUniqueCode=" + isUniqueCode + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
/**
|
||||||
|
* 卡券ID
|
||||||
|
*/
|
||||||
|
private String cardId;
|
||||||
|
/**
|
||||||
|
* 卡券Code码,use_custom_code字段为true的卡券必须填写,非自定义code和导入code模式的卡券不必填写。
|
||||||
|
*/
|
||||||
|
private String cardCode;
|
||||||
|
/**
|
||||||
|
* 指定领取者的openid,只有该用户能领取。bind_openid字段为true的卡券必须填写,非指定openid不必填写。
|
||||||
|
*/
|
||||||
|
private String openId;
|
||||||
|
/**
|
||||||
|
* 用户首次领卡时,会通过领取事件推送给商户;
|
||||||
|
* 对于会员卡的二维码,用户每次扫码打开会员卡后点击任何url,会将该值拼入url中,方便开发者定位扫码来源
|
||||||
|
*/
|
||||||
|
private String sceneValue;
|
||||||
|
/**
|
||||||
|
* 指定下发二维码,生成的二维码随机分配一个code,领取后不可再次扫描。填写true或false。默认false,注意填写该字段时,
|
||||||
|
* 卡券须通过审核且库存不为0。
|
||||||
|
*/
|
||||||
|
private boolean isUniqueCode;
|
||||||
|
|
||||||
|
public Builder(String cardId) {
|
||||||
|
this.cardId = cardId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 卡券Code码,use_custom_code字段为true的卡券必须填写,非自定义code和导入code模式的卡券不必填写。
|
||||||
|
*
|
||||||
|
* @param cardCode
|
||||||
|
* 卡券code码
|
||||||
|
*/
|
||||||
|
public Builder cardCode(String cardCode) {
|
||||||
|
this.cardCode = cardCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定领取者的openid,只有该用户能领取。bind_openid字段为true的卡券必须填写,非指定openid不必填写。
|
||||||
|
*
|
||||||
|
* @param openId
|
||||||
|
* 用户openid
|
||||||
|
*/
|
||||||
|
public Builder openId(String openId) {
|
||||||
|
this.openId = openId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户首次领卡时,会通过领取事件推送给商户;
|
||||||
|
* 对于会员卡的二维码,用户每次扫码打开会员卡后点击任何url,会将该值拼入url中,方便开发者定位扫码来源
|
||||||
|
*
|
||||||
|
* @param sceneValue
|
||||||
|
* 场景值
|
||||||
|
*/
|
||||||
|
public Builder sceneValuer(String sceneValue) {
|
||||||
|
this.sceneValue = sceneValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定下发二维码,生成的二维码随机分配一个code,领取后不可再次扫描。填写true或false。默认false,注意填写该字段时,
|
||||||
|
* 卡券须通过审核且库存不为0。
|
||||||
|
*/
|
||||||
|
public Builder openUniqueCode() {
|
||||||
|
this.isUniqueCode = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardQR build() {
|
||||||
|
return new CardQR(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.foxinmy.weixin4j.card;
|
package com.foxinmy.weixin4j.model.card;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
import com.foxinmy.weixin4j.type.card.CardType;
|
import com.foxinmy.weixin4j.type.card.CardType;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.foxinmy.weixin4j.card;
|
package com.foxinmy.weixin4j.model.card;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.foxinmy.weixin4j.card;
|
package com.foxinmy.weixin4j.model.card;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.foxinmy.weixin4j.card;
|
package com.foxinmy.weixin4j.model.card;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
import com.foxinmy.weixin4j.type.card.CardType;
|
import com.foxinmy.weixin4j.type.card.CardType;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.foxinmy.weixin4j.card;
|
package com.foxinmy.weixin4j.model.card;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
import com.foxinmy.weixin4j.type.card.CardType;
|
import com.foxinmy.weixin4j.type.card.CardType;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.foxinmy.weixin4j.card;
|
package com.foxinmy.weixin4j.model.card;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
import com.foxinmy.weixin4j.type.card.CardType;
|
import com.foxinmy.weixin4j.type.card.CardType;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.foxinmy.weixin4j.card;
|
package com.foxinmy.weixin4j.model.card;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
import com.foxinmy.weixin4j.type.card.CardType;
|
import com.foxinmy.weixin4j.type.card.CardType;
|
||||||
@ -0,0 +1,173 @@
|
|||||||
|
package com.foxinmy.weixin4j.model.qr;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
|
import com.foxinmy.weixin4j.model.card.CardQR;
|
||||||
|
import com.foxinmy.weixin4j.type.QRType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二维码参数对象
|
||||||
|
*
|
||||||
|
* @className QRParameter
|
||||||
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
|
* @date 2014年4月8日
|
||||||
|
* @since JDK 1.6
|
||||||
|
* @see #createTemporaryQR(int, long) 创建整型临时二维码
|
||||||
|
* @see #createPermanenceQR(int) 创建整型永久二维码
|
||||||
|
* @see #createPermanenceQR(String) 创建字符串型永久二维码
|
||||||
|
* @see #createCardCouponQR(Integer, CardQR...) 创建卡券二维码
|
||||||
|
*/
|
||||||
|
public class QRParameter implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 6611187606558274253L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二维码的类型
|
||||||
|
*
|
||||||
|
* @see com.foxinmy.weixin4j.type.QRType
|
||||||
|
*/
|
||||||
|
@JSONField(name = "action_name")
|
||||||
|
private QRType qrType;
|
||||||
|
/**
|
||||||
|
* 二维码的有效时间
|
||||||
|
*/
|
||||||
|
@JSONField(name = "expire_seconds")
|
||||||
|
private Integer expireSeconds;
|
||||||
|
/**
|
||||||
|
* 二维码的内容
|
||||||
|
*/
|
||||||
|
@JSONField(name = "action_info")
|
||||||
|
private JSONObject sceneContent;
|
||||||
|
|
||||||
|
private QRParameter(QRType qrType, Integer expireSeconds,
|
||||||
|
JSONObject sceneContent) {
|
||||||
|
this.qrType = qrType;
|
||||||
|
this.expireSeconds = expireSeconds;
|
||||||
|
this.sceneContent = sceneContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getExpireSeconds() {
|
||||||
|
return expireSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QRType getQrType() {
|
||||||
|
return qrType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject getSceneContent() {
|
||||||
|
return sceneContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建临时二维码
|
||||||
|
*
|
||||||
|
* @param expireSeconds
|
||||||
|
* 二维码有效时间,以秒为单位。 最大不超过2592000(即30天)
|
||||||
|
* @param sceneValue
|
||||||
|
* 二维码的场景值 <font color="red">临时二维码最大值为无符号32位非0整型</font>
|
||||||
|
* @return 二维码参数
|
||||||
|
*/
|
||||||
|
public static QRParameter createTemporaryQR(int expireSeconds,
|
||||||
|
long sceneValue) {
|
||||||
|
JSONObject sceneContent = new JSONObject();
|
||||||
|
JSONObject scene = new JSONObject();
|
||||||
|
scene.put("scene_id", sceneValue);
|
||||||
|
sceneContent.put("scene", scene);
|
||||||
|
return new QRParameter(QRType.QR_SCENE, expireSeconds, sceneContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建永久二维码(场景值为int)
|
||||||
|
*
|
||||||
|
* @param sceneValue
|
||||||
|
* 场景值 最大值为100000 (目前参数只支持1--100000)
|
||||||
|
*/
|
||||||
|
public static QRParameter createPermanenceQR(int sceneValue) {
|
||||||
|
JSONObject sceneContent = new JSONObject();
|
||||||
|
JSONObject scene = new JSONObject();
|
||||||
|
scene.put("scene_id", sceneValue);
|
||||||
|
sceneContent.put("scene", scene);
|
||||||
|
return new QRParameter(QRType.QR_LIMIT_SCENE, null, sceneContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建永久二维码(场景值为string)
|
||||||
|
*
|
||||||
|
* @param sceneValue
|
||||||
|
* 场景值
|
||||||
|
*/
|
||||||
|
public static QRParameter createPermanenceQR(String sceneValue) {
|
||||||
|
JSONObject sceneContent = new JSONObject();
|
||||||
|
JSONObject scene = new JSONObject();
|
||||||
|
scene.put("scene_str", sceneValue);
|
||||||
|
sceneContent.put("scene", scene);
|
||||||
|
return new QRParameter(QRType.QR_LIMIT_STR_SCENE, null, sceneContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建卡券二维码
|
||||||
|
*
|
||||||
|
* @param expireSeconds
|
||||||
|
* 指定二维码的有效时间,范围是60 ~ 1800秒。不填默认为365天有效
|
||||||
|
* @param cardQRs
|
||||||
|
* 二维码参数:二维码领取单张卡券/多张卡券
|
||||||
|
*/
|
||||||
|
public static QRParameter createCardCouponQR(Integer expireSeconds,
|
||||||
|
CardQR... cardQRs) {
|
||||||
|
QRType qrType = QRType.QR_CARD;
|
||||||
|
JSONObject sceneContent = new JSONObject();
|
||||||
|
if (cardQRs.length > 1) {
|
||||||
|
qrType = QRType.QR_MULTIPLE_CARD;
|
||||||
|
JSONObject multipleCard = new JSONObject();
|
||||||
|
multipleCard.put("card_list", cardQRs);
|
||||||
|
sceneContent.put("multiple_card", multipleCard);
|
||||||
|
} else {
|
||||||
|
sceneContent.put("card", cardQRs[0]);
|
||||||
|
}
|
||||||
|
return new QRParameter(qrType, expireSeconds, sceneContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result
|
||||||
|
+ ((expireSeconds == null) ? 0 : expireSeconds.hashCode());
|
||||||
|
result = prime * result + ((qrType == null) ? 0 : qrType.hashCode());
|
||||||
|
result = prime * result
|
||||||
|
+ ((sceneContent == null) ? 0 : sceneContent.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
QRParameter other = (QRParameter) obj;
|
||||||
|
if (expireSeconds == null) {
|
||||||
|
if (other.expireSeconds != null)
|
||||||
|
return false;
|
||||||
|
} else if (!expireSeconds.equals(other.expireSeconds))
|
||||||
|
return false;
|
||||||
|
if (qrType != other.qrType)
|
||||||
|
return false;
|
||||||
|
if (sceneContent == null) {
|
||||||
|
if (other.sceneContent != null)
|
||||||
|
return false;
|
||||||
|
} else if (!sceneContent.equals(other.sceneContent))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "QRParameter [qrType=" + qrType + ", expireSeconds="
|
||||||
|
+ expireSeconds + ", sceneContent=" + sceneContent + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.foxinmy.weixin4j.mp.model;
|
package com.foxinmy.weixin4j.model.qr;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
@ -19,6 +19,8 @@ public class QRResult implements Serializable {
|
|||||||
|
|
||||||
private String ticket;
|
private String ticket;
|
||||||
private String url;
|
private String url;
|
||||||
|
@JSONField(name = "show_qrcode_url")
|
||||||
|
private String showUrl;
|
||||||
@JSONField(name = "expire_seconds")
|
@JSONField(name = "expire_seconds")
|
||||||
private int expireSeconds;
|
private int expireSeconds;
|
||||||
private byte[] content;
|
private byte[] content;
|
||||||
@ -39,6 +41,14 @@ public class QRResult implements Serializable {
|
|||||||
this.url = url;
|
this.url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getShowUrl() {
|
||||||
|
return showUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShowUrl(String showUrl) {
|
||||||
|
this.showUrl = showUrl;
|
||||||
|
}
|
||||||
|
|
||||||
public int getExpireSeconds() {
|
public int getExpireSeconds() {
|
||||||
return expireSeconds;
|
return expireSeconds;
|
||||||
}
|
}
|
||||||
@ -57,7 +67,8 @@ public class QRResult implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "QRResult [ticket=" + ticket + ", url=" + url
|
return "QRResult [ticket=" + ticket + ", url=" + url + ", showUrl="
|
||||||
+ ", expireSeconds=" + expireSeconds + ", content=...]";
|
+ showUrl + ", expireSeconds=" + expireSeconds
|
||||||
|
+ ", content=..." + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ public class TokenManager extends CacheManager<Token> {
|
|||||||
/**
|
/**
|
||||||
* 获取token字符串
|
* 获取token字符串
|
||||||
*
|
*
|
||||||
* @return
|
* @return token字符串
|
||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
*/
|
*/
|
||||||
public String getAccessToken() throws WeixinException {
|
public String getAccessToken() throws WeixinException {
|
||||||
|
|||||||
@ -21,5 +21,13 @@ public enum QRType {
|
|||||||
/**
|
/**
|
||||||
* 永久二维码(场景值为字符串长度在1-64之间)
|
* 永久二维码(场景值为字符串长度在1-64之间)
|
||||||
*/
|
*/
|
||||||
QR_LIMIT_STR_SCENE;
|
QR_LIMIT_STR_SCENE,
|
||||||
|
/**
|
||||||
|
* 卡券二维码:单个卡券
|
||||||
|
*/
|
||||||
|
QR_CARD,
|
||||||
|
/**
|
||||||
|
* 卡券二维码:多个卡券
|
||||||
|
*/
|
||||||
|
QR_MULTIPLE_CARD;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,18 +4,21 @@ import java.io.InputStream;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.card.CardCoupon;
|
|
||||||
import com.foxinmy.weixin4j.card.CardCoupons;
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||||
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
||||||
import com.foxinmy.weixin4j.model.Button;
|
import com.foxinmy.weixin4j.model.Button;
|
||||||
import com.foxinmy.weixin4j.model.WeixinAccount;
|
import com.foxinmy.weixin4j.model.WeixinAccount;
|
||||||
|
import com.foxinmy.weixin4j.model.card.CardCoupon;
|
||||||
|
import com.foxinmy.weixin4j.model.card.CardCoupons;
|
||||||
|
import com.foxinmy.weixin4j.model.card.CardQR;
|
||||||
import com.foxinmy.weixin4j.model.media.MediaCounter;
|
import com.foxinmy.weixin4j.model.media.MediaCounter;
|
||||||
import com.foxinmy.weixin4j.model.media.MediaDownloadResult;
|
import com.foxinmy.weixin4j.model.media.MediaDownloadResult;
|
||||||
import com.foxinmy.weixin4j.model.media.MediaItem;
|
import com.foxinmy.weixin4j.model.media.MediaItem;
|
||||||
import com.foxinmy.weixin4j.model.media.MediaRecord;
|
import com.foxinmy.weixin4j.model.media.MediaRecord;
|
||||||
import com.foxinmy.weixin4j.model.media.MediaUploadResult;
|
import com.foxinmy.weixin4j.model.media.MediaUploadResult;
|
||||||
import com.foxinmy.weixin4j.model.paging.Pageable;
|
import com.foxinmy.weixin4j.model.paging.Pageable;
|
||||||
|
import com.foxinmy.weixin4j.model.qr.QRParameter;
|
||||||
|
import com.foxinmy.weixin4j.model.qr.QRResult;
|
||||||
import com.foxinmy.weixin4j.mp.api.CardApi;
|
import com.foxinmy.weixin4j.mp.api.CardApi;
|
||||||
import com.foxinmy.weixin4j.mp.api.CustomApi;
|
import com.foxinmy.weixin4j.mp.api.CustomApi;
|
||||||
import com.foxinmy.weixin4j.mp.api.DataApi;
|
import com.foxinmy.weixin4j.mp.api.DataApi;
|
||||||
@ -43,8 +46,6 @@ import com.foxinmy.weixin4j.mp.model.KfSession.KfSessionCounter;
|
|||||||
import com.foxinmy.weixin4j.mp.model.Menu;
|
import com.foxinmy.weixin4j.mp.model.Menu;
|
||||||
import com.foxinmy.weixin4j.mp.model.MenuMatchRule;
|
import com.foxinmy.weixin4j.mp.model.MenuMatchRule;
|
||||||
import com.foxinmy.weixin4j.mp.model.MenuSetting;
|
import com.foxinmy.weixin4j.mp.model.MenuSetting;
|
||||||
import com.foxinmy.weixin4j.mp.model.QRParameter;
|
|
||||||
import com.foxinmy.weixin4j.mp.model.QRResult;
|
|
||||||
import com.foxinmy.weixin4j.mp.model.SemQuery;
|
import com.foxinmy.weixin4j.mp.model.SemQuery;
|
||||||
import com.foxinmy.weixin4j.mp.model.SemResult;
|
import com.foxinmy.weixin4j.mp.model.SemResult;
|
||||||
import com.foxinmy.weixin4j.mp.model.Tag;
|
import com.foxinmy.weixin4j.mp.model.Tag;
|
||||||
@ -176,6 +177,7 @@ public class WeixinProxy {
|
|||||||
componentTokenManager), perTicketManager.getCacheStorager()));
|
componentTokenManager), perTicketManager.getCacheStorager()));
|
||||||
this.settings = new Weixin4jSettings<WeixinAccount>(new WeixinAccount(
|
this.settings = new Weixin4jSettings<WeixinAccount>(new WeixinAccount(
|
||||||
perTicketManager.getAuthAppId(), null));
|
perTicketManager.getAuthAppId(), null));
|
||||||
|
this.settings.setCacheStorager(perTicketManager.getCacheStorager());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1376,8 +1378,8 @@ public class WeixinProxy {
|
|||||||
* 二维码参数
|
* 二维码参数
|
||||||
* @return 二维码结果对象
|
* @return 二维码结果对象
|
||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
* @see com.foxinmy.weixin4j.mp.model.QRResult
|
* @see com.foxinmy.weixin4j.model.qr.QRResult
|
||||||
* @see com.foxinmy.weixin4j.mp.model.QRParameter
|
* @see com.foxinmy.weixin4j.model.qr.QRParameter
|
||||||
* @see com.foxinmy.weixin4j.mp.api.QrApi
|
* @see com.foxinmy.weixin4j.mp.api.QrApi
|
||||||
* @see <a href=
|
* @see <a href=
|
||||||
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1443433542&token=&lang=zh_CN">
|
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1443433542&token=&lang=zh_CN">
|
||||||
@ -1904,5 +1906,25 @@ public class WeixinProxy {
|
|||||||
return cardApi.setCardSelfConsumeCell(cardId, isOpen);
|
return cardApi.setCardSelfConsumeCell(cardId, isOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建卡券二维码: 开发者可调用该接口生成一张卡券二维码供用户扫码后添加卡券到卡包。
|
||||||
|
*
|
||||||
|
* @param expireSeconds
|
||||||
|
* 指定二维码的有效时间,范围是60 ~ 1800秒。不填默认为365天有效
|
||||||
|
* @param cardQRs
|
||||||
|
* 二维码参数:二维码领取单张卡券/多张卡券
|
||||||
|
* @return 二维码结果对象
|
||||||
|
* @see com.foxinmy.weixin4j.model.qr.QRResult
|
||||||
|
* @see com.foxinmy.weixin4j.model.qr.QRParameter
|
||||||
|
* @see com.foxinmy.weixin4j.mp.api.CardApi
|
||||||
|
* @see <a
|
||||||
|
* href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025062&token=&lang=zh_CN">投放卡券</a>
|
||||||
|
* @throws WeixinException
|
||||||
|
*/
|
||||||
|
public QRResult createCardQR(Integer expireSeconds, CardQR... cardQRs)
|
||||||
|
throws WeixinException {
|
||||||
|
return cardApi.createCardQR(expireSeconds, cardQRs);
|
||||||
|
}
|
||||||
|
|
||||||
public final static String VERSION = "1.7.0";
|
public final static String VERSION = "1.7.0";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,21 @@
|
|||||||
package com.foxinmy.weixin4j.mp.api;
|
package com.foxinmy.weixin4j.mp.api;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.foxinmy.weixin4j.card.CardCoupon;
|
import com.alibaba.fastjson.TypeReference;
|
||||||
import com.foxinmy.weixin4j.card.CardCoupons;
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||||
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
||||||
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
||||||
import com.foxinmy.weixin4j.model.Token;
|
import com.foxinmy.weixin4j.model.Token;
|
||||||
|
import com.foxinmy.weixin4j.model.card.CardCoupon;
|
||||||
|
import com.foxinmy.weixin4j.model.card.CardCoupons;
|
||||||
|
import com.foxinmy.weixin4j.model.card.CardQR;
|
||||||
|
import com.foxinmy.weixin4j.model.qr.QRParameter;
|
||||||
|
import com.foxinmy.weixin4j.model.qr.QRResult;
|
||||||
import com.foxinmy.weixin4j.token.TokenManager;
|
import com.foxinmy.weixin4j.token.TokenManager;
|
||||||
|
import com.foxinmy.weixin4j.util.IOUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 卡券API
|
* 卡券API
|
||||||
@ -111,4 +119,41 @@ public class CardApi extends MpApi {
|
|||||||
token.getAccessToken()), params.toJSONString());
|
token.getAccessToken()), params.toJSONString());
|
||||||
return response.getAsResult();
|
return response.getAsResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建卡券二维码: 开发者可调用该接口生成一张卡券二维码供用户扫码后添加卡券到卡包。
|
||||||
|
*
|
||||||
|
* @param expireSeconds
|
||||||
|
* 指定二维码的有效时间,范围是60 ~ 1800秒。不填默认为365天有效
|
||||||
|
* @param cardQRs
|
||||||
|
* 二维码参数:二维码领取单张卡券/多张卡券
|
||||||
|
* @return 二维码结果对象
|
||||||
|
* @see com.foxinmy.weixin4j.model.qr.QRResult
|
||||||
|
* @see com.foxinmy.weixin4j.model.qr.QRParameter
|
||||||
|
* @see <a
|
||||||
|
* href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025062&token=&lang=zh_CN">投放卡券</a>
|
||||||
|
* @throws WeixinException
|
||||||
|
*/
|
||||||
|
public QRResult createCardQR(Integer expireSeconds, CardQR... cardQRs)
|
||||||
|
throws WeixinException {
|
||||||
|
QRParameter parameter = QRParameter.createCardCouponQR(expireSeconds,
|
||||||
|
cardQRs);
|
||||||
|
Token token = tokenManager.getCache();
|
||||||
|
String qr_uri = getRequestUri("card_qr_ticket_uri");
|
||||||
|
WeixinResponse response = weixinExecutor.post(
|
||||||
|
String.format(qr_uri, token.getAccessToken()),
|
||||||
|
JSON.toJSONString(parameter));
|
||||||
|
QRResult result = response.getAsObject(new TypeReference<QRResult>() {
|
||||||
|
});
|
||||||
|
qr_uri = String.format(getRequestUri("qr_image_uri"),
|
||||||
|
result.getTicket());
|
||||||
|
response = weixinExecutor.get(qr_uri);
|
||||||
|
result.setShowUrl(qr_uri);
|
||||||
|
try {
|
||||||
|
result.setContent(IOUtil.toByteArray(response.getBody()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new WeixinException(e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
package com.foxinmy.weixin4j.mp.api;
|
package com.foxinmy.weixin4j.mp.api;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.TypeReference;
|
import com.alibaba.fastjson.TypeReference;
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||||
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
||||||
import com.foxinmy.weixin4j.model.Token;
|
import com.foxinmy.weixin4j.model.Token;
|
||||||
import com.foxinmy.weixin4j.mp.model.QRParameter;
|
import com.foxinmy.weixin4j.model.qr.QRParameter;
|
||||||
import com.foxinmy.weixin4j.mp.model.QRResult;
|
import com.foxinmy.weixin4j.model.qr.QRResult;
|
||||||
import com.foxinmy.weixin4j.token.TokenManager;
|
import com.foxinmy.weixin4j.token.TokenManager;
|
||||||
import com.foxinmy.weixin4j.util.IOUtil;
|
import com.foxinmy.weixin4j.util.IOUtil;
|
||||||
|
|
||||||
@ -34,8 +35,8 @@ public class QrApi extends MpApi {
|
|||||||
* 二维码参数
|
* 二维码参数
|
||||||
* @return 二维码结果对象
|
* @return 二维码结果对象
|
||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
* @see com.foxinmy.weixin4j.mp.model.QRResult
|
* @see com.foxinmy.weixin4j.model.qr.QRResult
|
||||||
* @see com.foxinmy.weixin4j.mp.model.QRParameter
|
* @see com.foxinmy.weixin4j.model.qr.QRParameter
|
||||||
* @see <a
|
* @see <a
|
||||||
* href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1443433542&token=&lang=zh_CN">生成二维码</a>
|
* href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1443433542&token=&lang=zh_CN">生成二维码</a>
|
||||||
*/
|
*/
|
||||||
@ -44,12 +45,13 @@ public class QrApi extends MpApi {
|
|||||||
String qr_uri = getRequestUri("qr_ticket_uri");
|
String qr_uri = getRequestUri("qr_ticket_uri");
|
||||||
WeixinResponse response = weixinExecutor.post(
|
WeixinResponse response = weixinExecutor.post(
|
||||||
String.format(qr_uri, token.getAccessToken()),
|
String.format(qr_uri, token.getAccessToken()),
|
||||||
parameter.getContent());
|
JSON.toJSONString(parameter));
|
||||||
QRResult result = response.getAsObject(new TypeReference<QRResult>() {
|
QRResult result = response.getAsObject(new TypeReference<QRResult>() {
|
||||||
});
|
});
|
||||||
qr_uri = getRequestUri("qr_image_uri");
|
qr_uri = String.format(getRequestUri("qr_image_uri"),
|
||||||
response = weixinExecutor
|
result.getTicket());
|
||||||
.get(String.format(qr_uri, result.getTicket()));
|
response = weixinExecutor.get(qr_uri);
|
||||||
|
result.setShowUrl(qr_uri);
|
||||||
try {
|
try {
|
||||||
result.setContent(IOUtil.toByteArray(response.getBody()));
|
result.setContent(IOUtil.toByteArray(response.getBody()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|||||||
@ -186,6 +186,8 @@ card_create_uri={api_base_url}/card/create?access_token=%s
|
|||||||
card_paycell_uri={api_base_url}/card/paycell/set?access_token=%s
|
card_paycell_uri={api_base_url}/card/paycell/set?access_token=%s
|
||||||
# \u8bbe\u7f6e\u81ea\u52a9\u6838\u9500\u63a5\u53e3
|
# \u8bbe\u7f6e\u81ea\u52a9\u6838\u9500\u63a5\u53e3
|
||||||
card_selfconsumecell_uri={api_base_url}/card/selfconsumecell/set?access_token=%s
|
card_selfconsumecell_uri={api_base_url}/card/selfconsumecell/set?access_token=%s
|
||||||
|
# \u521b\u5efa\u5361\u5238\u4e8c\u7ef4\u7801\u63a5\u53e3
|
||||||
|
card_qr_ticket_uri={api_base_url}/card/qrcode/create?access_token=%s
|
||||||
|
|
||||||
# \u4f7f\u7528\u6388\u6743\u7801\u6362\u53d6\u516c\u4f17\u53f7\u7684\u63a5\u53e3\u8c03\u7528\u51ed\u636e\u548c\u6388\u6743\u4fe1\u606f
|
# \u4f7f\u7528\u6388\u6743\u7801\u6362\u53d6\u516c\u4f17\u53f7\u7684\u63a5\u53e3\u8c03\u7528\u51ed\u636e\u548c\u6388\u6743\u4fe1\u606f
|
||||||
component_exchange_authorizer_uri={api_cgi_url}/component/api_query_auth?component_access_token=%s
|
component_exchange_authorizer_uri={api_cgi_url}/component/api_query_auth?component_access_token=%s
|
||||||
|
|||||||
@ -1,139 +0,0 @@
|
|||||||
package com.foxinmy.weixin4j.mp.model;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.type.QRType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 二维码参数对象
|
|
||||||
* <p>
|
|
||||||
* 目前有2种类型的二维码,分别是临时二维码和永久二维码,前者有过期时间,最大为1800秒,但能够生成较多数量,后者无过期时间,数量较少(目前参数只支持1--
|
|
||||||
* 100000)
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @className QRParameter
|
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
|
||||||
* @date 2014年4月8日
|
|
||||||
* @since JDK 1.6
|
|
||||||
* @see #createPermanenceInt(int) 创建整型永久二维码
|
|
||||||
* @see #createPermanenceStr(String) 创建字符串型永久二维码
|
|
||||||
* @see #createTemporary(long) 创建整型默认30秒有效期临时二维码
|
|
||||||
* @see #createTemporary(int, long) 创建整型有效期临时二维码
|
|
||||||
*/
|
|
||||||
public class QRParameter implements Serializable {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 6611187606558274253L;
|
|
||||||
private static final int DEFAULT_TEMPORARY_EXPIRE_SECONDS = 30;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 临时二维码的有效时间, 最大不超过2592000(即30天),此字段如果不填,则默认有效期为30秒
|
|
||||||
*/
|
|
||||||
private int expireSeconds;
|
|
||||||
/**
|
|
||||||
* 二维码类型
|
|
||||||
*
|
|
||||||
* @see com.foxinmy.weixin4j.type.QRType
|
|
||||||
*/
|
|
||||||
private QRType qrType;
|
|
||||||
/**
|
|
||||||
* 场景值I 根据qrType参数而定
|
|
||||||
*/
|
|
||||||
private String sceneValue;
|
|
||||||
|
|
||||||
private QRParameter() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getExpireSeconds() {
|
|
||||||
return expireSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public QRType getQrType() {
|
|
||||||
return qrType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSceneValue() {
|
|
||||||
return sceneValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String content;
|
|
||||||
|
|
||||||
public String getContent() {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建临时二维码
|
|
||||||
*
|
|
||||||
* @param expireSeconds
|
|
||||||
* 有效时间
|
|
||||||
* @param sceneValue
|
|
||||||
* 二维码的场景值 <font color="red">临时二维码最大值为无符号32位非0整型</font>
|
|
||||||
* @return 二维码参数
|
|
||||||
*/
|
|
||||||
public static QRParameter createTemporary(int expireSeconds, long sceneValue) {
|
|
||||||
QRParameter qr = new QRParameter();
|
|
||||||
qr.qrType = QRType.QR_SCENE;
|
|
||||||
qr.expireSeconds = expireSeconds;
|
|
||||||
qr.sceneValue = Long.toString(sceneValue);
|
|
||||||
qr.content = String.format(
|
|
||||||
"{\"expire_seconds\": %s, \"action_name\": \"%s\", \"action_info\": {\"scene\": {\"scene_id\": %s}}}",
|
|
||||||
expireSeconds, QRType.QR_SCENE.name(), sceneValue);
|
|
||||||
return qr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建临时二维码(默认有效期为30秒)
|
|
||||||
*
|
|
||||||
* @param sceneValue
|
|
||||||
* 二维码的场景值 <font color="red">临时二维码最大值为无符号32位非0整型</font>
|
|
||||||
* @return 二维码参数
|
|
||||||
*/
|
|
||||||
public static QRParameter createTemporary(long sceneValue) {
|
|
||||||
QRParameter qr = new QRParameter();
|
|
||||||
qr.qrType = QRType.QR_SCENE;
|
|
||||||
qr.expireSeconds = DEFAULT_TEMPORARY_EXPIRE_SECONDS;
|
|
||||||
qr.sceneValue = Long.toString(sceneValue);
|
|
||||||
qr.content = String.format(
|
|
||||||
"{\"expire_seconds\": %s, \"action_name\": \"%s\", \"action_info\": {\"scene\": {\"scene_id\": %s}}}",
|
|
||||||
DEFAULT_TEMPORARY_EXPIRE_SECONDS, QRType.QR_SCENE.name(), sceneValue);
|
|
||||||
return qr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建永久二维码(场景值为int)
|
|
||||||
*
|
|
||||||
* @param sceneValue
|
|
||||||
* 场景值 最大值为100000
|
|
||||||
* @return 二维码参数
|
|
||||||
*/
|
|
||||||
public static QRParameter createPermanenceInt(int sceneValue) {
|
|
||||||
QRParameter qr = new QRParameter();
|
|
||||||
qr.qrType = QRType.QR_LIMIT_SCENE;
|
|
||||||
qr.sceneValue = Integer.toString(sceneValue);
|
|
||||||
qr.content = String.format("{\"action_name\": \"%s\", \"action_info\": {\"scene\": {\"scene_id\": %s}}}",
|
|
||||||
QRType.QR_LIMIT_SCENE.name(), sceneValue);
|
|
||||||
return qr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建永久二维码(场景值为string)
|
|
||||||
*
|
|
||||||
* @param sceneValue
|
|
||||||
* 场景值
|
|
||||||
* @return 二维码参数
|
|
||||||
*/
|
|
||||||
public static QRParameter createPermanenceStr(String sceneValue) {
|
|
||||||
QRParameter qr = new QRParameter();
|
|
||||||
qr.qrType = QRType.QR_LIMIT_STR_SCENE;
|
|
||||||
qr.sceneValue = sceneValue;
|
|
||||||
qr.content = String.format("{\"action_name\": \"%s\", \"action_info\": {\"scene\": {\"scene_str\": \"%s\"}}}",
|
|
||||||
QRType.QR_LIMIT_STR_SCENE, sceneValue);
|
|
||||||
return qr;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "QRParameter [expireSeconds=" + expireSeconds + ", qrType=" + qrType + ", sceneValue=" + sceneValue
|
|
||||||
+ "]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -7,10 +7,10 @@ import org.junit.Assert;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.card.CardCoupon;
|
|
||||||
import com.foxinmy.weixin4j.card.CardCoupons;
|
|
||||||
import com.foxinmy.weixin4j.card.CouponBaseInfo;
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||||
|
import com.foxinmy.weixin4j.model.card.CardCoupon;
|
||||||
|
import com.foxinmy.weixin4j.model.card.CardCoupons;
|
||||||
|
import com.foxinmy.weixin4j.model.card.CouponBaseInfo;
|
||||||
import com.foxinmy.weixin4j.mp.api.CardApi;
|
import com.foxinmy.weixin4j.mp.api.CardApi;
|
||||||
import com.foxinmy.weixin4j.type.card.CardCodeType;
|
import com.foxinmy.weixin4j.type.card.CardCodeType;
|
||||||
import com.foxinmy.weixin4j.type.card.CardColor;
|
import com.foxinmy.weixin4j.type.card.CardColor;
|
||||||
|
|||||||
@ -40,7 +40,7 @@ public class MenuTest extends TokenTest {
|
|||||||
String domain = "http://wx.jdxg.doubimeizhi.com";
|
String domain = "http://wx.jdxg.doubimeizhi.com";
|
||||||
buttons.add(new Button("立即下单", domain, ButtonType.view));
|
buttons.add(new Button("立即下单", domain, ButtonType.view));
|
||||||
|
|
||||||
buttons.add(new Button("个人中心", domain + "/user", ButtonType.view));
|
//buttons.add(new Button("个人中心", domain + "/user", ButtonType.view));
|
||||||
|
|
||||||
Button button = new Button("小哥介绍", domain, ButtonType.view);
|
Button button = new Button("小哥介绍", domain, ButtonType.view);
|
||||||
button.pushSub(new Button("小哥介绍", "http://x.eqxiu.com/s/89oy462U",
|
button.pushSub(new Button("小哥介绍", "http://x.eqxiu.com/s/89oy462U",
|
||||||
@ -52,7 +52,7 @@ public class MenuTest extends TokenTest {
|
|||||||
ButtonType.view));
|
ButtonType.view));
|
||||||
button.pushSub(new Button("服务流程", "FLOW", ButtonType.click));
|
button.pushSub(new Button("服务流程", "FLOW", ButtonType.click));
|
||||||
button.pushSub(new Button("在线客服", "KF", ButtonType.click));
|
button.pushSub(new Button("在线客服", "KF", ButtonType.click));
|
||||||
buttons.add(button);
|
//buttons.add(button);
|
||||||
|
|
||||||
ApiResult result = menuApi.createMenu(buttons);
|
ApiResult result = menuApi.createMenu(buttons);
|
||||||
Assert.assertEquals("0", result.getReturnCode());
|
Assert.assertEquals("0", result.getReturnCode());
|
||||||
|
|||||||
@ -7,9 +7,9 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||||
|
import com.foxinmy.weixin4j.model.qr.QRParameter;
|
||||||
|
import com.foxinmy.weixin4j.model.qr.QRResult;
|
||||||
import com.foxinmy.weixin4j.mp.api.QrApi;
|
import com.foxinmy.weixin4j.mp.api.QrApi;
|
||||||
import com.foxinmy.weixin4j.mp.model.QRParameter;
|
|
||||||
import com.foxinmy.weixin4j.mp.model.QRResult;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 二维码相关测试
|
* 二维码相关测试
|
||||||
@ -23,27 +23,27 @@ public class QRTest extends TokenTest {
|
|||||||
private QrApi qrApi;
|
private QrApi qrApi;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
public void init() throws WeixinException {
|
||||||
qrApi = new QrApi(tokenManager);
|
qrApi = new QrApi(tokenManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void temp_qr() throws WeixinException, IOException {
|
public void temp_qr() throws WeixinException, IOException {
|
||||||
QRResult result = qrApi.createQR(QRParameter
|
QRResult result = qrApi.createQR(QRParameter.createTemporaryQR(1200,
|
||||||
.createTemporary(1200, 1200));
|
1200L));
|
||||||
Assert.assertTrue(!result.getTicket().isEmpty());
|
Assert.assertTrue(!result.getTicket().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void forever_qr_int() throws WeixinException, IOException {
|
public void forever_qr_int() throws WeixinException, IOException {
|
||||||
QRResult result = qrApi.createQR(QRParameter.createPermanenceInt(2));
|
QRResult result = qrApi.createQR(QRParameter.createPermanenceQR(2));
|
||||||
Assert.assertTrue(!result.getTicket().isEmpty());
|
Assert.assertTrue(!result.getTicket().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void forever_qr_str() throws WeixinException, IOException {
|
public void forever_qr_str() throws WeixinException, IOException {
|
||||||
QRResult result = qrApi.createQR(QRParameter
|
QRResult result = qrApi.createQR(QRParameter
|
||||||
.createPermanenceStr("1200中文"));
|
.createPermanenceQR("1200中文"));
|
||||||
Assert.assertTrue(!result.getTicket().isEmpty());
|
Assert.assertTrue(!result.getTicket().isEmpty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,9 +15,10 @@ import com.foxinmy.weixin4j.type.TicketType;
|
|||||||
* @className WeixinTicketCreator
|
* @className WeixinTicketCreator
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年12月25日
|
* @date 2015年12月25日
|
||||||
* @since JDK 1.6 <a href=
|
* @since JDK 1.6
|
||||||
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BE%AE%E4%BF%A1JS-SDK%E6%8E%A5%E5%8F%A3#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95"
|
* @see <a href=
|
||||||
* >JSTICKET</a>
|
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BE%AE%E4%BF%A1JS-SDK%E6%8E%A5%E5%8F%A3#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95"
|
||||||
|
* >JSTICKET</a>
|
||||||
*/
|
*/
|
||||||
public class WeixinTicketCreator extends TokenCreator {
|
public class WeixinTicketCreator extends TokenCreator {
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user