增加会员卡管理服务的更新会员信息接口的实现 (#283)

* 修复UserInfo反序列化的bug,补充其单元测试

* 增加`更新会员信息`接口的实现

* 增加会员卡相关接口的测试类  包含下述方法: 1. 会员卡激活接口 2. 会员信息获取接口 3. 更新会员信息接口
This commit is contained in:
mgcnrx11 2017-07-15 18:53:23 +08:00 committed by Binary Wang
parent 22287a482d
commit 9368177d00
12 changed files with 574 additions and 4 deletions

View File

@ -2,6 +2,8 @@ package me.chanjar.weixin.mp.api;
import me.chanjar.weixin.common.exception.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;
/**
@ -35,4 +37,17 @@ public interface WxMpMemberCardService {
* @throws WxErrorException 接口调用失败抛出的异常
*/
WxMpMemberCardUserInfoResult getUserInfo(String cardId, String code) throws WxErrorException;
/**
* 当会员持卡消费后支持开发者调用该接口更新会员信息会员卡交易后的每次信息变更需通过该接口通知微信便于后续消息通知及其他扩展功能
*
* 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题同时传入add_bonus和bonus时
* add_bonus作为积分变动消息中的变量值而bonus作为卡面上的总积分额度显示余额变动同理
* 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息余额变动同理
*
* @param updateUserMessage 更新会员信息所需字段消息
* @return 调用返回的JSON字符串
* @throws WxErrorException 接口调用失败抛出的异常
*/
WxMpMemberCardUpdateResult updateUserMemberCard(WxMpMemberCardUpdateMessage updateUserMessage) throws WxErrorException;
}

View File

@ -9,6 +9,8 @@ import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpMemberCardService;
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.util.json.WxMpGsonBuilder;
import org.slf4j.Logger;
@ -26,6 +28,7 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService {
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;
@ -75,4 +78,27 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService {
new TypeToken<WxMpMemberCardUserInfoResult>() {
}.getType());
}
/**
* 当会员持卡消费后支持开发者调用该接口更新会员信息会员卡交易后的每次信息变更需通过该接口通知微信便于后续消息通知及其他扩展功能
*
* 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题同时传入add_bonus和bonus时
* add_bonus作为积分变动消息中的变量值而bonus作为卡面上的总积分额度显示余额变动同理
* 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息余额变动同理
*
* @param updateUserMessage 更新会员信息所需字段消息
* @return 调用返回的JSON字符串
* @throws WxErrorException 接口调用失败抛出的异常
*/
@Override
public WxMpMemberCardUpdateResult updateUserMemberCard(WxMpMemberCardUpdateMessage updateUserMessage)
throws WxErrorException {
String responseContent = this.getWxMpService().post(MEMBER_CARD_UPDATE_USER, GSON.toJson(updateUserMessage));
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement,
new TypeToken<WxMpMemberCardUpdateResult>() {
}.getType());
}
}

View File

@ -0,0 +1,73 @@
package me.chanjar.weixin.mp.bean.membercard;
import com.google.gson.annotations.SerializedName;
/**
* 控制原生消息结构体包含各字段的消息控制字段
*
* 用于 `7 更新会员信息` 的接口参数调用
* https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283
*
* @author YuJian(mgcnrx11@gmail.com)
* @version 2017/7/15
*/
public class NotifyOptional {
// 积分变动时是否触发系统模板消息默认为true
@SerializedName("is_notify_bonus")
private Boolean isNotifyBonus;
// 余额变动时是否触发系统模板消息默认为true
@SerializedName("is_notify_balance")
private Boolean isNotifyBalance;
// 自定义group1变动时是否触发系统模板消息默认为false23同理
@SerializedName("is_notify_custom_field1")
private Boolean isNotifyCustomField1;
@SerializedName("is_notify_custom_field2")
private Boolean isNotifyCustomField2;
@SerializedName("is_notify_custom_field3")
private Boolean isNotifyCustomField3;
public Boolean getNotifyBonus() {
return isNotifyBonus;
}
public void setNotifyBonus(Boolean notifyBonus) {
isNotifyBonus = notifyBonus;
}
public Boolean getNotifyBalance() {
return isNotifyBalance;
}
public void setNotifyBalance(Boolean notifyBalance) {
isNotifyBalance = notifyBalance;
}
public Boolean getNotifyCustomField1() {
return isNotifyCustomField1;
}
public void setNotifyCustomField1(Boolean notifyCustomField1) {
isNotifyCustomField1 = notifyCustomField1;
}
public Boolean getNotifyCustomField2() {
return isNotifyCustomField2;
}
public void setNotifyCustomField2(Boolean notifyCustomField2) {
isNotifyCustomField2 = notifyCustomField2;
}
public Boolean getNotifyCustomField3() {
return isNotifyCustomField3;
}
public void setNotifyCustomField3(Boolean notifyCustomField3) {
isNotifyCustomField3 = notifyCustomField3;
}
}

View File

@ -0,0 +1,156 @@
package me.chanjar.weixin.mp.bean.membercard;
import com.google.gson.annotations.SerializedName;
/**
* 更新会员信息所需字段消息
*
* 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题同时传入add_bonus和bonus时
* add_bonus作为积分变动消息中的变量值而bonus作为卡面上的总积分额度显示余额变动同理
* 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息余额变动同理
*
* @author YuJian(mgcnrx11@gmail.com)
* @version 2017/7/15
*/
public class WxMpMemberCardUpdateMessage {
// 领取会员卡用户获得的code
private String code;
// 卡券ID,自定义code卡券必填
@SerializedName("card_id")
private String cardId;
// 支持商家激活时针对单个会员卡分配自定义的会员卡背景
@SerializedName("background_pic_url")
private String backgroundPicUrl;
// 需要设置的积分全量值传入的数值会直接显示
private Integer bonus;
// 本次积分变动值传负数代表减少
@SerializedName("add_bonus")
private Integer addBounus;
// 商家自定义积分消耗记录不超过14个汉字
@SerializedName("record_bonus")
private String recordBonus;
// 需要设置的余额全量值传入的数值会直接显示在卡面
private Integer balance;
// 本次余额变动值传负数代表减少
@SerializedName("add_balance")
private Integer addBalance;
// 商家自定义金额消耗记录不超过14个汉字
@SerializedName("record_balance")
private String recordBalance;
// 创建时字段custom_field定义类型的最新数值限制为4个汉字12字节
@SerializedName("custom_field_value1")
private String customFieldValue1;
@SerializedName("custom_field_value2")
private String customFieldValue2;
@SerializedName("custom_field_value3")
private String customFieldValue3;
@SerializedName("notify_optional")
private NotifyOptional notifyOptional;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public String getBackgroundPicUrl() {
return backgroundPicUrl;
}
public void setBackgroundPicUrl(String backgroundPicUrl) {
this.backgroundPicUrl = backgroundPicUrl;
}
public Integer getBonus() {
return bonus;
}
public void setBonus(Integer bonus) {
this.bonus = bonus;
}
public Integer getAddBounus() {
return addBounus;
}
public void setAddBounus(Integer addBounus) {
this.addBounus = addBounus;
}
public String getRecordBonus() {
return recordBonus;
}
public void setRecordBonus(String recordBonus) {
this.recordBonus = recordBonus;
}
public Integer getBalance() {
return balance;
}
public void setBalance(Integer balance) {
this.balance = balance;
}
public Integer getAddBalance() {
return addBalance;
}
public void setAddBalance(Integer addBalance) {
this.addBalance = addBalance;
}
public String getRecordBalance() {
return recordBalance;
}
public void setRecordBalance(String recordBalance) {
this.recordBalance = recordBalance;
}
public String getCustomFieldValue1() {
return customFieldValue1;
}
public void setCustomFieldValue1(String customFieldValue1) {
this.customFieldValue1 = customFieldValue1;
}
public String getCustomFieldValue2() {
return customFieldValue2;
}
public void setCustomFieldValue2(String customFieldValue2) {
this.customFieldValue2 = customFieldValue2;
}
public String getCustomFieldValue3() {
return customFieldValue3;
}
public void setCustomFieldValue3(String customFieldValue3) {
this.customFieldValue3 = customFieldValue3;
}
public NotifyOptional getNotifyOptional() {
return notifyOptional;
}
public void setNotifyOptional(NotifyOptional notifyOptional) {
this.notifyOptional = notifyOptional;
}
}

View File

@ -0,0 +1,82 @@
package me.chanjar.weixin.mp.bean.membercard;
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
import java.io.Serializable;
/**
* 用于 `7 更新会员信息` 的接口调用后的返回结果
* https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283
*
* @author YuJian(mgcnrx11@gmail.com)
* @version 2017/7/15
*/
public class WxMpMemberCardUpdateResult implements Serializable {
private static final long serialVersionUID = 9084886191442098311L;
private String errorCode;
private String errorMsg;
private String openId;
private Integer resultBonus;
private Integer resultBalance;
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public Integer getResultBonus() {
return resultBonus;
}
public void setResultBonus(Integer resultBonus) {
this.resultBonus = resultBonus;
}
public Integer getResultBalance() {
return resultBalance;
}
public void setResultBalance(Integer resultBalance) {
this.resultBalance = resultBalance;
}
@Override
public String toString() {
return "WxMpMemberCardUpdateResult{" +
"errorCode='" + errorCode + '\'' +
", errorMsg='" + errorMsg + '\'' +
", openId='" + openId + '\'' +
", resultBonus=" + resultBonus +
", resultBalance=" + resultBalance +
'}';
}
public static WxMpMemberCardUpdateResult fromJson(String json) {
return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpMemberCardUpdateResult.class);
}
}

View File

@ -1,5 +1,7 @@
package me.chanjar.weixin.mp.bean.membercard;
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
import java.io.Serializable;
/**
@ -133,5 +135,9 @@ public class WxMpMemberCardUserInfoResult implements Serializable {
", hasActive=" + hasActive +
'}';
}
public static WxMpMemberCardUserInfoResult fromJson(String json) {
return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpMemberCardUserInfoResult.class);
}
}

View File

@ -6,6 +6,7 @@ import me.chanjar.weixin.mp.bean.*;
import me.chanjar.weixin.mp.bean.datacube.WxDataCubeUserCumulate;
import me.chanjar.weixin.mp.bean.datacube.WxDataCubeUserSummary;
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult;
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult;
import me.chanjar.weixin.mp.bean.material.*;
import me.chanjar.weixin.mp.bean.result.*;
@ -51,6 +52,7 @@ public class WxMpGsonBuilder {
INSTANCE.registerTypeAdapter(WxMpTemplateIndustry.class, new WxMpIndustryGsonAdapter());
INSTANCE.registerTypeAdapter(WxMpUserBlacklistGetResult.class, new WxUserBlacklistGetResultGsonAdapter());
INSTANCE.registerTypeAdapter(WxMpMemberCardUserInfoResult.class, new WxMpMemberCardUserInfoResultGsonAdapter());
INSTANCE.registerTypeAdapter(WxMpMemberCardUpdateResult.class, new WxMpMemberCardUpdateResultGsonAdapter());
}
public static Gson create() {

View File

@ -0,0 +1,37 @@
package me.chanjar.weixin.mp.util.json;
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.util.json.GsonHelper;
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult;
import java.lang.reflect.Type;
/**
* Json to WxMpMemberCardUpdateResult 的转换适配器
*
* @author YuJian
* @version 2017/7/15
*/
public class WxMpMemberCardUpdateResultGsonAdapter implements JsonDeserializer<WxMpMemberCardUpdateResult> {
@Override
public WxMpMemberCardUpdateResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext
jsonDeserializationContext) throws JsonParseException {
WxMpMemberCardUpdateResult result = new WxMpMemberCardUpdateResult();
JsonObject jsonObject = jsonElement.getAsJsonObject();
result.setOpenId(GsonHelper.getString(jsonObject, "openid"));
result.setErrorCode(GsonHelper.getString(jsonObject, "errcode"));
result.setErrorMsg(GsonHelper.getString(jsonObject, "errmsg"));
result.setResultBalance(GsonHelper.getInteger(jsonObject, "result_balance"));
result.setResultBonus(GsonHelper.getInteger(jsonObject, "result_bonus"));
return result;
}
}

View File

@ -9,9 +9,9 @@ import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult;
import java.lang.reflect.Type;
/**
* Created by YuJian on 2017/7/11.
* Json to WxMpMemberCardUserInfoResult 的转换适配器
*
* @author YuJian
* @author YuJian(mgcnrx11@gmail.com)
* @version 2017/7/11
*/
public class WxMpMemberCardUserInfoResultGsonAdapter implements JsonDeserializer<WxMpMemberCardUserInfoResult> {
@ -57,8 +57,7 @@ public class WxMpMemberCardUserInfoResultGsonAdapter implements JsonDeserializer
JsonArray valueListArray = customField.getAsJsonArray("value_list");
String[] valueList = new String[valueListArray.size()];
for (int j = 0; j < valueListArray.size(); j++) {
JsonObject valueListObj = valueListArray.getAsJsonObject();
valueList[i] = valueListObj.getAsString();
valueList[j] = valueListArray.get(j).getAsString();
}
customNameValues.setValueList(valueList);
customFieldListValues[i] = customNameValues;

View File

@ -0,0 +1,61 @@
package me.chanjar.weixin.mp.api.impl;
import com.google.inject.Inject;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.test.ApiTestModule;
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 org.testng.annotations.Guice;
import org.testng.annotations.Test;
import static org.testng.AssertJUnit.assertNotNull;
/**
* 会员卡相关接口的测试类
* 数据均为测试数据由于直接与调用微信的接口需要填写真实数据进行测试才能通过
*/
@Test
@Guice(modules = ApiTestModule.class)
public class WxMpMemberCardServiceImplTest {
@Inject
protected WxMpService wxService;
private String cardId = "abc";
private String code = "123";
private String openId = "xyz";
@Test
public void testActivateMemberCard() throws Exception {
WxMpMemberCardActivatedMessage activatedMessage = new WxMpMemberCardActivatedMessage();
activatedMessage.setMembershipNumber(openId);
activatedMessage.setCode(code);
activatedMessage.setCardId(cardId);
activatedMessage.setInitBonus(2000);
activatedMessage.setInitBonusRecord("测试激活送积分");
String response = this.wxService.getMemberCardService().activateMemberCard(activatedMessage);
assertNotNull(response);
System.out.println(response);
}
@Test
public void testGetUserInfo() throws Exception {
WxMpMemberCardUserInfoResult result = this.wxService.getMemberCardService().getUserInfo(cardId, code);
assertNotNull(result);
System.out.println(result);
}
@Test
public void testUpdateUserMemberCard() throws Exception {
WxMpMemberCardUpdateMessage updateMessage = new WxMpMemberCardUpdateMessage();
updateMessage.setAddBounus(100);
updateMessage.setBonus(1000);
updateMessage.setCardId(cardId);
updateMessage.setCode(code);
WxMpMemberCardUpdateResult result = this.wxService.getMemberCardService().updateUserMemberCard(updateMessage);
assertNotNull(result);
System.out.println(result);
}
}

View File

@ -0,0 +1,33 @@
package me.chanjar.weixin.mp.bean.membercard;
import org.testng.annotations.Test;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
/**
*
* @author YuJian
* @version 2017/7/15
*/
public class WxMpMemberCardUpdateResultTest {
@Test
public void testFromJson() throws Exception {
String json = "{\n" +
" \"errcode\": 0,\n" +
" \"errmsg\": \"ok\",\n" +
" \"result_bonus\": 100,\n" +
" \"result_balance\": 200,\n" +
" \"openid\": \"oFS7Fjl0WsZ9AMZqrI80nbIq8xrA\"\n" +
"}";
WxMpMemberCardUpdateResult result = WxMpMemberCardUpdateResult.fromJson(json);
assertNotNull(result);
assertTrue(result.getErrorCode().equalsIgnoreCase("0"));
System.out.println(result);
}
}

View File

@ -0,0 +1,80 @@
package me.chanjar.weixin.mp.bean.membercard;
import org.testng.annotations.Test;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
/**
*
* @author YuJian
* @version 2017/7/15
*/
public class WxMpMemberCardUserInfoResultTest {
@Test
public void testFromJson() throws Exception {
String json = "{\n" +
" \"errcode\": 0,\n" +
" \"errmsg\": \"ok\",\n" +
" \"openid\": \"obLatjjwDolFj******wNqRXw\",\n" +
" \"nickname\": \"*******\",\n" +
" \"membership_number\": \"658*****445\",\n" +
" \"bonus\": 995,\n" +
" \"sex\": \"MALE\",\n" +
" \"user_info\": {\n" +
" \"common_field_list\": [\n" +
" {\n" +
" \"name\": \"USER_FORM_INFO_FLAG_MOBILE\",\n" +
" \"value\": \"15*****518\"\n" +
" },\n" +
" {\n" +
" \"name\": \"USER_FORM_INFO_FLAG_NAME\",\n" +
" \"value\": \"HK\"\n" +
" },\n" +
" {\n" +
" \"name\": \"USER_FORM_INFO_FLAG_EDUCATION_BACKGROUND\",\n" +
" \"value\": \"研究生\"\n" +
" }\n" +
" ],\n" +
" \"custom_field_list\": [\n" +
" {\n" +
" \"name\": \"兴趣\",\n" +
" \"value\": \"钢琴\",\n" +
" \"value_list\": []\n" +
" },\n" +
" {\n" +
" \"name\": \"喜好\",\n" +
" \"value\": \"郭敬明\",\n" +
" \"value_list\": []\n" +
" },\n" +
" {\n" +
" \"name\": \"职业\",\n" +
" \"value\": \"\",\n" +
" \"value_list\": [\n" +
" \"赛车手\",\n" +
" \"旅行家\"\n" +
" ]\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"user_card_status\": \"NORMAL\",\n" +
" \"has_active\": false\n" +
"}";
WxMpMemberCardUserInfoResult userInfoResult = WxMpMemberCardUserInfoResult.fromJson(json);
assertNotNull(userInfoResult);
assertFalse(userInfoResult.getHasActive());
assertTrue(userInfoResult.getSex().equalsIgnoreCase("MALE"));
assertNotNull(userInfoResult.getUserInfo());
assertNotNull(userInfoResult.getUserInfo().getCommonFieldList());
assertNotNull(userInfoResult.getUserInfo().getCustomFieldList());
assertTrue(userInfoResult.getUserInfo().getCommonFieldList().length == 3);
assertTrue(userInfoResult.getUserInfo().getCustomFieldList()[2].getValueList()[0].equalsIgnoreCase("赛车手"));
System.out.println(userInfoResult);
}
}