Merge remote-tracking branch 'wechat/develop' into develop

This commit is contained in:
曾浩 2020-09-14 09:13:15 +08:00
commit 3fbeec4018
37 changed files with 789 additions and 163 deletions

View File

@ -6,7 +6,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId> <artifactId>wx-java</artifactId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>WxJava - Weixin/Wechat Java SDK</name> <name>WxJava - Weixin/Wechat Java SDK</name>
<description>微信开发Java SDK</description> <description>微信开发Java SDK</description>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId> <artifactId>wx-java</artifactId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
</parent> </parent>
<packaging>pom</packaging> <packaging>pom</packaging>
<artifactId>wx-java-spring-boot-starters</artifactId> <artifactId>wx-java-spring-boot-starters</artifactId>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>wx-java-spring-boot-starters</artifactId> <artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>wx-java-spring-boot-starters</artifactId> <artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>wx-java-spring-boot-starters</artifactId> <artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -133,6 +133,7 @@ public class WxOpenStorageAutoConfiguration {
Config config = new Config(); Config config = new Config();
config.useSingleServer() config.useSingleServer()
.setAddress("redis://" + redis.getHost() + ":" + redis.getPort()) .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
.setDatabase(redis.getDatabase())
.setPassword(redis.getPassword()); .setPassword(redis.getPassword());
config.setTransportMode(TransportMode.NIO); config.setTransportMode(TransportMode.NIO);
return Redisson.create(config); return Redisson.create(config);

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>wx-java-spring-boot-starters</artifactId> <artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId> <artifactId>wx-java</artifactId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
</parent> </parent>
<artifactId>weixin-graal</artifactId> <artifactId>weixin-graal</artifactId>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId> <artifactId>wx-java</artifactId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
</parent> </parent>
<artifactId>weixin-java-common</artifactId> <artifactId>weixin-java-common</artifactId>

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId> <artifactId>wx-java</artifactId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
</parent> </parent>
<artifactId>weixin-java-cp</artifactId> <artifactId>weixin-java-cp</artifactId>

View File

@ -4,6 +4,7 @@ import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.message.WxCpLinkedCorpMessage; import me.chanjar.weixin.cp.bean.message.WxCpLinkedCorpMessage;
import me.chanjar.weixin.cp.bean.message.WxCpMessage; import me.chanjar.weixin.cp.bean.message.WxCpMessage;
import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult; import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult;
import me.chanjar.weixin.cp.bean.message.WxCpMessageSendStatistics;
/** /**
* 消息推送接口. * 消息推送接口.
@ -24,6 +25,21 @@ public interface WxCpMessageService {
*/ */
WxCpMessageSendResult send(WxCpMessage message) throws WxErrorException; WxCpMessageSendResult send(WxCpMessage message) throws WxErrorException;
/**
* <pre>
* 查询应用消息发送统计
* 请求方式POSTHTTPS
* 请求地址https://qyapi.weixin.qq.com/cgi-bin/message/get_statistics?access_token=ACCESS_TOKEN
*
* 详情请见: https://work.weixin.qq.com/api/doc/90000/90135/92369
* </pre>
*
* @param timeType 查询哪天的数据0当天1昨天默认为0
* @return 统计结果
* @throws WxErrorException the wx error exception
*/
WxCpMessageSendStatistics getStatistics(int timeType) throws WxErrorException;
/** /**
* <pre> * <pre>
* 互联企业的应用支持推送文本图片视频文件图文等类型 * 互联企业的应用支持推送文本图片视频文件图文等类型

View File

@ -1,5 +1,6 @@
package me.chanjar.weixin.cp.api.impl; package me.chanjar.weixin.cp.api.impl;
import com.google.common.collect.ImmutableMap;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpMessageService; import me.chanjar.weixin.cp.api.WxCpMessageService;
@ -7,7 +8,9 @@ import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpLinkedCorpMessage; import me.chanjar.weixin.cp.bean.message.WxCpLinkedCorpMessage;
import me.chanjar.weixin.cp.bean.message.WxCpMessage; import me.chanjar.weixin.cp.bean.message.WxCpMessage;
import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult; import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; import me.chanjar.weixin.cp.bean.message.WxCpMessageSendStatistics;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Message;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
/** /**
* 消息推送接口实现类. * 消息推送接口实现类.
@ -27,7 +30,13 @@ public class WxCpMessageServiceImpl implements WxCpMessageService {
} }
return WxCpMessageSendResult.fromJson(this.cpService.post(this.cpService.getWxCpConfigStorage() return WxCpMessageSendResult.fromJson(this.cpService.post(this.cpService.getWxCpConfigStorage()
.getApiUrl(WxCpApiPathConsts.Message.MESSAGE_SEND), message.toJson())); .getApiUrl(Message.MESSAGE_SEND), message.toJson()));
}
@Override
public WxCpMessageSendStatistics getStatistics(int timeType) throws WxErrorException {
return WxCpMessageSendStatistics.fromJson(this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(Message.GET_STATISTICS),
WxCpGsonBuilder.create().toJson(ImmutableMap.of("time_type", timeType))));
} }
@Override @Override
@ -38,6 +47,6 @@ public class WxCpMessageServiceImpl implements WxCpMessageService {
} }
return WxCpMessageSendResult.fromJson(this.cpService.post(this.cpService.getWxCpConfigStorage() return WxCpMessageSendResult.fromJson(this.cpService.post(this.cpService.getWxCpConfigStorage()
.getApiUrl(WxCpApiPathConsts.Message.LINKEDCORP_MESSAGE_SEND), message.toJson())); .getApiUrl(Message.LINKEDCORP_MESSAGE_SEND), message.toJson()));
} }
} }

View File

@ -0,0 +1,43 @@
package me.chanjar.weixin.cp.bean.message;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.List;
/**
* 应用消息发送统计信息.
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
* @date 2020-09-13
*/
@Data
public class WxCpMessageSendStatistics {
public static WxCpMessageSendStatistics fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpMessageSendStatistics.class);
}
private List<StatisticItem> statistics;
@Data
public static class StatisticItem {
/**
* 应用名
*/
@SerializedName("app_name")
private String appName;
/**
* 应用id
*/
@SerializedName("agentid")
private Integer agentId;
/**
* 发消息成功人次
*/
@SerializedName("count")
private Integer count;
}
}

View File

@ -1,6 +1,8 @@
package me.chanjar.weixin.cp.constant; package me.chanjar.weixin.cp.constant;
import lombok.experimental.UtilityClass;
/** /**
* <pre> * <pre>
* 企业微信api地址常量类 * 企业微信api地址常量类
@ -9,6 +11,7 @@ package me.chanjar.weixin.cp.constant;
* *
* @author <a href="https://github.com/binarywang">Binary Wang</a> * @author <a href="https://github.com/binarywang">Binary Wang</a>
*/ */
@UtilityClass
public final class WxCpApiPathConsts { public final class WxCpApiPathConsts {
public static final String DEFAULT_CP_BASE_URL = "https://qyapi.weixin.qq.com"; public static final String DEFAULT_CP_BASE_URL = "https://qyapi.weixin.qq.com";
@ -26,30 +29,39 @@ public final class WxCpApiPathConsts {
* 消息推送相关接口 * 消息推送相关接口
* https://work.weixin.qq.com/api/doc/90000/90135/90235 * https://work.weixin.qq.com/api/doc/90000/90135/90235
*/ */
@UtilityClass
public static class Message { public static class Message {
/** /**
* 发送应用消息 * 发送应用消息
*/ */
public static final String MESSAGE_SEND = "/cgi-bin/message/send"; public static final String MESSAGE_SEND = "/cgi-bin/message/send";
/**
* 查询应用消息发送统计
*/
public static final String GET_STATISTICS = "/cgi-bin/message/get_statistics";
/** /**
* 互联企业发送应用消息 * 互联企业发送应用消息
*/ */
public static final String LINKEDCORP_MESSAGE_SEND = "/cgi-bin/linkedcorp/message/send"; public static final String LINKEDCORP_MESSAGE_SEND = "/cgi-bin/linkedcorp/message/send";
} }
@UtilityClass
public static class Agent { public static class Agent {
public static final String AGENT_GET = "/cgi-bin/agent/get?agentid=%d"; public static final String AGENT_GET = "/cgi-bin/agent/get?agentid=%d";
public static final String AGENT_SET = "/cgi-bin/agent/set"; public static final String AGENT_SET = "/cgi-bin/agent/set";
public static final String AGENT_LIST = "/cgi-bin/agent/list"; public static final String AGENT_LIST = "/cgi-bin/agent/list";
} }
@UtilityClass
public static class OAuth2 { public static class OAuth2 {
public static final String GET_USER_INFO = "/cgi-bin/user/getuserinfo?code=%s&agentid=%d"; public static final String GET_USER_INFO = "/cgi-bin/user/getuserinfo?code=%s&agentid=%d";
public static final String GET_USER_DETAIL = "/cgi-bin/user/getuserdetail"; public static final String GET_USER_DETAIL = "/cgi-bin/user/getuserdetail";
public static final String URL_OAUTH2_AUTHORIZE = "https://open.weixin.qq.com/connect/oauth2/authorize"; public static final String URL_OAUTH2_AUTHORIZE = "https://open.weixin.qq.com/connect/oauth2/authorize";
} }
@UtilityClass
public static class Chat { public static class Chat {
public static final String APPCHAT_CREATE = "/cgi-bin/appchat/create"; public static final String APPCHAT_CREATE = "/cgi-bin/appchat/create";
public static final String APPCHAT_UPDATE = "/cgi-bin/appchat/update"; public static final String APPCHAT_UPDATE = "/cgi-bin/appchat/update";
@ -57,6 +69,7 @@ public final class WxCpApiPathConsts {
public static final String APPCHAT_SEND = "/cgi-bin/appchat/send"; public static final String APPCHAT_SEND = "/cgi-bin/appchat/send";
} }
@UtilityClass
public static class Department { public static class Department {
public static final String DEPARTMENT_CREATE = "/cgi-bin/department/create"; public static final String DEPARTMENT_CREATE = "/cgi-bin/department/create";
public static final String DEPARTMENT_UPDATE = "/cgi-bin/department/update"; public static final String DEPARTMENT_UPDATE = "/cgi-bin/department/update";
@ -64,6 +77,7 @@ public final class WxCpApiPathConsts {
public static final String DEPARTMENT_LIST = "/cgi-bin/department/list"; public static final String DEPARTMENT_LIST = "/cgi-bin/department/list";
} }
@UtilityClass
public static class Media { public static class Media {
public static final String MEDIA_GET = "/cgi-bin/media/get"; public static final String MEDIA_GET = "/cgi-bin/media/get";
public static final String MEDIA_UPLOAD = "/cgi-bin/media/upload?type="; public static final String MEDIA_UPLOAD = "/cgi-bin/media/upload?type=";
@ -71,12 +85,14 @@ public final class WxCpApiPathConsts {
public static final String JSSDK_MEDIA_GET = "/cgi-bin/media/get/jssdk"; public static final String JSSDK_MEDIA_GET = "/cgi-bin/media/get/jssdk";
} }
@UtilityClass
public static class Menu { public static class Menu {
public static final String MENU_CREATE = "/cgi-bin/menu/create?agentid=%d"; public static final String MENU_CREATE = "/cgi-bin/menu/create?agentid=%d";
public static final String MENU_DELETE = "/cgi-bin/menu/delete?agentid=%d"; public static final String MENU_DELETE = "/cgi-bin/menu/delete?agentid=%d";
public static final String MENU_GET = "/cgi-bin/menu/get?agentid=%d"; public static final String MENU_GET = "/cgi-bin/menu/get?agentid=%d";
} }
@UtilityClass
public static class Oa { public static class Oa {
public static final String GET_CHECKIN_DATA = "/cgi-bin/checkin/getcheckindata"; public static final String GET_CHECKIN_DATA = "/cgi-bin/checkin/getcheckindata";
public static final String GET_CHECKIN_OPTION = "/cgi-bin/checkin/getcheckinoption"; public static final String GET_CHECKIN_OPTION = "/cgi-bin/checkin/getcheckinoption";
@ -87,6 +103,7 @@ public final class WxCpApiPathConsts {
public static final String APPLY_EVENT = "/cgi-bin/oa/applyevent"; public static final String APPLY_EVENT = "/cgi-bin/oa/applyevent";
} }
@UtilityClass
public static class Tag { public static class Tag {
public static final String TAG_CREATE = "/cgi-bin/tag/create"; public static final String TAG_CREATE = "/cgi-bin/tag/create";
public static final String TAG_UPDATE = "/cgi-bin/tag/update"; public static final String TAG_UPDATE = "/cgi-bin/tag/update";
@ -97,10 +114,12 @@ public final class WxCpApiPathConsts {
public static final String TAG_DEL_TAG_USERS = "/cgi-bin/tag/deltagusers"; public static final String TAG_DEL_TAG_USERS = "/cgi-bin/tag/deltagusers";
} }
@UtilityClass
public static class TaskCard { public static class TaskCard {
public static final String UPDATE_TASK_CARD = "/cgi-bin/message/update_taskcard"; public static final String UPDATE_TASK_CARD = "/cgi-bin/message/update_taskcard";
} }
@UtilityClass
public static class Tp { public static class Tp {
public static final String JSCODE_TO_SESSION = "/cgi-bin/service/miniprogram/jscode2session"; public static final String JSCODE_TO_SESSION = "/cgi-bin/service/miniprogram/jscode2session";
public static final String GET_CORP_TOKEN = "/cgi-bin/service/get_corp_token"; public static final String GET_CORP_TOKEN = "/cgi-bin/service/get_corp_token";
@ -111,6 +130,7 @@ public final class WxCpApiPathConsts {
public static final String GET_AUTH_INFO = "/cgi-bin/service/get_auth_info"; public static final String GET_AUTH_INFO = "/cgi-bin/service/get_auth_info";
} }
@UtilityClass
public static class User { public static class User {
public static final String USER_AUTHENTICATE = "/cgi-bin/user/authsucc?userid="; public static final String USER_AUTHENTICATE = "/cgi-bin/user/authsucc?userid=";
public static final String USER_CREATE = "/cgi-bin/user/create"; public static final String USER_CREATE = "/cgi-bin/user/create";
@ -127,6 +147,7 @@ public final class WxCpApiPathConsts {
public static final String GET_EXTERNAL_CONTACT = "/cgi-bin/crm/get_external_contact?external_userid="; public static final String GET_EXTERNAL_CONTACT = "/cgi-bin/crm/get_external_contact?external_userid=";
} }
@UtilityClass
public static class ExternalContact { public static class ExternalContact {
@Deprecated @Deprecated
public static final String GET_EXTERNAL_CONTACT = "/cgi-bin/crm/get_external_contact?external_userid="; public static final String GET_EXTERNAL_CONTACT = "/cgi-bin/crm/get_external_contact?external_userid=";

View File

@ -1,5 +1,7 @@
package me.chanjar.weixin.cp.constant; package me.chanjar.weixin.cp.constant;
import lombok.experimental.UtilityClass;
/** /**
* <pre> * <pre>
* 企业微信常量 * 企业微信常量
@ -8,11 +10,13 @@ package me.chanjar.weixin.cp.constant;
* *
* @author <a href="https://github.com/binarywang">Binary Wang</a> * @author <a href="https://github.com/binarywang">Binary Wang</a>
*/ */
@UtilityClass
public class WxCpConsts { public class WxCpConsts {
/** /**
* 企业微信端推送过来的事件类型. * 企业微信端推送过来的事件类型.
* 参考文档https://work.weixin.qq.com/api/doc#12974 * 参考文档https://work.weixin.qq.com/api/doc#12974
*/ */
@UtilityClass
public static class EventType { public static class EventType {
/** /**
* 成员关注事件. * 成员关注事件.
@ -105,6 +109,7 @@ public class WxCpConsts {
/** /**
* 企业外部联系人变更事件的CHANGE_TYPE * 企业外部联系人变更事件的CHANGE_TYPE
*/ */
@UtilityClass
public static class ExternalContactChangeType { public static class ExternalContactChangeType {
/** /**
* 新增外部联系人 * 新增外部联系人
@ -128,6 +133,7 @@ public class WxCpConsts {
/** /**
* 企业微信通讯录变更事件. * 企业微信通讯录变更事件.
*/ */
@UtilityClass
public static class ContactChangeType { public static class ContactChangeType {
/** /**
* 新增成员事件. * 新增成员事件.
@ -169,6 +175,7 @@ public class WxCpConsts {
/** /**
* 互联企业发送应用消息的消息类型. * 互联企业发送应用消息的消息类型.
*/ */
@UtilityClass
public static class LinkedCorpMsgType { public static class LinkedCorpMsgType {
/** /**
* 文本消息. * 文本消息.
@ -213,6 +220,7 @@ public class WxCpConsts {
/** /**
* 群机器人的消息类型. * 群机器人的消息类型.
*/ */
@UtilityClass
public static class GroupRobotMsgType { public static class GroupRobotMsgType {
/** /**
* 文本消息. * 文本消息.
@ -238,6 +246,7 @@ public class WxCpConsts {
/** /**
* 应用推送消息的消息类型. * 应用推送消息的消息类型.
*/ */
@UtilityClass
public static class AppChatMsgType { public static class AppChatMsgType {
/** /**
* 文本消息. * 文本消息.

View File

@ -12,6 +12,7 @@ import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpLinkedCorpMessage; import me.chanjar.weixin.cp.bean.message.WxCpLinkedCorpMessage;
import me.chanjar.weixin.cp.bean.message.WxCpMessage; import me.chanjar.weixin.cp.bean.message.WxCpMessage;
import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult; import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult;
import me.chanjar.weixin.cp.bean.message.WxCpMessageSendStatistics;
import org.testng.annotations.AfterTest; import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
import org.testng.annotations.Guice; import org.testng.annotations.Guice;
@ -20,6 +21,7 @@ import org.testng.annotations.Test;
import static com.github.dreamhead.moco.Moco.file; import static com.github.dreamhead.moco.Moco.file;
import static com.github.dreamhead.moco.MocoJsonRunner.jsonHttpServer; import static com.github.dreamhead.moco.MocoJsonRunner.jsonHttpServer;
import static me.chanjar.weixin.cp.api.ApiTestModuleWithMockServer.mockServerPort; import static me.chanjar.weixin.cp.api.ApiTestModuleWithMockServer.mockServerPort;
import static org.assertj.core.api.Assertions.assertThat;
import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNotNull;
/** /**
@ -29,8 +31,8 @@ import static org.testng.Assert.assertNotNull;
* @date 2020-08-30 * @date 2020-08-30
*/ */
@Test @Test
@Guice(modules = ApiTestModuleWithMockServer.class) //@Guice(modules = ApiTestModuleWithMockServer.class)
//@Guice(modules = ApiTestModule.class) @Guice(modules = ApiTestModule.class)
public class WxCpMessageServiceImplTest { public class WxCpMessageServiceImplTest {
@Inject @Inject
protected WxCpService wxService; protected WxCpService wxService;
@ -154,11 +156,24 @@ public class WxCpMessageServiceImplTest {
} }
@Test @Test
public void testLinkedCorpMessageSend() throws WxErrorException { public void testSendLinkedCorpMessage() throws WxErrorException {
this.wxService.getMessageService().sendLinkedCorpMessage(WxCpLinkedCorpMessage.builder() this.wxService.getMessageService().sendLinkedCorpMessage(WxCpLinkedCorpMessage.builder()
.msgType(WxConsts.KefuMsgType.TEXT) .msgType(WxConsts.KefuMsgType.TEXT)
.toUsers(new String[]{configStorage.getUserId()}) .toUsers(new String[]{configStorage.getUserId()})
.content("欢迎欢迎,热烈欢迎\n换行测试\n超链接:<a href=\"http://www.baidu.com\">Hello World</a>") .content("欢迎欢迎,热烈欢迎\n换行测试\n超链接:<a href=\"http://www.baidu.com\">Hello World</a>")
.build()); .build());
} }
@Test
public void testSend() {
// see other test methods
}
@Test
public void testGetStatistics() throws WxErrorException {
final WxCpMessageSendStatistics statistics = this.wxService.getMessageService().getStatistics(1);
assertNotNull(statistics);
assertThat(statistics.getStatistics()).isNotNull();
}
} }

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId> <artifactId>wx-java</artifactId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
</parent> </parent>
<artifactId>weixin-java-miniapp</artifactId> <artifactId>weixin-java-miniapp</artifactId>

View File

@ -1,7 +1,7 @@
package cn.binarywang.wx.miniapp.api; package cn.binarywang.wx.miniapp.api;
import cn.binarywang.wx.miniapp.bean.WxMaLiveInfo; import cn.binarywang.wx.miniapp.bean.live.WxMaLiveGoodInfo;
import cn.binarywang.wx.miniapp.bean.WxMaLiveResult; import cn.binarywang.wx.miniapp.bean.live.WxMaLiveResult;
import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.error.WxErrorException;
import java.util.List; import java.util.List;
@ -37,7 +37,7 @@ public interface WxMaLiveGoodsService {
* @return 返回auditIdgoodsId * @return 返回auditIdgoodsId
* @throws WxErrorException . * @throws WxErrorException .
*/ */
WxMaLiveResult addGoods(WxMaLiveInfo.Goods goods) throws WxErrorException; WxMaLiveResult addGoods(WxMaLiveGoodInfo goods) throws WxErrorException;
/** /**
* 撤回审核 * 撤回审核
@ -91,7 +91,7 @@ public interface WxMaLiveGoodsService {
* @return 更新商品是否成功 * @return 更新商品是否成功
* @throws WxErrorException . * @throws WxErrorException .
*/ */
boolean updateGoods(WxMaLiveInfo.Goods goods) throws WxErrorException; boolean updateGoods(WxMaLiveGoodInfo goods) throws WxErrorException;
/** /**
* 获取商品状态 * 获取商品状态

View File

@ -1,7 +1,7 @@
package cn.binarywang.wx.miniapp.api; package cn.binarywang.wx.miniapp.api;
import cn.binarywang.wx.miniapp.bean.WxMaLiveInfo; import cn.binarywang.wx.miniapp.bean.live.WxMaLiveResult;
import cn.binarywang.wx.miniapp.bean.WxMaLiveResult; import cn.binarywang.wx.miniapp.bean.live.WxMaLiveRoomInfo;
import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.error.WxErrorException;
import java.util.List; import java.util.List;
@ -31,7 +31,7 @@ public interface WxMaLiveService {
* @return . * @return .
* @throws WxErrorException . * @throws WxErrorException .
*/ */
Integer createRoom(WxMaLiveInfo.RoomInfo roomInfo) throws WxErrorException; Integer createRoom(WxMaLiveRoomInfo roomInfo) throws WxErrorException;
/** /**
* 获取直播房间列表.分页 * 获取直播房间列表.分页

View File

@ -2,16 +2,14 @@ package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaLiveGoodsService; import cn.binarywang.wx.miniapp.api.WxMaLiveGoodsService;
import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaLiveInfo; import cn.binarywang.wx.miniapp.bean.live.WxMaLiveGoodInfo;
import cn.binarywang.wx.miniapp.bean.WxMaLiveResult; import cn.binarywang.wx.miniapp.bean.live.WxMaLiveResult;
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.enums.WxType;
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.json.GsonParser; import me.chanjar.weixin.common.util.json.GsonParser;
@ -32,15 +30,9 @@ public class WxMaLiveGoodsServiceImpl implements WxMaLiveGoodsService {
private final WxMaService wxMaService; private final WxMaService wxMaService;
@Override @Override
public WxMaLiveResult addGoods(WxMaLiveInfo.Goods goods) throws WxErrorException { public WxMaLiveResult addGoods(WxMaLiveGoodInfo goods) throws WxErrorException {
Map<String, Object> map = new HashMap<>(2); return WxMaLiveResult.fromJson(this.wxMaService.post(ADD_GOODS,
map.put("goodsInfo", goods); WxMaGsonBuilder.create().toJson(ImmutableMap.of("goodsInfo", goods))));
String responseContent = this.wxMaService.post(ADD_GOODS, WxMaGsonBuilder.create().toJson(map));
JsonObject jsonObject = GsonParser.parse(responseContent);
if (jsonObject.get("errcode").getAsInt() != 0) {
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
}
return WxMaLiveResult.fromJson(jsonObject.toString());
} }
@Override @Override
@ -48,11 +40,7 @@ public class WxMaLiveGoodsServiceImpl implements WxMaLiveGoodsService {
Map<String, Integer> map = new HashMap<>(4); Map<String, Integer> map = new HashMap<>(4);
map.put("auditId", auditId); map.put("auditId", auditId);
map.put("goodsId", goodsId); map.put("goodsId", goodsId);
String responseContent = this.wxMaService.post(RESET_AUDIT_GOODS, WxMaGsonBuilder.create().toJson(map)); this.wxMaService.post(RESET_AUDIT_GOODS, WxMaGsonBuilder.create().toJson(map));
JsonObject jsonObject = GsonParser.parse(responseContent);
if (jsonObject.get("errcode").getAsInt() != 0) {
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
}
return true; return true;
} }
@ -62,9 +50,6 @@ public class WxMaLiveGoodsServiceImpl implements WxMaLiveGoodsService {
map.put("goodsId", goodsId); map.put("goodsId", goodsId);
String responseContent = this.wxMaService.post(AUDIT_GOODS, WxMaGsonBuilder.create().toJson(map)); String responseContent = this.wxMaService.post(AUDIT_GOODS, WxMaGsonBuilder.create().toJson(map));
JsonObject jsonObject = GsonParser.parse(responseContent); JsonObject jsonObject = GsonParser.parse(responseContent);
if (jsonObject.get("errcode").getAsInt() != 0) {
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
}
return jsonObject.get("auditId").getAsString(); return jsonObject.get("auditId").getAsString();
} }
@ -72,23 +57,15 @@ public class WxMaLiveGoodsServiceImpl implements WxMaLiveGoodsService {
public boolean deleteGoods(Integer goodsId) throws WxErrorException { public boolean deleteGoods(Integer goodsId) throws WxErrorException {
Map<String, Integer> map = new HashMap<>(2); Map<String, Integer> map = new HashMap<>(2);
map.put("goodsId", goodsId); map.put("goodsId", goodsId);
String responseContent = this.wxMaService.post(DELETE_GOODS, WxMaGsonBuilder.create().toJson(map)); this.wxMaService.post(DELETE_GOODS, WxMaGsonBuilder.create().toJson(map));
JsonObject jsonObject = GsonParser.parse(responseContent);
if (jsonObject.get("errcode").getAsInt() != 0) {
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
}
return true; return true;
} }
@Override @Override
public boolean updateGoods(WxMaLiveInfo.Goods goods) throws WxErrorException { public boolean updateGoods(WxMaLiveGoodInfo goods) throws WxErrorException {
Map<String, Object> map = new HashMap<>(2); Map<String, Object> map = new HashMap<>(2);
map.put("goodsInfo", goods); map.put("goodsInfo", goods);
String responseContent = this.wxMaService.post(UPDATE_GOODS, WxMaGsonBuilder.create().toJson(map)); this.wxMaService.post(UPDATE_GOODS, WxMaGsonBuilder.create().toJson(map));
JsonObject jsonObject = GsonParser.parse(responseContent);
if (jsonObject.get("errcode").getAsInt() != 0) {
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
}
return true; return true;
} }
@ -97,11 +74,7 @@ public class WxMaLiveGoodsServiceImpl implements WxMaLiveGoodsService {
Map<String, Object> map = new HashMap<>(2); Map<String, Object> map = new HashMap<>(2);
map.put("goods_ids", goodsIds); map.put("goods_ids", goodsIds);
String responseContent = this.wxMaService.post(GET_GOODS_WARE_HOUSE, WxMaGsonBuilder.create().toJson(map)); String responseContent = this.wxMaService.post(GET_GOODS_WARE_HOUSE, WxMaGsonBuilder.create().toJson(map));
JsonObject jsonObject = GsonParser.parse(responseContent); return WxMaLiveResult.fromJson(responseContent);
if (jsonObject.get("errcode").getAsInt() != 0) {
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
}
return WxMaLiveResult.fromJson(jsonObject.toString());
} }
@Override @Override
@ -109,9 +82,6 @@ public class WxMaLiveGoodsServiceImpl implements WxMaLiveGoodsService {
ImmutableMap<String, ? extends Serializable> params = ImmutableMap.of("status", status, "offset", offset, "limit", limit); ImmutableMap<String, ? extends Serializable> params = ImmutableMap.of("status", status, "offset", offset, "limit", limit);
String responseContent = wxMaService.get(GET_APPROVED_GOODS, Joiner.on("&").withKeyValueSeparator("=").join(params)); String responseContent = wxMaService.get(GET_APPROVED_GOODS, Joiner.on("&").withKeyValueSeparator("=").join(params));
JsonObject jsonObject = GsonParser.parse(responseContent); JsonObject jsonObject = GsonParser.parse(responseContent);
if (jsonObject.get("errcode").getAsInt() != 0) {
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
}
JsonArray goodsArr = jsonObject.getAsJsonArray("goods"); JsonArray goodsArr = jsonObject.getAsJsonArray("goods");
if (goodsArr.size() > 0) { if (goodsArr.size() > 0) {
for (int i = 0; i < goodsArr.size(); i++) { for (int i = 0; i < goodsArr.size(); i++) {

View File

@ -2,8 +2,8 @@ package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaLiveService; import cn.binarywang.wx.miniapp.api.WxMaLiveService;
import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaLiveInfo; import cn.binarywang.wx.miniapp.bean.live.WxMaLiveResult;
import cn.binarywang.wx.miniapp.bean.WxMaLiveResult; import cn.binarywang.wx.miniapp.bean.live.WxMaLiveRoomInfo;
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -31,7 +31,7 @@ public class WxMaLiveServiceImpl implements WxMaLiveService {
private final WxMaService wxMaService; private final WxMaService wxMaService;
@Override @Override
public Integer createRoom(WxMaLiveInfo.RoomInfo roomInfo) throws WxErrorException { public Integer createRoom(WxMaLiveRoomInfo roomInfo) throws WxErrorException {
String responseContent = this.wxMaService.post(CREATE_ROOM, WxMaGsonBuilder.create().toJson(roomInfo)); String responseContent = this.wxMaService.post(CREATE_ROOM, WxMaGsonBuilder.create().toJson(roomInfo));
JsonObject jsonObject = GsonParser.parse(responseContent); JsonObject jsonObject = GsonParser.parse(responseContent);
if (jsonObject.get("errcode").getAsInt() != 0) { if (jsonObject.get("errcode").getAsInt() != 0) {

View File

@ -1,60 +0,0 @@
package cn.binarywang.wx.miniapp.bean;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 直播接口入参
*
* @author yjwang
* @date 2020/4/5
*/
@Data
public class WxMaLiveInfo implements Serializable {
private static final long serialVersionUID = 7285263767524755887L;
/**
* 直播列表
*/
@Data
public static class RoomInfo implements Serializable {
private static final long serialVersionUID = 7745775280267417154L;
private String name;
private Integer roomid;
private String coverImg;
private String shareImg;
private Integer liveStatus;
private Long startTime;
private Long endTime;
private String anchorName;
private String anchorWechat;
private String anchorImg;
private Integer type;
private Integer screenType;
private Integer closeLike;
private Integer closeGoods;
private Integer closeComment;
private List<Goods> goods;
}
/**
* 商品列表
*/
@Data
public static class Goods implements Serializable {
private static final long serialVersionUID = 5769245932149287574L;
private Integer goodsId;
private String coverImgUrl;
private String url;
private Integer priceType;
private String price;
private String price2;
private String name;
/**
* 1, 2表示是为api添加商品否则是在MP添加商品
*/
private String thirdPartyTag;
}
}

View File

@ -0,0 +1,24 @@
package cn.binarywang.wx.miniapp.bean.live;
import lombok.Data;
import java.io.Serializable;
/**
* 直播商品信息
*/
@Data
public class WxMaLiveGoodInfo implements Serializable {
private static final long serialVersionUID = 5769245932149287574L;
private Integer goodsId;
private String coverImgUrl;
private String url;
private Integer priceType;
private String price;
private String price2;
private String name;
/**
* 1, 2表示是为api添加商品否则是在MP添加商品
*/
private String thirdPartyTag;
}

View File

@ -1,4 +1,4 @@
package cn.binarywang.wx.miniapp.bean; package cn.binarywang.wx.miniapp.bean.live;
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
@ -18,8 +18,6 @@ import java.util.List;
@Data @Data
public class WxMaLiveResult implements Serializable { public class WxMaLiveResult implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private Integer errcode;
private String errmsg;
private Integer total; private Integer total;
private Integer auditId; private Integer auditId;
private Integer goodsId; private Integer goodsId;

View File

@ -0,0 +1,86 @@
package cn.binarywang.wx.miniapp.bean.live;
import lombok.Data;
import java.io.Serializable;
/**
* 直播间信息
*/
@Data
public class WxMaLiveRoomInfo implements Serializable {
private static final long serialVersionUID = 7745775280267417154L;
/**
* 直播间名字最短3个汉字最长17个汉字1个汉字相当于2个字符
**/
private String name;
/**
* 背景图填入mediaIDmediaID获取后三天内有效图片mediaID的获取请参考以下文档 https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/New_temporary_materials.html直播间背景图图片规则建议像素1080*1920大小不超过2M
**/
private String coverImg;
/**
* 直播计划开始时间开播时间需要在当前时间的10分钟后 并且 开始时间不能在 6 个月后
**/
private Long startTime;
/**
* 直播计划结束时间开播时间和结束时间间隔不得短于30分钟不得超过24小时
**/
private Long endTime;
/**
* 主播昵称最短2个汉字最长15个汉字1个汉字相当于2个字符
**/
private String anchorName;
/**
* 主播微信号如果未实名认证需要先前往小程序直播小程序进行实名验证, 小程序二维码链接https://res.wx.qq.com/op_res/BbVNeczA1XudfjVqCVoKgfuWe7e3aUhokktRVOqf_F0IqS6kYR--atCpVNUUC3zr
**/
private String anchorWechat;
/**
* 主播副号微信号如果未实名认证需要先前往小程序直播小程序进行实名验证, 小程序二维码链接https://res.wx.qq.com/op_res/BbVNeczA1XudfjVqCVoKgfuWe7e3aUhokktRVOqf_F0IqS6kYR--atCpVNUUC3zr
**/
private String subAnchorWechat;
/**
* 分享图填入mediaIDmediaID获取后三天内有效图片mediaID的获取请参考以下文档 https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/New_temporary_materials.html直播间分享图图片规则建议像素800*640大小不超过1M
**/
private String shareImg;
/**
* 购物直播频道封面图填入mediaIDmediaID获取后三天内有效图片mediaID的获取请参考以下文档 https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/New_temporary_materials.html; 购物直播频道封面图图片规则建议像素800*800大小不超过100KB
**/
private String feedsImg;
/**
* 是否开启官方收录 1: 开启0关闭默认开启收录
**/
private Integer isFeedsPublic;
/**
* 直播间类型 1: 推流0手机直播
**/
private Integer type;
/**
* 横屏竖屏 1横屏0竖屏横屏视频宽高比为16:94:31.85:1 竖屏视频宽高比为9:162:3
**/
private Integer screenType;
/**
* 是否关闭点赞 0开启1关闭若关闭直播开始后不允许开启
**/
private Integer closeLike;
/**
* 是否关闭货架 0开启1关闭若关闭直播开始后不允许开启
**/
private Integer closeGoods;
/**
* 是否关闭评论 0开启1关闭若关闭直播开始后不允许开启
**/
private Integer closeComment;
/**
* 是否关闭回放 0开启1关闭默认关闭回放
**/
private Integer closeReplay;
/**
* 是否关闭分享 0开启1关闭默认开启分享直播开始后不允许修改
**/
private Integer loseShare;
/**
* closeKf Number 是否关闭客服 0开启1关闭 默认关闭客服
**/
private Integer closeKf;
}

View File

@ -1,8 +1,8 @@
package cn.binarywang.wx.miniapp.api.impl; package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaLiveInfo; import cn.binarywang.wx.miniapp.bean.live.WxMaLiveGoodInfo;
import cn.binarywang.wx.miniapp.bean.WxMaLiveResult; import cn.binarywang.wx.miniapp.bean.live.WxMaLiveResult;
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.bean.result.WxMediaUploadResult; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
@ -31,7 +31,7 @@ public class WxMaLiveGoodsServiceImplTest {
//上传临时素材 //上传临时素材
WxMediaUploadResult mediaUpload = this.wxService.getMediaService().uploadMedia("image", new File("E:\\1.png")); WxMediaUploadResult mediaUpload = this.wxService.getMediaService().uploadMedia("image", new File("E:\\1.png"));
WxMaLiveInfo.Goods goods = new WxMaLiveInfo.Goods(); WxMaLiveGoodInfo goods = new WxMaLiveGoodInfo();
goods.setCoverImgUrl(mediaUpload.getMediaId()); goods.setCoverImgUrl(mediaUpload.getMediaId());
goods.setName("宫廷奢华真丝四件套"); goods.setName("宫廷奢华真丝四件套");
goods.setPrice("1599"); goods.setPrice("1599");
@ -64,7 +64,7 @@ public class WxMaLiveGoodsServiceImplTest {
@Test @Test
public void updateGoods() throws Exception { public void updateGoods() throws Exception {
WxMaLiveInfo.Goods goods = new WxMaLiveInfo.Goods(); WxMaLiveGoodInfo goods = new WxMaLiveGoodInfo();
goods.setGoodsId(8); goods.setGoodsId(8);
goods.setName("宫廷奢华真丝四件套"); goods.setName("宫廷奢华真丝四件套");
goods.setCoverImgUrl("http://mmbiz.qpic.cn/mmbiz_png/omYktZNGamuUQE0WPVfqdnLV61JDhluXOac7PiaoZeticFpcR7wvicC0aXUC2VXkl7r1gN0QSKosv2satn6oCFeiaQ/0"); goods.setCoverImgUrl("http://mmbiz.qpic.cn/mmbiz_png/omYktZNGamuUQE0WPVfqdnLV61JDhluXOac7PiaoZeticFpcR7wvicC0aXUC2VXkl7r1gN0QSKosv2satn6oCFeiaQ/0");

View File

@ -1,8 +1,8 @@
package cn.binarywang.wx.miniapp.api.impl; package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaLiveInfo; import cn.binarywang.wx.miniapp.bean.live.WxMaLiveResult;
import cn.binarywang.wx.miniapp.bean.WxMaLiveResult; import cn.binarywang.wx.miniapp.bean.live.WxMaLiveRoomInfo;
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.bean.result.WxMediaUploadResult; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
@ -33,7 +33,7 @@ public class WxMaLiveServiceImplTest {
//上传临时素材 //上传临时素材
WxMediaUploadResult mediaUpload = this.wxService.getMediaService().uploadMedia("image", new File("E:\\1.png")); WxMediaUploadResult mediaUpload = this.wxService.getMediaService().uploadMedia("image", new File("E:\\1.png"));
WxMaLiveInfo.RoomInfo roomInfo = new WxMaLiveInfo.RoomInfo(); WxMaLiveRoomInfo roomInfo = new WxMaLiveRoomInfo();
roomInfo.setName("订阅通知直播间"); roomInfo.setName("订阅通知直播间");
roomInfo.setCoverImg(mediaUpload.getMediaId()); roomInfo.setCoverImg(mediaUpload.getMediaId());
Calendar c = Calendar.getInstance(); Calendar c = Calendar.getInstance();

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId> <artifactId>wx-java</artifactId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
</parent> </parent>
<artifactId>weixin-java-mp</artifactId> <artifactId>weixin-java-mp</artifactId>

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId> <artifactId>wx-java</artifactId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
</parent> </parent>
<artifactId>weixin-java-open</artifactId> <artifactId>weixin-java-open</artifactId>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId> <artifactId>wx-java</artifactId>
<version>3.9.1.B</version> <version>3.9.2.B</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -0,0 +1,57 @@
package com.github.binarywang.wxpay.bean.ecommerce;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author: f00lish
* @date: 2020/09/12
*/
@Data
@NoArgsConstructor
public class FundBalanceResult {
/**
* <pre>
* 字段名二级商户号
* 变量名sub_mchid
* 是否必填
* 类型string(32)
* 描述
* 电商平台二级商户号由微信支付生成并下发
* 示例值1900000109
* </pre>
*/
@SerializedName("sub_mchid")
private String subMchid;
/**
* <pre>
* 字段名可用余额
* 变量名available_amount
* 是否必填
* 类型int64
* 描述
* 可用余额单位此余额可做提现操作
* 示例值100
* </pre>
*/
@SerializedName("available_amount")
private Integer availableAmount;
/**
* <pre>
* 字段名不可用余额
* 变量名pending_amount
* 是否必填
* 类型int64
* 描述
* 不可用余额单位
* 示例值100
* </pre>
*/
@SerializedName("pending_amount")
private Integer pendingAmount;
}

View File

@ -0,0 +1,192 @@
package com.github.binarywang.wxpay.bean.ecommerce;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 请求分账 对象
* <pre>
* 文档地址https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/profitsharing/chapter3_1.shtml
* </pre>
* @author: f00lish
* @date: 2020/09/12
*/
@Data
@NoArgsConstructor
public class ProfitSharingRequest implements Serializable {
private static final long serialVersionUID = -8662837652326828377L;
/**
* <pre>
* 字段名公众账号ID
* 变量名appid
* 是否必填
* 类型string32
* 描述
* 微信分配的公众账号ID
* 示例值wx8888888888888888
* </pre>
*/
@SerializedName(value = "appid")
private String appid;
/**
* <pre>
* 字段名二级商户号
* 变量名sub_mchid
* 是否必填
* 类型string32
* 描述
* 分账出资的电商平台二级商户填写微信支付分配的商户号
* 示例值1900000109
* </pre>
*/
@SerializedName(value = "sub_mchid")
private String subMchid;
/**
* <pre>
* 字段名微信订单号
* 变量名transaction_id
* 是否必填
* 类型string32
* 描述
* 微信支付订单号
* 示例值4208450740201411110007820472
* </pre>
*/
@SerializedName(value = "transaction_id")
private String transactionId;
/**
* <pre>
* 字段名商户分账单号
* 变量名out_order_no
* 是否必填
* 类型string64
* 描述
* 商户系统内部的分账单号在商户系统内部唯一单次分账多次分账完结分账应使用不同的商户分账单号同一分账单号多次请求等同一次
* 示例值P20150806125346
* </pre>
*/
@SerializedName(value = "out_order_no")
private String outOrderNo;
/**
* <pre>
* 字段名分账接收方列表
* 变量名receivers
* 是否必填
* 类型array
* 描述
* 分账接收方列表支持设置出资商户作为分账接收方单次分账最多可有5个分账接收方
* </pre>
*/
@SerializedName(value = "receivers")
private Receiver[] receivers;
/**
* <pre>
* 字段名是否分账完成
* 变量名finish
* 是否必填
* 类型bool
* 描述
* 是否完成分账
* 1如果为true该笔订单剩余未分账的金额会解冻回电商平台二级商户
* 2如果为false该笔订单剩余未分账的金额不会解冻回电商平台二级商户可以对该笔订单再次进行分账
* 示例值true
* </pre>
*/
@SerializedName(value = "finish")
private Boolean finish;
@Data
@NoArgsConstructor
public static class Receiver implements Serializable {
private static final long serialVersionUID = 8995144356011793136L;
/**
* <pre>
* 字段名分账接收方类型
* 变量名type
* 是否必填
* 类型string32
* 描述
* 分账接收方类型枚举值
* MERCHANT_ID商户
* PERSONAL_OPENID个人
* 示例值MERCHANT_ID
* </pre>
*/
@SerializedName(value = "type")
private String type;
/**
* <pre>
* 字段名分账接收方账号
* 变量名receiver_account
* 是否必填
* 类型string32
* 描述
* 分账接收方账号
* 类型是MERCHANT_ID时是商户ID
* 类型是PERSONAL_OPENID时是个人openidopenid获取方法 https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/guide/chapter2_1.shtml#menu1
* 示例值1900000109
* </pre>
*/
@SerializedName(value = "receiver_account")
private String receiverAccount;
/**
* <pre>
* 字段名分账金额
* 变量名amount
* 是否必填
* 类型int
* 描述
* 分账金额单位为分只能为整数不能超过原订单支付金额及最大分账比例金额
* 示例值190
* </pre>
*/
@SerializedName(value = "amount")
private Integer amount;
/**
* <pre>
* 字段名分账描述
* 变量名description
* 是否必填
* 类型string180
* 描述
* 分账的原因描述分账账单中需要体现
* 示例值分给商户1900000109
* </pre>
*/
@SerializedName(value = "description")
private String description;
/**
* <pre>
* 字段名分账个人姓名
* 变量名receiver_name
* 是否必填
* 类型string10240
* 描述
* 可选项在接收方类型为个人的时可选填若有值会检查与 receiver_name 是否实名匹配不匹配会拒绝分账请求
* 1分账接收方类型是PERSONAL_OPENID时是个人姓名的密文选传传则校验 此字段的加密方法详见敏感信息加密说明
* 2使用微信支付平台证书中的公钥
* 3使用RSAES-OAEP算法进行加密
* 4将请求中HTTP头部的Wechatpay-Serial设置为证书序列号
* 示例值hu89ohu89ohu89o
* </pre>
*/
@SerializedName(value = "receiver_name")
private String receiverName;
}
}

View File

@ -0,0 +1,71 @@
package com.github.binarywang.wxpay.bean.ecommerce;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
/**
* 请求分账 结果响应
* @author: f00lish
* @date: 2020/09/12
*/
public class ProfitSharingResult implements Serializable {
private static final long serialVersionUID = 9026456165403642050L;
/**
* <pre>
* 字段名二级商户号
* 变量名sub_mchid
* 是否必填
* 类型string32
* 描述
* 分账出资的电商平台二级商户填写微信支付分配的商户号
* 示例值1900000109
* </pre>
*/
@SerializedName(value = "sub_mchid")
private String subMchid;
/**
* <pre>
* 字段名微信订单号
* 变量名transaction_id
* 是否必填
* 类型string32
* 描述
* 微信支付订单号
* 示例值4208450740201411110007820472
* </pre>
*/
@SerializedName(value = "transaction_id")
private String transactionId;
/**
* <pre>
* 字段名商户分账单号
* 变量名out_order_no
* 是否必填
* 类型string64
* 描述
* 商户系统内部的分账单号在商户系统内部唯一单次分账多次分账完结分账应使用不同的商户分账单号同一分账单号多次请求等同一次
* 示例值P20150806125346
* </pre>
*/
@SerializedName(value = "out_order_no")
private String outOrderNo;
/**
* <pre>
* 字段名微信分账单号
* 变量名order_id
* 是否必填
* 类型string (64)
* 描述
* 微信分账单号微信系统返回的唯一标识
* 示例值6754760740201411110007865434
* </pre>
*/
@SerializedName(value = "order_id")
private String orderId;
}

View File

@ -0,0 +1,32 @@
package com.github.binarywang.wxpay.bean.ecommerce.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 服务商账户类型
* @author: f00lish
* @date: 2020/09/12
*/
@Getter
@AllArgsConstructor
public enum SpAccountTypeEnum {
/**
* 基本账户
*/
BASIC("BASIC"),
/**
* 运营账户
*/
OPERATION("OPERATION"),
/**
* 手续费账户
*/
FEES("FEES");
/**
* 账户类型
*/
private final String value;
}

View File

@ -18,38 +18,66 @@ public class PayScoreNotifyData implements Serializable {
private static final long serialVersionUID = -8538014389773390989L; private static final long serialVersionUID = -8538014389773390989L;
/** /**
* id : EV-2018022511223320873 * 通知ID
* create_time : 20180225112233
* resource_type : encrypt-resource
* event_type : PAYSCORE.USER_CONFIRM
* resource : {"algorithm":"AEAD_AES_256_GCM","ciphertext":"...","nonce":"...","associated_data":""}
*/ */
@SerializedName("id") @SerializedName("id")
private String id; private String id;
/**
* 通知创建时间
*/
@SerializedName("create_time") @SerializedName("create_time")
private String createTime; private String createTime;
/**
* 通知数据类型
*/
@SerializedName("resource_type") @SerializedName("resource_type")
private String resourceType; private String resourceType;
/**
* 通知类型
*/
@SerializedName("event_type") @SerializedName("event_type")
private String eventType; private String eventType;
/**
* 通知数据
*/
@SerializedName("resource") @SerializedName("resource")
private Resource resource; private Resource resource;
/**
* 回调摘要
* summary
*/
@SerializedName("summary")
private String summary;
@Data @Data
public static class Resource implements Serializable { public static class Resource implements Serializable {
private static final long serialVersionUID = 8530711804335261449L; private static final long serialVersionUID = 8530711804335261449L;
/** /**
* algorithm : AEAD_AES_256_GCM * 加密算法类型
* ciphertext : ...
* nonce : ...
* associated_data :
*/ */
@SerializedName("algorithm") @SerializedName("algorithm")
private String algorithm; private String algorithm;
/**
* 数据密文
*/
@SerializedName("ciphertext") @SerializedName("ciphertext")
private String cipherText; private String cipherText;
/**
* 附加数据
*/
@SerializedName("nonce") @SerializedName("nonce")
private String nonce; private String nonce;
/**
* 随机串
*/
@SerializedName("associated_data") @SerializedName("associated_data")
private String associatedData; private String associatedData;
} }

View File

@ -1,6 +1,7 @@
package com.github.binarywang.wxpay.service; package com.github.binarywang.wxpay.service;
import com.github.binarywang.wxpay.bean.ecommerce.*; import com.github.binarywang.wxpay.bean.ecommerce.*;
import com.github.binarywang.wxpay.bean.ecommerce.enums.SpAccountTypeEnum;
import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum; import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.exception.WxPayException;
@ -11,7 +12,7 @@ import com.github.binarywang.wxpay.exception.WxPayException;
* </pre> * </pre>
* *
* @author cloudX * @author cloudX
* @date 2020/08/17 * @date 2020 /08/17
*/ */
public interface EcommerceService { public interface EcommerceService {
/** /**
@ -62,8 +63,9 @@ public interface EcommerceService {
* </pre> * </pre>
* *
* @param tradeType 支付方式 * @param tradeType 支付方式
* @param request 请求对象 * @param request 请求对象
* @return 微信合单支付返回 * @return 微信合单支付返回 transactions result
* @throws WxPayException the wx pay exception
*/ */
TransactionsResult combine(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException; TransactionsResult combine(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException;
@ -74,9 +76,11 @@ public interface EcommerceService {
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/e-combine.shtml * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/e-combine.shtml
* </pre> * </pre>
* *
* @param <T> the type parameter
* @param tradeType 支付方式 * @param tradeType 支付方式
* @param request 请求对象 * @param request 请求对象
* @return 调起支付需要的参数 * @return 调起支付需要的参数 t
* @throws WxPayException the wx pay exception
*/ */
<T> T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException; <T> T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException;
@ -87,32 +91,38 @@ public interface EcommerceService {
* </pre> * </pre>
* *
* @param notifyData 通知数据 * @param notifyData 通知数据
* @param header 通知头部数据不传则表示不校验头 * @param header 通知头部数据不传则表示不校验头
* @return 解密后通知数据 * @return 解密后通知数据 combine transactions notify result
* @throws WxPayException the wx pay exception
*/ */
CombineTransactionsNotifyResult parseCombineNotifyResult(String notifyData, SignatureHeader header) throws WxPayException; CombineTransactionsNotifyResult parseCombineNotifyResult(String notifyData, SignatureHeader header) throws WxPayException;
/** /**
* <pre> * <pre>
* 服务商模式普通支付API(APP支付JSAPI支付H5支付NATIVE支付). * 服务商模式普通支付API(APP支付JSAPI支付H5支付NATIVE支付).
* 请求URLhttps://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi * 请求URLhttps://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/transactions_sl.shtml * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/transactions_sl.shtml
* </pre> * </pre>
*
* @param tradeType 支付方式 * @param tradeType 支付方式
* @param request 请求对象 * @param request 请求对象
* @return 调起支付需要的参数 * @return 调起支付需要的参数 transactions result
* @throws WxPayException the wx pay exception
*/ */
TransactionsResult partner(TradeTypeEnum tradeType, PartnerTransactionsRequest request) throws WxPayException; TransactionsResult partner(TradeTypeEnum tradeType, PartnerTransactionsRequest request) throws WxPayException;
/** /**
* <pre> * <pre>
* 服务商模式普通支付API(APP支付JSAPI支付H5支付NATIVE支付). * 服务商模式普通支付API(APP支付JSAPI支付H5支付NATIVE支付).
* 请求URLhttps://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi * 请求URLhttps://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/transactions_sl.shtml * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/transactions_sl.shtml
* </pre> * </pre>
*
* @param <T> the type parameter
* @param tradeType 支付方式 * @param tradeType 支付方式
* @param request 请求对象 * @param request 请求对象
* @return 调起支付需要的参数 * @return 调起支付需要的参数 t
* @throws WxPayException the wx pay exception
*/ */
<T> T partnerTransactions(TradeTypeEnum tradeType, PartnerTransactionsRequest request) throws WxPayException; <T> T partnerTransactions(TradeTypeEnum tradeType, PartnerTransactionsRequest request) throws WxPayException;
@ -123,8 +133,72 @@ public interface EcommerceService {
* </pre> * </pre>
* *
* @param notifyData 通知数据 * @param notifyData 通知数据
* @param header 通知头部数据不传则表示不校验头 * @param header 通知头部数据不传则表示不校验头
* @return 解密后通知数据 * @return 解密后通知数据 partner transactions notify result
* @throws WxPayException the wx pay exception
*/ */
PartnerTransactionsNotifyResult parsePartnerNotifyResult(String notifyData, SignatureHeader header) throws WxPayException; PartnerTransactionsNotifyResult parsePartnerNotifyResult(String notifyData, SignatureHeader header) throws WxPayException;
/**
* <pre>
* 服务商账户实时余额
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/amount.shtml
* </pre>
*
* @param accountType 服务商账户类型
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
FundBalanceResult spNowBalance(SpAccountTypeEnum accountType) throws WxPayException;
/**
* <pre>
* 服务商账户日终余额
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/amount.shtml
* </pre>
*
* @param accountType 服务商账户类型
* @param date 查询日期 2020-09-11
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
FundBalanceResult spDayEndBalance(SpAccountTypeEnum accountType, String date) throws WxPayException;
/**
* <pre>
* 二级商户号账户实时余额
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/amount.shtml
* </pre>
*
* @param subMchid 二级商户号
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
FundBalanceResult subNowBalance(String subMchid) throws WxPayException;
/**
* <pre>
* 二级商户号账户日终余额
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/amount.shtml
* </pre>
*
* @param subMchid 二级商户号
* @param date 查询日期 2020-09-11
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
FundBalanceResult subDayEndBalance(String subMchid, String date) throws WxPayException;
/**
* <pre>
* 请求分账API
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/profitsharing/chapter3_1.shtml
* </pre>
*
* @param request 分账请求
* @return 返回数据 profit sharing result
* @throws WxPayException the wx pay exception
*/
ProfitSharingResult profitSharing(ProfitSharingRequest request) throws WxPayException;
} }

View File

@ -1,6 +1,7 @@
package com.github.binarywang.wxpay.service.impl; package com.github.binarywang.wxpay.service.impl;
import com.github.binarywang.wxpay.bean.ecommerce.*; import com.github.binarywang.wxpay.bean.ecommerce.*;
import com.github.binarywang.wxpay.bean.ecommerce.enums.SpAccountTypeEnum;
import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum; import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.EcommerceService; import com.github.binarywang.wxpay.service.EcommerceService;
@ -115,6 +116,45 @@ public class EcommerceServiceImpl implements EcommerceService {
} }
} }
@Override
public FundBalanceResult spNowBalance(SpAccountTypeEnum accountType) throws WxPayException {
String url = String.format("%s/v3/merchant/fund/balance/%s", this.payService.getPayBaseUrl(), accountType);
URI uri = URI.create(url);
String response = this.payService.getV3(uri);
return GSON.fromJson(response, FundBalanceResult.class);
}
@Override
public FundBalanceResult spDayEndBalance(SpAccountTypeEnum accountType, String date) throws WxPayException {
String url = String.format("%s/v3/merchant/fund/dayendbalance/%s?date=%s", this.payService.getPayBaseUrl(), accountType, date);
URI uri = URI.create(url);
String response = this.payService.getV3(uri);
return GSON.fromJson(response, FundBalanceResult.class);
}
@Override
public FundBalanceResult subNowBalance(String subMchid) throws WxPayException {
String url = String.format("%s/v3/ecommerce/fund/balance/%s", this.payService.getPayBaseUrl(), subMchid);
URI uri = URI.create(url);
String response = this.payService.getV3(uri);
return GSON.fromJson(response, FundBalanceResult.class);
}
@Override
public FundBalanceResult subDayEndBalance(String subMchid, String date) throws WxPayException {
String url = String.format("%s/v3/ecommerce/fund/enddaybalance/%s?date=%s", this.payService.getPayBaseUrl(), subMchid, date);
URI uri = URI.create(url);
String response = this.payService.getV3(uri);
return GSON.fromJson(response, FundBalanceResult.class);
}
@Override
public ProfitSharingResult profitSharing(ProfitSharingRequest request) throws WxPayException {
String url = String.format("%s/v3/ecommerce/profitsharing/orders", this.payService.getPayBaseUrl());
String response = this.payService.postV3(url, GSON.toJson(request));
return GSON.fromJson(response, ProfitSharingResult.class);
}
private boolean verifyNotifySign(SignatureHeader header, String data) { private boolean verifyNotifySign(SignatureHeader header, String data) {
String beforeSign = String.format("%s\n%s\n%s\n", String beforeSign = String.format("%s\n%s\n%s\n",
header.getTimeStamp(), header.getTimeStamp(),