批量发红包

This commit is contained in:
jinyu
2017-01-05 10:44:53 +08:00
parent a92933ff1b
commit e4a77e96df
7 changed files with 317 additions and 105 deletions
@@ -1,8 +1,15 @@
package com.foxinmy.weixin4j.api;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
@@ -49,11 +56,19 @@ public class CashApi extends MchApi {
*
* @param redpacket
* 红包信息
* @see #sendRedpack(Redpacket,String)
* @param openId
* 接受收红包的用户的openid 必填
* @see #sendRedpacks(Redpacket, String...)
*/
public RedpacketSendResult sendRedpack(Redpacket redpacket)
public RedpacketSendResult sendRedpack(Redpacket redpacket, String openId)
throws WeixinException {
return sendRedpack(redpacket, null);
try {
return sendRedpacks(redpacket, openId).get(0).get();
} catch (InterruptedException e) {
throw new WeixinException("send redpack error", e);
} catch (ExecutionException e) {
throw new WeixinException("send redpack error", e);
}
}
/**
@@ -61,8 +76,8 @@ public class CashApi extends MchApi {
*
* @param redpacket
* 红包信息
* @param appId
* 应用ID 可为空 主要是针对企业号支付时传入的agentid
* @param openIds
* 接受收红包的用户的openid 必填
* @return 发放结果
* @see com.foxinmy.weixin4j.payment.mch.Redpacket
* @see com.foxinmy.weixin4j.payment.mch.RedpacketSendResult
@@ -74,23 +89,44 @@ public class CashApi extends MchApi {
* 发放裂变红包接口</a>
* @throws WeixinException
*/
public RedpacketSendResult sendRedpack(Redpacket redpacket, String appId)
throws WeixinException {
public List<Future<RedpacketSendResult>> sendRedpacks(Redpacket redpacket,
String... openIds) {
String appId = redpacket.getAppId();
super.declareMerchant(redpacket);
JSONObject obj = (JSONObject) JSON.toJSON(redpacket);
final JSONObject obj = (JSONObject) JSON.toJSON(redpacket);
if (StringUtil.isNotBlank(appId)) {
obj.put("appid", appId);
}
obj.put("wxappid", obj.remove("appid"));
obj.put("sign", weixinSignature.sign(obj));
String param = XmlStream.map2xml(obj);
WeixinResponse response = getWeixinSSLExecutor()
.post(redpacket.getTotalNum() > 1 ? getRequestUri("groupredpack_send_uri")
: getRequestUri("redpack_send_uri"), param);
String text = response.getAsString()
.replaceFirst("<wxappid>", "<appid>")
.replaceFirst("</wxappid>", "</appid>");
return XmlStream.fromXML(text, RedpacketSendResult.class);
final String redpack_uri = redpacket.getTotalNum() > 1 ? getRequestUri("groupredpack_send_uri")
: getRequestUri("redpack_send_uri");
int sendLength = openIds.length;
ExecutorService sendExecutor = Executors.newFixedThreadPool(Math.max(1,
sendLength / 10)); // 十分之一?
List<Future<RedpacketSendResult>> callSendList = new ArrayList<Future<RedpacketSendResult>>(
sendLength);
for (final String openId : openIds) {
Future<RedpacketSendResult> futureSend = sendExecutor
.submit(new Callable<RedpacketSendResult>() {
@Override
public RedpacketSendResult call() throws Exception {
obj.put("re_openid", openId);
obj.put("sign", weixinSignature.sign(obj));
String param = XmlStream.map2xml(obj);
WeixinResponse response = getWeixinSSLExecutor()
.post(redpack_uri, param);
String text = response.getAsString()
.replaceFirst("<wxappid>", "<appid>")
.replaceFirst("</wxappid>", "</appid>");
return XmlStream.fromXML(text,
RedpacketSendResult.class);
}
});
callSendList.add(futureSend);
}
// 关闭启动线程,不再接受新的任务
sendExecutor.shutdown();
return callSendList;
}
/**
@@ -3,6 +3,8 @@ package com.foxinmy.weixin4j.payment;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Future;
import com.alibaba.fastjson.JSON;
import com.foxinmy.weixin4j.api.CashApi;
@@ -679,11 +681,15 @@ public class WeixinPayProxy {
*
* @param redpacket
* 红包信息
* @see #sendRedpack(Redpacket,String)
* @param openId
* 接受收红包的用户的openid 必填
* @see com.foxinmy.weixin4j.api.CashApi
* @throws WeixinException
* @see #sendRedpacks(Redpacket, String...)
*/
public RedpacketSendResult sendRedpack(Redpacket redpacket)
public RedpacketSendResult sendRedpack(Redpacket redpacket, String openId)
throws WeixinException {
return cashApi.sendRedpack(redpacket);
return cashApi.sendRedpack(redpacket, openId);
}
/**
@@ -691,8 +697,8 @@ public class WeixinPayProxy {
*
* @param redpacket
* 红包信息
* @param appId
* 应用ID 可为空 主要是针对企业号支付时传入的agentid
* @param openIds
* 接受收红包的用户的openid 必填
* @return 发放结果
* @see com.foxinmy.weixin4j.api.CashApi
* @see com.foxinmy.weixin4j.payment.mch.Redpacket
@@ -705,9 +711,9 @@ public class WeixinPayProxy {
* 发放裂变红包接口</a>
* @throws WeixinException
*/
public RedpacketSendResult sendRedpack(Redpacket redpacket, String appId)
throws WeixinException {
return cashApi.sendRedpack(redpacket, appId);
public List<Future<RedpacketSendResult>> sendRedpacks(Redpacket redpacket,
String... openIds) {
return cashApi.sendRedpacks(redpacket, openIds);
}
/**
@@ -6,6 +6,7 @@ import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.type.mch.RedpacketSceneType;
import com.foxinmy.weixin4j.util.DateUtil;
/**
@@ -38,12 +39,6 @@ public class Redpacket extends MerchantResult {
@XmlElement(name = "send_name")
@JSONField(name = "send_name")
private String sendName;
/**
* 接收红包的用户的openid
*/
@XmlElement(name = "re_openid")
@JSONField(name = "re_openid")
private String openId;
/**
* 付款金额,单位分
*/
@@ -96,6 +91,18 @@ public class Redpacket extends MerchantResult {
@XmlElement(name = "consume_mch_id")
@JSONField(name = "consume_mch_id")
private String consumeMchId;
/**
* 发放红包使用场景,红包金额大于200时必传
*/
@XmlElement(name = "scene_id")
@JSONField(name = "scene_id")
private RedpacketSceneType sceneType;
/**
* 活动信息
*/
@XmlElement(name = "risk_info")
@JSONField(name = "risk_info")
private String risk;
protected Redpacket() {
// jaxb required
@@ -108,8 +115,6 @@ public class Redpacket extends MerchantResult {
* 商户侧一天内不可重复的订单号 接口根据商户订单号支持重入 如出现超时可再调用 必填
* @param sendName
* 红包发送者名称 必填
* @param openId
* 接受收红包的用户的openid 必填
* @param totalAmount
* 付款金额 <font color="red">单位为元,自动格式化为分</font> 必填
* @param totalNum
@@ -123,12 +128,11 @@ public class Redpacket extends MerchantResult {
* @param remark
* 备注 必填
*/
public Redpacket(String outTradeNo, String sendName, String openId,
double totalAmount, int totalNum, String wishing, String clientIp,
String actName, String remark) {
public Redpacket(String outTradeNo, String sendName, double totalAmount,
int totalNum, String wishing, String clientIp, String actName,
String remark) {
this.outTradeNo = outTradeNo;
this.sendName = sendName;
this.openId = openId;
this.totalNum = totalNum;
this.wishing = wishing;
this.clientIp = clientIp;
@@ -146,14 +150,10 @@ public class Redpacket extends MerchantResult {
return sendName;
}
public String getOpenId() {
return openId;
}
public int getTotalAmount() {
return totalAmount;
}
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
@@ -204,14 +204,34 @@ public class Redpacket extends MerchantResult {
this.consumeMchId = consumeMchId;
}
public RedpacketSceneType getSceneType() {
return sceneType;
}
public void setSceneType(RedpacketSceneType sceneType) {
this.sceneType = sceneType;
}
public String getRisk() {
return risk;
}
public void setRisk(String risk) {
this.risk = risk;
}
public void setRisk(RedpacketRisk risk) {
this.risk = risk.toContent();
}
@Override
public String toString() {
return "Redpacket [msgAppId=" + msgAppId + ", consumeMchId="
+ consumeMchId + ", outTradeNo=" + outTradeNo + ", sendName="
+ sendName + ", openId=" + openId + ", totalAmount="
+ totalAmount + ", totalNum=" + totalNum + ", amtType="
+ amtType + ", wishing=" + wishing + ", clientIp=" + clientIp
+ ", actName=" + actName + ", remark=" + remark + ", "
+ super.toString() + "]";
return "Redpacket [outTradeNo=" + outTradeNo + ", sendName=" + sendName
+ ", totalAmount=" + totalAmount + ", totalNum=" + totalNum
+ ", amtType=" + amtType + ", wishing=" + wishing
+ ", clientIp=" + clientIp + ", actName=" + actName
+ ", remark=" + remark + ", msgAppId=" + msgAppId
+ ", consumeMchId=" + consumeMchId + ", sceneType=" + sceneType
+ ", risk=" + risk + ", " + super.toString() + "]";
}
}
@@ -0,0 +1,85 @@
package com.foxinmy.weixin4j.payment.mch;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import com.foxinmy.weixin4j.util.Consts;
import com.foxinmy.weixin4j.util.DateUtil;
import com.foxinmy.weixin4j.util.MapUtil;
/**
* 发送红包的活动信息
*
* @className RedpacketRisk
* @author jinyu(foxinmy@gmail.com)
* @date 2017年1月4日
* @since JDK 1.6
* @see
*/
public class RedpacketRisk {
private Map<String, String> risk;
public RedpacketRisk() {
this.risk = new HashMap<String, String>();
}
/**
* 用户操作的时间戳
*
* @return
*/
public RedpacketRisk postTimestamp() {
risk.put("posttime", DateUtil.timestamp2string());
return this;
}
/**
* 业务系统账号的手机号,国家代码-手机号。不需要+号
*
* @param mobile
* @return
*/
public RedpacketRisk mobile(String mobile) {
risk.put("mobile", mobile);
return this;
}
/**
* 用户操作的客户端版本
*
* @param clientVersion
* @return
*/
public RedpacketRisk clientVersion(String clientVersion) {
risk.put("clientversion", clientVersion);
return this;
}
/**
* mac 地址或者设备唯一标识
*
* @param deviceid
* @return
*/
public RedpacketRisk deviceid(String deviceid) {
risk.put("deviceid", deviceid);
return this;
}
public Map<String, String> getRisk() {
return risk;
}
public String toContent() {
if (risk.isEmpty())
return null;
try {
return URLEncoder.encode(MapUtil.toJoinString(risk, false, false),
Consts.UTF_8.name());
} catch (UnsupportedEncodingException e) {
return null;
}
}
}
@@ -0,0 +1,44 @@
package com.foxinmy.weixin4j.type.mch;
/**
* 发放红包使用场景
*
* @className RedpacketSceneType
* @author jinyu(foxinmy@gmail.com)
* @date 2017年1月4日
* @since JDK 1.6
*/
public enum RedpacketSceneType {
/**
* 商品促销
*/
PRODUCT_1,
/**
* 抽奖
*/
PRODUCT_2,
/**
* 虚拟物品兑奖
*/
PRODUCT_3,
/**
* 企业内部福利
*/
PRODUCT_4,
/**
* 渠道分润
*/
PRODUCT_5,
/**
* 保险回馈
*/
PRODUCT_6,
/**
* 彩票派奖
*/
PRODUCT_7,
/**
* 税务刮奖
*/
PRODUCT_8
}
@@ -26,10 +26,10 @@ public class CashTest extends PayTest {
@Test
public void sendRedpacket() throws WeixinException {
Redpacket redpacket = new Redpacket("HB001", "无忧钱庄",
"oyFLst1bqtuTcxK-ojF8hOGtLQao", 1d, 1, "红包测试", "127.0.0.1",
"快来领取红包吧!", "来就送钱");
RedpacketSendResult result = PAY.sendRedpack(redpacket);
Redpacket redpacket = new Redpacket("HB001", "无忧钱庄", 1d, 1, "红包测试",
"127.0.0.1", "快来领取红包吧!", "来就送钱");
RedpacketSendResult result = PAY.sendRedpack(redpacket,
"oyFLst1bqtuTcxK-ojF8hOGtLQao");
Assert.assertEquals(Consts.SUCCESS, result.getReturnCode());
Assert.assertEquals(Consts.SUCCESS, result.getResultCode());
System.err.println(result);