@@ -42,6 +42,7 @@ Icon | |||||
.Spotlight-V100 | .Spotlight-V100 | ||||
.TemporaryItems | .TemporaryItems | ||||
.Trashes | .Trashes | ||||
.vscode | |||||
.VolumeIcon.icns | .VolumeIcon.icns | ||||
.AppleDB | .AppleDB | ||||
.AppleDesktop | .AppleDesktop | ||||
@@ -0,0 +1,3 @@ | |||||
{ | |||||
"java.configuration.updateBuildConfiguration": "interactive" | |||||
} |
@@ -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.3.0</version> | |||||
<version>3.4.0</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> | ||||
@@ -105,6 +105,8 @@ | |||||
<module>weixin-java-pay</module> | <module>weixin-java-pay</module> | ||||
<module>weixin-java-miniapp</module> | <module>weixin-java-miniapp</module> | ||||
<module>weixin-java-open</module> | <module>weixin-java-open</module> | ||||
<module>starters/wx-java-pay-starter</module> | |||||
<module>starters/wx-java-mp-starter</module> | |||||
<!--module>weixin-java-osgi</module--> | <!--module>weixin-java-osgi</module--> | ||||
</modules> | </modules> | ||||
@@ -0,0 +1,32 @@ | |||||
# wx-java-mp-starter | |||||
## 快速开始 | |||||
1. 引入依赖 | |||||
```xml | |||||
<dependency> | |||||
<groupId>com.github.binarywang</groupId> | |||||
<artifactId>wx-java-mp-starter</artifactId> | |||||
<version>${version}</version> | |||||
</dependency> | |||||
``` | |||||
2. 添加配置(application.properties) | |||||
```properties | |||||
# 公众号配置(必填) | |||||
wx.mp.appId = @appId | |||||
wx.mp.secret = @secret | |||||
wx.mp.token = @token | |||||
wx.mp.aesKey = @aesKey | |||||
# 存储配置redis(可选) | |||||
wx.mp.config-storage.type = redis | |||||
wx.mp.config-storage.redis.host = 127.0.0.1 | |||||
wx.mp.config-storage.redis.port = 6379 | |||||
``` | |||||
3. 支持自动注入的类型 | |||||
`WxMpService`以及相关的服务类, 比如: `wxMpService.getXxxService`。 | |||||
@@ -0,0 +1,72 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<parent> | |||||
<groupId>com.github.binarywang</groupId> | |||||
<artifactId>wx-java</artifactId> | |||||
<version>3.4.0</version> | |||||
<relativePath>../../</relativePath> | |||||
</parent> | |||||
<artifactId>wx-java-mp-starter</artifactId> | |||||
<name>WxJava - Spring Boot Starter for MP</name> | |||||
<description>微信公众号开发的Spring Boot Starter</description> | |||||
<properties> | |||||
<spring.boot.version>2.1.4.RELEASE</spring.boot.version> | |||||
</properties> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-autoconfigure</artifactId> | |||||
<version>${spring.boot.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-configuration-processor</artifactId> | |||||
<version>${spring.boot.version}</version> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.github.binarywang</groupId> | |||||
<artifactId>weixin-java-mp</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>redis.clients</groupId> | |||||
<artifactId>jedis</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.projectlombok</groupId> | |||||
<artifactId>lombok</artifactId> | |||||
<scope>provided</scope> | |||||
</dependency> | |||||
</dependencies> | |||||
<build> | |||||
<plugins> | |||||
<plugin> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-maven-plugin</artifactId> | |||||
<version>${spring.boot.version}</version> | |||||
</plugin> | |||||
<plugin> | |||||
<groupId>org.apache.maven.plugins</groupId> | |||||
<artifactId>maven-source-plugin</artifactId> | |||||
<version>2.2.1</version> | |||||
<executions> | |||||
<execution> | |||||
<id>attach-sources</id> | |||||
<goals> | |||||
<goal>jar-no-fork</goal> | |||||
</goals> | |||||
</execution> | |||||
</executions> | |||||
</plugin> | |||||
</plugins> | |||||
</build> | |||||
</project> |
@@ -0,0 +1,42 @@ | |||||
package com.binarywang.spring.starter.wxjava.mp; | |||||
import lombok.Data; | |||||
import java.io.Serializable; | |||||
/** | |||||
* Redis配置 | |||||
*/ | |||||
@Data | |||||
public class RedisProperties implements Serializable { | |||||
/** | |||||
* 主机地址 | |||||
*/ | |||||
private String host = "127.0.0.1"; | |||||
/** | |||||
* 端口号 | |||||
*/ | |||||
private int port = 6379; | |||||
/** | |||||
* 密码 | |||||
*/ | |||||
private String password; | |||||
/** | |||||
* 超时 | |||||
*/ | |||||
private int timeout = 2000; | |||||
/** | |||||
* 数据库 | |||||
*/ | |||||
private int database = 0; | |||||
private Integer maxActive; | |||||
private Integer maxIdle; | |||||
private Integer maxWaitMillis; | |||||
private Integer minIdle; | |||||
} |
@@ -0,0 +1,11 @@ | |||||
package com.binarywang.spring.starter.wxjava.mp; | |||||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |||||
import org.springframework.context.annotation.Configuration; | |||||
import org.springframework.context.annotation.Import; | |||||
@Configuration | |||||
@EnableConfigurationProperties(WxMpProperties.class) | |||||
@Import({WxMpStorageAutoConfiguration.class, WxMpServiceAutoConfiguration.class}) | |||||
public class WxMpAutoConfiguration { | |||||
} |
@@ -0,0 +1,58 @@ | |||||
package com.binarywang.spring.starter.wxjava.mp; | |||||
import lombok.Data; | |||||
import org.springframework.boot.context.properties.ConfigurationProperties; | |||||
import java.io.Serializable; | |||||
import static com.binarywang.spring.starter.wxjava.mp.WxMpProperties.PREFIX; | |||||
import static com.binarywang.spring.starter.wxjava.mp.WxMpProperties.StorageType.memory; | |||||
/** | |||||
* 微信接入相关配置属性 | |||||
*/ | |||||
@Data | |||||
@ConfigurationProperties(PREFIX) | |||||
public class WxMpProperties { | |||||
public static final String PREFIX = "wx.mp"; | |||||
/** | |||||
* 设置微信公众号的appid | |||||
*/ | |||||
private String appId; | |||||
/** | |||||
* 设置微信公众号的app secret | |||||
*/ | |||||
private String secret; | |||||
/** | |||||
* 设置微信公众号的token | |||||
*/ | |||||
private String token; | |||||
/** | |||||
* 设置微信公众号的EncodingAESKey | |||||
*/ | |||||
private String aesKey; | |||||
/** | |||||
* 存储策略, memory, redis | |||||
*/ | |||||
private ConfigStorage configStorage = new ConfigStorage(); | |||||
@Data | |||||
public static class ConfigStorage implements Serializable { | |||||
private StorageType type = memory; | |||||
private RedisProperties redis = new RedisProperties(); | |||||
} | |||||
public enum StorageType { | |||||
memory, redis | |||||
} | |||||
} |
@@ -0,0 +1,53 @@ | |||||
package com.binarywang.spring.starter.wxjava.mp; | |||||
import me.chanjar.weixin.mp.api.WxMpConfigStorage; | |||||
import me.chanjar.weixin.mp.api.WxMpService; | |||||
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; | |||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | |||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | |||||
import org.springframework.context.ApplicationContext; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.context.annotation.Configuration; | |||||
/** | |||||
* 微信公众号相关服务自动注册 | |||||
*/ | |||||
@Configuration | |||||
public class WxMpServiceAutoConfiguration { | |||||
@Autowired | |||||
private ApplicationContext ctx; | |||||
@Bean | |||||
@ConditionalOnMissingBean | |||||
public WxMpService wxMpService(WxMpConfigStorage configStorage) { | |||||
WxMpService wxMpService = new WxMpServiceImpl(); | |||||
wxMpService.setWxMpConfigStorage(configStorage); | |||||
registerWxMpSubService(wxMpService); | |||||
return wxMpService; | |||||
} | |||||
@ConditionalOnBean(WxMpService.class) | |||||
public Object registerWxMpSubService(WxMpService wxMpService) { | |||||
ConfigurableListableBeanFactory factory = (ConfigurableListableBeanFactory) ctx.getAutowireCapableBeanFactory(); | |||||
factory.registerSingleton("wxMpKefuService", wxMpService.getKefuService()); | |||||
factory.registerSingleton("wxMpMaterialService", wxMpService.getMaterialService()); | |||||
factory.registerSingleton("wxMpMenuService", wxMpService.getMenuService()); | |||||
factory.registerSingleton("wxMpUserService", wxMpService.getUserService()); | |||||
factory.registerSingleton("wxMpUserTagService", wxMpService.getUserTagService()); | |||||
factory.registerSingleton("wxMpQrcodeService", wxMpService.getQrcodeService()); | |||||
factory.registerSingleton("wxMpCardService", wxMpService.getCardService()); | |||||
factory.registerSingleton("wxMpDataCubeService", wxMpService.getDataCubeService()); | |||||
factory.registerSingleton("wxMpUserBlacklistService", wxMpService.getBlackListService()); | |||||
factory.registerSingleton("wxMpStoreService", wxMpService.getStoreService()); | |||||
factory.registerSingleton("wxMpTemplateMsgService", wxMpService.getTemplateMsgService()); | |||||
factory.registerSingleton("wxMpSubscribeMsgService", wxMpService.getSubscribeMsgService()); | |||||
factory.registerSingleton("wxMpDeviceService", wxMpService.getDeviceService()); | |||||
factory.registerSingleton("wxMpShakeService", wxMpService.getShakeService()); | |||||
factory.registerSingleton("wxMpMemberCardService", wxMpService.getMemberCardService()); | |||||
factory.registerSingleton("wxMpMassMessageService", wxMpService.getMassMessageService()); | |||||
return Boolean.TRUE; | |||||
} | |||||
} |
@@ -0,0 +1,84 @@ | |||||
package com.binarywang.spring.starter.wxjava.mp; | |||||
import me.chanjar.weixin.mp.api.WxMpConfigStorage; | |||||
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage; | |||||
import me.chanjar.weixin.mp.api.WxMpInRedisConfigStorage; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.context.annotation.Configuration; | |||||
import redis.clients.jedis.JedisPool; | |||||
import redis.clients.jedis.JedisPoolConfig; | |||||
/** | |||||
* 微信公众号存储策略自动配置 | |||||
*/ | |||||
@Configuration | |||||
public class WxMpStorageAutoConfiguration { | |||||
@Autowired | |||||
private WxMpProperties properties; | |||||
@Autowired(required = false) | |||||
private JedisPool jedisPool; | |||||
@Bean | |||||
@ConditionalOnMissingBean(WxMpConfigStorage.class) | |||||
public WxMpConfigStorage wxMpInMemoryConfigStorage() { | |||||
WxMpProperties.ConfigStorage storage = properties.getConfigStorage(); | |||||
WxMpProperties.StorageType type = storage.getType(); | |||||
if (type == WxMpProperties.StorageType.redis) { | |||||
return getWxMpInRedisConfigStorage(); | |||||
} | |||||
return getWxMpInMemoryConfigStorage(); | |||||
} | |||||
private WxMpInMemoryConfigStorage getWxMpInMemoryConfigStorage() { | |||||
WxMpInMemoryConfigStorage config = new WxMpInMemoryConfigStorage(); | |||||
setWxMpInfo(config); | |||||
return config; | |||||
} | |||||
private WxMpInRedisConfigStorage getWxMpInRedisConfigStorage() { | |||||
JedisPool poolToUse = jedisPool; | |||||
if (poolToUse == null) { | |||||
poolToUse = getJedisPool(); | |||||
} | |||||
WxMpInRedisConfigStorage config = new WxMpInRedisConfigStorage(poolToUse); | |||||
setWxMpInfo(config); | |||||
return config; | |||||
} | |||||
private void setWxMpInfo(WxMpInMemoryConfigStorage config) { | |||||
config.setAppId(properties.getAppId()); | |||||
config.setSecret(properties.getSecret()); | |||||
config.setToken(properties.getToken()); | |||||
config.setAesKey(properties.getAesKey()); | |||||
} | |||||
private JedisPool getJedisPool() { | |||||
WxMpProperties.ConfigStorage storage = properties.getConfigStorage(); | |||||
RedisProperties redis = storage.getRedis(); | |||||
JedisPoolConfig config = new JedisPoolConfig(); | |||||
if (redis.getMaxActive() != null) { | |||||
config.setMaxTotal(redis.getMaxActive()); | |||||
} | |||||
if (redis.getMaxIdle() != null) { | |||||
config.setMaxIdle(redis.getMaxIdle()); | |||||
} | |||||
if (redis.getMaxWaitMillis() != null) { | |||||
config.setMaxWaitMillis(redis.getMaxWaitMillis()); | |||||
} | |||||
if (redis.getMinIdle() != null) { | |||||
config.setMinIdle(redis.getMinIdle()); | |||||
} | |||||
config.setTestOnBorrow(true); | |||||
config.setTestWhileIdle(true); | |||||
JedisPool pool = new JedisPool(config, redis.getHost(), redis.getPort(), | |||||
redis.getTimeout(), redis.getPassword(), redis.getDatabase()); | |||||
return pool; | |||||
} | |||||
} |
@@ -0,0 +1 @@ | |||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.mp.WxMpAutoConfiguration |
@@ -0,0 +1,27 @@ | |||||
# 使用说明 | |||||
1. 在自己的Spring Boot项目里,引入maven依赖 | |||||
```xml | |||||
<dependency> | |||||
<groupId>com.github.binarywang</groupId> | |||||
<artifactId>wx-java-pay-starter</artifactId> | |||||
<version>${version}</version> | |||||
</dependency> | |||||
``` | |||||
2. 添加配置(application.yml) | |||||
```yml | |||||
wx: | |||||
pay: | |||||
appId: wx5b69c56ac01ed858 | |||||
mchId: 1462547202 | |||||
mchKey: OGL9fvig9y2HrXrQ86tM4jTwyv4ja6G5 | |||||
subAppId: | |||||
subMchId: | |||||
keyPath: | |||||
``` | |||||
@@ -0,0 +1,68 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
<parent> | |||||
<artifactId>wx-java</artifactId> | |||||
<groupId>com.github.binarywang</groupId> | |||||
<version>3.4.0</version> | |||||
<relativePath>../../</relativePath> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>wx-java-pay-starter</artifactId> | |||||
<name>WxJava - Spring Boot Starter for WxPay</name> | |||||
<description>微信支付开发的Spring Boot Starter</description> | |||||
<properties> | |||||
<spring.boot.version>2.1.4.RELEASE</spring.boot.version> | |||||
</properties> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-autoconfigure</artifactId> | |||||
<version>${spring.boot.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-configuration-processor</artifactId> | |||||
<version>${spring.boot.version}</version> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.projectlombok</groupId> | |||||
<artifactId>lombok</artifactId> | |||||
<scope>provided</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.github.binarywang</groupId> | |||||
<artifactId>weixin-java-pay</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
</dependencies> | |||||
<build> | |||||
<plugins> | |||||
<plugin> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-maven-plugin</artifactId> | |||||
<version>${spring.boot.version}</version> | |||||
</plugin> | |||||
<plugin> | |||||
<groupId>org.apache.maven.plugins</groupId> | |||||
<artifactId>maven-source-plugin</artifactId> | |||||
<version>2.2.1</version> | |||||
<executions> | |||||
<execution> | |||||
<id>attach-sources</id> | |||||
<goals> | |||||
<goal>jar-no-fork</goal> | |||||
</goals> | |||||
</execution> | |||||
</executions> | |||||
</plugin> | |||||
</plugins> | |||||
</build> | |||||
</project> |
@@ -0,0 +1,57 @@ | |||||
package com.binarywang.spring.starter.wxjava.pay.config; | |||||
import com.binarywang.spring.starter.wxjava.pay.properties.WxPayProperties; | |||||
import com.github.binarywang.wxpay.config.WxPayConfig; | |||||
import com.github.binarywang.wxpay.service.WxPayService; | |||||
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | |||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | |||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |||||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.context.annotation.Configuration; | |||||
/** | |||||
* <pre> | |||||
* 微信支付自动配置 | |||||
* Created by BinaryWang on 2019/4/17. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
@Configuration | |||||
@EnableConfigurationProperties(WxPayProperties.class) | |||||
@ConditionalOnClass(WxPayService.class) | |||||
@ConditionalOnProperty(prefix = "wx.pay", value = "enabled", matchIfMissing = true) | |||||
public class WxPayAutoConfiguration { | |||||
private WxPayProperties properties; | |||||
@Autowired | |||||
public WxPayAutoConfiguration(WxPayProperties properties) { | |||||
this.properties = properties; | |||||
} | |||||
/** | |||||
* 构造微信支付服务对象. | |||||
* | |||||
* @return 微信支付service | |||||
*/ | |||||
@Bean | |||||
@ConditionalOnMissingBean(WxPayService.class) | |||||
public WxPayService wxPayService() { | |||||
final WxPayServiceImpl wxPayService = new WxPayServiceImpl(); | |||||
WxPayConfig payConfig = new WxPayConfig(); | |||||
payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId())); | |||||
payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId())); | |||||
payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey())); | |||||
payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId())); | |||||
payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId())); | |||||
payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath())); | |||||
wxPayService.setConfig(payConfig); | |||||
return wxPayService; | |||||
} | |||||
} |
@@ -0,0 +1,46 @@ | |||||
package com.binarywang.spring.starter.wxjava.pay.properties; | |||||
import lombok.Data; | |||||
import org.springframework.boot.context.properties.ConfigurationProperties; | |||||
/** | |||||
* <pre> | |||||
* 微信支付属性配置类 | |||||
* Created by Binary Wang on 2019/4/17. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
@Data | |||||
@ConfigurationProperties(prefix = "wx.pay") | |||||
public class WxPayProperties { | |||||
/** | |||||
* 设置微信公众号或者小程序等的appid. | |||||
*/ | |||||
private String appId; | |||||
/** | |||||
* 微信支付商户号. | |||||
*/ | |||||
private String mchId; | |||||
/** | |||||
* 微信支付商户密钥. | |||||
*/ | |||||
private String mchKey; | |||||
/** | |||||
* 服务商模式下的子商户公众账号ID,普通模式请不要配置,请在配置文件中将对应项删除. | |||||
*/ | |||||
private String subAppId; | |||||
/** | |||||
* 服务商模式下的子商户号,普通模式请不要配置,最好是请在配置文件中将对应项删除. | |||||
*/ | |||||
private String subMchId; | |||||
/** | |||||
* apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定. | |||||
*/ | |||||
private String keyPath; | |||||
} |
@@ -0,0 +1 @@ | |||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.pay.config.WxPayAutoConfiguration |
@@ -1,17 +1,16 @@ | |||||
<?xml version="1.0"?> | <?xml version="1.0"?> | ||||
<project | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | ||||
xmlns="http://maven.apache.org/POM/4.0.0"> | xmlns="http://maven.apache.org/POM/4.0.0"> | ||||
<modelVersion>4.0.0</modelVersion> | <modelVersion>4.0.0</modelVersion> | ||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>wx-java</artifactId> | <artifactId>wx-java</artifactId> | ||||
<version>3.3.0</version> | |||||
<version>3.4.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-common</artifactId> | <artifactId>weixin-java-common</artifactId> | ||||
<name>WxJava - Common</name> | |||||
<name>WxJava - Common Java SDK</name> | |||||
<description>微信开发Java SDK公共模块</description> | <description>微信开发Java SDK公共模块</description> | ||||
<dependencies> | <dependencies> | ||||
@@ -62,6 +62,11 @@ public class WxConsts { | |||||
* 图文消息(点击跳转到图文消息页面). | * 图文消息(点击跳转到图文消息页面). | ||||
*/ | */ | ||||
public static final String MPNEWS = "mpnews"; | public static final String MPNEWS = "mpnews"; | ||||
/** | |||||
* markdown消息. | |||||
* (目前仅支持markdown语法的子集,微工作台(原企业号)不支持展示markdown消息) | |||||
*/ | |||||
public static final String MARKDOWN = "markdown"; | |||||
/** | /** | ||||
* 发送文件(CP专用). | * 发送文件(CP专用). | ||||
*/ | */ | ||||
@@ -83,6 +88,11 @@ public class WxConsts { | |||||
* 小程序卡片(要求小程序与公众号已关联) | * 小程序卡片(要求小程序与公众号已关联) | ||||
*/ | */ | ||||
public static final String MINIPROGRAMPAGE = "miniprogrampage"; | public static final String MINIPROGRAMPAGE = "miniprogrampage"; | ||||
/** | |||||
* 任务卡片消息 | |||||
*/ | |||||
public static final String TASKCARD = "taskcard"; | |||||
} | } | ||||
/** | /** | ||||
@@ -39,6 +39,7 @@ public class WxCryptUtil { | |||||
try { | try { | ||||
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | ||||
factory.setExpandEntityReferences(false); | factory.setExpandEntityReferences(false); | ||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); | |||||
return factory.newDocumentBuilder(); | return factory.newDocumentBuilder(); | ||||
} catch (ParserConfigurationException exc) { | } catch (ParserConfigurationException exc) { | ||||
throw new IllegalArgumentException(exc); | throw new IllegalArgumentException(exc); | ||||
@@ -57,7 +57,7 @@ public class ApacheMediaDownloadRequestExecutor extends BaseMediaDownloadRequest | |||||
String fileName = new HttpResponseProxy(response).getFileName(); | String fileName = new HttpResponseProxy(response).getFileName(); | ||||
if (StringUtils.isBlank(fileName)) { | if (StringUtils.isBlank(fileName)) { | ||||
return null; | |||||
fileName = String.valueOf(System.currentTimeMillis()); | |||||
} | } | ||||
return FileUtils.createTmpFile(inputStream, FilenameUtils.getBaseName(fileName), FilenameUtils.getExtension(fileName), | return FileUtils.createTmpFile(inputStream, FilenameUtils.getBaseName(fileName), FilenameUtils.getExtension(fileName), | ||||
@@ -19,6 +19,7 @@ package me.chanjar.weixin.common.util.res; | |||||
import java.text.MessageFormat; | import java.text.MessageFormat; | ||||
import java.util.*; | import java.util.*; | ||||
import java.util.concurrent.ConcurrentHashMap; | |||||
/** | /** | ||||
* An internationalization / localization helper class which reduces | * An internationalization / localization helper class which reduces | ||||
@@ -46,7 +47,7 @@ import java.util.*; | |||||
*/ | */ | ||||
public class StringManager { | public class StringManager { | ||||
private static final Map<String, Map<Locale, StringManager>> MANAGERS = new Hashtable<>(); | |||||
private static final Map<String, Map<Locale, StringManager>> MANAGERS = new ConcurrentHashMap<>(); | |||||
private static int LOCALE_CACHE_SIZE = 10; | private static int LOCALE_CACHE_SIZE = 10; | ||||
/** | /** | ||||
* The ResourceBundle for this StringManager. | * The ResourceBundle for this StringManager. | ||||
@@ -40,6 +40,7 @@ public class WxCryptUtilTest { | |||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | ||||
documentBuilderFactory.setExpandEntityReferences(false); | documentBuilderFactory.setExpandEntityReferences(false); | ||||
documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); | |||||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | ||||
Document document = documentBuilder.parse(new InputSource(new StringReader(encryptedXml))); | Document document = documentBuilder.parse(new InputSource(new StringReader(encryptedXml))); | ||||
@@ -83,6 +84,8 @@ public class WxCryptUtilTest { | |||||
String afterEncrpt = pc.encrypt(this.replyMsg); | String afterEncrpt = pc.encrypt(this.replyMsg); | ||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | ||||
dbf.setExpandEntityReferences(false); | dbf.setExpandEntityReferences(false); | ||||
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); | |||||
DocumentBuilder db = dbf.newDocumentBuilder(); | DocumentBuilder db = dbf.newDocumentBuilder(); | ||||
StringReader sr = new StringReader(afterEncrpt); | StringReader sr = new StringReader(afterEncrpt); | ||||
InputSource is = new InputSource(sr); | InputSource is = new InputSource(sr); | ||||
@@ -7,11 +7,11 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>wx-java</artifactId> | <artifactId>wx-java</artifactId> | ||||
<version>3.3.0</version> | |||||
<version>3.4.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-cp</artifactId> | <artifactId>weixin-java-cp</artifactId> | ||||
<name>WxJava - CP</name> | |||||
<name>WxJava - CP Java SDK</name> | |||||
<description>微信企业号/企业微信 Java SDK</description> | <description>微信企业号/企业微信 Java SDK</description> | ||||
<dependencies> | <dependencies> | ||||
@@ -84,6 +84,11 @@ public class WxCpConsts { | |||||
*/ | */ | ||||
public static final String LOCATION_SELECT = "location_select"; | public static final String LOCATION_SELECT = "location_select"; | ||||
/** | |||||
* 任务卡片事件推送. | |||||
*/ | |||||
public static final String TASKCARD_CLICK = "taskcard_click"; | |||||
} | } | ||||
/** | /** | ||||
@@ -126,4 +131,46 @@ public class WxCpConsts { | |||||
public static final String UPDATE_TAG = "update_tag"; | public static final String UPDATE_TAG = "update_tag"; | ||||
} | } | ||||
/** | |||||
* 应用推送消息的消息类型. | |||||
*/ | |||||
public static class AppChatMsgType { | |||||
/** | |||||
* 文本消息. | |||||
*/ | |||||
public static final String TEXT = "text"; | |||||
/** | |||||
* 图片消息. | |||||
*/ | |||||
public static final String IMAGE = "image"; | |||||
/** | |||||
* 语音消息. | |||||
*/ | |||||
public static final String VOICE = "voice"; | |||||
/** | |||||
* 视频消息. | |||||
*/ | |||||
public static final String VIDEO = "video"; | |||||
/** | |||||
* 发送文件(CP专用). | |||||
*/ | |||||
public static final String FILE = "file"; | |||||
/** | |||||
* 文本卡片消息(CP专用). | |||||
*/ | |||||
public static final String TEXTCARD = "textcard"; | |||||
/** | |||||
* 图文消息(点击跳转到外链). | |||||
*/ | |||||
public static final String NEWS = "news"; | |||||
/** | |||||
* 图文消息(点击跳转到图文消息页面). | |||||
*/ | |||||
public static final String MPNEWS = "mpnews"; | |||||
/** | |||||
* markdown消息. | |||||
*/ | |||||
public static final String MARKDOWN = "markdown"; | |||||
} | |||||
} | } |
@@ -3,46 +3,75 @@ package me.chanjar.weixin.cp.api; | |||||
import java.util.List; | import java.util.List; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; | |||||
import me.chanjar.weixin.cp.bean.WxCpChat; | import me.chanjar.weixin.cp.bean.WxCpChat; | ||||
/** | /** | ||||
* 群聊服务 | |||||
* 群聊服务. | |||||
* | * | ||||
* @author gaigeshen | * @author gaigeshen | ||||
*/ | */ | ||||
public interface WxCpChatService { | public interface WxCpChatService { | ||||
String APPCHAT_CREATE = "https://qyapi.weixin.qq.com/cgi-bin/appchat/create"; | |||||
String APPCHAT_UPDATE = "https://qyapi.weixin.qq.com/cgi-bin/appchat/update"; | |||||
String APPCHAT_GET_CHATID = "https://qyapi.weixin.qq.com/cgi-bin/appchat/get?chatid="; | |||||
/** | /** | ||||
* 创建群聊会话,注意:刚创建的群,如果没有下发消息,在企业微信不会出现该群。 | |||||
* 创建群聊会话,注意:刚创建的群,如果没有下发消息,在企业微信不会出现该群. | |||||
* | * | ||||
* @param name 群聊名,最多50个utf8字符,超过将截断 | |||||
* @param owner 指定群主的id。如果不指定,系统会随机从userlist中选一人作为群主 | |||||
* @param users 群成员id列表。至少2人,至多500人 | |||||
* @param name 群聊名,最多50个utf8字符,超过将截断 | |||||
* @param owner 指定群主的id。如果不指定,系统会随机从userlist中选一人作为群主 | |||||
* @param users 群成员id列表。至少2人,至多500人 | |||||
* @param chatId 群聊的唯一标志,不能与已有的群重复;字符串类型,最长32个字符。只允许字符0-9及字母a-zA-Z。如果不填,系统会随机生成群id | * @param chatId 群聊的唯一标志,不能与已有的群重复;字符串类型,最长32个字符。只允许字符0-9及字母a-zA-Z。如果不填,系统会随机生成群id | ||||
* @return 创建群聊会话的结果,群聊的唯一标志 | |||||
* @return 创建的群聊会话chatId | |||||
* @throws WxErrorException 发生异常 | * @throws WxErrorException 发生异常 | ||||
*/ | */ | ||||
String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException; | String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException; | ||||
/** | /** | ||||
* 修改群聊会话 | |||||
* | |||||
* @param chatId 群聊id | |||||
* @param name 新的群聊名。若不需更新,请忽略此参数(null or empty)。最多50个utf8字符,超过将截断 | |||||
* @param owner 新群主的id。若不需更新,请忽略此参数(null or empty) | |||||
* @param usersToAdd 添加成员的id列表,若不需要更新,则传递空对象或者空集合 | |||||
* chatCreate 同名方法 | |||||
*/ | |||||
String create(String name, String owner, List<String> users, String chatId) throws WxErrorException; | |||||
/** | |||||
* 修改群聊会话. | |||||
* | |||||
* @param chatId 群聊id | |||||
* @param name 新的群聊名。若不需更新,请忽略此参数(null or empty)。最多50个utf8字符,超过将截断 | |||||
* @param owner 新群主的id。若不需更新,请忽略此参数(null or empty) | |||||
* @param usersToAdd 添加成员的id列表,若不需要更新,则传递空对象或者空集合 | |||||
* @param usersToDelete 踢出成员的id列表,若不需要更新,则传递空对象或者空集合 | * @param usersToDelete 踢出成员的id列表,若不需要更新,则传递空对象或者空集合 | ||||
* @throws WxErrorException 发生异常 | * @throws WxErrorException 发生异常 | ||||
*/ | */ | ||||
void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException; | void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException; | ||||
/** | /** | ||||
* 获取群聊会话 | |||||
* | |||||
* chatUpdate 同名方法 | |||||
*/ | |||||
void update(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException; | |||||
/** | |||||
* 获取群聊会话. | |||||
* | |||||
* @param chatId 群聊编号 | * @param chatId 群聊编号 | ||||
* @return 群聊会话 | * @return 群聊会话 | ||||
* @throws WxErrorException 发生异常 | * @throws WxErrorException 发生异常 | ||||
*/ | */ | ||||
WxCpChat chatGet(String chatId) throws WxErrorException; | WxCpChat chatGet(String chatId) throws WxErrorException; | ||||
/** | |||||
* chatGet 同名方法 | |||||
*/ | |||||
WxCpChat get(String chatId) throws WxErrorException; | |||||
/** | |||||
* 应用支持推送文本、图片、视频、文件、图文等类型. | |||||
* 请求方式: POST(HTTPS) | |||||
* 请求地址: https://qyapi.weixin.qq.com/cgi-bin/appchat/send?access_token=ACCESS_TOKEN | |||||
* 文档地址:https://work.weixin.qq.com/api/doc#90000/90135/90248 | |||||
* | |||||
* @param message 要发送的消息内容对象 | |||||
*/ | |||||
void sendMsg(WxCpAppChatMessage message) throws WxErrorException; | |||||
} | } |
@@ -26,7 +26,7 @@ public interface WxCpDepartmentService { | |||||
* @return 部门id | * @return 部门id | ||||
* @throws WxErrorException 异常 | * @throws WxErrorException 异常 | ||||
*/ | */ | ||||
Integer create(WxCpDepart depart) throws WxErrorException; | |||||
Long create(WxCpDepart depart) throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -0,0 +1,66 @@ | |||||
package me.chanjar.weixin.cp.api; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.cp.bean.WxCpApprovalDataResult; | |||||
import me.chanjar.weixin.cp.bean.WxCpCheckinData; | |||||
import me.chanjar.weixin.cp.bean.WxCpCheckinOption; | |||||
import me.chanjar.weixin.cp.bean.WxCpDialRecord; | |||||
import java.util.Date; | |||||
import java.util.List; | |||||
/** | |||||
* @author Element | |||||
* @Package me.chanjar.weixin.cp.api | |||||
* @date 2019-04-06 10:52 | |||||
* @Description: <pre> | |||||
* 企业微信OA相关接口 | |||||
* | |||||
* </pre> | |||||
*/ | |||||
public interface WxCpOAService { | |||||
/** | |||||
* <pre> | |||||
* 获取打卡数据 | |||||
* API doc : https://work.weixin.qq.com/api/doc#90000/90135/90262 | |||||
* </pre> | |||||
* | |||||
* @param openCheckinDataType 打卡类型。1:上下班打卡;2:外出打卡;3:全部打卡 | |||||
* @param starttime 获取打卡记录的开始时间 | |||||
* @param endtime 获取打卡记录的结束时间 | |||||
* @param userIdList 需要获取打卡记录的用户列表 | |||||
*/ | |||||
List<WxCpCheckinData> getCheckinData(Integer openCheckinDataType, Date starttime, Date endtime, List<String> userIdList) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 获取打卡规则 | |||||
* API doc : https://work.weixin.qq.com/api/doc#90000/90135/90263 | |||||
* </pre> | |||||
* | |||||
* @param datetime 需要获取规则的当天日期 | |||||
* @param userIdList 需要获取打卡规则的用户列表 | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | |||||
List<WxCpCheckinOption> getCheckinOption(Date datetime, List<String> userIdList) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 获取审批数据 | |||||
* 通过本接口来获取公司一段时间内的审批记录。一次拉取调用最多拉取10000个审批记录,可以通过多次拉取的方式来满足需求,但调用频率不可超过600次/分。 | |||||
* API doc : https://work.weixin.qq.com/api/doc#90000/90135/91530 | |||||
* </pre> | |||||
* | |||||
* @param starttime 获取审批记录的开始时间 | |||||
* @param endtime 获取审批记录的结束时间 | |||||
* @param nextSpnum 第一个拉取的审批单号,不填从该时间段的第一个审批单拉取 | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | |||||
WxCpApprovalDataResult getApprovalData(Date starttime, Date endtime, Long nextSpnum) throws WxErrorException; | |||||
List<WxCpDialRecord> getDialRecord(Date starttime, Date endtime, Integer offset, Integer limit) throws WxErrorException; | |||||
} |
@@ -7,6 +7,7 @@ import me.chanjar.weixin.common.session.WxSessionManager; | |||||
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | ||||
import me.chanjar.weixin.common.util.http.RequestExecutor; | import me.chanjar.weixin.common.util.http.RequestExecutor; | ||||
import me.chanjar.weixin.common.util.http.RequestHttp; | import me.chanjar.weixin.common.util.http.RequestHttp; | ||||
import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | |||||
import me.chanjar.weixin.cp.bean.WxCpMessage; | import me.chanjar.weixin.cp.bean.WxCpMessage; | ||||
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | ||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpConfigStorage; | ||||
@@ -16,6 +17,15 @@ import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||||
* @author chanjaster | * @author chanjaster | ||||
*/ | */ | ||||
public interface WxCpService { | public interface WxCpService { | ||||
String GET_JSAPI_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket"; | |||||
String GET_AGENT_CONFIG_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?&type=agent_config"; | |||||
String MESSAGE_SEND = "https://qyapi.weixin.qq.com/cgi-bin/message/send"; | |||||
String GET_CALLBACK_IP = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip"; | |||||
String BATCH_REPLACE_PARTY = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty"; | |||||
String BATCH_REPLACE_USER = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser"; | |||||
String BATCH_GET_RESULT = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid="; | |||||
String JSCODE_TO_SESSION_URL = "https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session"; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 验证推送过来的消息的正确性 | * 验证推送过来的消息的正确性 | ||||
@@ -68,6 +78,33 @@ public interface WxCpService { | |||||
*/ | */ | ||||
String getJsapiTicket(boolean forceRefresh) throws WxErrorException; | String getJsapiTicket(boolean forceRefresh) throws WxErrorException; | ||||
/** | |||||
* 获得jsapi_ticket,不强制刷新jsapi_ticket | |||||
* 应用的jsapi_ticket用于计算agentConfig(参见“通过agentConfig注入应用的权限”)的签名,签名计算方法与上述介绍的config的签名算法完全相同,但需要注意以下区别: | |||||
* | |||||
* 签名的jsapi_ticket必须使用以下接口获取。且必须用wx.agentConfig中的agentid对应的应用secret去获取access_token。 | |||||
* 签名用的noncestr和timestamp必须与wx.agentConfig中的nonceStr和timestamp相同。 | |||||
* @see #getJsapiTicket(boolean) | |||||
*/ | |||||
String getAgentJsapiTicket() throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 获取应用的jsapi_ticket | |||||
* 应用的jsapi_ticket用于计算agentConfig(参见“通过agentConfig注入应用的权限”)的签名,签名计算方法与上述介绍的config的签名算法完全相同,但需要注意以下区别: | |||||
* | |||||
* 签名的jsapi_ticket必须使用以下接口获取。且必须用wx.agentConfig中的agentid对应的应用secret去获取access_token。 | |||||
* 签名用的noncestr和timestamp必须与wx.agentConfig中的nonceStr和timestamp相同。 | |||||
* | |||||
* 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干 | |||||
* | |||||
* 详情请见:https://work.weixin.qq.com/api/doc#10029/%E8%8E%B7%E5%8F%96%E5%BA%94%E7%94%A8%E7%9A%84jsapi_ticket | |||||
* </pre> | |||||
* | |||||
* @param forceRefresh 强制刷新 | |||||
*/ | |||||
String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 创建调用jsapi时所需要的签名 | * 创建调用jsapi时所需要的签名 | ||||
@@ -89,6 +126,13 @@ public interface WxCpService { | |||||
*/ | */ | ||||
WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException; | WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException; | ||||
/** | |||||
* 小程序登录凭证校验 | |||||
* | |||||
* @param jsCode 登录时获取的 code | |||||
*/ | |||||
WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 获取微信服务器的ip段 | * 获取微信服务器的ip段 | ||||
@@ -165,6 +209,13 @@ public interface WxCpService { | |||||
*/ | */ | ||||
WxSession getSession(String id, boolean create); | WxSession getSession(String id, boolean create); | ||||
/** | |||||
* 获取WxSessionManager 对象 | |||||
* | |||||
* @return WxSessionManager | |||||
*/ | |||||
WxSessionManager getSessionManager(); | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 设置WxSessionManager,只有当需要使用个性化的WxSessionManager的时候才需要调用此方法, | * 设置WxSessionManager,只有当需要使用个性化的WxSessionManager的时候才需要调用此方法, | ||||
@@ -250,8 +301,17 @@ public interface WxCpService { | |||||
*/ | */ | ||||
WxCpChatService getChatService(); | WxCpChatService getChatService(); | ||||
/** | |||||
* 获取任务卡片服务 | |||||
* | |||||
* @return 任务卡片服务 | |||||
*/ | |||||
WxCpTaskCardService getTaskCardService(); | |||||
WxCpAgentService getAgentService(); | WxCpAgentService getAgentService(); | ||||
WxCpOAService getOAService(); | |||||
/** | /** | ||||
* http请求对象 | * http请求对象 | ||||
*/ | */ | ||||
@@ -0,0 +1,30 @@ | |||||
package me.chanjar.weixin.cp.api; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import java.util.List; | |||||
/** | |||||
* <pre> | |||||
* 任务卡片管理接口. | |||||
* Created by Jeff on 2019-05-16. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/domainname">Jeff</a> | |||||
* @date 2019-05-16 | |||||
*/ | |||||
public interface WxCpTaskCardService { | |||||
/** | |||||
* <pre> | |||||
* 更新任务卡片消息状态 | |||||
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/91579 | |||||
* | |||||
* 注意: 这个方法使用WxCpConfigStorage里的agentId | |||||
* </pre> | |||||
* | |||||
* @param userIds 企业的成员ID列表 | |||||
* @param taskId 任务卡片ID | |||||
* @param clickedKey 已点击按钮的Key | |||||
*/ | |||||
void update(List<String> userIds, String taskId, String clickedKey) throws WxErrorException; | |||||
} |
@@ -1,16 +1,10 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | package me.chanjar.weixin.cp.api.impl; | ||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import com.google.common.base.Joiner; | |||||
import com.google.gson.JsonArray; | import com.google.gson.JsonArray; | ||||
import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||
import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | import me.chanjar.weixin.common.bean.WxJsapiSignature; | ||||
import me.chanjar.weixin.common.error.WxError; | import me.chanjar.weixin.common.error.WxError; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
@@ -24,20 +18,23 @@ import me.chanjar.weixin.common.util.http.RequestExecutor; | |||||
import me.chanjar.weixin.common.util.http.RequestHttp; | import me.chanjar.weixin.common.util.http.RequestHttp; | ||||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | ||||
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | ||||
import me.chanjar.weixin.cp.api.WxCpAgentService; | |||||
import me.chanjar.weixin.cp.api.WxCpChatService; | |||||
import me.chanjar.weixin.cp.api.WxCpDepartmentService; | |||||
import me.chanjar.weixin.cp.api.WxCpMediaService; | |||||
import me.chanjar.weixin.cp.api.WxCpMenuService; | |||||
import me.chanjar.weixin.cp.api.WxCpOAuth2Service; | |||||
import me.chanjar.weixin.cp.api.WxCpService; | |||||
import me.chanjar.weixin.cp.api.WxCpTagService; | |||||
import me.chanjar.weixin.cp.api.WxCpUserService; | |||||
import me.chanjar.weixin.cp.api.*; | |||||
import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | |||||
import me.chanjar.weixin.cp.bean.WxCpMessage; | import me.chanjar.weixin.cp.bean.WxCpMessage; | ||||
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | ||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpConfigStorage; | ||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, RequestHttp<H, P> { | |||||
/** | |||||
* @author chanjarster | |||||
*/ | |||||
public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestHttp<H, P> { | |||||
protected final Logger log = LoggerFactory.getLogger(this.getClass()); | protected final Logger log = LoggerFactory.getLogger(this.getClass()); | ||||
private WxCpUserService userService = new WxCpUserServiceImpl(this); | private WxCpUserService userService = new WxCpUserServiceImpl(this); | ||||
@@ -48,6 +45,8 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ | |||||
private WxCpOAuth2Service oauth2Service = new WxCpOAuth2ServiceImpl(this); | private WxCpOAuth2Service oauth2Service = new WxCpOAuth2ServiceImpl(this); | ||||
private WxCpTagService tagService = new WxCpTagServiceImpl(this); | private WxCpTagService tagService = new WxCpTagServiceImpl(this); | ||||
private WxCpAgentService agentService = new WxCpAgentServiceImpl(this); | private WxCpAgentService agentService = new WxCpAgentServiceImpl(this); | ||||
private WxCpOAService oaService = new WxCpOAServiceImpl(this); | |||||
private WxCpTaskCardService taskCardService = new WxCpTaskCardServiceImpl(this); | |||||
/** | /** | ||||
* 全局的是否正在刷新access token的锁 | * 全局的是否正在刷新access token的锁 | ||||
@@ -59,14 +58,19 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ | |||||
*/ | */ | ||||
protected final Object globalJsapiTicketRefreshLock = new Object(); | protected final Object globalJsapiTicketRefreshLock = new Object(); | ||||
/** | |||||
* 全局的是否正在刷新agent的jsapi_ticket的锁 | |||||
*/ | |||||
protected final Object globalAgentJsapiTicketRefreshLock = new Object(); | |||||
protected WxCpConfigStorage configStorage; | protected WxCpConfigStorage configStorage; | ||||
private WxSessionManager sessionManager = new StandardSessionManager(); | |||||
protected WxSessionManager sessionManager = new StandardSessionManager(); | |||||
/** | /** | ||||
* 临时文件目录 | * 临时文件目录 | ||||
*/ | */ | ||||
protected File tmpDirFile; | |||||
private File tmpDirFile; | |||||
private int retrySleepMillis = 1000; | private int retrySleepMillis = 1000; | ||||
private int maxRetryTimes = 5; | private int maxRetryTimes = 5; | ||||
@@ -86,6 +90,30 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ | |||||
return getAccessToken(false); | return getAccessToken(false); | ||||
} | } | ||||
@Override | |||||
public String getAgentJsapiTicket() throws WxErrorException { | |||||
return this.getAgentJsapiTicket(false); | |||||
} | |||||
@Override | |||||
public String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException { | |||||
if (forceRefresh) { | |||||
this.configStorage.expireAgentJsapiTicket(); | |||||
} | |||||
if (this.configStorage.isAgentJsapiTicketExpired()) { | |||||
synchronized (this.globalAgentJsapiTicketRefreshLock) { | |||||
if (this.configStorage.isAgentJsapiTicketExpired()) { | |||||
String responseContent = this.get(WxCpService.GET_AGENT_CONFIG_TICKET, null); | |||||
JsonObject jsonObject = new JsonParser().parse(responseContent).getAsJsonObject(); | |||||
this.configStorage.updateAgentJsapiTicket(jsonObject.get("ticket").getAsString(), | |||||
jsonObject.get("expires_in").getAsInt()); | |||||
} | |||||
} | |||||
} | |||||
return this.configStorage.getAgentJsapiTicket(); | |||||
} | |||||
@Override | @Override | ||||
public String getJsapiTicket() throws WxErrorException { | public String getJsapiTicket() throws WxErrorException { | ||||
@@ -97,20 +125,18 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ | |||||
if (forceRefresh) { | if (forceRefresh) { | ||||
this.configStorage.expireJsapiTicket(); | this.configStorage.expireJsapiTicket(); | ||||
} | } | ||||
if (this.configStorage.isJsapiTicketExpired()) { | if (this.configStorage.isJsapiTicketExpired()) { | ||||
synchronized (this.globalJsapiTicketRefreshLock) { | synchronized (this.globalJsapiTicketRefreshLock) { | ||||
if (this.configStorage.isJsapiTicketExpired()) { | if (this.configStorage.isJsapiTicketExpired()) { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket"; | |||||
String responseContent = execute(SimpleGetRequestExecutor.create(this), url, null); | |||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||||
JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); | |||||
String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); | |||||
int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | |||||
this.configStorage.updateJsapiTicket(jsapiTicket, | |||||
expiresInSeconds); | |||||
String responseContent = this.get(WxCpService.GET_JSAPI_TICKET, null); | |||||
JsonObject tmpJsonObject = new JsonParser().parse(responseContent).getAsJsonObject(); | |||||
this.configStorage.updateJsapiTicket(tmpJsonObject.get("ticket").getAsString(), | |||||
tmpJsonObject.get("expires_in").getAsInt()); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
return this.configStorage.getJsapiTicket(); | return this.configStorage.getJsapiTicket(); | ||||
} | } | ||||
@@ -139,18 +165,27 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ | |||||
@Override | @Override | ||||
public WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException { | public WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send"; | |||||
Integer agentId = message.getAgentId(); | Integer agentId = message.getAgentId(); | ||||
if(null == agentId){ | |||||
if (null == agentId) { | |||||
message.setAgentId(this.getWxCpConfigStorage().getAgentId()); | message.setAgentId(this.getWxCpConfigStorage().getAgentId()); | ||||
} | } | ||||
return WxCpMessageSendResult.fromJson(this.post(url, message.toJson())); | |||||
return WxCpMessageSendResult.fromJson(this.post(WxCpService.MESSAGE_SEND, message.toJson())); | |||||
} | |||||
@Override | |||||
public WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException { | |||||
Map<String, String> params = new HashMap<>(2); | |||||
params.put("js_code", jsCode); | |||||
params.put("grant_type", "authorization_code"); | |||||
String result = this.get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); | |||||
return WxCpMaJsCode2SessionResult.fromJson(result); | |||||
} | } | ||||
@Override | @Override | ||||
public String[] getCallbackIp() throws WxErrorException { | public String[] getCallbackIp() throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip"; | |||||
String responseContent = get(url, null); | |||||
String responseContent = get(WxCpService.GET_CALLBACK_IP, null); | |||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); | JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); | ||||
String[] ips = new String[jsonArray.size()]; | String[] ips = new String[jsonArray.size()]; | ||||
@@ -171,7 +206,7 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ | |||||
} | } | ||||
/** | /** | ||||
* 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 | |||||
* 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求. | |||||
*/ | */ | ||||
@Override | @Override | ||||
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | ||||
@@ -285,25 +320,28 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ | |||||
this.sessionManager = sessionManager; | this.sessionManager = sessionManager; | ||||
} | } | ||||
@Override | |||||
public WxSessionManager getSessionManager() { | |||||
return this.sessionManager; | |||||
} | |||||
@Override | @Override | ||||
public String replaceParty(String mediaId) throws WxErrorException { | public String replaceParty(String mediaId) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty"; | |||||
JsonObject jsonObject = new JsonObject(); | JsonObject jsonObject = new JsonObject(); | ||||
jsonObject.addProperty("media_id", mediaId); | jsonObject.addProperty("media_id", mediaId); | ||||
return post(url, jsonObject.toString()); | |||||
return post(WxCpService.BATCH_REPLACE_PARTY, jsonObject.toString()); | |||||
} | } | ||||
@Override | @Override | ||||
public String replaceUser(String mediaId) throws WxErrorException { | public String replaceUser(String mediaId) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser"; | |||||
JsonObject jsonObject = new JsonObject(); | JsonObject jsonObject = new JsonObject(); | ||||
jsonObject.addProperty("media_id", mediaId); | jsonObject.addProperty("media_id", mediaId); | ||||
return post(url, jsonObject.toString()); | |||||
return post(WxCpService.BATCH_REPLACE_USER, jsonObject.toString()); | |||||
} | } | ||||
@Override | @Override | ||||
public String getTaskResult(String joinId) throws WxErrorException { | public String getTaskResult(String joinId) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid=" + joinId; | |||||
String url = WxCpService.BATCH_GET_RESULT + joinId; | |||||
return get(url, null); | return get(url, null); | ||||
} | } | ||||
@@ -350,6 +388,16 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ | |||||
return chatService; | return chatService; | ||||
} | } | ||||
@Override | |||||
public WxCpOAService getOAService() { | |||||
return oaService; | |||||
} | |||||
@Override | |||||
public WxCpTaskCardService getTaskCardService() { | |||||
return taskCardService; | |||||
} | |||||
@Override | @Override | ||||
public RequestHttp<?, ?> getRequestHttp() { | public RequestHttp<?, ?> getRequestHttp() { | ||||
return this; | return this; |
@@ -1,36 +1,35 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | package me.chanjar.weixin.cp.api.impl; | ||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | import me.chanjar.weixin.common.util.json.WxGsonBuilder; | ||||
import me.chanjar.weixin.cp.api.WxCpChatService; | import me.chanjar.weixin.cp.api.WxCpChatService; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; | |||||
import me.chanjar.weixin.cp.bean.WxCpChat; | import me.chanjar.weixin.cp.bean.WxCpChat; | ||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import org.apache.commons.lang3.StringUtils; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
/** | /** | ||||
* 群聊服务实现 | |||||
* 群聊服务实现. | |||||
* | * | ||||
* @author gaigeshen | * @author gaigeshen | ||||
*/ | */ | ||||
public class WxCpChatServiceImpl implements WxCpChatService { | |||||
public class WxCpChatServiceImpl implements WxCpChatService { | |||||
private static final JsonParser JSON_PARSER = new JsonParser(); | |||||
private final WxCpService cpService; | |||||
private final WxCpService internalService; | |||||
/** | /** | ||||
* 创建群聊服务实现的实例 | |||||
* | |||||
* @param internalService 企业微信的服务 | |||||
* 创建群聊服务实现的实例. | |||||
* | |||||
* @param cpService 企业微信的服务 | |||||
*/ | */ | ||||
public WxCpChatServiceImpl(WxCpService internalService) { | |||||
this.internalService = internalService; | |||||
WxCpChatServiceImpl(WxCpService cpService) { | |||||
this.cpService = cpService; | |||||
} | } | ||||
@Override | @Override | ||||
@@ -48,12 +47,18 @@ public class WxCpChatServiceImpl implements WxCpChatService { | |||||
if (StringUtils.isNotBlank(chatId)) { | if (StringUtils.isNotBlank(chatId)) { | ||||
data.put("chatid", chatId); | data.put("chatid", chatId); | ||||
} | } | ||||
String result = internalService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/create", WxGsonBuilder.create().toJson(data)); | |||||
String result = this.cpService.post(APPCHAT_CREATE, WxGsonBuilder.create().toJson(data)); | |||||
return new JsonParser().parse(result).getAsJsonObject().get("chatid").getAsString(); | return new JsonParser().parse(result).getAsJsonObject().get("chatid").getAsString(); | ||||
} | } | ||||
@Override | @Override | ||||
public void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException { | |||||
public String create(String name, String owner, List<String> users, String chatId) throws WxErrorException { | |||||
return chatCreate(name, owner, users, chatId); | |||||
} | |||||
@Override | |||||
public void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) | |||||
throws WxErrorException { | |||||
Map<String, Object> data = new HashMap<>(5); | Map<String, Object> data = new HashMap<>(5); | ||||
if (StringUtils.isNotBlank(chatId)) { | if (StringUtils.isNotBlank(chatId)) { | ||||
data.put("chatid", chatId); | data.put("chatid", chatId); | ||||
@@ -70,14 +75,30 @@ public class WxCpChatServiceImpl implements WxCpChatService { | |||||
if (usersToDelete != null && !usersToDelete.isEmpty()) { | if (usersToDelete != null && !usersToDelete.isEmpty()) { | ||||
data.put("del_user_list", usersToDelete); | data.put("del_user_list", usersToDelete); | ||||
} | } | ||||
internalService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/update", WxGsonBuilder.create().toJson(data)); | |||||
this.cpService.post(APPCHAT_UPDATE, WxGsonBuilder.create().toJson(data)); | |||||
} | |||||
@Override | |||||
public void update(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException { | |||||
chatUpdate(chatId, name, owner, usersToAdd, usersToDelete); | |||||
} | } | ||||
@Override | @Override | ||||
public WxCpChat chatGet(String chatId) throws WxErrorException { | public WxCpChat chatGet(String chatId) throws WxErrorException { | ||||
String result = internalService.get("https://qyapi.weixin.qq.com/cgi-bin/appchat/get?chatid=" + chatId, null); | |||||
return WxCpGsonBuilder.create().fromJson( | |||||
new JsonParser().parse(result).getAsJsonObject().getAsJsonObject("chat_info").toString(), WxCpChat.class); | |||||
String result = this.cpService.get(APPCHAT_GET_CHATID + chatId, null); | |||||
return WxCpGsonBuilder.create() | |||||
.fromJson(JSON_PARSER.parse(result).getAsJsonObject().getAsJsonObject("chat_info").toString(), WxCpChat.class); | |||||
} | |||||
@Override | |||||
public WxCpChat get(String chatId) throws WxErrorException { | |||||
return chatGet(chatId); | |||||
} | |||||
@Override | |||||
public void sendMsg(WxCpAppChatMessage message) throws WxErrorException { | |||||
this.cpService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/send", message.toJson()); | |||||
} | } | ||||
} | } |
@@ -28,11 +28,11 @@ public class WxCpDepartmentServiceImpl implements WxCpDepartmentService { | |||||
} | } | ||||
@Override | @Override | ||||
public Integer create(WxCpDepart depart) throws WxErrorException { | |||||
public Long create(WxCpDepart depart) throws WxErrorException { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; | String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; | ||||
String responseContent = this.mainService.post(url, depart.toJson()); | String responseContent = this.mainService.post(url, depart.toJson()); | ||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id")); | |||||
return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("id")); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -0,0 +1,165 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | |||||
import com.google.gson.JsonArray; | |||||
import com.google.gson.JsonElement; | |||||
import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonParser; | |||||
import com.google.gson.reflect.TypeToken; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.cp.api.WxCpOAService; | |||||
import me.chanjar.weixin.cp.api.WxCpService; | |||||
import me.chanjar.weixin.cp.bean.WxCpApprovalDataResult; | |||||
import me.chanjar.weixin.cp.bean.WxCpCheckinData; | |||||
import me.chanjar.weixin.cp.bean.WxCpCheckinOption; | |||||
import me.chanjar.weixin.cp.bean.WxCpDialRecord; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
import java.util.Date; | |||||
import java.util.List; | |||||
/** | |||||
* @author Element | |||||
* @Package me.chanjar.weixin.cp.api.impl | |||||
* @date 2019-04-06 11:20 | |||||
* @Description: TODO | |||||
*/ | |||||
public class WxCpOAServiceImpl implements WxCpOAService { | |||||
private WxCpService mainService; | |||||
public WxCpOAServiceImpl(WxCpService mainService) { | |||||
this.mainService = mainService; | |||||
} | |||||
@Override | |||||
public List<WxCpCheckinData> getCheckinData(Integer openCheckinDataType, Date starttime, Date endtime, List<String> userIdList) throws WxErrorException { | |||||
if (starttime == null || endtime == null) { | |||||
throw new RuntimeException("starttime and endtime can't be null"); | |||||
} | |||||
if (userIdList == null || userIdList.size() > 100) { | |||||
throw new RuntimeException("用户列表不能为空,不超过100个,若用户超过100个,请分批获取"); | |||||
} | |||||
long endtimestamp = endtime.getTime() / 1000L; | |||||
long starttimestamp = starttime.getTime() / 1000L; | |||||
if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= 30 * 24 * 60 * 60) { | |||||
throw new RuntimeException("获取记录时间跨度不超过一个月"); | |||||
} | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckindata"; | |||||
JsonObject jsonObject = new JsonObject(); | |||||
JsonArray jsonArray = new JsonArray(); | |||||
jsonObject.addProperty("opencheckindatatype", openCheckinDataType); | |||||
jsonObject.addProperty("starttime", starttimestamp); | |||||
jsonObject.addProperty("endtime", endtimestamp); | |||||
for (String userid : userIdList) { | |||||
jsonArray.add(userid); | |||||
} | |||||
jsonObject.add("useridlist", jsonArray); | |||||
String responseContent = this.mainService.post(url, jsonObject.toString()); | |||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||||
return WxCpGsonBuilder.create() | |||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("checkindata"), | |||||
new TypeToken<List<WxCpCheckinData>>() { | |||||
}.getType() | |||||
); | |||||
} | |||||
@Override | |||||
public List<WxCpCheckinOption> getCheckinOption(Date datetime, List<String> userIdList) throws WxErrorException { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckinoption"; | |||||
if (datetime == null) { | |||||
throw new RuntimeException("datetime can't be null"); | |||||
} | |||||
if (userIdList == null || userIdList.size() > 100) { | |||||
throw new RuntimeException("用户列表不能为空,不超过100个,若用户超过100个,请分批获取"); | |||||
} | |||||
JsonArray jsonArray = new JsonArray(); | |||||
for (String userid : userIdList) { | |||||
jsonArray.add(userid); | |||||
} | |||||
JsonObject jsonObject = new JsonObject(); | |||||
jsonObject.addProperty("datetime", datetime.getTime() / 1000L); | |||||
jsonObject.add("useridlist", jsonArray); | |||||
String responseContent = this.mainService.post(url, jsonObject.toString()); | |||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||||
return WxCpGsonBuilder.create() | |||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("info"), | |||||
new TypeToken<List<WxCpCheckinOption>>() { | |||||
}.getType() | |||||
); | |||||
} | |||||
@Override | |||||
public WxCpApprovalDataResult getApprovalData(Date starttime, Date endtime, Long nextSpnum) throws WxErrorException { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/corp/getapprovaldata"; | |||||
JsonObject jsonObject = new JsonObject(); | |||||
jsonObject.addProperty("starttime", starttime.getTime() / 1000L); | |||||
jsonObject.addProperty("endtime", endtime.getTime() / 1000L); | |||||
if (nextSpnum != null) { | |||||
jsonObject.addProperty("next_spnum", nextSpnum); | |||||
} | |||||
String responseContent = this.mainService.post(url, jsonObject.toString()); | |||||
return WxCpGsonBuilder.create().fromJson(responseContent, WxCpApprovalDataResult.class); | |||||
} | |||||
@Override | |||||
public List<WxCpDialRecord> getDialRecord(Date starttime, Date endtime, Integer offset, Integer limit) throws WxErrorException { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/dial/get_dial_record"; | |||||
JsonObject jsonObject = new JsonObject(); | |||||
if (offset == null) { | |||||
offset = 0; | |||||
} | |||||
if (limit == null || limit <= 0) { | |||||
limit = 100; | |||||
} | |||||
jsonObject.addProperty("offset", offset); | |||||
jsonObject.addProperty("limit", limit); | |||||
if (starttime != null && endtime != null) { | |||||
long endtimestamp = endtime.getTime() / 1000L; | |||||
long starttimestamp = starttime.getTime() / 1000L; | |||||
if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= 30 * 24 * 60 * 60) { | |||||
throw new RuntimeException("受限于网络传输,起止时间的最大跨度为30天,如超过30天,则以结束时间为基准向前取30天进行查询"); | |||||
} | |||||
jsonObject.addProperty("start_time", starttimestamp); | |||||
jsonObject.addProperty("end_time", endtimestamp); | |||||
} | |||||
String responseContent = this.mainService.post(url, jsonObject.toString()); | |||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||||
return WxCpGsonBuilder.create() | |||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("record"), | |||||
new TypeToken<List<WxCpDialRecord>>() { | |||||
}.getType() | |||||
); | |||||
} | |||||
} |
@@ -8,6 +8,7 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.common.util.http.HttpType; | import me.chanjar.weixin.common.util.http.HttpType; | ||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | ||||
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; | import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; | ||||
import me.chanjar.weixin.cp.api.WxCpOAService; | |||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpConfigStorage; | ||||
import org.apache.http.HttpHost; | import org.apache.http.HttpHost; | ||||
import org.apache.http.client.config.RequestConfig; | import org.apache.http.client.config.RequestConfig; | ||||
@@ -18,7 +19,7 @@ import org.apache.http.impl.client.CloseableHttpClient; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
public class WxCpServiceApacheHttpClientImpl extends WxCpServiceAbstractImpl<CloseableHttpClient, HttpHost> { | |||||
public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<CloseableHttpClient, HttpHost> { | |||||
protected CloseableHttpClient httpClient; | protected CloseableHttpClient httpClient; | ||||
protected HttpHost httpProxy; | protected HttpHost httpProxy; | ||||
@@ -39,37 +40,37 @@ public class WxCpServiceApacheHttpClientImpl extends WxCpServiceAbstractImpl<Clo | |||||
@Override | @Override | ||||
public String getAccessToken(boolean forceRefresh) throws WxErrorException { | public String getAccessToken(boolean forceRefresh) throws WxErrorException { | ||||
if (this.configStorage.isAccessTokenExpired() || forceRefresh) { | |||||
synchronized (this.globalAccessTokenRefreshLock) { | |||||
if (this.configStorage.isAccessTokenExpired()) { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | |||||
+ "&corpid=" + this.configStorage.getCorpId() | |||||
+ "&corpsecret=" + this.configStorage.getCorpSecret(); | |||||
try { | |||||
HttpGet httpGet = new HttpGet(url); | |||||
if (this.httpProxy != null) { | |||||
RequestConfig config = RequestConfig.custom() | |||||
.setProxy(this.httpProxy).build(); | |||||
httpGet.setConfig(config); | |||||
} | |||||
String resultContent = null; | |||||
try (CloseableHttpClient httpclient = getRequestHttpClient(); | |||||
CloseableHttpResponse response = httpclient.execute(httpGet)) { | |||||
resultContent = new BasicResponseHandler().handleResponse(response); | |||||
} finally { | |||||
httpGet.releaseConnection(); | |||||
} | |||||
WxError error = WxError.fromJson(resultContent, WxType.CP); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
this.configStorage.updateAccessToken( | |||||
accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
} catch (IOException e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) { | |||||
return this.configStorage.getAccessToken(); | |||||
} | |||||
synchronized (this.globalAccessTokenRefreshLock) { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | |||||
+ "&corpid=" + this.configStorage.getCorpId() | |||||
+ "&corpsecret=" + this.configStorage.getCorpSecret(); | |||||
try { | |||||
HttpGet httpGet = new HttpGet(url); | |||||
if (this.httpProxy != null) { | |||||
RequestConfig config = RequestConfig.custom() | |||||
.setProxy(this.httpProxy).build(); | |||||
httpGet.setConfig(config); | |||||
} | |||||
String resultContent; | |||||
try (CloseableHttpClient httpclient = getRequestHttpClient(); | |||||
CloseableHttpResponse response = httpclient.execute(httpGet)) { | |||||
resultContent = new BasicResponseHandler().handleResponse(response); | |||||
} finally { | |||||
httpGet.releaseConnection(); | |||||
} | } | ||||
WxError error = WxError.fromJson(resultContent, WxType.CP); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
this.configStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
} catch (IOException e) { | |||||
throw new RuntimeException(e); | |||||
} | } | ||||
} | } | ||||
return this.configStorage.getAccessToken(); | return this.configStorage.getAccessToken(); | ||||
@@ -8,7 +8,7 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.common.util.http.HttpType; | import me.chanjar.weixin.common.util.http.HttpType; | ||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpConfigStorage; | ||||
public class WxCpServiceJoddHttpImpl extends WxCpServiceAbstractImpl<HttpConnectionProvider, ProxyInfo> { | |||||
public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionProvider, ProxyInfo> { | |||||
protected HttpConnectionProvider httpClient; | protected HttpConnectionProvider httpClient; | ||||
protected ProxyInfo httpProxy; | protected ProxyInfo httpProxy; | ||||
@@ -30,30 +30,29 @@ public class WxCpServiceJoddHttpImpl extends WxCpServiceAbstractImpl<HttpConnect | |||||
@Override | @Override | ||||
public String getAccessToken(boolean forceRefresh) throws WxErrorException { | public String getAccessToken(boolean forceRefresh) throws WxErrorException { | ||||
if (this.configStorage.isAccessTokenExpired() || forceRefresh) { | |||||
synchronized (this.globalAccessTokenRefreshLock) { | |||||
if (this.configStorage.isAccessTokenExpired()) { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | |||||
+ "&corpid=" + this.configStorage.getCorpId() | |||||
+ "&corpsecret=" + this.configStorage.getCorpSecret(); | |||||
if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) { | |||||
return this.configStorage.getAccessToken(); | |||||
} | |||||
synchronized (this.globalAccessTokenRefreshLock) { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | |||||
+ "&corpid=" + this.configStorage.getCorpId() | |||||
+ "&corpsecret=" + this.configStorage.getCorpSecret(); | |||||
HttpRequest request = HttpRequest.get(url); | |||||
if (this.httpProxy != null) { | |||||
httpClient.useProxy(this.httpProxy); | |||||
} | |||||
request.withConnectionProvider(httpClient); | |||||
HttpResponse response = request.send(); | |||||
HttpRequest request = HttpRequest.get(url); | |||||
if (this.httpProxy != null) { | |||||
httpClient.useProxy(this.httpProxy); | |||||
} | |||||
request.withConnectionProvider(httpClient); | |||||
HttpResponse response = request.send(); | |||||
String resultContent = response.bodyText(); | |||||
WxError error = WxError.fromJson(resultContent, WxType.CP); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
this.configStorage.updateAccessToken( | |||||
accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
} | |||||
String resultContent = response.bodyText(); | |||||
WxError error = WxError.fromJson(resultContent, WxType.CP); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | } | ||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
this.configStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
} | } | ||||
return this.configStorage.getAccessToken(); | return this.configStorage.getAccessToken(); | ||||
} | } | ||||
@@ -11,7 +11,7 @@ import okhttp3.*; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
public class WxCpServiceOkHttpImpl extends WxCpServiceAbstractImpl<OkHttpClient, OkHttpProxyInfo> { | |||||
public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkHttpProxyInfo> { | |||||
protected OkHttpClient httpClient; | protected OkHttpClient httpClient; | ||||
protected OkHttpProxyInfo httpProxy; | protected OkHttpProxyInfo httpProxy; | ||||
@@ -33,34 +33,33 @@ public class WxCpServiceOkHttpImpl extends WxCpServiceAbstractImpl<OkHttpClient, | |||||
@Override | @Override | ||||
public String getAccessToken(boolean forceRefresh) throws WxErrorException { | public String getAccessToken(boolean forceRefresh) throws WxErrorException { | ||||
this.log.debug("WxCpServiceOkHttpImpl is running"); | |||||
if (this.configStorage.isAccessTokenExpired() || forceRefresh) { | |||||
synchronized (this.globalAccessTokenRefreshLock) { | |||||
if (this.configStorage.isAccessTokenExpired()) { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | |||||
+ "&corpid=" + this.configStorage.getCorpId() | |||||
+ "&corpsecret=" + this.configStorage.getCorpSecret(); | |||||
//得到httpClient | |||||
OkHttpClient client = getRequestHttpClient(); | |||||
//请求的request | |||||
Request request = new Request.Builder().url(url).get().build(); | |||||
String resultContent = null; | |||||
try { | |||||
Response response = client.newCall(request).execute(); | |||||
resultContent = response.body().string(); | |||||
} catch (IOException e) { | |||||
this.log.error(e.getMessage(), e); | |||||
} | |||||
if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) { | |||||
return this.configStorage.getAccessToken(); | |||||
} | |||||
synchronized (this.globalAccessTokenRefreshLock) { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | |||||
+ "&corpid=" + this.configStorage.getCorpId() | |||||
+ "&corpsecret=" + this.configStorage.getCorpSecret(); | |||||
//得到httpClient | |||||
OkHttpClient client = getRequestHttpClient(); | |||||
//请求的request | |||||
Request request = new Request.Builder().url(url).get().build(); | |||||
String resultContent = null; | |||||
try { | |||||
Response response = client.newCall(request).execute(); | |||||
resultContent = response.body().string(); | |||||
} catch (IOException e) { | |||||
this.log.error(e.getMessage(), e); | |||||
} | |||||
WxError error = WxError.fromJson(resultContent, WxType.CP); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
this.configStorage.updateAccessToken(accessToken.getAccessToken(), | |||||
accessToken.getExpiresIn()); | |||||
WxError error = WxError.fromJson(resultContent, WxType.CP); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | } | ||||
} | |||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
this.configStorage.updateAccessToken(accessToken.getAccessToken(), | |||||
accessToken.getExpiresIn()); | |||||
} | } | ||||
return this.configStorage.getAccessToken(); | return this.configStorage.getAccessToken(); | ||||
} | } | ||||
@@ -0,0 +1,39 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | |||||
import lombok.RequiredArgsConstructor; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
import me.chanjar.weixin.cp.api.WxCpService; | |||||
import me.chanjar.weixin.cp.api.WxCpTaskCardService; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
/** | |||||
* <pre> | |||||
* 任务卡片管理接口. | |||||
* Created by Jeff on 2019-05-16. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/domainname">Jeff</a> | |||||
* @date 2019-05-16 | |||||
*/ | |||||
@RequiredArgsConstructor | |||||
public class WxCpTaskCardServiceImpl implements WxCpTaskCardService { | |||||
private final WxCpService mainService; | |||||
@Override | |||||
public void update(List<String> userIds, String taskId, String clickedKey) throws WxErrorException { | |||||
Integer agentId = this.mainService.getWxCpConfigStorage().getAgentId(); | |||||
Map<String, Object> data = new HashMap<>(4); | |||||
data.put("userids", userIds); | |||||
data.put("agentid", agentId); | |||||
data.put("task_id", taskId); | |||||
data.put("clicked_key", clickedKey); | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/update_taskcard"; | |||||
this.mainService.post(url, WxGsonBuilder.create().toJson(data)); | |||||
} | |||||
} |
@@ -0,0 +1,163 @@ | |||||
package me.chanjar.weixin.cp.bean; | |||||
import com.google.gson.JsonArray; | |||||
import com.google.gson.JsonObject; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Builder; | |||||
import lombok.Data; | |||||
import lombok.NoArgsConstructor; | |||||
import me.chanjar.weixin.cp.WxCpConsts.AppChatMsgType; | |||||
import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | |||||
import me.chanjar.weixin.cp.bean.article.NewArticle; | |||||
import java.io.Serializable; | |||||
import java.util.List; | |||||
/** | |||||
* <pre> | |||||
* 应用推送消息 | |||||
* Created by Binary Wang on 2019/1/26. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
@Data | |||||
@Builder | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
public class WxCpAppChatMessage implements Serializable { | |||||
private static final long serialVersionUID = -5469013416372240229L; | |||||
private String msgType; | |||||
private String content; | |||||
private String chatId; | |||||
private String mediaId; | |||||
private String title; | |||||
private String description; | |||||
private Boolean safe; | |||||
private String url; | |||||
private String btnTxt; | |||||
private List<NewArticle> articles; | |||||
private List<MpnewsArticle> mpnewsArticles; | |||||
/** | |||||
* 构建文本消息. | |||||
*/ | |||||
public static WxCpAppChatMessage buildTextMsg(String chatId, String content, boolean safe) { | |||||
final WxCpAppChatMessage message = new WxCpAppChatMessage(); | |||||
message.setMsgType(AppChatMsgType.TEXT); | |||||
message.setContent(content); | |||||
message.setChatId(chatId); | |||||
message.setSafe(safe); | |||||
return message; | |||||
} | |||||
/** | |||||
* 生成json字符串. | |||||
*/ | |||||
public String toJson() { | |||||
JsonObject messageJson = new JsonObject(); | |||||
messageJson.addProperty("msgtype", this.getMsgType()); | |||||
messageJson.addProperty("chatid", this.getChatId()); | |||||
if (this.getSafe() != null && this.getSafe()) { | |||||
messageJson.addProperty("safe", 1); | |||||
} | |||||
this.handleMsgType(messageJson); | |||||
return messageJson.toString(); | |||||
} | |||||
private void handleMsgType(JsonObject messageJson) { | |||||
switch (this.getMsgType()) { | |||||
case AppChatMsgType.TEXT: { | |||||
JsonObject text = new JsonObject(); | |||||
text.addProperty("content", this.getContent()); | |||||
messageJson.add("text", text); | |||||
break; | |||||
} | |||||
case AppChatMsgType.MARKDOWN: { | |||||
JsonObject text = new JsonObject(); | |||||
text.addProperty("content", this.getContent()); | |||||
messageJson.add("markdown", text); | |||||
break; | |||||
} | |||||
case AppChatMsgType.TEXTCARD: { | |||||
JsonObject text = new JsonObject(); | |||||
text.addProperty("title", this.getTitle()); | |||||
text.addProperty("description", this.getDescription()); | |||||
text.addProperty("url", this.getUrl()); | |||||
text.addProperty("btntxt", this.getBtnTxt()); | |||||
messageJson.add("textcard", text); | |||||
break; | |||||
} | |||||
case AppChatMsgType.IMAGE: { | |||||
JsonObject image = new JsonObject(); | |||||
image.addProperty("media_id", this.getMediaId()); | |||||
messageJson.add("image", image); | |||||
break; | |||||
} | |||||
case AppChatMsgType.FILE: { | |||||
JsonObject image = new JsonObject(); | |||||
image.addProperty("media_id", this.getMediaId()); | |||||
messageJson.add("file", image); | |||||
break; | |||||
} | |||||
case AppChatMsgType.VOICE: { | |||||
JsonObject voice = new JsonObject(); | |||||
voice.addProperty("media_id", this.getMediaId()); | |||||
messageJson.add("voice", voice); | |||||
break; | |||||
} | |||||
case AppChatMsgType.VIDEO: { | |||||
JsonObject video = new JsonObject(); | |||||
video.addProperty("media_id", this.getMediaId()); | |||||
video.addProperty("title", this.getTitle()); | |||||
video.addProperty("description", this.getDescription()); | |||||
messageJson.add("video", video); | |||||
break; | |||||
} | |||||
case AppChatMsgType.NEWS: { | |||||
JsonObject newsJsonObject = new JsonObject(); | |||||
JsonArray articleJsonArray = new JsonArray(); | |||||
for (NewArticle article : this.getArticles()) { | |||||
JsonObject articleJson = new JsonObject(); | |||||
articleJson.addProperty("title", article.getTitle()); | |||||
articleJson.addProperty("description", article.getDescription()); | |||||
articleJson.addProperty("url", article.getUrl()); | |||||
articleJson.addProperty("picurl", article.getPicUrl()); | |||||
articleJsonArray.add(articleJson); | |||||
} | |||||
newsJsonObject.add("articles", articleJsonArray); | |||||
messageJson.add("news", newsJsonObject); | |||||
break; | |||||
} | |||||
case AppChatMsgType.MPNEWS: { | |||||
JsonObject newsJsonObject = new JsonObject(); | |||||
if (this.getMediaId() != null) { | |||||
newsJsonObject.addProperty("media_id", this.getMediaId()); | |||||
} else { | |||||
JsonArray articleJsonArray = new JsonArray(); | |||||
for (MpnewsArticle article : this.getMpnewsArticles()) { | |||||
JsonObject articleJson = new JsonObject(); | |||||
articleJson.addProperty("title", article.getTitle()); | |||||
articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); | |||||
articleJson.addProperty("author", article.getAuthor()); | |||||
articleJson.addProperty("content_source_url", article.getContentSourceUrl()); | |||||
articleJson.addProperty("content", article.getContent()); | |||||
articleJson.addProperty("digest", article.getDigest()); | |||||
articleJsonArray.add(articleJson); | |||||
} | |||||
newsJsonObject.add("articles", articleJsonArray); | |||||
} | |||||
messageJson.add("mpnews", newsJsonObject); | |||||
break; | |||||
} | |||||
default: { | |||||
//do nothing | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,69 @@ | |||||
package me.chanjar.weixin.cp.bean; | |||||
import com.google.gson.annotations.SerializedName; | |||||
import lombok.Data; | |||||
import java.io.Serializable; | |||||
import java.util.Map; | |||||
/** | |||||
* @author Element | |||||
* @Package me.chanjar.weixin.cp.bean | |||||
* @date 2019-04-06 14:36 | |||||
* @Description: 企业微信 OA 审批数据 | |||||
*/ | |||||
@Data | |||||
public class WxCpApprovalDataResult implements Serializable { | |||||
private static final long serialVersionUID = -1046940445840716590L; | |||||
@SerializedName("errcode") | |||||
private Integer errCode; | |||||
@SerializedName("errmsg") | |||||
private String errMsg; | |||||
private Integer count; | |||||
private Integer total; | |||||
@SerializedName("next_spnum") | |||||
private Long nextSpnum; | |||||
private WxCpApprovalData[] data; | |||||
@Data | |||||
public static class WxCpApprovalData implements Serializable{ | |||||
private static final long serialVersionUID = -3051785319608491640L; | |||||
private String spname; | |||||
@SerializedName("apply_name") | |||||
private String applyName; | |||||
@SerializedName("apply_org") | |||||
private String applyOrg; | |||||
@SerializedName("approval_name") | |||||
private String[] approvalName; | |||||
@SerializedName("notify_name") | |||||
private String[] notifyName; | |||||
@SerializedName("sp_status") | |||||
private Integer spStatus; | |||||
@SerializedName("sp_num") | |||||
private Long spNum; | |||||
@SerializedName("apply_time") | |||||
private Long applyTime; | |||||
@SerializedName("apply_user_id") | |||||
private String applyUserId; | |||||
@SerializedName("comm") | |||||
private Map<String,String> comm; | |||||
} | |||||
} |
@@ -0,0 +1,51 @@ | |||||
package me.chanjar.weixin.cp.bean; | |||||
import com.google.gson.annotations.SerializedName; | |||||
import lombok.Data; | |||||
import java.io.Serializable; | |||||
import java.util.List; | |||||
/** | |||||
* @author Element | |||||
* @Package me.chanjar.weixin.cp.bean | |||||
* @date 2019-04-06 11:01 | |||||
* @Description: 企业微信打卡数据 | |||||
*/ | |||||
@Data | |||||
public class WxCpCheckinData implements Serializable { | |||||
private static final long serialVersionUID = 1915820330847799605L; | |||||
@SerializedName("userid") | |||||
private String userId; | |||||
@SerializedName("groupname") | |||||
private String groupName; | |||||
@SerializedName("checkin_type") | |||||
private String checkinType; | |||||
@SerializedName("exception_type") | |||||
private String exceptionType; | |||||
@SerializedName("checkin_time") | |||||
private Long checkinTime; | |||||
@SerializedName("location_title") | |||||
private String locationTitle; | |||||
@SerializedName("location_detail") | |||||
private String locationDetail; | |||||
@SerializedName("wifiname") | |||||
private String wifiName; | |||||
@SerializedName("wifimac") | |||||
private String wifiMAC; | |||||
private String notes; | |||||
@SerializedName("mediaids") | |||||
private List<String> mediaIds; | |||||
} |
@@ -0,0 +1,151 @@ | |||||
package me.chanjar.weixin.cp.bean; | |||||
import com.google.gson.annotations.SerializedName; | |||||
import lombok.Data; | |||||
import java.io.Serializable; | |||||
import java.util.List; | |||||
/** | |||||
* @author Element | |||||
* @Package me.chanjar.weixin.cp.bean | |||||
* @date 2019-04-06 13:22 | |||||
* @Description: 企业微信打卡规则 | |||||
*/ | |||||
@Data | |||||
public class WxCpCheckinOption implements Serializable { | |||||
private static final long serialVersionUID = -1964233697990417482L; | |||||
@SerializedName("userid") | |||||
private String userId; | |||||
private Group group; | |||||
@Data | |||||
public static class CheckinDate implements Serializable { | |||||
private static final long serialVersionUID = -5601722383347110974L; | |||||
private List<Integer> workdays; | |||||
@SerializedName("checkintime") | |||||
private CheckinTime[] checkinTime; | |||||
@SerializedName("flex_time") | |||||
private Long flexTime; | |||||
@SerializedName("noneed_offwork") | |||||
private Boolean noneedOffwork; | |||||
@SerializedName("limit_aheadtime") | |||||
private Long limitAheadtime; | |||||
} | |||||
@Data | |||||
public static class CheckinTime implements Serializable { | |||||
private static final long serialVersionUID = -8579954143265336276L; | |||||
@SerializedName("work_sec") | |||||
private Long workSec; | |||||
@SerializedName("off_work_sec") | |||||
private Long offWorkSec; | |||||
@SerializedName("remind_work_sec") | |||||
private Long remindWorkSec; | |||||
@SerializedName("remind_off_work_sec") | |||||
private Long remindOffWorkSec; | |||||
} | |||||
@Data | |||||
public static class Group implements Serializable { | |||||
private static final long serialVersionUID = -5888406969613403044L; | |||||
@SerializedName("groupid") | |||||
private Long id; | |||||
@SerializedName("groupname") | |||||
private String name; | |||||
@SerializedName("grouptype") | |||||
private Integer type; | |||||
@SerializedName("checkindate") | |||||
private List<CheckinDate> checkinDate; | |||||
@SerializedName("spe_workdays") | |||||
private List<SpeDay> speWorkdays; | |||||
@SerializedName("spe_offdays") | |||||
private List<SpeDay> speOffdays; | |||||
@SerializedName("sync_holidays") | |||||
private Boolean syncHolidays; | |||||
@SerializedName("need_photo") | |||||
private Boolean needPhoto; | |||||
@SerializedName("note_can_use_local_pic") | |||||
private Boolean note_can_use_local_pic; | |||||
@SerializedName("allow_checkin_offworkday") | |||||
private Boolean allow_checkin_offworkday; | |||||
@SerializedName("allow_apply_offworkday") | |||||
private Boolean allow_apply_offworkday; | |||||
@SerializedName("wifimac_infos") | |||||
private List<WifiMACInfo> wifiMACInfos; | |||||
@SerializedName("loc_infos") | |||||
private List<LocInfo> locInfos; | |||||
} | |||||
@Data | |||||
public static class WifiMACInfo implements Serializable{ | |||||
private static final long serialVersionUID = -4657809185716627368L; | |||||
@SerializedName("wifiname") | |||||
private String name; | |||||
@SerializedName("wifimac") | |||||
private String mac; | |||||
} | |||||
@Data | |||||
public static class LocInfo implements Serializable{ | |||||
private static final long serialVersionUID = -618965280668099608L; | |||||
private Long lat; | |||||
private Long lng; | |||||
@SerializedName("loc_title") | |||||
private String title; | |||||
@SerializedName("loc_detail") | |||||
private String detail; | |||||
private Long distance; | |||||
} | |||||
@Data | |||||
public static class SpeDay implements Serializable{ | |||||
private static final long serialVersionUID = -3538818921359212748L; | |||||
private Long timestamp; | |||||
private String notes; | |||||
@SerializedName("checkintime") | |||||
private List<CheckinTime> checkinTime; | |||||
} | |||||
} |
@@ -0,0 +1,73 @@ | |||||
package me.chanjar.weixin.cp.bean; | |||||
import com.google.gson.annotations.SerializedName; | |||||
import lombok.Data; | |||||
import java.io.Serializable; | |||||
import java.util.List; | |||||
/** | |||||
* @author Element | |||||
* @Package me.chanjar.weixin.cp.bean | |||||
* @date 2019-04-06 15:38 | |||||
* @Description: 公费电话拨打记录 | |||||
*/ | |||||
@Data | |||||
public class WxCpDialRecord implements Serializable { | |||||
private static final long serialVersionUID = 4178886812949929116L; | |||||
@SerializedName("call_time") | |||||
private Long callTime; | |||||
/** | |||||
* 总通话时长,单位为分钟 | |||||
*/ | |||||
@SerializedName("total_duration") | |||||
private Integer totalDuration; | |||||
/** | |||||
* 通话类型,1-单人通话 2-多人通话 | |||||
*/ | |||||
@SerializedName("call_type") | |||||
private Integer callType; | |||||
private Caller caller; | |||||
private List<Callee> callee; | |||||
/** | |||||
* 主叫信息 | |||||
*/ | |||||
@Data | |||||
public static class Caller implements Serializable{ | |||||
private static final long serialVersionUID = 4792200404338145607L; | |||||
@SerializedName("userid") | |||||
private String userId; | |||||
private Integer duration; | |||||
} | |||||
/** | |||||
* 被叫信息 | |||||
*/ | |||||
@Data | |||||
public static class Callee implements Serializable{ | |||||
private static final long serialVersionUID = 2390963671336179550L; | |||||
/** | |||||
* 被叫用户的userid,当被叫用户为企业内用户时返回 | |||||
*/ | |||||
@SerializedName("userid") | |||||
private String userId; | |||||
/** | |||||
* 被叫用户的号码,当被叫用户为外部用户时返回 | |||||
*/ | |||||
private String phone; | |||||
private Integer duration; | |||||
} | |||||
} |
@@ -37,7 +37,7 @@ public class WxCpInviteResult implements Serializable { | |||||
private String errMsg; | private String errMsg; | ||||
@SerializedName("invaliduser") | @SerializedName("invaliduser") | ||||
private String invalidUsers; | |||||
private String[] invalidUsers; | |||||
@SerializedName("invalidparty") | @SerializedName("invalidparty") | ||||
private String[] invalidParties; | private String[] invalidParties; | ||||
@@ -45,16 +45,4 @@ public class WxCpInviteResult implements Serializable { | |||||
@SerializedName("invalidtag") | @SerializedName("invalidtag") | ||||
private String[] invalidTags; | private String[] invalidTags; | ||||
public List<String> getInvalidUserList() { | |||||
return this.content2List(this.invalidUsers); | |||||
} | |||||
private List<String> content2List(String content) { | |||||
if (StringUtils.isBlank(content)) { | |||||
return Collections.emptyList(); | |||||
} | |||||
return Splitter.on("|").splitToList(content); | |||||
} | |||||
} | } |
@@ -0,0 +1,33 @@ | |||||
package me.chanjar.weixin.cp.bean; | |||||
import com.google.gson.annotations.SerializedName; | |||||
import lombok.Data; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
import java.io.Serializable; | |||||
/** | |||||
* <pre> | |||||
* 小程序登录凭证校验 | |||||
* 文档地址:https://work.weixin.qq.com/api/doc#90000/90136/90289/wx.qy.login | |||||
* </pre> | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
@Data | |||||
public class WxCpMaJsCode2SessionResult implements Serializable { | |||||
private static final long serialVersionUID = 6229609023682814765L; | |||||
@SerializedName("session_key") | |||||
private String sessionKey; | |||||
@SerializedName("userid") | |||||
private String userId; | |||||
@SerializedName("corpid") | |||||
private String corpId; | |||||
public static WxCpMaJsCode2SessionResult fromJson(String json) { | |||||
return WxCpGsonBuilder.create().fromJson(json, WxCpMaJsCode2SessionResult.class); | |||||
} | |||||
} |
@@ -1,22 +1,18 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import java.io.Serializable; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import com.google.gson.JsonArray; | |||||
import com.google.gson.JsonObject; | |||||
import lombok.Data; | import lombok.Data; | ||||
import me.chanjar.weixin.common.api.WxConsts; | |||||
import me.chanjar.weixin.common.api.WxConsts.KefuMsgType; | |||||
import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | ||||
import me.chanjar.weixin.cp.bean.article.NewArticle; | import me.chanjar.weixin.cp.bean.article.NewArticle; | ||||
import me.chanjar.weixin.cp.bean.messagebuilder.FileBuilder; | |||||
import me.chanjar.weixin.cp.bean.messagebuilder.ImageBuilder; | |||||
import me.chanjar.weixin.cp.bean.messagebuilder.MpnewsBuilder; | |||||
import me.chanjar.weixin.cp.bean.messagebuilder.NewsBuilder; | |||||
import me.chanjar.weixin.cp.bean.messagebuilder.TextBuilder; | |||||
import me.chanjar.weixin.cp.bean.messagebuilder.TextCardBuilder; | |||||
import me.chanjar.weixin.cp.bean.messagebuilder.VideoBuilder; | |||||
import me.chanjar.weixin.cp.bean.messagebuilder.VoiceBuilder; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
import me.chanjar.weixin.cp.bean.messagebuilder.*; | |||||
import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import java.io.Serializable; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
/** | /** | ||||
* 消息. | * 消息. | ||||
@@ -45,6 +41,12 @@ public class WxCpMessage implements Serializable { | |||||
private List<NewArticle> articles = new ArrayList<>(); | private List<NewArticle> articles = new ArrayList<>(); | ||||
private List<MpnewsArticle> mpnewsArticles = new ArrayList<>(); | private List<MpnewsArticle> mpnewsArticles = new ArrayList<>(); | ||||
/** | |||||
* 任务卡片特有的属性 | |||||
*/ | |||||
private String taskId; | |||||
private List<TaskCardButton> taskButtons = new ArrayList<>(); | |||||
/** | /** | ||||
* 获得文本消息builder. | * 获得文本消息builder. | ||||
*/ | */ | ||||
@@ -94,6 +96,13 @@ public class WxCpMessage implements Serializable { | |||||
return new MpnewsBuilder(); | return new MpnewsBuilder(); | ||||
} | } | ||||
/** | |||||
* 获得markdown消息builder. | |||||
*/ | |||||
public static MarkdownMsgBuilder MARKDOWN() { | |||||
return new MarkdownMsgBuilder(); | |||||
} | |||||
/** | /** | ||||
* 获得文件消息builder. | * 获得文件消息builder. | ||||
*/ | */ | ||||
@@ -101,17 +110,26 @@ public class WxCpMessage implements Serializable { | |||||
return new FileBuilder(); | return new FileBuilder(); | ||||
} | } | ||||
/** | |||||
* 获得任务卡片消息builder. | |||||
*/ | |||||
public static TaskCardBuilder TASKCARD() { | |||||
return new TaskCardBuilder(); | |||||
} | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 请使用 | * 请使用 | ||||
* {@link WxConsts.KefuMsgType#TEXT} | |||||
* {@link WxConsts.KefuMsgType#IMAGE} | |||||
* {@link WxConsts.KefuMsgType#VOICE} | |||||
* {@link WxConsts.KefuMsgType#MUSIC} | |||||
* {@link WxConsts.KefuMsgType#VIDEO} | |||||
* {@link WxConsts.KefuMsgType#NEWS} | |||||
* {@link WxConsts.KefuMsgType#MPNEWS} | |||||
* {@link KefuMsgType#TEXT} | |||||
* {@link KefuMsgType#IMAGE} | |||||
* {@link KefuMsgType#VOICE} | |||||
* {@link KefuMsgType#MUSIC} | |||||
* {@link KefuMsgType#VIDEO} | |||||
* {@link KefuMsgType#NEWS} | |||||
* {@link KefuMsgType#MPNEWS} | |||||
* {@link KefuMsgType#MARKDOWN} | |||||
* {@link KefuMsgType#TASKCARD} | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param msgType 消息类型 | * @param msgType 消息类型 | ||||
@@ -121,7 +139,162 @@ public class WxCpMessage implements Serializable { | |||||
} | } | ||||
public String toJson() { | public String toJson() { | ||||
return WxCpGsonBuilder.create().toJson(this); | |||||
JsonObject messageJson = new JsonObject(); | |||||
if (this.getAgentId() != null) { | |||||
messageJson.addProperty("agentid", this.getAgentId()); | |||||
} | |||||
if (StringUtils.isNotBlank(this.getToUser())) { | |||||
messageJson.addProperty("touser", this.getToUser()); | |||||
} | |||||
messageJson.addProperty("msgtype", this.getMsgType()); | |||||
if (StringUtils.isNotBlank(this.getToParty())) { | |||||
messageJson.addProperty("toparty", this.getToParty()); | |||||
} | |||||
if (StringUtils.isNotBlank(this.getToTag())) { | |||||
messageJson.addProperty("totag", this.getToTag()); | |||||
} | |||||
this.handleMsgType(messageJson); | |||||
if (StringUtils.isNotBlank(this.getSafe())) { | |||||
messageJson.addProperty("safe", this.getSafe()); | |||||
} | |||||
return messageJson.toString(); | |||||
} | |||||
private void handleMsgType(JsonObject messageJson) { | |||||
switch (this.getMsgType()) { | |||||
case KefuMsgType.TEXT: { | |||||
JsonObject text = new JsonObject(); | |||||
text.addProperty("content", this.getContent()); | |||||
messageJson.add("text", text); | |||||
break; | |||||
} | |||||
case KefuMsgType.MARKDOWN: { | |||||
JsonObject text = new JsonObject(); | |||||
text.addProperty("content", this.getContent()); | |||||
messageJson.add("markdown", text); | |||||
break; | |||||
} | |||||
case KefuMsgType.TEXTCARD: { | |||||
JsonObject text = new JsonObject(); | |||||
text.addProperty("title", this.getTitle()); | |||||
text.addProperty("description", this.getDescription()); | |||||
text.addProperty("url", this.getUrl()); | |||||
text.addProperty("btntxt", this.getBtnTxt()); | |||||
messageJson.add("textcard", text); | |||||
break; | |||||
} | |||||
case KefuMsgType.IMAGE: { | |||||
JsonObject image = new JsonObject(); | |||||
image.addProperty("media_id", this.getMediaId()); | |||||
messageJson.add("image", image); | |||||
break; | |||||
} | |||||
case KefuMsgType.FILE: { | |||||
JsonObject image = new JsonObject(); | |||||
image.addProperty("media_id", this.getMediaId()); | |||||
messageJson.add("file", image); | |||||
break; | |||||
} | |||||
case KefuMsgType.VOICE: { | |||||
JsonObject voice = new JsonObject(); | |||||
voice.addProperty("media_id", this.getMediaId()); | |||||
messageJson.add("voice", voice); | |||||
break; | |||||
} | |||||
case KefuMsgType.VIDEO: { | |||||
JsonObject video = new JsonObject(); | |||||
video.addProperty("media_id", this.getMediaId()); | |||||
video.addProperty("thumb_media_id", this.getThumbMediaId()); | |||||
video.addProperty("title", this.getTitle()); | |||||
video.addProperty("description", this.getDescription()); | |||||
messageJson.add("video", video); | |||||
break; | |||||
} | |||||
case KefuMsgType.NEWS: { | |||||
JsonObject newsJsonObject = new JsonObject(); | |||||
JsonArray articleJsonArray = new JsonArray(); | |||||
for (NewArticle article : this.getArticles()) { | |||||
JsonObject articleJson = new JsonObject(); | |||||
articleJson.addProperty("title", article.getTitle()); | |||||
articleJson.addProperty("description", article.getDescription()); | |||||
articleJson.addProperty("url", article.getUrl()); | |||||
articleJson.addProperty("picurl", article.getPicUrl()); | |||||
articleJsonArray.add(articleJson); | |||||
} | |||||
newsJsonObject.add("articles", articleJsonArray); | |||||
messageJson.add("news", newsJsonObject); | |||||
break; | |||||
} | |||||
case KefuMsgType.MPNEWS: { | |||||
JsonObject newsJsonObject = new JsonObject(); | |||||
if (this.getMediaId() != null) { | |||||
newsJsonObject.addProperty("media_id", this.getMediaId()); | |||||
} else { | |||||
JsonArray articleJsonArray = new JsonArray(); | |||||
for (MpnewsArticle article : this.getMpnewsArticles()) { | |||||
JsonObject articleJson = new JsonObject(); | |||||
articleJson.addProperty("title", article.getTitle()); | |||||
articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); | |||||
articleJson.addProperty("author", article.getAuthor()); | |||||
articleJson.addProperty("content_source_url", article.getContentSourceUrl()); | |||||
articleJson.addProperty("content", article.getContent()); | |||||
articleJson.addProperty("digest", article.getDigest()); | |||||
articleJson.addProperty("show_cover_pic", article.getShowCoverPic()); | |||||
articleJsonArray.add(articleJson); | |||||
} | |||||
newsJsonObject.add("articles", articleJsonArray); | |||||
} | |||||
messageJson.add("mpnews", newsJsonObject); | |||||
break; | |||||
} | |||||
case KefuMsgType.TASKCARD: { | |||||
JsonObject text = new JsonObject(); | |||||
text.addProperty("title", this.getTitle()); | |||||
text.addProperty("description", this.getDescription()); | |||||
if (StringUtils.isNotBlank(this.getUrl())) { | |||||
text.addProperty("url", this.getUrl()); | |||||
} | |||||
text.addProperty("task_id", this.getTaskId()); | |||||
JsonArray buttonJsonArray = new JsonArray(); | |||||
for (TaskCardButton button : this.getTaskButtons()) { | |||||
JsonObject buttonJson = new JsonObject(); | |||||
buttonJson.addProperty("key", button.getKey()); | |||||
buttonJson.addProperty("name", button.getName()); | |||||
if (StringUtils.isNotBlank(button.getReplaceName())) { | |||||
buttonJson.addProperty("replace_name", button.getReplaceName()); | |||||
} | |||||
if (StringUtils.isNotBlank(button.getColor())) { | |||||
buttonJson.addProperty("color", button.getColor()); | |||||
} | |||||
if (button.getBold() != null) { | |||||
buttonJson.addProperty("is_bold", button.getBold()); | |||||
} | |||||
buttonJsonArray.add(buttonJson); | |||||
} | |||||
text.add("btn", buttonJsonArray); | |||||
messageJson.add("taskcard", text); | |||||
break; | |||||
} | |||||
default: { | |||||
// do nothing | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -0,0 +1,42 @@ | |||||
package me.chanjar.weixin.cp.bean; | |||||
import com.google.gson.annotations.SerializedName; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Data; | |||||
import lombok.NoArgsConstructor; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
import java.io.Serializable; | |||||
import java.util.List; | |||||
/** | |||||
* <pre> | |||||
* 更新任务卡片消息状态的返回类 | |||||
* 参考文档:https://work.weixin.qq.com/api/doc#90000/90135/91579 | |||||
* Created by Jeff on 2019-05-16. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/domainname">Jeff</a> | |||||
* @date 2019-05-16 | |||||
*/ | |||||
@Data | |||||
@AllArgsConstructor | |||||
@NoArgsConstructor | |||||
public class WxCpTaskCardUpdateResult implements Serializable { | |||||
@SerializedName("errcode") | |||||
private Integer errcode; | |||||
@SerializedName("errmsg") | |||||
private String errmsg; | |||||
/** | |||||
* 用户列表 | |||||
*/ | |||||
@SerializedName("invaliduser") | |||||
private List<String> invalidUsers; | |||||
public static WxCpTaskCardUpdateResult fromJson(String json) { | |||||
return WxCpGsonBuilder.create().fromJson(json, WxCpTaskCardUpdateResult.class); | |||||
} | |||||
} |
@@ -20,7 +20,8 @@ public class WxCpUser implements Serializable { | |||||
private static final long serialVersionUID = -5696099236344075582L; | private static final long serialVersionUID = -5696099236344075582L; | ||||
private String userId; | private String userId; | ||||
private String name; | private String name; | ||||
private Integer[] departIds; | |||||
private Long[] departIds; | |||||
private Integer[] orders; | |||||
private String position; | private String position; | ||||
private String mobile; | private String mobile; | ||||
private Gender gender; | private Gender gender; | ||||
@@ -1,24 +1,26 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.Serializable; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import org.apache.commons.io.IOUtils; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; | ||||
import com.thoughtworks.xstream.annotations.XStreamImplicit; | |||||
import lombok.Data; | import lombok.Data; | ||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.common.util.XmlUtils; | |||||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | ||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpConfigStorage; | ||||
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | ||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | ||||
import org.apache.commons.io.IOUtils; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.Serializable; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -36,6 +38,11 @@ import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | |||||
public class WxCpXmlMessage implements Serializable { | public class WxCpXmlMessage implements Serializable { | ||||
private static final long serialVersionUID = -1042994982179476410L; | private static final long serialVersionUID = -1042994982179476410L; | ||||
/** | |||||
* 使用dom4j解析的存放所有xml属性和值的map. | |||||
*/ | |||||
private Map<String, Object> allFieldsMap; | |||||
/////////////////////// | /////////////////////// | ||||
// 以下都是微信推送过来的消息的xml的element所对应的属性 | // 以下都是微信推送过来的消息的xml的element所对应的属性 | ||||
/////////////////////// | /////////////////////// | ||||
@@ -149,6 +156,10 @@ public class WxCpXmlMessage implements Serializable { | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | @XStreamConverter(value = XStreamCDataConverter.class) | ||||
private String recognition; | private String recognition; | ||||
@XStreamAlias("TaskId") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String taskId; | |||||
/** | /** | ||||
* 通讯录变更事件. | * 通讯录变更事件. | ||||
* 请参考常量 me.chanjar.weixin.cp.WxCpConsts.ContactChangeType | * 请参考常量 me.chanjar.weixin.cp.WxCpConsts.ContactChangeType | ||||
@@ -240,6 +251,13 @@ public class WxCpXmlMessage implements Serializable { | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | @XStreamConverter(value = XStreamCDataConverter.class) | ||||
private String telephone; | private String telephone; | ||||
/** | |||||
* 地址. | |||||
*/ | |||||
@XStreamAlias("Address") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String address; | |||||
/** | /** | ||||
* 扩展属性. | * 扩展属性. | ||||
*/ | */ | ||||
@@ -320,17 +338,20 @@ public class WxCpXmlMessage implements Serializable { | |||||
*/ | */ | ||||
@XStreamAlias("TotalCount") | @XStreamAlias("TotalCount") | ||||
private Integer totalCount; | private Integer totalCount; | ||||
/** | /** | ||||
* 过滤. | * 过滤. | ||||
* (过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数,原则上,filterCount = sentCount + errorCount | * (过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数,原则上,filterCount = sentCount + errorCount | ||||
*/ | */ | ||||
@XStreamAlias("FilterCount") | @XStreamAlias("FilterCount") | ||||
private Integer filterCount; | private Integer filterCount; | ||||
/** | /** | ||||
* 发送成功的粉丝数. | * 发送成功的粉丝数. | ||||
*/ | */ | ||||
@XStreamAlias("SentCount") | @XStreamAlias("SentCount") | ||||
private Integer sentCount; | private Integer sentCount; | ||||
/** | /** | ||||
* 发送失败的粉丝数. | * 发送失败的粉丝数. | ||||
*/ | */ | ||||
@@ -349,7 +370,9 @@ public class WxCpXmlMessage implements Serializable { | |||||
protected static WxCpXmlMessage fromXml(String xml) { | protected static WxCpXmlMessage fromXml(String xml) { | ||||
//修改微信变态的消息内容格式,方便解析 | //修改微信变态的消息内容格式,方便解析 | ||||
xml = xml.replace("</PicList><PicList>", ""); | xml = xml.replace("</PicList><PicList>", ""); | ||||
return XStreamTransformer.fromXml(WxCpXmlMessage.class, xml); | |||||
final WxCpXmlMessage xmlMessage = XStreamTransformer.fromXml(WxCpXmlMessage.class, xml); | |||||
xmlMessage.setAllFieldsMap(XmlUtils.xml2Map(xml)); | |||||
return xmlMessage; | |||||
} | } | ||||
protected static WxCpXmlMessage fromXml(InputStream is) { | protected static WxCpXmlMessage fromXml(InputStream is) { | ||||
@@ -402,9 +425,11 @@ public class WxCpXmlMessage implements Serializable { | |||||
@Data | @Data | ||||
public static class ExtAttr { | public static class ExtAttr { | ||||
@XStreamAlias("Item") | |||||
@XStreamImplicit(itemFieldName = "Item") | |||||
protected final List<Item> items = new ArrayList<>(); | protected final List<Item> items = new ArrayList<>(); | ||||
@XStreamAlias("Item") | |||||
@Data | @Data | ||||
public static class Item { | public static class Item { | ||||
@XStreamAlias("Name") | @XStreamAlias("Name") | ||||
@@ -1,9 +1,12 @@ | |||||
package me.chanjar.weixin.cp.bean.article; | package me.chanjar.weixin.cp.bean.article; | ||||
import lombok.Data; | |||||
import java.io.Serializable; | import java.io.Serializable; | ||||
import lombok.AllArgsConstructor; | |||||
import lombok.Builder; | |||||
import lombok.Data; | |||||
import lombok.NoArgsConstructor; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* Created by BinaryWang on 2017/3/27. | * Created by BinaryWang on 2017/3/27. | ||||
@@ -12,6 +15,9 @@ import java.io.Serializable; | |||||
* @author Binary Wang | * @author Binary Wang | ||||
*/ | */ | ||||
@Data | @Data | ||||
@Builder | |||||
@AllArgsConstructor | |||||
@NoArgsConstructor | |||||
public class NewArticle implements Serializable { | public class NewArticle implements Serializable { | ||||
private static final long serialVersionUID = 4087852055781140659L; | private static final long serialVersionUID = 4087852055781140659L; | ||||
@@ -0,0 +1,32 @@ | |||||
package me.chanjar.weixin.cp.bean.messagebuilder; | |||||
import me.chanjar.weixin.common.api.WxConsts; | |||||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
/** | |||||
* <pre> | |||||
* markdown类型的消息builder | |||||
* Created by Binary Wang on 2019/1/20. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
public class MarkdownMsgBuilder extends BaseBuilder<MarkdownMsgBuilder> { | |||||
private String content; | |||||
public MarkdownMsgBuilder() { | |||||
this.msgType = WxConsts.KefuMsgType.MARKDOWN; | |||||
} | |||||
public MarkdownMsgBuilder content(String content) { | |||||
this.content = content; | |||||
return this; | |||||
} | |||||
@Override | |||||
public WxCpMessage build() { | |||||
WxCpMessage m = super.build(); | |||||
m.setContent(this.content); | |||||
return m; | |||||
} | |||||
} |
@@ -0,0 +1,68 @@ | |||||
package me.chanjar.weixin.cp.bean.messagebuilder; | |||||
import me.chanjar.weixin.common.api.WxConsts; | |||||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; | |||||
import java.util.List; | |||||
/** | |||||
* <pre> | |||||
* 任务卡片消息Builder | |||||
* 用法: WxCustomMessage m = WxCustomMessage.TASKCARD().title(...)....toUser(...).build(); | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/domainname">Jeff</a> | |||||
* @date 2019-05-16 | |||||
*/ | |||||
public class TaskCardBuilder extends BaseBuilder<TaskCardBuilder> { | |||||
private String title; | |||||
private String description; | |||||
private String url; | |||||
private String taskId; | |||||
/** | |||||
* 按钮个数为1~2个 | |||||
*/ | |||||
private List<TaskCardButton> buttons; | |||||
public TaskCardBuilder() { | |||||
this.msgType = WxConsts.KefuMsgType.TASKCARD; | |||||
} | |||||
public TaskCardBuilder title(String title) { | |||||
this.title = title; | |||||
return this; | |||||
} | |||||
public TaskCardBuilder description(String description) { | |||||
this.description = description; | |||||
return this; | |||||
} | |||||
public TaskCardBuilder url(String url) { | |||||
this.url = url; | |||||
return this; | |||||
} | |||||
public TaskCardBuilder taskId(String taskId) { | |||||
this.taskId = taskId; | |||||
return this; | |||||
} | |||||
public TaskCardBuilder buttons(List<TaskCardButton> buttons) { | |||||
this.buttons = buttons; | |||||
return this; | |||||
} | |||||
@Override | |||||
public WxCpMessage build() { | |||||
WxCpMessage m = super.build(); | |||||
m.setSafe(null); | |||||
m.setTitle(this.title); | |||||
m.setDescription(this.description); | |||||
m.setUrl(this.url); | |||||
m.setTaskId(this.taskId); | |||||
m.setTaskButtons(this.buttons); | |||||
return m; | |||||
} | |||||
} |
@@ -0,0 +1,23 @@ | |||||
package me.chanjar.weixin.cp.bean.taskcard; | |||||
import lombok.Builder; | |||||
import lombok.Data; | |||||
/** | |||||
* <pre> | |||||
* 任务卡片按钮 | |||||
* Created by Jeff on 2019-05-16. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/domainname">Jeff</a> | |||||
* @date 2019-05-16 | |||||
*/ | |||||
@Data | |||||
@Builder | |||||
public class TaskCardButton { | |||||
private String key; | |||||
private String name; | |||||
private String replaceName; | |||||
private String color; | |||||
private Boolean bold; | |||||
} |
@@ -36,11 +36,23 @@ public interface WxCpConfigStorage { | |||||
/** | /** | ||||
* 应该是线程安全的 | * 应该是线程安全的 | ||||
* | |||||
* @param jsapiTicket | |||||
*/ | */ | ||||
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | ||||
String getAgentJsapiTicket(); | |||||
boolean isAgentJsapiTicketExpired(); | |||||
/** | |||||
* 强制将jsapi ticket过期掉 | |||||
*/ | |||||
void expireAgentJsapiTicket(); | |||||
/** | |||||
* 应该是线程安全的 | |||||
*/ | |||||
void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds); | |||||
String getCorpId(); | String getCorpId(); | ||||
String getCorpSecret(); | String getCorpSecret(); | ||||
@@ -1,11 +1,11 @@ | |||||
package me.chanjar.weixin.cp.config; | package me.chanjar.weixin.cp.config; | ||||
import java.io.File; | |||||
import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | ||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import java.io.File; | |||||
/** | /** | ||||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | ||||
* | * | ||||
@@ -32,6 +32,9 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
protected volatile String jsapiTicket; | protected volatile String jsapiTicket; | ||||
protected volatile long jsapiTicketExpiresTime; | protected volatile long jsapiTicketExpiresTime; | ||||
protected volatile String agentJsapiTicket; | |||||
protected volatile long agentJsapiTicketExpiresTime; | |||||
protected volatile File tmpDirFile; | protected volatile File tmpDirFile; | ||||
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | ||||
@@ -95,6 +98,28 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | ||||
} | } | ||||
@Override | |||||
public String getAgentJsapiTicket() { | |||||
return this.agentJsapiTicket; | |||||
} | |||||
@Override | |||||
public boolean isAgentJsapiTicketExpired() { | |||||
return System.currentTimeMillis() > this.agentJsapiTicketExpiresTime; | |||||
} | |||||
@Override | |||||
public void expireAgentJsapiTicket() { | |||||
this.agentJsapiTicketExpiresTime = 0; | |||||
} | |||||
@Override | |||||
public void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds) { | |||||
this.agentJsapiTicket = jsapiTicket; | |||||
// 预留200秒的时间 | |||||
this.agentJsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||||
} | |||||
@Override | @Override | ||||
public void expireJsapiTicket() { | public void expireJsapiTicket() { | ||||
this.jsapiTicketExpiresTime = 0; | this.jsapiTicketExpiresTime = 0; | ||||
@@ -9,26 +9,21 @@ import redis.clients.jedis.JedisPoolConfig; | |||||
import java.io.File; | import java.io.File; | ||||
/** | /** | ||||
* Jedis client implementor for wechat config storage. | |||||
* <pre> | * <pre> | ||||
* 使用说明:本实现仅供参考,并不完整, | |||||
* 使用说明:本实现仅供参考,并不完整. | |||||
* 比如为减少项目依赖,未加入redis分布式锁的实现,如有需要请自行实现。 | * 比如为减少项目依赖,未加入redis分布式锁的实现,如有需要请自行实现。 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @author gaigeshen | * @author gaigeshen | ||||
*/ | */ | ||||
public class WxCpJedisConfigStorage implements WxCpConfigStorage { | public class WxCpJedisConfigStorage implements WxCpConfigStorage { | ||||
/** | |||||
* Redis keys here | |||||
*/ | |||||
private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN"; | private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN"; | ||||
private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME"; | private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME"; | ||||
private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET"; | private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET"; | ||||
private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME"; | private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME"; | ||||
/** | |||||
* Redis clients pool | |||||
*/ | |||||
private static final String AGENT_JSAPI_TICKET_KEY = "WX_CP_AGENT_%s_JSAPI_TICKET"; | |||||
private static final String AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY = "WX_CP_AGENT_%s_JSAPI_TICKET_EXPIRES_TIME"; | |||||
private final JedisPool jedisPool; | private final JedisPool jedisPool; | ||||
private volatile String corpId; | private volatile String corpId; | ||||
private volatile String corpSecret; | private volatile String corpSecret; | ||||
@@ -46,7 +41,7 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||||
public WxCpJedisConfigStorage(JedisPool jedisPool) { | public WxCpJedisConfigStorage(JedisPool jedisPool) { | ||||
this.jedisPool = jedisPool; | this.jedisPool = jedisPool; | ||||
} | } | ||||
public WxCpJedisConfigStorage(String host, int port) { | public WxCpJedisConfigStorage(String host, int port) { | ||||
jedisPool = new JedisPool(host, port); | jedisPool = new JedisPool(host, port); | ||||
} | } | ||||
@@ -83,8 +78,7 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||||
String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY); | String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY); | ||||
if (expiresTimeStr != null) { | if (expiresTimeStr != null) { | ||||
Long expiresTime = Long.parseLong(expiresTimeStr); | |||||
return System.currentTimeMillis() > expiresTime; | |||||
return System.currentTimeMillis() > Long.parseLong(expiresTimeStr); | |||||
} | } | ||||
return true; | return true; | ||||
@@ -123,17 +117,15 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||||
@Override | @Override | ||||
public boolean isJsapiTicketExpired() { | public boolean isJsapiTicketExpired() { | ||||
try (Jedis jedis = this.jedisPool.getResource()) { | try (Jedis jedis = this.jedisPool.getResource()) { | ||||
String expiresTimeStr = jedis.get(JS_API_TICKET_EXPIRES_TIME_KEY); | String expiresTimeStr = jedis.get(JS_API_TICKET_EXPIRES_TIME_KEY); | ||||
if (expiresTimeStr != null) { | if (expiresTimeStr != null) { | ||||
Long expiresTime = Long.parseLong(expiresTimeStr); | |||||
long expiresTime = Long.parseLong(expiresTimeStr); | |||||
return System.currentTimeMillis() > expiresTime; | return System.currentTimeMillis() > expiresTime; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
@@ -146,16 +138,51 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||||
@Override | @Override | ||||
public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | ||||
try (Jedis jedis = this.jedisPool.getResource()) { | try (Jedis jedis = this.jedisPool.getResource()) { | ||||
jedis.set(JS_API_TICKET_KEY, jsapiTicket); | jedis.set(JS_API_TICKET_KEY, jsapiTicket); | ||||
jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, | jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, | ||||
(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + "")); | (System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + "")); | ||||
} | } | ||||
} | } | ||||
@Override | |||||
public String getAgentJsapiTicket() { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
return jedis.get(String.format(AGENT_JSAPI_TICKET_KEY, agentId)); | |||||
} | |||||
} | |||||
@Override | |||||
public boolean isAgentJsapiTicketExpired() { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
String expiresTimeStr = jedis.get(String.format(AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY, agentId)); | |||||
if (expiresTimeStr != null) { | |||||
return System.currentTimeMillis() > Long.parseLong(expiresTimeStr); | |||||
} | |||||
return true; | |||||
} | |||||
} | |||||
@Override | |||||
public void expireAgentJsapiTicket() { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
jedis.set(String.format(AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY, agentId), "0"); | |||||
} | |||||
} | |||||
@Override | |||||
public void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds) { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
jedis.set(String.format(AGENT_JSAPI_TICKET_KEY, agentId), jsapiTicket); | |||||
jedis.set(String.format(AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY, agentId), | |||||
(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + "")); | |||||
} | |||||
} | |||||
@Override | @Override | ||||
public String getCorpId() { | public String getCorpId() { | ||||
return this.corpId; | return this.corpId; | ||||
@@ -73,7 +73,7 @@ public class WxCpMessageRouter { | |||||
this.wxCpService = wxCpService; | this.wxCpService = wxCpService; | ||||
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE); | this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE); | ||||
this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker(); | this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker(); | ||||
this.sessionManager = new StandardSessionManager(); | |||||
this.sessionManager = wxCpService.getSessionManager(); | |||||
this.exceptionHandler = new LogExceptionHandler(); | this.exceptionHandler = new LogExceptionHandler(); | ||||
} | } | ||||
@@ -7,7 +7,6 @@ import me.chanjar.weixin.common.error.WxError; | |||||
import me.chanjar.weixin.common.util.json.WxErrorAdapter; | import me.chanjar.weixin.common.util.json.WxErrorAdapter; | ||||
import me.chanjar.weixin.cp.bean.WxCpChat; | import me.chanjar.weixin.cp.bean.WxCpChat; | ||||
import me.chanjar.weixin.cp.bean.WxCpDepart; | import me.chanjar.weixin.cp.bean.WxCpDepart; | ||||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
import me.chanjar.weixin.cp.bean.WxCpTag; | import me.chanjar.weixin.cp.bean.WxCpTag; | ||||
import me.chanjar.weixin.cp.bean.WxCpUser; | import me.chanjar.weixin.cp.bean.WxCpUser; | ||||
@@ -20,7 +19,6 @@ public class WxCpGsonBuilder { | |||||
static { | static { | ||||
INSTANCE.disableHtmlEscaping(); | INSTANCE.disableHtmlEscaping(); | ||||
INSTANCE.registerTypeAdapter(WxCpMessage.class, new WxCpMessageGsonAdapter()); | |||||
INSTANCE.registerTypeAdapter(WxCpChat.class, new WxCpChatGsonAdapter()); | INSTANCE.registerTypeAdapter(WxCpChat.class, new WxCpChatGsonAdapter()); | ||||
INSTANCE.registerTypeAdapter(WxCpDepart.class, new WxCpDepartGsonAdapter()); | INSTANCE.registerTypeAdapter(WxCpDepart.class, new WxCpDepartGsonAdapter()); | ||||
INSTANCE.registerTypeAdapter(WxCpUser.class, new WxCpUserGsonAdapter()); | INSTANCE.registerTypeAdapter(WxCpUser.class, new WxCpUserGsonAdapter()); | ||||
@@ -1,127 +0,0 @@ | |||||
/* | |||||
* KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. | |||||
* | |||||
* This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended | |||||
* only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction | |||||
* arose from modification of the original source, or other redistribution of this source | |||||
* is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | |||||
*/ | |||||
package me.chanjar.weixin.cp.util.json; | |||||
import com.google.gson.*; | |||||
import me.chanjar.weixin.common.api.WxConsts; | |||||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | |||||
import me.chanjar.weixin.cp.bean.article.NewArticle; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import java.lang.reflect.Type; | |||||
/** | |||||
* @author Daniel Qian | |||||
*/ | |||||
public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> { | |||||
@Override | |||||
public JsonElement serialize(WxCpMessage message, Type typeOfSrc, JsonSerializationContext context) { | |||||
JsonObject messageJson = new JsonObject(); | |||||
messageJson.addProperty("agentid", message.getAgentId()); | |||||
if (StringUtils.isNotBlank(message.getToUser())) { | |||||
messageJson.addProperty("touser", message.getToUser()); | |||||
} | |||||
messageJson.addProperty("msgtype", message.getMsgType()); | |||||
if (StringUtils.isNotBlank(message.getToParty())) { | |||||
messageJson.addProperty("toparty", message.getToParty()); | |||||
} | |||||
if (StringUtils.isNotBlank(message.getToTag())) { | |||||
messageJson.addProperty("totag", message.getToTag()); | |||||
} | |||||
if (WxConsts.KefuMsgType.TEXT.equals(message.getMsgType())) { | |||||
JsonObject text = new JsonObject(); | |||||
text.addProperty("content", message.getContent()); | |||||
messageJson.add("text", text); | |||||
} | |||||
if (WxConsts.KefuMsgType.TEXTCARD.equals(message.getMsgType())) { | |||||
JsonObject text = new JsonObject(); | |||||
text.addProperty("title", message.getTitle()); | |||||
text.addProperty("description", message.getDescription()); | |||||
text.addProperty("url", message.getUrl()); | |||||
text.addProperty("btntxt", message.getBtnTxt()); | |||||
messageJson.add("textcard", text); | |||||
} | |||||
if (WxConsts.KefuMsgType.IMAGE.equals(message.getMsgType())) { | |||||
JsonObject image = new JsonObject(); | |||||
image.addProperty("media_id", message.getMediaId()); | |||||
messageJson.add("image", image); | |||||
} | |||||
if (WxConsts.KefuMsgType.FILE.equals(message.getMsgType())) { | |||||
JsonObject image = new JsonObject(); | |||||
image.addProperty("media_id", message.getMediaId()); | |||||
messageJson.add("file", image); | |||||
} | |||||
if (WxConsts.KefuMsgType.VOICE.equals(message.getMsgType())) { | |||||
JsonObject voice = new JsonObject(); | |||||
voice.addProperty("media_id", message.getMediaId()); | |||||
messageJson.add("voice", voice); | |||||
} | |||||
if (StringUtils.isNotBlank(message.getSafe())) { | |||||
messageJson.addProperty("safe", message.getSafe()); | |||||
} | |||||
if (WxConsts.KefuMsgType.VIDEO.equals(message.getMsgType())) { | |||||
JsonObject video = new JsonObject(); | |||||
video.addProperty("media_id", message.getMediaId()); | |||||
video.addProperty("thumb_media_id", message.getThumbMediaId()); | |||||
video.addProperty("title", message.getTitle()); | |||||
video.addProperty("description", message.getDescription()); | |||||
messageJson.add("video", video); | |||||
} | |||||
if (WxConsts.KefuMsgType.NEWS.equals(message.getMsgType())) { | |||||
JsonObject newsJsonObject = new JsonObject(); | |||||
JsonArray articleJsonArray = new JsonArray(); | |||||
for (NewArticle article : message.getArticles()) { | |||||
JsonObject articleJson = new JsonObject(); | |||||
articleJson.addProperty("title", article.getTitle()); | |||||
articleJson.addProperty("description", article.getDescription()); | |||||
articleJson.addProperty("url", article.getUrl()); | |||||
articleJson.addProperty("picurl", article.getPicUrl()); | |||||
articleJsonArray.add(articleJson); | |||||
} | |||||
newsJsonObject.add("articles", articleJsonArray); | |||||
messageJson.add("news", newsJsonObject); | |||||
} | |||||
if (WxConsts.KefuMsgType.MPNEWS.equals(message.getMsgType())) { | |||||
JsonObject newsJsonObject = new JsonObject(); | |||||
if (message.getMediaId() != null) { | |||||
newsJsonObject.addProperty("media_id", message.getMediaId()); | |||||
} else { | |||||
JsonArray articleJsonArray = new JsonArray(); | |||||
for (MpnewsArticle article : message.getMpnewsArticles()) { | |||||
JsonObject articleJson = new JsonObject(); | |||||
articleJson.addProperty("title", article.getTitle()); | |||||
articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); | |||||
articleJson.addProperty("author", article.getAuthor()); | |||||
articleJson.addProperty("content_source_url", article.getContentSourceUrl()); | |||||
articleJson.addProperty("content", article.getContent()); | |||||
articleJson.addProperty("digest", article.getDigest()); | |||||
articleJson.addProperty("show_cover_pic", article.getShowCoverPic()); | |||||
articleJsonArray.add(articleJson); | |||||
} | |||||
newsJsonObject.add("articles", articleJsonArray); | |||||
} | |||||
messageJson.add("mpnews", newsJsonObject); | |||||
} | |||||
return messageJson; | |||||
} | |||||
} |
@@ -6,6 +6,7 @@ | |||||
* arose from modification of the original source, or other redistribution of this source | * arose from modification of the original source, or other redistribution of this source | ||||
* is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | ||||
*/ | */ | ||||
package me.chanjar.weixin.cp.util.json; | package me.chanjar.weixin.cp.util.json; | ||||
import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||
@@ -24,11 +25,14 @@ import me.chanjar.weixin.cp.bean.Gender; | |||||
import me.chanjar.weixin.cp.bean.WxCpUser; | import me.chanjar.weixin.cp.bean.WxCpUser; | ||||
/** | /** | ||||
* cp user gson adapter. | |||||
* | |||||
* @author Daniel Qian | * @author Daniel Qian | ||||
*/ | */ | ||||
public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSerializer<WxCpUser> { | public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSerializer<WxCpUser> { | ||||
private static final String EXTERNAL_PROFILE = "external_profile"; | private static final String EXTERNAL_PROFILE = "external_profile"; | ||||
private static final String EXTERNAL_ATTR = "external_attr"; | private static final String EXTERNAL_ATTR = "external_attr"; | ||||
private static final String EXTATTR = "extattr"; | |||||
@Override | @Override | ||||
public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { | public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { | ||||
@@ -37,14 +41,24 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri | |||||
if (o.get("department") != null) { | if (o.get("department") != null) { | ||||
JsonArray departJsonArray = o.get("department").getAsJsonArray(); | JsonArray departJsonArray = o.get("department").getAsJsonArray(); | ||||
Integer[] departIds = new Integer[departJsonArray.size()]; | |||||
Long[] departIds = new Long[departJsonArray.size()]; | |||||
int i = 0; | int i = 0; | ||||
for (JsonElement jsonElement : departJsonArray) { | for (JsonElement jsonElement : departJsonArray) { | ||||
departIds[i++] = jsonElement.getAsInt(); | |||||
departIds[i++] = jsonElement.getAsLong(); | |||||
} | } | ||||
user.setDepartIds(departIds); | user.setDepartIds(departIds); | ||||
} | } | ||||
if (o.get("order") != null) { | |||||
JsonArray departJsonArray = o.get("order").getAsJsonArray(); | |||||
Integer[] orders = new Integer[departJsonArray.size()]; | |||||
int i = 0; | |||||
for (JsonElement jsonElement : departJsonArray) { | |||||
orders[i++] = jsonElement.getAsInt(); | |||||
} | |||||
user.setOrders(orders); | |||||
} | |||||
user.setUserId(GsonHelper.getString(o, "userid")); | user.setUserId(GsonHelper.getString(o, "userid")); | ||||
user.setName(GsonHelper.getString(o, "name")); | user.setName(GsonHelper.getString(o, "name")); | ||||
user.setPosition(GsonHelper.getString(o, "position")); | user.setPosition(GsonHelper.getString(o, "position")); | ||||
@@ -62,64 +76,73 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri | |||||
user.setQrCode(GsonHelper.getString(o, "qr_code")); | user.setQrCode(GsonHelper.getString(o, "qr_code")); | ||||
user.setToInvite(GsonHelper.getBoolean(o, "to_invite")); | user.setToInvite(GsonHelper.getBoolean(o, "to_invite")); | ||||
if (GsonHelper.isNotNull(o.get("extattr"))) { | |||||
JsonArray attrJsonElements = o.get("extattr").getAsJsonObject().get("attrs").getAsJsonArray(); | |||||
for (JsonElement attrJsonElement : attrJsonElements) { | |||||
WxCpUser.Attr attr = new WxCpUser.Attr( | |||||
GsonHelper.getString(attrJsonElement.getAsJsonObject(), "name"), | |||||
GsonHelper.getString(attrJsonElement.getAsJsonObject(), "value") | |||||
); | |||||
user.getExtAttrs().add(attr); | |||||
} | |||||
if (GsonHelper.isNotNull(o.get(EXTATTR))) { | |||||
this.buildExtraAttrs(o, user); | |||||
} | } | ||||
if (GsonHelper.isNotNull(o.get(EXTERNAL_PROFILE))) { | if (GsonHelper.isNotNull(o.get(EXTERNAL_PROFILE))) { | ||||
JsonArray attrJsonElements = o.get(EXTERNAL_PROFILE).getAsJsonObject().get(EXTERNAL_ATTR).getAsJsonArray(); | |||||
for (JsonElement element : attrJsonElements) { | |||||
final Integer type = GsonHelper.getInteger(element.getAsJsonObject(), "type"); | |||||
final String name = GsonHelper.getString(element.getAsJsonObject(), "name"); | |||||
this.buildExternalAttrs(o, user); | |||||
} | |||||
switch (type) { | |||||
case 0: { | |||||
user.getExternalAttrs() | |||||
.add(WxCpUser.ExternalAttribute.builder() | |||||
.type(type) | |||||
.name(name) | |||||
.value(GsonHelper.getString(element.getAsJsonObject().get("text").getAsJsonObject(), "value")) | |||||
.build() | |||||
); | |||||
break; | |||||
} | |||||
case 1: { | |||||
final JsonObject web = element.getAsJsonObject().get("web").getAsJsonObject(); | |||||
user.getExternalAttrs() | |||||
.add(WxCpUser.ExternalAttribute.builder() | |||||
.type(type) | |||||
.name(name) | |||||
.url(GsonHelper.getString(web, "url")) | |||||
.title(GsonHelper.getString(web, "title")) | |||||
.build() | |||||
); | |||||
break; | |||||
} | |||||
case 2: { | |||||
final JsonObject miniprogram = element.getAsJsonObject().get("miniprogram").getAsJsonObject(); | |||||
user.getExternalAttrs() | |||||
.add(WxCpUser.ExternalAttribute.builder() | |||||
.type(type) | |||||
.name(name) | |||||
.appid(GsonHelper.getString(miniprogram, "appid")) | |||||
.pagePath(GsonHelper.getString(miniprogram, "pagepath")) | |||||
.title(GsonHelper.getString(miniprogram, "title")) | |||||
.build() | |||||
); | |||||
break; | |||||
} | |||||
default://ignored | |||||
return user; | |||||
} | |||||
private void buildExtraAttrs(JsonObject o, WxCpUser user) { | |||||
JsonArray attrJsonElements = o.get(EXTATTR).getAsJsonObject().get("attrs").getAsJsonArray(); | |||||
for (JsonElement attrJsonElement : attrJsonElements) { | |||||
WxCpUser.Attr attr = new WxCpUser.Attr( | |||||
GsonHelper.getString(attrJsonElement.getAsJsonObject(), "name"), | |||||
GsonHelper.getString(attrJsonElement.getAsJsonObject(), "value") | |||||
); | |||||
user.getExtAttrs().add(attr); | |||||
} | |||||
} | |||||
private void buildExternalAttrs(JsonObject o, WxCpUser user) { | |||||
JsonArray attrJsonElements = o.get(EXTERNAL_PROFILE).getAsJsonObject().get(EXTERNAL_ATTR).getAsJsonArray(); | |||||
for (JsonElement element : attrJsonElements) { | |||||
final Integer type = GsonHelper.getInteger(element.getAsJsonObject(), "type"); | |||||
final String name = GsonHelper.getString(element.getAsJsonObject(), "name"); | |||||
switch (type) { | |||||
case 0: { | |||||
user.getExternalAttrs() | |||||
.add(WxCpUser.ExternalAttribute.builder() | |||||
.type(type) | |||||
.name(name) | |||||
.value(GsonHelper.getString(element.getAsJsonObject().get("text").getAsJsonObject(), "value")) | |||||
.build() | |||||
); | |||||
break; | |||||
} | } | ||||
case 1: { | |||||
final JsonObject web = element.getAsJsonObject().get("web").getAsJsonObject(); | |||||
user.getExternalAttrs() | |||||
.add(WxCpUser.ExternalAttribute.builder() | |||||
.type(type) | |||||
.name(name) | |||||
.url(GsonHelper.getString(web, "url")) | |||||
.title(GsonHelper.getString(web, "title")) | |||||
.build() | |||||
); | |||||
break; | |||||
} | |||||
case 2: { | |||||
final JsonObject miniprogram = element.getAsJsonObject().get("miniprogram").getAsJsonObject(); | |||||
user.getExternalAttrs() | |||||
.add(WxCpUser.ExternalAttribute.builder() | |||||
.type(type) | |||||
.name(name) | |||||
.appid(GsonHelper.getString(miniprogram, "appid")) | |||||
.pagePath(GsonHelper.getString(miniprogram, "pagepath")) | |||||
.title(GsonHelper.getString(miniprogram, "title")) | |||||
.build() | |||||
); | |||||
break; | |||||
} | |||||
default://ignored | |||||
} | } | ||||
} | } | ||||
return user; | |||||
} | } | ||||
@Override | @Override | ||||
@@ -133,11 +156,20 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri | |||||
} | } | ||||
if (user.getDepartIds() != null) { | if (user.getDepartIds() != null) { | ||||
JsonArray jsonArray = new JsonArray(); | JsonArray jsonArray = new JsonArray(); | ||||
for (Integer departId : user.getDepartIds()) { | |||||
for (Long departId : user.getDepartIds()) { | |||||
jsonArray.add(new JsonPrimitive(departId)); | jsonArray.add(new JsonPrimitive(departId)); | ||||
} | } | ||||
o.add("department", jsonArray); | o.add("department", jsonArray); | ||||
} | } | ||||
if (user.getOrders() != null) { | |||||
JsonArray jsonArray = new JsonArray(); | |||||
for (Integer order : user.getOrders()) { | |||||
jsonArray.add(new JsonPrimitive(order)); | |||||
} | |||||
o.add("order", jsonArray); | |||||
} | |||||
if (user.getPosition() != null) { | if (user.getPosition() != null) { | ||||
o.addProperty("position", user.getPosition()); | o.addProperty("position", user.getPosition()); | ||||
} | } | ||||
@@ -191,14 +223,14 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri | |||||
} | } | ||||
JsonObject attrsJson = new JsonObject(); | JsonObject attrsJson = new JsonObject(); | ||||
attrsJson.add("attrs", attrsJsonArray); | attrsJson.add("attrs", attrsJsonArray); | ||||
o.add("extattr", attrsJson); | |||||
o.add(EXTATTR, attrsJson); | |||||
} | } | ||||
if (user.getExternalAttrs().size() > 0) { | if (user.getExternalAttrs().size() > 0) { | ||||
JsonArray attrsJsonArray = new JsonArray(); | JsonArray attrsJsonArray = new JsonArray(); | ||||
for (WxCpUser.ExternalAttribute attr : user.getExternalAttrs()) { | for (WxCpUser.ExternalAttribute attr : user.getExternalAttrs()) { | ||||
JsonObject attrJson = new JsonObject(); | JsonObject attrJson = new JsonObject(); | ||||
attrJson.addProperty("type",attr.getType()); | |||||
attrJson.addProperty("type", attr.getType()); | |||||
attrJson.addProperty("name", attr.getName()); | attrJson.addProperty("name", attr.getName()); | ||||
switch (attr.getType()) { | switch (attr.getType()) { | ||||
case 0: { | case 0: { | ||||
@@ -1,11 +1,12 @@ | |||||
package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
import org.testng.annotations.*; | |||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.bean.WxCpMessage; | import me.chanjar.weixin.cp.bean.WxCpMessage; | ||||
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | ||||
import org.testng.annotations.*; | |||||
import static org.testng.Assert.*; | import static org.testng.Assert.*; | ||||
@@ -14,7 +15,7 @@ import static org.testng.Assert.*; | |||||
* @author Daniel Qian | * @author Daniel Qian | ||||
* | * | ||||
*/ | */ | ||||
@Test(groups = "customMessageAPI") | |||||
@Test | |||||
@Guice(modules = ApiTestModule.class) | @Guice(modules = ApiTestModule.class) | ||||
public class WxCpMessageAPITest { | public class WxCpMessageAPITest { | ||||
@@ -59,4 +60,51 @@ public class WxCpMessageAPITest { | |||||
System.out.println(messageSendResult.getInvalidUserList()); | System.out.println(messageSendResult.getInvalidUserList()); | ||||
System.out.println(messageSendResult.getInvalidTagList()); | System.out.println(messageSendResult.getInvalidTagList()); | ||||
} | } | ||||
@Test | |||||
public void testSendMessage_markdown() throws WxErrorException { | |||||
WxCpMessage message = WxCpMessage | |||||
.MARKDOWN() | |||||
.toUser(configStorage.getUserId()) | |||||
.content("您的会议室已经预定,稍后会同步到`邮箱` \n" + | |||||
" >**事项详情** \n" + | |||||
" >事 项:<font color=\\\"info\\\">开会</font> \n" + | |||||
" >组织者:@miglioguan \n" + | |||||
" >参与者:@miglioguan、@kunliu、@jamdeezhou、@kanexiong、@kisonwang \n" + | |||||
" > \n" + | |||||
" >会议室:<font color=\\\"info\\\">广州TIT 1楼 301</font> \n" + | |||||
" >日 期:<font color=\\\"warning\\\">2018年5月18日</font> \n" + | |||||
" >时 间:<font color=\\\"comment\\\">上午9:00-11:00</font> \n" + | |||||
" > \n" + | |||||
" >请准时参加会议。 \n" + | |||||
" > \n" + | |||||
" >如需修改会议信息,请点击:[修改会议信息](https://work.weixin.qq.com)") | |||||
.build(); | |||||
WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message); | |||||
assertNotNull(messageSendResult); | |||||
System.out.println(messageSendResult); | |||||
System.out.println(messageSendResult.getInvalidPartyList()); | |||||
System.out.println(messageSendResult.getInvalidUserList()); | |||||
System.out.println(messageSendResult.getInvalidTagList()); | |||||
} | |||||
@Test | |||||
public void testSendMessage_textCard() throws WxErrorException { | |||||
WxCpMessage message = WxCpMessage | |||||
.TEXTCARD() | |||||
.toUser(configStorage.getUserId()) | |||||
.btnTxt("更多") | |||||
.description( "<div class=\"gray\">2016年9月26日</div> <div class=\"normal\">恭喜你抽中iPhone 7一台,领奖码:xxxx</div><div class=\"highlight\">请于2016年10月10日前联系行政同事领取</div>") | |||||
.url("URL") | |||||
.title("领奖通知") | |||||
.build(); | |||||
WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message); | |||||
assertNotNull(messageSendResult); | |||||
System.out.println(messageSendResult); | |||||
System.out.println(messageSendResult.getInvalidPartyList()); | |||||
System.out.println(messageSendResult.getInvalidUserList()); | |||||
System.out.println(messageSendResult.getInvalidTagList()); | |||||
} | |||||
} | } |
@@ -0,0 +1,36 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | |||||
import com.google.inject.Inject; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.cp.api.ApiTestModule; | |||||
import me.chanjar.weixin.cp.api.WxCpService; | |||||
import org.testng.annotations.Guice; | |||||
import org.testng.annotations.Test; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.testng.Assert.*; | |||||
/** | |||||
* <pre> | |||||
* Created by BinaryWang on 2019/3/31. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
@Test | |||||
@Guice(modules = ApiTestModule.class) | |||||
public class BaseWxCpServiceImplTest { | |||||
@Inject | |||||
protected WxCpService wxService; | |||||
@Test | |||||
public void testGetAgentJsapiTicket() throws WxErrorException { | |||||
assertThat(this.wxService.getAgentJsapiTicket()).isNotEmpty(); | |||||
assertThat(this.wxService.getAgentJsapiTicket(true)).isNotEmpty(); | |||||
} | |||||
@Test | |||||
public void testJsCode2Session() throws WxErrorException { | |||||
assertThat(this.wxService.jsCode2Session("111")).isNotNull(); | |||||
} | |||||
} |
@@ -2,15 +2,21 @@ package me.chanjar.weixin.cp.api.impl; | |||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import me.chanjar.weixin.cp.bean.WxCpChat; | |||||
import org.testng.Assert; | |||||
import org.testng.annotations.Guice; | |||||
import org.testng.annotations.Test; | |||||
import org.testng.*; | |||||
import org.testng.annotations.*; | |||||
import com.google.common.collect.Lists; | |||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.cp.WxCpConsts.AppChatMsgType; | |||||
import me.chanjar.weixin.cp.api.ApiTestModule; | import me.chanjar.weixin.cp.api.ApiTestModule; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; | |||||
import me.chanjar.weixin.cp.bean.WxCpChat; | |||||
import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | |||||
import me.chanjar.weixin.cp.bean.article.NewArticle; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
/** | /** | ||||
* 测试群聊服务 | * 测试群聊服务 | ||||
@@ -19,28 +25,134 @@ import me.chanjar.weixin.cp.api.WxCpService; | |||||
*/ | */ | ||||
@Guice(modules = ApiTestModule.class) | @Guice(modules = ApiTestModule.class) | ||||
public class WxCpChatServiceImplTest { | public class WxCpChatServiceImplTest { | ||||
private String chatId; | |||||
private String userId; | |||||
@Inject | @Inject | ||||
private WxCpService wxCpService; | |||||
private WxCpService cpService; | |||||
@BeforeTest | |||||
public void init() { | |||||
this.chatId = "mychatid"; | |||||
this.userId = ((ApiTestModule.WxXmlCpInMemoryConfigStorage) this.cpService.getWxCpConfigStorage()).getUserId(); | |||||
} | |||||
@Test | @Test | ||||
public void create() throws Exception { | |||||
wxCpService.getChatService().chatCreate("测试群聊", "gaige_shen", Arrays.asList("gaige_shen", "ZhangXiaoMing"), "mychatid"); | |||||
public void testChatCreate() throws Exception { | |||||
final String result = cpService.getChatService().chatCreate("测试群聊", userId, | |||||
Arrays.asList(userId, userId), chatId); | |||||
assertThat(result).isNotEmpty(); | |||||
assertThat(result).isEqualTo(chatId); | |||||
} | } | ||||
@Test | @Test | ||||
public void get() throws Exception { | |||||
WxCpChat chat = wxCpService.getChatService().chatGet("mychatid"); | |||||
public void testChatGet() throws Exception { | |||||
WxCpChat chat = this.cpService.getChatService().chatGet(chatId); | |||||
System.out.println(chat); | System.out.println(chat); | ||||
Assert.assertEquals(chat.getName(), "测试群聊"); | Assert.assertEquals(chat.getName(), "测试群聊"); | ||||
} | } | ||||
@Test | @Test | ||||
public void update() throws Exception { | |||||
wxCpService.getChatService().chatUpdate("mychatid", "", "", Arrays.asList("ZhengWuYao"), null); | |||||
WxCpChat chat = wxCpService.getChatService().chatGet("mychatid"); | |||||
public void testChatUpdate() throws Exception { | |||||
this.cpService.getChatService().chatUpdate(chatId, "", "", Arrays.asList("ZhengWuYao"), null); | |||||
WxCpChat chat = this.cpService.getChatService().chatGet(chatId); | |||||
System.out.println(chat); | System.out.println(chat); | ||||
Assert.assertEquals(chat.getUsers().size(), 3); | Assert.assertEquals(chat.getUsers().size(), 3); | ||||
} | } | ||||
@DataProvider | |||||
public Object[][] messages() { | |||||
return new Object[][]{ | |||||
{WxCpAppChatMessage.builder() | |||||
.msgType(AppChatMsgType.TEXT) | |||||
.chatId(chatId) | |||||
.content("你的快递已到\n请携带工卡前往邮件中心领取") | |||||
.build() | |||||
}, | |||||
{WxCpAppChatMessage.builder() | |||||
.msgType(AppChatMsgType.IMAGE) | |||||
.chatId(chatId) | |||||
.mediaId("3_xWGPXZhpOKZrlRISWrjhPrDUZqZ-jIEVzxd56jLuqM") | |||||
.build() | |||||
}, | |||||
{WxCpAppChatMessage.builder() | |||||
.msgType(AppChatMsgType.VOICE) | |||||
.chatId(chatId) | |||||
.mediaId("3X5t6HkdN1hUgB7OzrdRnc8v0yI0CqlAxFxnCkS3msTnTLanpYrV4esLv4foZVnlf") | |||||
.build() | |||||
}, | |||||
{WxCpAppChatMessage.builder() | |||||
.msgType(AppChatMsgType.VIDEO) | |||||
.chatId(chatId) | |||||
.mediaId("3otWyy_acbID8fyltmCOW5hGVD8oa0_p0za5jhukxKTUDoGT71lqTvtQAWoycXpQf") | |||||
.title("aaaa") | |||||
.description("ddddd") | |||||
.build() | |||||
}, | |||||
{WxCpAppChatMessage.builder() | |||||
.msgType(AppChatMsgType.FILE) | |||||
.chatId(chatId) | |||||
.mediaId("34AyVyDdndVhB4Z2tT-_FYKZ7Xqrr47LPC11GHH4oy7o") | |||||
.build() | |||||
}, | |||||
{WxCpAppChatMessage.builder() | |||||
.msgType(AppChatMsgType.TEXTCARD) | |||||
.chatId(chatId) | |||||
.btnTxt("更多") | |||||
.title("领奖通知") | |||||
.url("https://zhidao.baidu.com/question/2073647112026042748.html") | |||||
.description("<div class=\"gray\">2016年9月26日</div> <div class=\"normal\"> 恭喜你抽中iPhone 7一台,领奖码:520258</div><div class=\"highlight\">请于2016年10月10日前联系行 政同事领取</div>") | |||||
.build() | |||||
}, | |||||
{WxCpAppChatMessage.builder() | |||||
.msgType(AppChatMsgType.NEWS) | |||||
.chatId(chatId) | |||||
.articles(Lists.newArrayList(NewArticle.builder() | |||||
.title("领奖通知") | |||||
.url("https://zhidao.baidu.com/question/2073647112026042748.html") | |||||
.description("今年中秋节公司有豪礼相送") | |||||
.picUrl("http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png") | |||||
.build() | |||||
)) | |||||
.build() | |||||
}, | |||||
{WxCpAppChatMessage.builder() | |||||
.msgType(AppChatMsgType.MPNEWS) | |||||
.chatId(chatId) | |||||
.mpnewsArticles(Lists.newArrayList(MpnewsArticle.newBuilder() | |||||
.title("地球一小时") | |||||
.thumbMediaId("3_xWGPXZhpOKZrlRISWrjhPrDUZqZ-jIEVzxd56jLuqM") | |||||
.author("Author") | |||||
.contentSourceUrl("https://work.weixin.qq.com") | |||||
.content("3月24日20:30-21:30 \n办公区将关闭照明一小时,请各部门同事相互转告") | |||||
.digest("3月24日20:30-21:30 \n办公区将关闭照明一小时") | |||||
.build() | |||||
)) | |||||
.build() | |||||
}, | |||||
{WxCpAppChatMessage.builder() | |||||
.msgType(AppChatMsgType.MARKDOWN) | |||||
.chatId(chatId) | |||||
.content("您的会议室已经预定,稍后会同步到`邮箱` \n" + | |||||
" >**事项详情** \n" + | |||||
" >事 项:<font color=\\\"info\\\">开会</font> \n" + | |||||
" >组织者:@miglioguan \n" + | |||||
" >参与者:@miglioguan、@kunliu、@jamdeezhou、@kanexiong、@kisonwang \n" + | |||||
" > \n" + | |||||
" >会议室:<font color=\\\"info\\\">广州TIT 1楼 301</font> \n" + | |||||
" >日 期:<font color=\\\"warning\\\">2018年5月18日</font> \n" + | |||||
" >时 间:<font color=\\\"comment\\\">上午9:00-11:00</font> \n" + | |||||
" > \n" + | |||||
" >请准时参加会议。 \n" + | |||||
" > \n" + | |||||
" >如需修改会议信息,请点击:[修改会议信息](https://work.weixin.qq.com)") | |||||
.build() | |||||
}, | |||||
}; | |||||
} | |||||
@Test(dataProvider = "messages") | |||||
public void testSendMsg(WxCpAppChatMessage message) throws WxErrorException { | |||||
this.cpService.getChatService().sendMsg(message); | |||||
} | |||||
} | } |
@@ -31,7 +31,7 @@ public class WxCpDepartmentServiceImplTest { | |||||
cpDepart.setName("子部门" + System.currentTimeMillis()); | cpDepart.setName("子部门" + System.currentTimeMillis()); | ||||
cpDepart.setParentId(1L); | cpDepart.setParentId(1L); | ||||
cpDepart.setOrder(1L); | cpDepart.setOrder(1L); | ||||
Integer departId = this.wxCpService.getDepartmentService().create(cpDepart); | |||||
Long departId = this.wxCpService.getDepartmentService().create(cpDepart); | |||||
System.out.println(departId); | System.out.println(departId); | ||||
} | } | ||||
@@ -0,0 +1,49 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | |||||
import com.google.common.collect.Lists; | |||||
import com.google.gson.Gson; | |||||
import com.google.inject.Inject; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.cp.api.ApiTestModule; | |||||
import me.chanjar.weixin.cp.api.WxCpService; | |||||
import me.chanjar.weixin.cp.bean.WxCpCheckinData; | |||||
import me.chanjar.weixin.cp.bean.WxCpCheckinOption; | |||||
import org.apache.commons.lang3.time.DateFormatUtils; | |||||
import org.testng.annotations.Guice; | |||||
import org.testng.annotations.Test; | |||||
import java.text.ParseException; | |||||
import java.util.ArrayList; | |||||
import java.util.Date; | |||||
import java.util.List; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
/** | |||||
* @author Element | |||||
* @date 2019-04-20 13:46 | |||||
*/ | |||||
@Guice(modules = ApiTestModule.class) | |||||
public class WxCpOAServiceImplTest { | |||||
@Inject | |||||
protected WxCpService wxService; | |||||
@Test | |||||
public void testGetCheckinData() throws ParseException, WxErrorException { | |||||
Date startTime = DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse("2019-04-11"); | |||||
Date endTime = DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse("2019-05-10"); | |||||
List<WxCpCheckinData> results = wxService.getOAService() | |||||
.getCheckinData(1, startTime, endTime, Lists.newArrayList("binary")); | |||||
assertThat(results).isNotNull(); | |||||
} | |||||
@Test | |||||
public void testGetCheckinOption() throws WxErrorException { | |||||
Date now = new Date(); | |||||
List<WxCpCheckinOption> results = wxService.getOAService() | |||||
.getCheckinOption(now, Lists.newArrayList("binary")); | |||||
assertThat(results).isNotNull(); | |||||
} | |||||
} |
@@ -0,0 +1,65 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | |||||
import com.google.inject.Inject; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.cp.api.ApiTestModule; | |||||
import me.chanjar.weixin.cp.api.WxCpService; | |||||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | |||||
import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; | |||||
import org.testng.annotations.Guice; | |||||
import org.testng.annotations.Test; | |||||
import java.util.Arrays; | |||||
import static org.testng.Assert.assertNotNull; | |||||
/** | |||||
* 测试任务卡片服务 | |||||
* | |||||
* @author <a href="https://github.com/domainname">Jeff</a> | |||||
* @date 2019-05-16 | |||||
*/ | |||||
@Guice(modules = ApiTestModule.class) | |||||
public class WxCpTaskCardServiceImplTest { | |||||
@Inject | |||||
private WxCpService wxCpService; | |||||
@Test | |||||
public void testSendTaskCard() throws WxErrorException { | |||||
TaskCardButton btn1 = TaskCardButton.builder() | |||||
.key("key1") | |||||
.name("同意") | |||||
.replaceName("已同意") | |||||
.bold(true) | |||||
.build(); | |||||
TaskCardButton btn2 = TaskCardButton.builder() | |||||
.key("key2") | |||||
.name("拒绝") | |||||
.replaceName("已拒绝") | |||||
.color("red") | |||||
.build(); | |||||
WxCpMessage message = WxCpMessage.TASKCARD() | |||||
.toUser("jeff|mr.t") | |||||
.title("有一个待审批的请求") | |||||
.description("申请:购买图书\n金额:100 元") | |||||
.taskId("task_1") | |||||
.url("http://www.qq.com") | |||||
.buttons(Arrays.asList(btn1, btn2)) | |||||
.build(); | |||||
WxCpMessageSendResult messageSendResult = this.wxCpService.messageSend(message); | |||||
assertNotNull(messageSendResult); | |||||
System.out.println(messageSendResult); | |||||
System.out.println(messageSendResult.getInvalidPartyList()); | |||||
System.out.println(messageSendResult.getInvalidUserList()); | |||||
System.out.println(messageSendResult.getInvalidTagList()); | |||||
} | |||||
@Test | |||||
public void testUpdate() throws Exception { | |||||
wxCpService.getTaskCardService().update(Arrays.asList("jeff", "mr.t"), "task_1", "key1"); | |||||
} | |||||
} |
@@ -42,7 +42,7 @@ public class WxCpUserServiceImplTest { | |||||
WxCpUser user = new WxCpUser(); | WxCpUser user = new WxCpUser(); | ||||
user.setUserId(userId); | user.setUserId(userId); | ||||
user.setName("Some Woman"); | user.setName("Some Woman"); | ||||
user.setDepartIds(new Integer[]{2}); | |||||
user.setDepartIds(new Long[]{2L}); | |||||
user.setEmail("none@none.com"); | user.setEmail("none@none.com"); | ||||
user.setGender(Gender.FEMALE); | user.setGender(Gender.FEMALE); | ||||
user.setMobile("13560084979"); | user.setMobile("13560084979"); | ||||
@@ -1,7 +1,7 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import org.testng.Assert; | |||||
import org.testng.annotations.Test; | |||||
import org.testng.*; | |||||
import org.testng.annotations.*; | |||||
/** | /** | ||||
* Created by huansinho on 2018/4/13. | * Created by huansinho on 2018/4/13. | ||||
@@ -10,7 +10,13 @@ import org.testng.annotations.Test; | |||||
public class WxCpAgentTest { | public class WxCpAgentTest { | ||||
public void testDeserialize() { | public void testDeserialize() { | ||||
String json = "{\"errcode\": 0,\"errmsg\": \"ok\",\"agentid\": 9,\"name\": \"测试应用\",\"square_logo_url\": \"http://wx.qlogo.cn/mmhead/alksjf;lasdjf;lasjfuodiuj3rj2o34j/0\",\"description\": \"这是一个企业号应用\",\"allow_userinfos\": {\"user\": [{\"userid\": \"0009854\"}, {\"userid\": \"1723\"}, {\"userid\": \"5625\"}]},\"allow_partys\": {\"partyid\": [42762742]},\"allow_tags\": {\"tagid\": [23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7]},\"close\": 0,\"redirect_domain\": \"weixin.com.cn\",\"report_location_flag\": 0,\"isreportenter\": 0,\"home_url\": \"\"}"; | |||||
String json = "{\"errcode\": 0,\"errmsg\": \"ok\",\"agentid\": 9,\"name\": \"测试应用\"," + | |||||
"\"square_logo_url\": \"http://wx.qlogo.cn/mmhead/alksjf;lasdjf;lasjfuodiuj3rj2o34j/0\"," + | |||||
"\"description\": \"这是一个企业号应用\",\"allow_userinfos\": {\"user\": [{\"userid\": \"0009854\"}," + | |||||
" {\"userid\": \"1723\"}, {\"userid\": \"5625\"}]},\"allow_partys\": {\"partyid\": [42762742]}," + | |||||
"\"allow_tags\": {\"tagid\": [23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7]}," + | |||||
"\"close\": 0,\"redirect_domain\": \"weixin.com.cn\",\"report_location_flag\": 0," + | |||||
"\"isreportenter\": 0,\"home_url\": \"\"}"; | |||||
WxCpAgent wxCpAgent = WxCpAgent.fromJson(json); | WxCpAgent wxCpAgent = WxCpAgent.fromJson(json); | ||||
@@ -18,7 +24,8 @@ public class WxCpAgentTest { | |||||
Assert.assertEquals(new Integer[]{42762742}, wxCpAgent.getAllowParties().getPartyIds().toArray()); | Assert.assertEquals(new Integer[]{42762742}, wxCpAgent.getAllowParties().getPartyIds().toArray()); | ||||
Assert.assertEquals(new Integer[]{23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7}, wxCpAgent.getAllowTags().getTagIds().toArray()); | |||||
Assert.assertEquals(new Integer[]{23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7}, | |||||
wxCpAgent.getAllowTags().getTagIds().toArray()); | |||||
} | } | ||||
@@ -2,10 +2,12 @@ package me.chanjar.weixin.cp.bean; | |||||
import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | ||||
import me.chanjar.weixin.cp.bean.article.NewArticle; | import me.chanjar.weixin.cp.bean.article.NewArticle; | ||||
import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; | |||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
import java.util.Arrays; | |||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.testng.Assert.assertEquals; | |||||
@Test | @Test | ||||
public class WxCpMessageTest { | public class WxCpMessageTest { | ||||
@@ -19,12 +21,16 @@ public class WxCpMessageTest { | |||||
public void testTextCardBuild() { | public void testTextCardBuild() { | ||||
WxCpMessage reply = WxCpMessage.TEXTCARD().toUser("OPENID") | WxCpMessage reply = WxCpMessage.TEXTCARD().toUser("OPENID") | ||||
.title("领奖通知") | .title("领奖通知") | ||||
.description("<div class=\"gray\">2016年9月26日</div> <div class=\"normal\">恭喜你抽中iPhone 7一台,领奖码:xxxx</div><div class=\"highlight\">请于2016年10月10日前联系行政同事领取</div>") | |||||
.description("<div class=\"gray\">2016年9月26日</div> <div class=\"normal\">恭喜你抽中iPhone 7一台," + | |||||
"领奖码:xxxx</div><div class=\"highlight\">请于2016年10月10日前联系行政同事领取</div>") | |||||
.url("http://www.qq.com") | .url("http://www.qq.com") | ||||
.btnTxt("更多") | .btnTxt("更多") | ||||
.build(); | .build(); | ||||
assertThat(reply.toJson()) | assertThat(reply.toJson()) | ||||
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"textcard\",\"textcard\":{\"title\":\"领奖通知\",\"description\":\"<div class=\\\"gray\\\">2016年9月26日</div> <div class=\\\"normal\\\">恭喜你抽中iPhone 7一台,领奖码:xxxx</div><div class=\\\"highlight\\\">请于2016年10月10日前联系行政同事领取</div>\",\"url\":\"http://www.qq.com\",\"btntxt\":\"更多\"},\"safe\":\"0\"}"); | |||||
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"textcard\",\"textcard\":{\"title\":\"领奖通知\"," + | |||||
"\"description\":\"<div class=\\\"gray\\\">2016年9月26日</div> <div class=\\\"normal\\\">" + | |||||
"恭喜你抽中iPhone 7一台,领奖码:xxxx</div><div class=\\\"highlight\\\">请于2016年10月10日前联系行政同事领取</div>\"," + | |||||
"\"url\":\"http://www.qq.com\",\"btntxt\":\"更多\"},\"safe\":\"0\"}"); | |||||
} | } | ||||
public void testImageBuild() { | public void testImageBuild() { | ||||
@@ -40,9 +46,11 @@ public class WxCpMessageTest { | |||||
} | } | ||||
public void testVideoBuild() { | public void testVideoBuild() { | ||||
WxCpMessage reply = WxCpMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID").description("DESCRIPTION").build(); | |||||
WxCpMessage reply = WxCpMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID") | |||||
.description("DESCRIPTION").build(); | |||||
assertThat(reply.toJson()) | assertThat(reply.toJson()) | ||||
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"safe\":\"0\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); | |||||
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\"," + | |||||
"\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"},\"safe\":\"0\"}"); | |||||
} | } | ||||
public void testNewsBuild() { | public void testNewsBuild() { | ||||
@@ -61,7 +69,10 @@ public class WxCpMessageTest { | |||||
WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); | WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); | ||||
assertThat(reply.toJson()) | assertThat(reply.toJson()) | ||||
.isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"safe\":\"0\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||||
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":" + | |||||
"[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}," + | |||||
"{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}," + | |||||
"\"safe\":\"0\"}"); | |||||
} | } | ||||
public void testMpnewsBuild_with_articles() { | public void testMpnewsBuild_with_articles() { | ||||
@@ -88,14 +99,45 @@ public class WxCpMessageTest { | |||||
WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").addArticle(article1, article2).build(); | WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").addArticle(article1, article2).build(); | ||||
assertThat(reply.toJson()) | assertThat(reply.toJson()) | ||||
.isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"safe\":\"0\",\"mpnews\":{\"articles\":[{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"},{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}]}}"); | |||||
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"articles\":" + | |||||
"[{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\"," + | |||||
"\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}" + | |||||
",{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\"," + | |||||
"\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}]}," + | |||||
"\"safe\":\"0\"}"); | |||||
} | } | ||||
public void testMpnewsBuild_with_media_id() { | public void testMpnewsBuild_with_media_id() { | ||||
WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").mediaId("mmm").build(); | WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").mediaId("mmm").build(); | ||||
assertThat(reply.toJson()) | assertThat(reply.toJson()) | ||||
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"safe\":\"0\",\"mpnews\":{\"media_id\":\"mmm\"}}"); | |||||
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"media_id\":\"mmm\"},\"safe\":\"0\"}"); | |||||
} | |||||
public void testTaskCardBuilder() { | |||||
TaskCardButton button1 = TaskCardButton.builder() | |||||
.key("yes") | |||||
.name("批准") | |||||
.replaceName("已批准") | |||||
.color("blue") | |||||
.bold(true) | |||||
.build(); | |||||
TaskCardButton button2 = TaskCardButton.builder() | |||||
.key("yes") | |||||
.name("拒绝") | |||||
.replaceName("已拒绝") | |||||
.color("red") | |||||
.bold(false) | |||||
.build(); | |||||
WxCpMessage reply = WxCpMessage.TASKCARD().toUser("OPENID") | |||||
.title("任务卡片") | |||||
.description("有一条待处理任务") | |||||
.url("http://www.qq.com") | |||||
.taskId("task_123") | |||||
.buttons(Arrays.asList(button1, button2)) | |||||
.build(); | |||||
assertThat(reply.toJson()) | |||||
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"taskcard\",\"taskcard\":{\"title\":\"任务卡片\",\"description\":\"有一条待处理任务\",\"url\":\"http://www.qq.com\",\"task_id\":\"task_123\",\"btn\":[{\"key\":\"yes\",\"name\":\"批准\",\"replace_name\":\"已批准\",\"color\":\"blue\",\"is_bold\":true},{\"key\":\"yes\",\"name\":\"拒绝\",\"replace_name\":\"已拒绝\",\"color\":\"red\",\"is_bold\":false}]}}"); | |||||
} | } | ||||
} | } |
@@ -1,9 +1,11 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import org.testng.annotations.*; | |||||
import org.testng.annotations.Test; | |||||
import static org.testng.Assert.*; | |||||
import static me.chanjar.weixin.cp.WxCpConsts.EventType.TASKCARD_CLICK; | |||||
import static org.testng.Assert.assertEquals; | |||||
import static org.testng.Assert.assertNotNull; | |||||
@Test | @Test | ||||
public class WxCpXmlMessageTest { | public class WxCpXmlMessageTest { | ||||
@@ -117,4 +119,58 @@ public class WxCpXmlMessageTest { | |||||
assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "aef52ae501537e552725c5d7f99c1741"); | assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "aef52ae501537e552725c5d7f99c1741"); | ||||
assertEquals(wxMessage.getSendPicsInfo().getPicList().get(1).getPicMd5Sum(), "c4564632a4fab91378c39bea6aad6f9e"); | assertEquals(wxMessage.getSendPicsInfo().getPicList().get(1).getPicMd5Sum(), "c4564632a4fab91378c39bea6aad6f9e"); | ||||
} | } | ||||
public void testExtAttr() { | |||||
String xml = "<xml>" + | |||||
" <ToUserName><![CDATA[w56c9fe3d50ad1ea2]]></ToUserName>" + | |||||
" <FromUserName><![CDATA[sys]]></FromUserName>" + | |||||
" <CreateTime>1557241961</CreateTime>" + | |||||
" <MsgType><![CDATA[event]]></MsgType>" + | |||||
" <Event><![CDATA[change_contact]]></Event>" + | |||||
" <ChangeType><![CDATA[update_user]]></ChangeType>" + | |||||
" <UserID><![CDATA[zhangsan]]></UserID>" + | |||||
" <ExtAttr>" + | |||||
" <Item><Name><![CDATA[爱好]]></Name><Value><![CDATA[111]]></Value><Text><Value><![CDATA[111]]></Value></Text></Item>" + | |||||
" <Item><Name><![CDATA[入职时间]]></Name><Value><![CDATA[11111]]></Value><Text><Value><![CDATA[11111]]></Value></Text></Item>" + | |||||
" <Item><Name><![CDATA[城市]]></Name><Value><![CDATA[11111]]></Value><Text><Value><![CDATA[11111]]></Value></Text></Item>" + | |||||
" </ExtAttr>" + | |||||
" <Address><![CDATA[11111]]></Address>" + | |||||
"</xml>"; | |||||
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); | |||||
assertEquals(wxMessage.getToUserName(), "w56c9fe3d50ad1ea2"); | |||||
assertEquals(wxMessage.getFromUserName(), "sys"); | |||||
assertEquals(wxMessage.getCreateTime(), new Long(1557241961)); | |||||
assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); | |||||
assertEquals(wxMessage.getEvent(), "change_contact"); | |||||
assertEquals(wxMessage.getChangeType(), "update_user"); | |||||
assertEquals(wxMessage.getUserId(), "zhangsan"); | |||||
assertNotNull(wxMessage.getExtAttrs()); | |||||
assertNotNull(wxMessage.getExtAttrs().getItems()); | |||||
assertEquals(wxMessage.getExtAttrs().getItems().size(), 3); | |||||
assertEquals(wxMessage.getExtAttrs().getItems().get(0).getName(), "爱好"); | |||||
} | |||||
public void testTaskCardEvent() { | |||||
String xml = "<xml>" + | |||||
"<ToUserName><![CDATA[toUser]]></ToUserName>" + | |||||
"<FromUserName><![CDATA[FromUser]]></FromUserName>" + | |||||
"<CreateTime>123456789</CreateTime>" + | |||||
"<MsgType><![CDATA[event]]></MsgType>" + | |||||
"<Event><![CDATA[taskcard_click]]></Event>" + | |||||
"<EventKey><![CDATA[key111]]></EventKey>" + | |||||
"<TaskId><![CDATA[taskid111]]></TaskId >" + | |||||
"<AgentID>1</AgentID>" + | |||||
"</xml>"; | |||||
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); | |||||
assertEquals(wxMessage.getToUserName(), "toUser"); | |||||
assertEquals(wxMessage.getFromUserName(), "FromUser"); | |||||
assertEquals(wxMessage.getCreateTime(), Long.valueOf(123456789L)); | |||||
assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); | |||||
assertEquals(wxMessage.getAgentId(), Integer.valueOf(1)); | |||||
assertEquals(wxMessage.getEvent(), TASKCARD_CLICK); | |||||
assertEquals(wxMessage.getEventKey(), "key111"); | |||||
assertEquals(wxMessage.getTaskId(), "taskid111"); | |||||
} | |||||
} | } |
@@ -1,28 +1,23 @@ | |||||
package me.chanjar.weixin.cp.demo; | package me.chanjar.weixin.cp.demo; | ||||
import java.io.InputStream; | |||||
import com.thoughtworks.xstream.XStream; | import com.thoughtworks.xstream.XStream; | ||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import lombok.ToString; | |||||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | import me.chanjar.weixin.common.util.xml.XStreamInitializer; | ||||
import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage; | import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage; | ||||
import java.io.InputStream; | |||||
/** | /** | ||||
* @author Daniel Qian | * @author Daniel Qian | ||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
class WxCpDemoInMemoryConfigStorage extends WxCpInMemoryConfigStorage { | |||||
@ToString | |||||
public class WxCpDemoInMemoryConfigStorage extends WxCpInMemoryConfigStorage { | |||||
public static WxCpDemoInMemoryConfigStorage fromXml(InputStream is) { | public static WxCpDemoInMemoryConfigStorage fromXml(InputStream is) { | ||||
XStream xstream = XStreamInitializer.getInstance(); | XStream xstream = XStreamInitializer.getInstance(); | ||||
xstream.processAnnotations(WxCpDemoInMemoryConfigStorage.class); | xstream.processAnnotations(WxCpDemoInMemoryConfigStorage.class); | ||||
return (WxCpDemoInMemoryConfigStorage) xstream.fromXML(is); | return (WxCpDemoInMemoryConfigStorage) xstream.fromXML(is); | ||||
} | } | ||||
@Override | |||||
public String toString() { | |||||
return "SimpleWxConfigProvider [appidOrCorpid=" + this.corpId + ", corpSecret=" + this.corpSecret + ", accessToken=" + this.accessToken | |||||
+ ", expiresTime=" + this.expiresTime + ", token=" + this.token + ", aesKey=" + this.aesKey + "]"; | |||||
} | |||||
} | } |
@@ -76,6 +76,13 @@ public class WxCpUserGsonAdapterTest { | |||||
final WxCpUser user = WxCpUser.fromJson(userJson); | final WxCpUser user = WxCpUser.fromJson(userJson); | ||||
assertThat(user).isNotNull(); | assertThat(user).isNotNull(); | ||||
assertThat(user.getOrders()).isNotEmpty(); | |||||
assertThat(user.getOrders().length).isEqualTo(2); | |||||
assertThat(user.getOrders()[0]).isEqualTo(1); | |||||
assertThat(user.getOrders()[1]).isEqualTo(2); | |||||
assertThat(user.getExternalAttrs()).isNotEmpty(); | assertThat(user.getExternalAttrs()).isNotEmpty(); | ||||
final WxCpUser.ExternalAttribute externalAttr1 = user.getExternalAttrs().get(0); | final WxCpUser.ExternalAttribute externalAttr1 = user.getExternalAttrs().get(0); | ||||
@@ -100,6 +107,7 @@ public class WxCpUserGsonAdapterTest { | |||||
@Test | @Test | ||||
public void testSerialize() { | public void testSerialize() { | ||||
WxCpUser user = new WxCpUser(); | WxCpUser user = new WxCpUser(); | ||||
user.setOrders(new Integer[]{1, 2}); | |||||
user.addExternalAttr(WxCpUser.ExternalAttribute.builder() | user.addExternalAttr(WxCpUser.ExternalAttribute.builder() | ||||
.type(0) | .type(0) | ||||
.name("文本名称") | .name("文本名称") | ||||
@@ -119,6 +127,10 @@ public class WxCpUserGsonAdapterTest { | |||||
.title("my miniprogram") | .title("my miniprogram") | ||||
.build()); | .build()); | ||||
assertThat(user.toJson()).isEqualTo("{\"external_profile\":{\"external_attr\":[{\"type\":0,\"name\":\"文本名称\",\"text\":{\"value\":\"文本\"}},{\"type\":1,\"name\":\"网页名称\",\"web\":{\"url\":\"http://www.test.com\",\"title\":\"标题\"}},{\"type\":2,\"name\":\"测试app\",\"miniprogram\":{\"appid\":\"wx8bd80126147df384\",\"pagepath\":\"/index\",\"title\":\"my miniprogram\"}}]}}"); | |||||
assertThat(user.toJson()).isEqualTo("{\"order\":[1,2],\"external_profile\":{\"external_attr\":" + | |||||
"[{\"type\":0,\"name\":\"文本名称\",\"text\":{\"value\":\"文本\"}}," + | |||||
"{\"type\":1,\"name\":\"网页名称\",\"web\":{\"url\":\"http://www.test.com\",\"title\":\"标题\"}}," + | |||||
"{\"type\":2,\"name\":\"测试app\"," + | |||||
"\"miniprogram\":{\"appid\":\"wx8bd80126147df384\",\"pagepath\":\"/index\",\"title\":\"my miniprogram\"}}]}}"); | |||||
} | } | ||||
} | } |
@@ -7,11 +7,12 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>wx-java</artifactId> | <artifactId>wx-java</artifactId> | ||||
<version>3.3.0</version> | |||||
<version>3.4.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-miniapp</artifactId> | <artifactId>weixin-java-miniapp</artifactId> | ||||
<name>WxJava - MiniApp</name> | |||||
<description>微信小程序Java SDK</description> | |||||
<name>WxJava - MiniApp Java SDK</name> | |||||
<description>微信小程序 Java SDK</description> | |||||
<dependencies> | <dependencies> | ||||
<dependency> | <dependency> | ||||
@@ -70,6 +71,11 @@ | |||||
<groupId>redis.clients</groupId> | <groupId>redis.clients</groupId> | ||||
<artifactId>jedis</artifactId> | <artifactId>jedis</artifactId> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>org.bouncycastle</groupId> | |||||
<artifactId>bcpkix-jdk15on</artifactId> | |||||
<version>1.59</version> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>org.projectlombok</groupId> | <groupId>org.projectlombok</groupId> | ||||
<artifactId>lombok</artifactId> | <artifactId>lombok</artifactId> | ||||
@@ -1,14 +1,10 @@ | |||||
package cn.binarywang.wx.miniapp.api; | package cn.binarywang.wx.miniapp.api; | ||||
import java.util.List; | |||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCategory; | |||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeAuditStatus; | |||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest; | |||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeSubmitAuditRequest; | |||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution; | |||||
import cn.binarywang.wx.miniapp.bean.code.*; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import java.util.List; | |||||
/** | /** | ||||
* 小程序代码管理相关 API(大部分只能是第三方平台调用) | * 小程序代码管理相关 API(大部分只能是第三方平台调用) | ||||
* 文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN | * 文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN | ||||
@@ -18,7 +14,7 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||||
*/ | */ | ||||
public interface WxMaCodeService { | public interface WxMaCodeService { | ||||
/** | /** | ||||
* 为授权的小程序帐号上传小程序代码 | |||||
* 为授权的小程序帐号上传小程序代码. | |||||
*/ | */ | ||||
String COMMIT_URL = "https://api.weixin.qq.com/wxa/commit"; | String COMMIT_URL = "https://api.weixin.qq.com/wxa/commit"; | ||||
String GET_QRCODE_URL = "https://api.weixin.qq.com/wxa/get_qrcode"; | String GET_QRCODE_URL = "https://api.weixin.qq.com/wxa/get_qrcode"; | ||||
@@ -35,7 +31,7 @@ public interface WxMaCodeService { | |||||
String UNDO_CODE_AUDIT_URL = "https://api.weixin.qq.com/wxa/undocodeaudit"; | String UNDO_CODE_AUDIT_URL = "https://api.weixin.qq.com/wxa/undocodeaudit"; | ||||
/** | /** | ||||
* 为授权的小程序帐号上传小程序代码(仅仅支持第三方开放平台) | |||||
* 为授权的小程序帐号上传小程序代码(仅仅支持第三方开放平台). | |||||
* | * | ||||
* @param commitRequest 参数 | * @param commitRequest 参数 | ||||
* @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档 | * @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档 | ||||
@@ -43,19 +39,19 @@ public interface WxMaCodeService { | |||||
void commit(WxMaCodeCommitRequest commitRequest) throws WxErrorException; | void commit(WxMaCodeCommitRequest commitRequest) throws WxErrorException; | ||||
/** | /** | ||||
* 获取体验小程序的体验二维码 | |||||
* 获取体验小程序的体验二维码. | |||||
* 文档地址: | * 文档地址: | ||||
* https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN | * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN | ||||
* | * | ||||
* @param path 指定体验版二维码跳转到某个具体页面(如果不需要的话,则不需要填path参数,可在路径后以“?参数”方式传入参数) | * @param path 指定体验版二维码跳转到某个具体页面(如果不需要的话,则不需要填path参数,可在路径后以“?参数”方式传入参数) | ||||
* 具体的路径加参数需要urlencode(方法内部处理),比如page/index?action=1编码后得到page%2Findex%3Faction%3D1 | |||||
* 具体的路径加参数需要urlencode(方法内部处理),比如page/index?action=1编码后得到page%2Findex%3Faction%3D1 | |||||
* @return 二维码 bytes | * @return 二维码 bytes | ||||
* @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档 | * @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档 | ||||
*/ | */ | ||||
byte[] getQrCode(String path) throws WxErrorException; | byte[] getQrCode(String path) throws WxErrorException; | ||||
/** | /** | ||||
* 获取授权小程序帐号的可选类目 | |||||
* 获取授权小程序帐号的可选类目. | |||||
* | * | ||||
* @return List<WxMaCategory> | * @return List<WxMaCategory> | ||||
* @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 | * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 | ||||
@@ -63,7 +59,7 @@ public interface WxMaCodeService { | |||||
List<WxMaCategory> getCategory() throws WxErrorException; | List<WxMaCategory> getCategory() throws WxErrorException; | ||||
/** | /** | ||||
* 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用) | |||||
* 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用). | |||||
* | * | ||||
* @return page_list 页面配置列表 | * @return page_list 页面配置列表 | ||||
* @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 | * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 | ||||
@@ -71,7 +67,7 @@ public interface WxMaCodeService { | |||||
List<String> getPage() throws WxErrorException; | List<String> getPage() throws WxErrorException; | ||||
/** | /** | ||||
* 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用) | |||||
* 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用). | |||||
* | * | ||||
* @param auditRequest 提交审核参数 | * @param auditRequest 提交审核参数 | ||||
* @return 审核编号 | * @return 审核编号 | ||||
@@ -80,7 +76,7 @@ public interface WxMaCodeService { | |||||
long submitAudit(WxMaCodeSubmitAuditRequest auditRequest) throws WxErrorException; | long submitAudit(WxMaCodeSubmitAuditRequest auditRequest) throws WxErrorException; | ||||
/** | /** | ||||
* 查询某个指定版本的审核状态(仅供第三方代小程序调用) | |||||
* 查询某个指定版本的审核状态(仅供第三方代小程序调用). | |||||
* | * | ||||
* @param auditId 提交审核时获得的审核id | * @param auditId 提交审核时获得的审核id | ||||
* @return 审核状态 | * @return 审核状态 | ||||
@@ -89,7 +85,7 @@ public interface WxMaCodeService { | |||||
WxMaCodeAuditStatus getAuditStatus(long auditId) throws WxErrorException; | WxMaCodeAuditStatus getAuditStatus(long auditId) throws WxErrorException; | ||||
/** | /** | ||||
* 查询最新一次提交的审核状态(仅供第三方代小程序调用) | |||||
* 查询最新一次提交的审核状态(仅供第三方代小程序调用). | |||||
* | * | ||||
* @return 审核状态 | * @return 审核状态 | ||||
* @throws WxErrorException 查询失败时返回,具体错误码请看此接口的注释文档 | * @throws WxErrorException 查询失败时返回,具体错误码请看此接口的注释文档 | ||||
@@ -97,14 +93,14 @@ public interface WxMaCodeService { | |||||
WxMaCodeAuditStatus getLatestAuditStatus() throws WxErrorException; | WxMaCodeAuditStatus getLatestAuditStatus() throws WxErrorException; | ||||
/** | /** | ||||
* 发布已通过审核的小程序(仅供第三方代小程序调用) | |||||
* 发布已通过审核的小程序(仅供第三方代小程序调用). | |||||
* | * | ||||
* @throws WxErrorException 发布失败时抛出,具体错误码请看此接口的注释文档 | * @throws WxErrorException 发布失败时抛出,具体错误码请看此接口的注释文档 | ||||
*/ | */ | ||||
void release() throws WxErrorException; | void release() throws WxErrorException; | ||||
/** | /** | ||||
* 修改小程序线上代码的可见状态(仅供第三方代小程序调用) | |||||
* 修改小程序线上代码的可见状态(仅供第三方代小程序调用). | |||||
* | * | ||||
* @param action 设置可访问状态,发布后默认可访问,close为不可见,open为可见 | * @param action 设置可访问状态,发布后默认可访问,close为不可见,open为可见 | ||||
* @throws WxErrorException 发布失败时抛出,具体错误码请看此接口的注释文档 | * @throws WxErrorException 发布失败时抛出,具体错误码请看此接口的注释文档 | ||||
@@ -112,14 +108,14 @@ public interface WxMaCodeService { | |||||
void changeVisitStatus(String action) throws WxErrorException; | void changeVisitStatus(String action) throws WxErrorException; | ||||
/** | /** | ||||
* 小程序版本回退(仅供第三方代小程序调用) | |||||
* 小程序版本回退(仅供第三方代小程序调用). | |||||
* | * | ||||
* @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 | * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 | ||||
*/ | */ | ||||
void revertCodeRelease() throws WxErrorException; | void revertCodeRelease() throws WxErrorException; | ||||
/** | /** | ||||
* 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用) | |||||
* 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用). | |||||
* | * | ||||
* @return 小程序版本分布信息 | * @return 小程序版本分布信息 | ||||
* @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 | * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 | ||||
@@ -127,7 +123,7 @@ public interface WxMaCodeService { | |||||
WxMaCodeVersionDistribution getSupportVersion() throws WxErrorException; | WxMaCodeVersionDistribution getSupportVersion() throws WxErrorException; | ||||
/** | /** | ||||
* 设置最低基础库版本(仅供第三方代小程序调用) | |||||
* 设置最低基础库版本(仅供第三方代小程序调用). | |||||
* | * | ||||
* @param version 版本 | * @param version 版本 | ||||
* @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 | * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 | ||||
@@ -135,7 +131,7 @@ public interface WxMaCodeService { | |||||
void setSupportVersion(String version) throws WxErrorException; | void setSupportVersion(String version) throws WxErrorException; | ||||
/** | /** | ||||
* 小程序审核撤回 | |||||
* 小程序审核撤回. | |||||
* 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次 | * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次 | ||||
* | * | ||||
* @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 | * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 | ||||
@@ -20,7 +20,7 @@ public interface WxMaMsgService { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 发送客服消息 | * 发送客服消息 | ||||
* 详情请见: <a href="https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/conversation.html">发送客服消息</a> | |||||
* 详情请见: <a href="https://developers.weixin.qq.com/miniprogram/dev/api-backend/customerServiceMessage.send.html">发送客服消息</a> | |||||
* 接口url格式:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN | * 接口url格式:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN | ||||
* </pre> | * </pre> | ||||
*/ | */ | ||||
@@ -29,7 +29,7 @@ public interface WxMaMsgService { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 发送模板消息 | * 发送模板消息 | ||||
* 详情请见: <a href="https://mp.weixin.qq.com/debug/wxadoc/dev/api/notice.html#接口说明">发送模板消息</a> | |||||
* 详情请见: <a href="https://developers.weixin.qq.com/miniprogram/dev/api-backend/templateMessage.send.html">发送模板消息</a> | |||||
* 接口url格式:https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN | * 接口url格式:https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN | ||||
* </pre> | * </pre> | ||||
*/ | */ | ||||
@@ -17,6 +17,10 @@ public interface WxMaService { | |||||
String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; | String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; | ||||
String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; | String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; | ||||
/** | |||||
* getPaidUnionId | |||||
*/ | |||||
String GET_PAID_UNION_ID_URL = "https://api.weixin.qq.com/wxa/getpaidunionid"; | |||||
/** | /** | ||||
* 获取登录后的session信息. | * 获取登录后的session信息. | ||||
@@ -56,6 +60,22 @@ public interface WxMaService { | |||||
*/ | */ | ||||
String getAccessToken(boolean forceRefresh) throws WxErrorException; | String getAccessToken(boolean forceRefresh) throws WxErrorException; | ||||
/** | |||||
* <pre> | |||||
* 用户支付完成后,获取该用户的 UnionId,无需用户授权。本接口支持第三方平台代理查询。 | |||||
* | |||||
* 注意:调用前需要用户完成支付,且在支付后的五分钟内有效。 | |||||
* 请求地址: GET https://api.weixin.qq.com/wxa/getpaidunionid?access_token=ACCESS_TOKEN&openid=OPENID | |||||
* 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/getPaidUnionId.html | |||||
* </pre> | |||||
* | |||||
* @param openid 必填 支付用户唯一标识 | |||||
* @param transactionId 非必填 微信支付订单号 | |||||
* @param mchId 非必填 微信支付分配的商户号,和商户订单号配合使用 | |||||
* @param outTradeNo 非必填 微信支付商户订单号,和商户号配合使用 | |||||
*/ | |||||
String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo) throws WxErrorException; | |||||
/** | /** | ||||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求. | * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求. | ||||
*/ | */ | ||||
@@ -168,18 +188,21 @@ public interface WxMaService { | |||||
/** | /** | ||||
* 返回分享相关查询服务. | * 返回分享相关查询服务. | ||||
* | |||||
* @return WxMaShareService | * @return WxMaShareService | ||||
*/ | */ | ||||
WxMaShareService getShareService(); | WxMaShareService getShareService(); | ||||
/** | /** | ||||
* 返回微信运动相关接口服务对象. | * 返回微信运动相关接口服务对象. | ||||
* | |||||
* @return WxMaShareService | * @return WxMaShareService | ||||
*/ | */ | ||||
WxMaRunService getRunService(); | WxMaRunService getRunService(); | ||||
/** | /** | ||||
* 返回内容安全相关接口服务对象. | * 返回内容安全相关接口服务对象. | ||||
* | |||||
* @return WxMaShareService | * @return WxMaShareService | ||||
*/ | */ | ||||
WxMaSecCheckService getSecCheckService(); | WxMaSecCheckService getSecCheckService(); | ||||
@@ -1,47 +1,33 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | package cn.binarywang.wx.miniapp.api.impl; | ||||
import java.io.IOException; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import java.util.concurrent.locks.Lock; | |||||
import org.apache.http.HttpHost; | |||||
import org.apache.http.client.config.RequestConfig; | |||||
import org.apache.http.client.methods.CloseableHttpResponse; | |||||
import org.apache.http.client.methods.HttpGet; | |||||
import org.apache.http.impl.client.BasicResponseHandler; | |||||
import org.apache.http.impl.client.CloseableHttpClient; | |||||
import cn.binarywang.wx.miniapp.api.WxMaAnalysisService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaCodeService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaJsapiService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaMediaService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaMsgService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaRunService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaSecCheckService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaSettingService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaShareService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaTemplateService; | |||||
import cn.binarywang.wx.miniapp.api.WxMaUserService; | |||||
import cn.binarywang.wx.miniapp.api.*; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | ||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
import com.google.common.base.Joiner; | import com.google.common.base.Joiner; | ||||
import com.google.gson.Gson; | import com.google.gson.Gson; | ||||
import com.google.gson.JsonParser; | |||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import me.chanjar.weixin.common.WxType; | |||||
import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
import me.chanjar.weixin.common.error.WxError; | import me.chanjar.weixin.common.error.WxError; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.DataUtils; | import me.chanjar.weixin.common.util.DataUtils; | ||||
import me.chanjar.weixin.common.util.crypto.SHA1; | import me.chanjar.weixin.common.util.crypto.SHA1; | ||||
import me.chanjar.weixin.common.util.http.HttpType; | |||||
import me.chanjar.weixin.common.util.http.RequestExecutor; | |||||
import me.chanjar.weixin.common.util.http.RequestHttp; | |||||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||||
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||||
import me.chanjar.weixin.common.util.http.*; | |||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | ||||
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; | import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; | ||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.apache.http.HttpHost; | |||||
import org.apache.http.client.config.RequestConfig; | |||||
import org.apache.http.client.methods.CloseableHttpResponse; | |||||
import org.apache.http.client.methods.HttpGet; | |||||
import org.apache.http.impl.client.BasicResponseHandler; | |||||
import org.apache.http.impl.client.CloseableHttpClient; | |||||
import java.io.IOException; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import java.util.concurrent.locks.Lock; | |||||
import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*; | import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*; | ||||
@@ -50,6 +36,7 @@ import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*; | |||||
*/ | */ | ||||
@Slf4j | @Slf4j | ||||
public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpClient, HttpHost> { | public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpClient, HttpHost> { | ||||
private static final JsonParser JSON_PARSER = new JsonParser(); | |||||
private CloseableHttpClient httpClient; | private CloseableHttpClient httpClient; | ||||
private HttpHost httpProxy; | private HttpHost httpProxy; | ||||
private WxMaConfig wxMaConfig; | private WxMaConfig wxMaConfig; | ||||
@@ -114,40 +101,68 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||||
@Override | @Override | ||||
public String getAccessToken(boolean forceRefresh) throws WxErrorException { | public String getAccessToken(boolean forceRefresh) throws WxErrorException { | ||||
if (!this.getWxMaConfig().isAccessTokenExpired() && !forceRefresh) { | |||||
return this.getWxMaConfig().getAccessToken(); | |||||
} | |||||
Lock lock = this.getWxMaConfig().getAccessTokenLock(); | Lock lock = this.getWxMaConfig().getAccessTokenLock(); | ||||
lock.lock(); | |||||
try { | try { | ||||
lock.lock(); | |||||
if (this.getWxMaConfig().isAccessTokenExpired() || forceRefresh) { | |||||
String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), | |||||
this.getWxMaConfig().getSecret()); | |||||
try { | |||||
HttpGet httpGet = new HttpGet(url); | |||||
if (this.getRequestHttpProxy() != null) { | |||||
RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); | |||||
httpGet.setConfig(config); | |||||
} | |||||
try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) { | |||||
String resultContent = new BasicResponseHandler().handleResponse(response); | |||||
WxError error = WxError.fromJson(resultContent); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(), | |||||
accessToken.getExpiresIn()); | |||||
} finally { | |||||
httpGet.releaseConnection(); | |||||
String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), | |||||
this.getWxMaConfig().getSecret()); | |||||
try { | |||||
HttpGet httpGet = new HttpGet(url); | |||||
if (this.getRequestHttpProxy() != null) { | |||||
RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); | |||||
httpGet.setConfig(config); | |||||
} | |||||
try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) { | |||||
String resultContent = new BasicResponseHandler().handleResponse(response); | |||||
WxError error = WxError.fromJson(resultContent); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | } | ||||
} catch (IOException e) { | |||||
throw new RuntimeException(e); | |||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
return this.getWxMaConfig().getAccessToken(); | |||||
} finally { | |||||
httpGet.releaseConnection(); | |||||
} | } | ||||
} catch (IOException e) { | |||||
throw new RuntimeException(e); | |||||
} | } | ||||
} finally { | } finally { | ||||
lock.unlock(); | lock.unlock(); | ||||
} | } | ||||
return this.getWxMaConfig().getAccessToken(); | |||||
} | |||||
@Override | |||||
public String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo) | |||||
throws WxErrorException { | |||||
Map<String, String> params = new HashMap<>(8); | |||||
params.put("openid", openid); | |||||
if (StringUtils.isNotEmpty(transactionId)) { | |||||
params.put("transaction_id", transactionId); | |||||
} | |||||
if (StringUtils.isNotEmpty(mchId)) { | |||||
params.put("mch_id", mchId); | |||||
} | |||||
if (StringUtils.isNotEmpty(outTradeNo)) { | |||||
params.put("out_trade_no", outTradeNo); | |||||
} | |||||
String responseContent = this.get(GET_PAID_UNION_ID_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); | |||||
WxError error = WxError.fromJson(responseContent, WxType.MiniApp); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
return JSON_PARSER.parse(responseContent).getAsJsonObject().get("unionid").getAsString(); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -168,7 +183,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||||
try { | try { | ||||
return SHA1.gen(this.getWxMaConfig().getToken(), timestamp, nonce).equals(signature); | return SHA1.gen(this.getWxMaConfig().getToken(), timestamp, nonce).equals(signature); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
this.log.error("Checking signature failed, and the reason is :" + e.getMessage()); | |||||
log.error("Checking signature failed, and the reason is :" + e.getMessage()); | |||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
@@ -246,7 +261,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||||
if (error.getErrorCode() == ERR_40001 | if (error.getErrorCode() == ERR_40001 | ||||
|| error.getErrorCode() == ERR_42001 | || error.getErrorCode() == ERR_42001 | ||||
|| error.getErrorCode() == ERR_40014) { | || error.getErrorCode() == ERR_40014) { | ||||
// 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token | |||||
// 强制设置WxMaConfig的access token过期了,这样在下一次请求里就会刷新access token | |||||
this.getWxMaConfig().expireAccessToken(); | this.getWxMaConfig().expireAccessToken(); | ||||
if (this.getWxMaConfig().autoRefreshToken()) { | if (this.getWxMaConfig().autoRefreshToken()) { | ||||
return this.execute(executor, uri, data); | return this.execute(executor, uri, data); | ||||
@@ -41,20 +41,26 @@ public class WxMaKefuMessage implements Serializable { | |||||
@Data | @Data | ||||
@AllArgsConstructor | @AllArgsConstructor | ||||
public static class KfText { | |||||
public static class KfText implements Serializable { | |||||
private static final long serialVersionUID = 151122958720941270L; | |||||
private String content; | private String content; | ||||
} | } | ||||
@Data | @Data | ||||
@AllArgsConstructor | @AllArgsConstructor | ||||
public static class KfImage { | |||||
public static class KfImage implements Serializable { | |||||
private static final long serialVersionUID = -5409342945117300782L; | |||||
@SerializedName("media_id") | @SerializedName("media_id") | ||||
private String mediaId; | private String mediaId; | ||||
} | } | ||||
@Data | @Data | ||||
@Builder | @Builder | ||||
public static class KfLink { | |||||
public static class KfLink implements Serializable { | |||||
private static final long serialVersionUID = -6728776817556127413L; | |||||
private String title; | private String title; | ||||
private String description; | private String description; | ||||
private String url; | private String url; | ||||
@@ -65,7 +71,9 @@ public class WxMaKefuMessage implements Serializable { | |||||
@Data | @Data | ||||
@Builder | @Builder | ||||
public static class KfMaPage { | |||||
public static class KfMaPage implements Serializable { | |||||
private static final long serialVersionUID = -5633492281871634466L; | |||||
private String title; | private String title; | ||||
@SerializedName("pagepath") | @SerializedName("pagepath") | ||||
@@ -5,7 +5,7 @@ import lombok.NoArgsConstructor; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* | |||||
* 参考文档 https://developers.weixin.qq.com/miniprogram/dev/api-backend/templateMessage.send.html | |||||
* Created by Binary Wang on 2018/9/23. | * Created by Binary Wang on 2018/9/23. | ||||
* </pre> | * </pre> | ||||
* | * | ||||
@@ -29,4 +29,5 @@ public class WxMaTemplateData { | |||||
this.color = color; | this.color = color; | ||||
} | } | ||||
} | } |
@@ -13,7 +13,7 @@ import lombok.Setter; | |||||
/** | /** | ||||
* 模板消息. | * 模板消息. | ||||
* 参考 https://mp.weixin.qq.com/debug/wxadoc/dev/api/notice.html#接口说明 模板消息部分 | |||||
* 参考 https://developers.weixin.qq.com/miniprogram/dev/api-backend/templateMessage.send.html | |||||
* | * | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
@@ -75,16 +75,6 @@ public class WxMaTemplateMessage implements Serializable { | |||||
*/ | */ | ||||
private List<WxMaTemplateData> data; | private List<WxMaTemplateData> data; | ||||
/** | |||||
* 模板内容字体的颜色,不填默认黑色. | |||||
* <pre> | |||||
* 参数:color | |||||
* 是否必填: 否 | |||||
* 描述: 模板内容字体的颜色,不填默认黑色 | |||||
* </pre> | |||||
*/ | |||||
private String color; | |||||
/** | /** | ||||
* 模板需要放大的关键词,不填则默认无放大. | * 模板需要放大的关键词,不填则默认无放大. | ||||
* <pre> | * <pre> | ||||
@@ -104,5 +104,11 @@ public class WxMaUniformMessage implements Serializable { | |||||
* 加入此字段是基于微信官方接口变化多端的考虑 | * 加入此字段是基于微信官方接口变化多端的考虑 | ||||
*/ | */ | ||||
private boolean usePath = false; | private boolean usePath = false; | ||||
/** | |||||
* 是否使用pagePath,否则使用pagepath. | |||||
* 加入此字段是基于微信官方接口变化多端的考虑 | |||||
*/ | |||||
private boolean usePagePath = false; | |||||
} | } | ||||
} | } |
@@ -18,18 +18,24 @@ import java.io.Serializable; | |||||
public class WxMaCodeAuditStatus implements Serializable { | public class WxMaCodeAuditStatus implements Serializable { | ||||
private static final long serialVersionUID = 4655119308692217268L; | private static final long serialVersionUID = 4655119308692217268L; | ||||
/** | /** | ||||
* 审核 ID | |||||
* 审核 ID. | |||||
*/ | */ | ||||
@SerializedName(value = "auditId", alternate = {"auditid"}) | @SerializedName(value = "auditId", alternate = {"auditid"}) | ||||
private Long auditId; | private Long auditId; | ||||
/** | /** | ||||
* 审核状态,其中0为审核成功,1为审核失败,2为审核中 | |||||
* 审核状态. | |||||
* 其中0为审核成功,1为审核失败,2为审核中 | |||||
*/ | */ | ||||
private Integer status; | private Integer status; | ||||
/** | /** | ||||
* 当status=1,审核被拒绝时,返回的拒绝原因 | |||||
* 当status=1,审核被拒绝时,返回的拒绝原因. | |||||
*/ | */ | ||||
private String reason; | private String reason; | ||||
/** | |||||
* 当status=1,审核被拒绝时,会返回审核失败的小程序截图示例。 xxx丨yyy丨zzz是media_id可通过获取永久素材接口 拉取截图内容). | |||||
*/ | |||||
@SerializedName(value = "screenshot") | |||||
private String screenShot; | |||||
public static WxMaCodeAuditStatus fromJson(String json) { | public static WxMaCodeAuditStatus fromJson(String json) { | ||||
return WxMaGsonBuilder.create().fromJson(json, WxMaCodeAuditStatus.class); | return WxMaGsonBuilder.create().fromJson(json, WxMaCodeAuditStatus.class); | ||||
@@ -1,19 +1,27 @@ | |||||
package cn.binarywang.wx.miniapp.util.crypt; | package cn.binarywang.wx.miniapp.util.crypt; | ||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||||
import me.chanjar.weixin.common.util.crypto.PKCS7Encoder; | |||||
import org.apache.commons.codec.binary.Base64; | |||||
import java.nio.charset.Charset; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.security.AlgorithmParameters; | |||||
import java.security.Key; | |||||
import java.security.Security; | |||||
import java.util.Arrays; | |||||
import javax.crypto.Cipher; | import javax.crypto.Cipher; | ||||
import javax.crypto.spec.IvParameterSpec; | import javax.crypto.spec.IvParameterSpec; | ||||
import javax.crypto.spec.SecretKeySpec; | import javax.crypto.spec.SecretKeySpec; | ||||
import java.nio.charset.StandardCharsets; | |||||
import java.security.AlgorithmParameters; | |||||
import org.apache.commons.codec.binary.Base64; | |||||
import org.bouncycastle.jce.provider.BouncyCastleProvider; | |||||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||||
import me.chanjar.weixin.common.util.crypto.PKCS7Encoder; | |||||
/** | /** | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
public class WxMaCryptUtils extends me.chanjar.weixin.common.util.crypto.WxCryptUtil { | public class WxMaCryptUtils extends me.chanjar.weixin.common.util.crypto.WxCryptUtil { | ||||
private static final Charset UTF_8 = StandardCharsets.UTF_8; | |||||
public WxMaCryptUtils(WxMaConfig config) { | public WxMaCryptUtils(WxMaConfig config) { | ||||
this.appidOrCorpid = config.getAppid(); | this.appidOrCorpid = config.getAppid(); | ||||
this.token = config.getToken(); | this.token = config.getToken(); | ||||
@@ -21,8 +29,9 @@ public class WxMaCryptUtils extends me.chanjar.weixin.common.util.crypto.WxCrypt | |||||
} | } | ||||
/** | /** | ||||
* AES解密 | |||||
* AES解密. | |||||
* | * | ||||
* @param sessionKey session_key | |||||
* @param encryptedData 消息密文 | * @param encryptedData 消息密文 | ||||
* @param ivStr iv字符串 | * @param ivStr iv字符串 | ||||
*/ | */ | ||||
@@ -34,9 +43,40 @@ public class WxMaCryptUtils extends me.chanjar.weixin.common.util.crypto.WxCrypt | |||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | ||||
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(sessionKey), "AES"), params); | cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(sessionKey), "AES"), params); | ||||
return new String(PKCS7Encoder.decode(cipher.doFinal(Base64.decodeBase64(encryptedData))), StandardCharsets.UTF_8); | |||||
return new String(PKCS7Encoder.decode(cipher.doFinal(Base64.decodeBase64(encryptedData))), UTF_8); | |||||
} catch (Exception e) { | |||||
throw new RuntimeException("AES解密失败!", e); | |||||
} | |||||
} | |||||
/** | |||||
* AES解密. | |||||
* | |||||
* @param sessionKey session_key | |||||
* @param encryptedData 消息密文 | |||||
* @param ivStr iv字符串 | |||||
*/ | |||||
public static String decryptAnotherWay(String sessionKey, String encryptedData, String ivStr) { | |||||
byte[] keyBytes = Base64.decodeBase64(sessionKey.getBytes(UTF_8)); | |||||
int base = 16; | |||||
if (keyBytes.length % base != 0) { | |||||
int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0); | |||||
byte[] temp = new byte[groups * base]; | |||||
Arrays.fill(temp, (byte) 0); | |||||
System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length); | |||||
keyBytes = temp; | |||||
} | |||||
Security.addProvider(new BouncyCastleProvider()); | |||||
Key key = new SecretKeySpec(keyBytes, "AES"); | |||||
try { | |||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); | |||||
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(Base64.decodeBase64(ivStr.getBytes(UTF_8)))); | |||||
return new String(cipher.doFinal(Base64.decodeBase64(encryptedData.getBytes(UTF_8))), UTF_8); | |||||
} catch (Exception e) { | } catch (Exception e) { | ||||
throw new RuntimeException("AES解密失败", e); | |||||
throw new RuntimeException("AES解密失败!", e); | |||||
} | } | ||||
} | } | ||||
@@ -27,10 +27,6 @@ public class WxMaTemplateMessageGsonAdapter implements JsonSerializer<WxMaTempla | |||||
messageJson.addProperty("form_id", message.getFormId()); | messageJson.addProperty("form_id", message.getFormId()); | ||||
} | } | ||||
if (message.getColor() != null) { | |||||
messageJson.addProperty("color", message.getColor()); | |||||
} | |||||
if (message.getEmphasisKeyword() != null) { | if (message.getEmphasisKeyword() != null) { | ||||
messageJson.addProperty("emphasis_keyword", message.getEmphasisKeyword()); | messageJson.addProperty("emphasis_keyword", message.getEmphasisKeyword()); | ||||
} | } | ||||
@@ -45,9 +41,6 @@ public class WxMaTemplateMessageGsonAdapter implements JsonSerializer<WxMaTempla | |||||
for (WxMaTemplateData datum : message.getData()) { | for (WxMaTemplateData datum : message.getData()) { | ||||
JsonObject dataJson = new JsonObject(); | JsonObject dataJson = new JsonObject(); | ||||
dataJson.addProperty("value", datum.getValue()); | dataJson.addProperty("value", datum.getValue()); | ||||
if (datum.getColor() != null) { | |||||
dataJson.addProperty("color", datum.getColor()); | |||||
} | |||||
data.add(datum.getName(), dataJson); | data.add(datum.getName(), dataJson); | ||||
} | } | ||||
@@ -36,6 +36,8 @@ public class WxMaUniformMessageGsonAdapter implements JsonSerializer<WxMaUniform | |||||
miniProgramJson.addProperty("appid", miniProgram.getAppid()); | miniProgramJson.addProperty("appid", miniProgram.getAppid()); | ||||
if (miniProgram.isUsePath()) { | if (miniProgram.isUsePath()) { | ||||
miniProgramJson.addProperty("path", miniProgram.getPagePath()); | miniProgramJson.addProperty("path", miniProgram.getPagePath()); | ||||
} else if (miniProgram.isUsePagePath()) { | |||||
miniProgramJson.addProperty("pagePath", miniProgram.getPagePath()); | |||||
} else { | } else { | ||||
miniProgramJson.addProperty("pagepath", miniProgram.getPagePath()); | miniProgramJson.addProperty("pagepath", miniProgram.getPagePath()); | ||||
} | } | ||||
@@ -79,9 +81,6 @@ public class WxMaUniformMessageGsonAdapter implements JsonSerializer<WxMaUniform | |||||
for (WxMaTemplateData templateData : message.getData()) { | for (WxMaTemplateData templateData : message.getData()) { | ||||
JsonObject dataJson = new JsonObject(); | JsonObject dataJson = new JsonObject(); | ||||
dataJson.addProperty("value", templateData.getValue()); | dataJson.addProperty("value", templateData.getValue()); | ||||
if (templateData.getColor() != null) { | |||||
dataJson.addProperty("color", templateData.getColor()); | |||||
} | |||||
data.add(templateData.getName(), dataJson); | data.add(templateData.getName(), dataJson); | ||||
} | } | ||||
} | } | ||||
@@ -10,9 +10,9 @@ import com.google.gson.JsonParseException; | |||||
import me.chanjar.weixin.common.util.json.GsonHelper; | import me.chanjar.weixin.common.util.json.GsonHelper; | ||||
import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||
import java.util.Hashtable; | |||||
import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.concurrent.ConcurrentHashMap; | |||||
/** | /** | ||||
* @author <a href="https://github.com/charmingoh">Charming</a> | * @author <a href="https://github.com/charmingoh">Charming</a> | ||||
@@ -36,7 +36,7 @@ public class WxMaVisitDistributionGsonAdapter implements JsonDeserializer<WxMaVi | |||||
} | } | ||||
JsonArray listArray = object.getAsJsonArray("list"); | JsonArray listArray = object.getAsJsonArray("list"); | ||||
Map<String, Map<Integer, Integer>> list = new Hashtable<>(listArray.size()); | |||||
Map<String, Map<Integer, Integer>> list = new ConcurrentHashMap<>(listArray.size()); | |||||
for (JsonElement indexElement : listArray) { | for (JsonElement indexElement : listArray) { | ||||
JsonObject indexObject = indexElement.getAsJsonObject(); | JsonObject indexObject = indexElement.getAsJsonObject(); | ||||
String index = GsonHelper.getString(indexObject, "index"); | String index = GsonHelper.getString(indexObject, "index"); | ||||
@@ -48,10 +48,10 @@ public class WxMaMsgServiceImplTest { | |||||
.formId("FORMID") | .formId("FORMID") | ||||
.page("index") | .page("index") | ||||
.data(Lists.newArrayList( | .data(Lists.newArrayList( | ||||
new WxMaTemplateData("keyword1", "339208499", "#173177"), | |||||
new WxMaTemplateData("keyword2", dateFormat.format(new Date()), "#173177"), | |||||
new WxMaTemplateData("keyword3", "粤海喜来登酒店", "#173177"), | |||||
new WxMaTemplateData("keyword4", "广州市天河区天河路208号", "#173177"))) | |||||
new WxMaTemplateData("keyword1", "339208499"), | |||||
new WxMaTemplateData("keyword2", dateFormat.format(new Date())), | |||||
new WxMaTemplateData("keyword3", "粤海喜来登酒店"), | |||||
new WxMaTemplateData("keyword4", "广州市天河区天河路208号"))) | |||||
.templateId(config.getTemplateId()) | .templateId(config.getTemplateId()) | ||||
.emphasisKeyword("keyword1.DATA") | .emphasisKeyword("keyword1.DATA") | ||||
.build(); | .build(); | ||||
@@ -1,7 +1,5 @@ | |||||
package cn.binarywang.wx.miniapp.api.impl; | package cn.binarywang.wx.miniapp.api.impl; | ||||
import java.io.File; | |||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
import org.testng.annotations.*; | import org.testng.annotations.*; | ||||
@@ -11,6 +9,7 @@ import cn.binarywang.wx.miniapp.test.ApiTestModule; | |||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.testng.Assert.*; | import static org.testng.Assert.*; | ||||
/** | /** | ||||
@@ -33,4 +32,9 @@ public class WxMaServiceImplTest { | |||||
assertTrue(StringUtils.isNotBlank(after)); | assertTrue(StringUtils.isNotBlank(after)); | ||||
} | } | ||||
@Test(expectedExceptions = {WxErrorException.class}) | |||||
public void testGetPaidUnionId() throws WxErrorException { | |||||
final String unionId = this.wxService.getPaidUnionId("1", null, "3", "4"); | |||||
assertThat(unionId).isNotEmpty(); | |||||
} | |||||
} | } |
@@ -0,0 +1,35 @@ | |||||
package cn.binarywang.wx.miniapp.util.crypt; | |||||
import org.testng.annotations.*; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
/** | |||||
* <pre> | |||||
* | |||||
* Created by Binary Wang on 2018/12/25. | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | |||||
public class WxMaCryptUtilsTest { | |||||
@Test | |||||
public void testDecrypt() { | |||||
String sessionKey = "7MG7jbTToVVRWRXVA885rg=="; | |||||
String encryptedData = "BY6VOgcWbwGcyrunK0ECWI8rnDsT69DucZ+M78tc1aL9aM/3bEAHFYd4fu7kRjWhD4YfjObw44T9vUqKyHIjbKs6hvtEasZZEIW35x4a91xVgN48ZqZ7MTQqUlP13kDUlkuwYh+/8g8yceu4kNbjowYrhihx+SV7CfjKCveJ7TSepr5Z7aLv1o+rfeelfOwn++WN/YoQsuZ6S3L4fWlWe5DAAUnFUI6cJvxxCohVzbrVXhyH2AqQdSjH2WnMYFeaGFIbcoxMznlk7oEwFn+hBj63dyT/swdYQfEdzuyCBmKXy8d6l1RKVX6Y65coTD8kIlbr+FKsqYrXVUIUBSwehqYuOdhYWZ9Bntl5DWU1oqzAPCnMn2cAIoQpQPKP7IGSxMOvCNAMhVXbE7BvnWuVuGF+AM5tXAa9IVUhcMImGwLQqm4iV5uBd+5OcFObh3A4VJk9iBCBWSkBHa/rV9CVoY0bFv2F9/2Hv82++Ybl274="; | |||||
String ivStr = "TarMFjnzHVxy8pdS93wQbw=="; | |||||
System.out.println(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | |||||
// System.out.println(WxMaCryptUtils.decryptAnotherWay(sessionKey, encryptedData, ivStr)); | |||||
} | |||||
@Test | |||||
public void testDecryptAnotherWay() { | |||||
String encryptedData = "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew=="; | |||||
String ivStr = "r7BXXKkLb8qrSNn05n0qiA=="; | |||||
String sessionKey = "tiihtNczf5v6AKRyjwEUhQ=="; | |||||
assertThat(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)) | |||||
.isEqualTo(WxMaCryptUtils.decryptAnotherWay(sessionKey, encryptedData, ivStr)); | |||||
} | |||||
} |
@@ -1,10 +1,9 @@ | |||||
package cn.binarywang.wx.miniapp.util.json; | package cn.binarywang.wx.miniapp.util.json; | ||||
import org.testng.annotations.*; | |||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateData; | import cn.binarywang.wx.miniapp.bean.WxMaTemplateData; | ||||
import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; | import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; | ||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
import org.testng.annotations.Test; | |||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
@@ -26,7 +25,7 @@ public class WxMaUniformMessageGsonAdapterTest { | |||||
.appid("APPID") | .appid("APPID") | ||||
.templateId("TEMPLATE_ID") | .templateId("TEMPLATE_ID") | ||||
.url("http://weixin.qq.com/download") | .url("http://weixin.qq.com/download") | ||||
.miniProgram(new WxMaUniformMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar", false)) | |||||
.miniProgram(new WxMaUniformMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar", false, false)) | |||||
.build(); | .build(); | ||||
message.addData(new WxMaTemplateData("first", "恭喜你购买成功!", "#173177")) | message.addData(new WxMaTemplateData("first", "恭喜你购买成功!", "#173177")) | ||||
.addData(new WxMaTemplateData("keyword1", "巧克力", "#173177")) | .addData(new WxMaTemplateData("keyword1", "巧克力", "#173177")) | ||||
@@ -72,7 +71,7 @@ public class WxMaUniformMessageGsonAdapterTest { | |||||
@Test | @Test | ||||
public void testSerialize_ma() { | public void testSerialize_ma() { | ||||
WxMaUniformMessage message = WxMaUniformMessage.builder() | |||||
WxMaUniformMessage message = WxMaUniformMessage.builder() | |||||
.isMpTemplateMsg(false) | .isMpTemplateMsg(false) | ||||
.toUser("OPENID") | .toUser("OPENID") | ||||
.page("page/page/index") | .page("page/page/index") | ||||
@@ -7,10 +7,11 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>wx-java</artifactId> | <artifactId>wx-java</artifactId> | ||||
<version>3.3.0</version> | |||||
<version>3.4.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-mp</artifactId> | <artifactId>weixin-java-mp</artifactId> | ||||
<name>WxJava - MP</name> | |||||
<name>WxJava - MP Java SDK</name> | |||||
<description>微信公众号Java SDK</description> | <description>微信公众号Java SDK</description> | ||||
<dependencies> | <dependencies> | ||||
@@ -1,10 +1,10 @@ | |||||
package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
import java.io.File; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.mp.enums.AiLangType; | import me.chanjar.weixin.mp.enums.AiLangType; | ||||
import java.io.File; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 微信AI开放接口(语音识别,微信翻译). | * 微信AI开放接口(语音识别,微信翻译). | ||||
@@ -15,24 +15,15 @@ import java.io.File; | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
public interface WxMpAiOpenService { | public interface WxMpAiOpenService { | ||||
String TRANSLATE_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?lfrom=%s<o=%s"; | |||||
String VOICE_UPLOAD_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext?format=%s&voice_id=%s&lang=%s"; | String VOICE_UPLOAD_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext?format=%s&voice_id=%s&lang=%s"; | ||||
String VOICE_QUERY_RESULT_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext"; | String VOICE_QUERY_RESULT_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext"; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 提交语音. | * 提交语音. | ||||
* 接口调用请求说明 | |||||
* | |||||
* http请求方式: POST | * http请求方式: POST | ||||
* http://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext?access_token=ACCESS_TOKEN&format=&voice_id=xxxxxx&lang=zh_CN | * http://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext?access_token=ACCESS_TOKEN&format=&voice_id=xxxxxx&lang=zh_CN | ||||
* 参数说明 | |||||
* | |||||
* 参数 是否必须 说明 | |||||
* access_token 是 接口调用凭证 | |||||
* format 是 文件格式 (只支持mp3,16k,单声道,最大1M) | |||||
* voice_id 是 语音唯一标识 | |||||
* lang 否 语言,zh_CN 或 en_US,默认中文 | |||||
* 语音内容放body里或者上传文件的形式 | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param lang 语言,zh_CN 或 en_US,默认中文 | * @param lang 语言,zh_CN 或 en_US,默认中文 | ||||
@@ -46,16 +37,9 @@ public interface WxMpAiOpenService { | |||||
* 获取语音识别结果. | * 获取语音识别结果. | ||||
* 接口调用请求说明 | * 接口调用请求说明 | ||||
* | * | ||||
* http请求方式: POST | |||||
* http://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext?access_token=ACCESS_TOKEN&voice_id=xxxxxx&lang=zh_CN | * http://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext?access_token=ACCESS_TOKEN&voice_id=xxxxxx&lang=zh_CN | ||||
* 请注意,添加完文件之后10s内调用这个接口 | * 请注意,添加完文件之后10s内调用这个接口 | ||||
* | * | ||||
* 参数说明 | |||||
* | |||||
* 参数 是否必须 说明 | |||||
* access_token 是 接口调用凭证 | |||||
* voice_id 是 语音唯一标识 | |||||
* lang 否 语言,zh_CN 或 en_US,默认中文 | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param lang 语言,zh_CN 或 en_US,默认中文 | * @param lang 语言,zh_CN 或 en_US,默认中文 | ||||
@@ -80,18 +64,12 @@ public interface WxMpAiOpenService { | |||||
* | * | ||||
* http请求方式: POST | * http请求方式: POST | ||||
* http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?access_token=ACCESS_TOKEN&lfrom=xxx<o=xxx | * http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?access_token=ACCESS_TOKEN&lfrom=xxx<o=xxx | ||||
* 参数说明 | |||||
* | * | ||||
* 参数 是否必须 说明 | |||||
* access_token 是 接口调用凭证 | |||||
* lfrom 是 源语言,zh_CN 或 en_US | |||||
* lto 是 目标语言,zh_CN 或 en_US | |||||
* 源内容放body里或者上传文件的形式(utf8格式,最大600Byte) | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param langFrom 源语言,zh_CN 或 en_US | * @param langFrom 源语言,zh_CN 或 en_US | ||||
* @param langTo 目标语言,zh_CN 或 en_US | |||||
* @param content 要翻译的文本内容 | |||||
* @param langTo 目标语言,zh_CN 或 en_US | |||||
* @param content 要翻译的文本内容 | |||||
*/ | */ | ||||
String translate(AiLangType langFrom, AiLangType langTo, String content) throws WxErrorException; | String translate(AiLangType langFrom, AiLangType langTo, String content) throws WxErrorException; | ||||
} | } |
@@ -2,10 +2,8 @@ package me.chanjar.weixin.mp.api; | |||||
import me.chanjar.weixin.common.bean.WxCardApiSignature; | import me.chanjar.weixin.common.bean.WxCardApiSignature; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateRequest; | |||||
import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateResult; | |||||
import me.chanjar.weixin.mp.bean.card.WxMpCardQrcodeCreateResult; | |||||
import me.chanjar.weixin.mp.bean.result.WxMpCardResult; | |||||
import me.chanjar.weixin.mp.bean.card.*; | |||||
import me.chanjar.weixin.mp.bean.card.WxMpCardResult; | |||||
/** | /** | ||||
* 卡券相关接口 | * 卡券相关接口 | ||||
@@ -14,6 +12,7 @@ import me.chanjar.weixin.mp.bean.result.WxMpCardResult; | |||||
* @author yuanqixun 2018-08-29 | * @author yuanqixun 2018-08-29 | ||||
*/ | */ | ||||
public interface WxMpCardService { | public interface WxMpCardService { | ||||
String CARD_CREATE = "https://api.weixin.qq.com/card/create"; | |||||
String CARD_GET = "https://api.weixin.qq.com/card/get"; | String CARD_GET = "https://api.weixin.qq.com/card/get"; | ||||
String CARD_GET_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; | String CARD_GET_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; | ||||
String CARD_CODE_DECRYPT = "https://api.weixin.qq.com/card/code/decrypt"; | String CARD_CODE_DECRYPT = "https://api.weixin.qq.com/card/code/decrypt"; | ||||
@@ -28,6 +27,11 @@ public interface WxMpCardService { | |||||
*/ | */ | ||||
String CARD_CODE_UNAVAILABLE = "https://api.weixin.qq.com/card/code/unavailable"; | String CARD_CODE_UNAVAILABLE = "https://api.weixin.qq.com/card/code/unavailable"; | ||||
/** | |||||
* 卡券删除 | |||||
*/ | |||||
String CARD_DELETE = "https://api.weixin.qq.com/card/delete"; | |||||
/** | /** | ||||
* 得到WxMpService | * 得到WxMpService | ||||
*/ | */ | ||||
@@ -81,8 +85,8 @@ public interface WxMpCardService { | |||||
String decryptCardCode(String encryptCode) throws WxErrorException; | String decryptCardCode(String encryptCode) throws WxErrorException; | ||||
/** | /** | ||||
* 卡券Code查询 | |||||
* | |||||
* 卡券Code查询. | |||||
* 文档地址: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025272&anchor=1 | |||||
* @param cardId 卡券ID代表一类卡券 | * @param cardId 卡券ID代表一类卡券 | ||||
* @param code 单张卡券的唯一标准 | * @param code 单张卡券的唯一标准 | ||||
* @param checkConsume 是否校验code核销状态,填入true和false时的code异常状态返回数据不同 | * @param checkConsume 是否校验code核销状态,填入true和false时的code异常状态返回数据不同 | ||||
@@ -142,6 +146,14 @@ public interface WxMpCardService { | |||||
*/ | */ | ||||
String addTestWhiteList(String openid) throws WxErrorException; | String addTestWhiteList(String openid) throws WxErrorException; | ||||
/** | |||||
* | |||||
* @param cardCreateMessage | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | |||||
WxMpCardCreateResult createCard(WxMpCardCreateMessage cardCreateMessage) throws WxErrorException; | |||||
/** | /** | ||||
* 创建卡券二维码 | * 创建卡券二维码 | ||||
* | * | ||||
@@ -182,4 +194,12 @@ public interface WxMpCardService { | |||||
*/ | */ | ||||
String unavailableCardCode(String cardId, String code, String reason) throws WxErrorException; | String unavailableCardCode(String cardId, String code, String reason) throws WxErrorException; | ||||
/** | |||||
* 删除卡券接口 | |||||
* @param cardId | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | |||||
WxMpCardDeleteResult deleteCard(String cardId) throws WxErrorException; | |||||
} | } |
@@ -0,0 +1,71 @@ | |||||
package me.chanjar.weixin.mp.api; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadFilter; | |||||
import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadResult; | |||||
import me.chanjar.weixin.mp.bean.marketing.WxMpUserAction; | |||||
import me.chanjar.weixin.mp.bean.marketing.WxMpUserActionSet; | |||||
import java.io.IOException; | |||||
import java.util.Date; | |||||
import java.util.List; | |||||
/** | |||||
* <pre> | |||||
* 微信营销接口 | |||||
* </pre> | |||||
* | |||||
* @author <a href="https://github.com/007gzs">007</a> | |||||
*/ | |||||
public interface WxMpMarketingService { | |||||
String API_URL_PREFIX = "https://api.weixin.qq.com/marketing/"; | |||||
/** | |||||
* <pre> | |||||
* 创建数据源 | |||||
* 接口调用请求说明 | |||||
* https://wximg.qq.com/wxp/pdftool/get.html?id=rkalQXDBM&pa=39 | |||||
* </pre> | |||||
* | |||||
* @param type 用户行为源类型 | |||||
* @param name 用户行为源名称 必填 | |||||
* @param description 用户行为源描述,字段长度最小 1 字节,长度最大 128 字节 | |||||
*/ | |||||
long addUserActionSets(String type, String name, String description) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 获取数据源信息 | |||||
* </pre> | |||||
* | |||||
* @param userActionSetId 数据源唯一ID | |||||
*/ | |||||
List<WxMpUserActionSet> getUserActionSets(Long userActionSetId) throws WxErrorException; | |||||
/** | |||||
* 回传数据 | |||||
* 接口调用请求说明 | |||||
* https://wximg.qq.com/wxp/pdftool/get.html?id=rkalQXDBM&pa=39 | |||||
* | |||||
* @param actions 用户行为源类型 | |||||
*/ | |||||
void addUserAction(List<WxMpUserAction> actions) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 获取朋友圈销售线索数据接口 | |||||
* 接口调用请求说明 | |||||
* | |||||
* http请求方式: POST | |||||
* http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?access_token=ACCESS_TOKEN&lfrom=xxx<o=xxx | |||||
* | |||||
* </pre> | |||||
* | |||||
* @param beginDate 开始日期 | |||||
* @param endDate 结束日期 | |||||
* @param filtering 过滤条件 | |||||
* @param page 页码,获取指定页数据 | |||||
* @param page_size 一页获取的数据条数(1-100) | |||||
*/ | |||||
WxMpAdLeadResult getAdLeads(Date beginDate, Date endDate, List<WxMpAdLeadFilter> filtering, Integer page, Integer page_size) throws WxErrorException, IOException; | |||||
} |
@@ -6,12 +6,7 @@ import me.chanjar.weixin.mp.bean.card.MemberCardActivateUserFormRequest; | |||||
import me.chanjar.weixin.mp.bean.card.MemberCardActivateUserFormResult; | import me.chanjar.weixin.mp.bean.card.MemberCardActivateUserFormResult; | ||||
import me.chanjar.weixin.mp.bean.card.MemberCardUpdateRequest; | import me.chanjar.weixin.mp.bean.card.MemberCardUpdateRequest; | ||||
import me.chanjar.weixin.mp.bean.card.WxMpCardCreateResult; | import me.chanjar.weixin.mp.bean.card.WxMpCardCreateResult; | ||||
import me.chanjar.weixin.mp.bean.membercard.ActivatePluginParam; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardActivatedMessage; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardCreateMessage; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateMessage; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult; | |||||
import me.chanjar.weixin.mp.bean.membercard.*; | |||||
/** | /** | ||||
* 会员卡相关接口. | * 会员卡相关接口. | ||||
@@ -22,14 +17,14 @@ import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult; | |||||
* @date 2018-08-30 | * @date 2018-08-30 | ||||
*/ | */ | ||||
public interface WxMpMemberCardService { | public interface WxMpMemberCardService { | ||||
String MEMBER_CARD_CREAET = "https://api.weixin.qq.com/card/create"; | |||||
String MEMBER_CARD_CREATE = "https://api.weixin.qq.com/card/create"; | |||||
String MEMBER_CARD_ACTIVATE = "https://api.weixin.qq.com/card/membercard/activate"; | String MEMBER_CARD_ACTIVATE = "https://api.weixin.qq.com/card/membercard/activate"; | ||||
String MEMBER_CARD_USER_INFO_GET = "https://api.weixin.qq.com/card/membercard/userinfo/get"; | String MEMBER_CARD_USER_INFO_GET = "https://api.weixin.qq.com/card/membercard/userinfo/get"; | ||||
String MEMBER_CARD_UPDATE_USER = "https://api.weixin.qq.com/card/membercard/updateuser"; | String MEMBER_CARD_UPDATE_USER = "https://api.weixin.qq.com/card/membercard/updateuser"; | ||||
/** | /** | ||||
* 会员卡激活之微信开卡接口(wx_activate=true情况调用). | * 会员卡激活之微信开卡接口(wx_activate=true情况调用). | ||||
*/ | */ | ||||
String MEMBER_CARD_ACTIVATEUSERFORM = "https://api.weixin.qq.com/card/membercard/activateuserform/set"; | |||||
String MEMBER_CARD_ACTIVATE_USER_FORM = "https://api.weixin.qq.com/card/membercard/activateuserform/set"; | |||||
/** | /** | ||||
* 获取会员卡开卡插件参数. | * 获取会员卡开卡插件参数. | ||||
@@ -41,19 +36,34 @@ public interface WxMpMemberCardService { | |||||
*/ | */ | ||||
String MEMBER_CARD_UPDATE = "https://api.weixin.qq.com/card/update"; | String MEMBER_CARD_UPDATE = "https://api.weixin.qq.com/card/update"; | ||||
/** | |||||
* 跳转型会员卡开卡字段. | |||||
* 获取用户提交资料(wx_activate=true情况调用),开发者根据activate_ticket获取到用户填写的信息 | |||||
*/ | |||||
String MEMBER_CARD_ACTIVATE_TEMP_INFO = "https://api.weixin.qq.com/card/membercard/activatetempinfo/get"; | |||||
/** | /** | ||||
* 得到WxMpService. | * 得到WxMpService. | ||||
* | |||||
* @return WxMpService | |||||
*/ | */ | ||||
WxMpService getWxMpService(); | WxMpService getWxMpService(); | ||||
/** | /** | ||||
* 会员卡创建接口. | * 会员卡创建接口. | ||||
* | |||||
* @param createJson 会员卡json字符串 | |||||
* @return 返回json字符串 | |||||
* @throws WxErrorException 接口调用失败抛出的异常 | |||||
*/ | */ | ||||
WxMpCardCreateResult createMemberCard(String createJson) throws WxErrorException; | WxMpCardCreateResult createMemberCard(String createJson) throws WxErrorException; | ||||
/** | /** | ||||
* 会员卡创建接口. | |||||
* 会员卡创建接口 | |||||
* | |||||
* @param createMessageMessage 会员卡创建对象 | |||||
* @return 会员卡信息的结果对象 | |||||
* @throws WxErrorException 接口调用失败抛出的异常 | |||||
*/ | */ | ||||
WxMpCardCreateResult createMemberCard(WxMpMemberCardCreateMessage createMessageMessage) throws WxErrorException; | WxMpCardCreateResult createMemberCard(WxMpMemberCardCreateMessage createMessageMessage) throws WxErrorException; | ||||
@@ -61,7 +71,7 @@ public interface WxMpMemberCardService { | |||||
* 会员卡激活接口. | * 会员卡激活接口. | ||||
* | * | ||||
* @param activatedMessage 激活所需参数 | * @param activatedMessage 激活所需参数 | ||||
* @return 返回json字符串 | |||||
* @return 会员卡激活后的json字符串 | |||||
* @throws WxErrorException 接口调用失败抛出的异常 | * @throws WxErrorException 接口调用失败抛出的异常 | ||||
*/ | */ | ||||
String activateMemberCard(WxMpMemberCardActivatedMessage activatedMessage) throws WxErrorException; | String activateMemberCard(WxMpMemberCardActivatedMessage activatedMessage) throws WxErrorException; | ||||
@@ -91,16 +101,40 @@ public interface WxMpMemberCardService { | |||||
/** | /** | ||||
* 设置会员卡激活的字段(会员卡设置:wx_activate=true 时需要). | * 设置会员卡激活的字段(会员卡设置:wx_activate=true 时需要). | ||||
* | |||||
* @param userFormRequest 会员卡激活字段对象 | |||||
* @return 会员卡激活后结果对象 | |||||
* @throws WxErrorException 接口调用失败抛出的异常 | |||||
*/ | */ | ||||
MemberCardActivateUserFormResult setActivateUserForm(MemberCardActivateUserFormRequest userFormRequest) throws WxErrorException; | MemberCardActivateUserFormResult setActivateUserForm(MemberCardActivateUserFormRequest userFormRequest) throws WxErrorException; | ||||
/** | /** | ||||
* 获取会员卡开卡插件参数(跳转型开卡组件需要参数). | * 获取会员卡开卡插件参数(跳转型开卡组件需要参数). | ||||
* | |||||
* @param cardId 会员卡的CardId,微信分配 | |||||
* @param outStr 会员卡设置商户的渠道 | |||||
* @return 会员卡开卡插件参数结果对象 | |||||
* @throws WxErrorException 接口调用失败抛出的异常 | |||||
*/ | */ | ||||
ActivatePluginParam getActivatePluginParam(String cardId, String outStr) throws WxErrorException; | ActivatePluginParam getActivatePluginParam(String cardId, String outStr) throws WxErrorException; | ||||
/** | /** | ||||
* 更新会员卡信息. | * 更新会员卡信息. | ||||
* | |||||
* @param memberCardUpdateRequest 会员卡更新对象 | |||||
* @return 会员卡更新后结果对象 | |||||
* @throws WxErrorException 接口调用失败抛出的异常 | |||||
*/ | */ | ||||
CardUpdateResult updateCardInfo(MemberCardUpdateRequest memberCardUpdateRequest) throws WxErrorException; | CardUpdateResult updateCardInfo(MemberCardUpdateRequest memberCardUpdateRequest) throws WxErrorException; | ||||
/** | |||||
* 解析跳转型开卡字段用户提交的资料. | |||||
* 开发者在URL上截取ticket后须先进行urldecode | |||||
* | |||||
* @param activateTicket 用户提交的资料 | |||||
* @return 开卡字段的会员信息对象 | |||||
* @throws WxErrorException 接口调用失败抛出的异常 | |||||
*/ | |||||
WxMpMemberCardActivateTempInfoResult getActivateTempInfo(String activateTicket) throws WxErrorException; | |||||
} | } |
@@ -5,6 +5,7 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | ||||
import me.chanjar.weixin.common.util.http.RequestExecutor; | import me.chanjar.weixin.common.util.http.RequestExecutor; | ||||
import me.chanjar.weixin.common.util.http.RequestHttp; | import me.chanjar.weixin.common.util.http.RequestHttp; | ||||
import me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl; | |||||
import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; | import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; | ||||
import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; | import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; | ||||
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | ||||
@@ -12,6 +13,8 @@ import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult; | |||||
import me.chanjar.weixin.mp.bean.result.WxMpUser; | import me.chanjar.weixin.mp.bean.result.WxMpUser; | ||||
import me.chanjar.weixin.mp.enums.TicketType; | import me.chanjar.weixin.mp.enums.TicketType; | ||||
import java.util.Map; | |||||
/** | /** | ||||
* 微信公众号API的Service. | * 微信公众号API的Service. | ||||
* | * | ||||
@@ -302,10 +305,50 @@ public interface WxMpService { | |||||
WxMpConfigStorage getWxMpConfigStorage(); | WxMpConfigStorage getWxMpConfigStorage(); | ||||
/** | /** | ||||
* 注入 {@link WxMpConfigStorage} 的实现. | |||||
* 设置 {@link WxMpConfigStorage} 的实现. 兼容老版本 | |||||
*/ | */ | ||||
void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider); | void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider); | ||||
/** | |||||
* {@link Map<String, WxMpConfigStorage>} 加入新的 {@link WxMpConfigStorage},适用于动态添加新的微信公众号配置 | |||||
* @param configStorage 新的微信配置 | |||||
*/ | |||||
void addConfigStorage(String mpId, WxMpConfigStorage configStorage); | |||||
/** | |||||
* 从{@link Map<String, WxMpConfigStorage>} 移除 {@link String mpId} 所对应的 {@link WxMpConfigStorage},适用于动态移除微信公众号配置 | |||||
* @param mpId 对应公众号的标识 | |||||
*/ | |||||
void removeConfigStorage(String mpId); | |||||
/** | |||||
* 注入多个 {@link WxMpConfigStorage} 的实现. 并为每个 {@link WxMpConfigStorage} 赋予不同的 {@link String mpId} 值 | |||||
* 随机采用一个{@link String mpId}进行Http初始化操作 | |||||
* @param configStorages WxMpConfigStorage map | |||||
*/ | |||||
void setMultiConfigStorages(Map<String, WxMpConfigStorage> configStorages); | |||||
/** | |||||
* 注入多个 {@link WxMpConfigStorage} 的实现. 并为每个 {@link WxMpConfigStorage} 赋予不同的 {@link String label} 值 | |||||
* @param configStorages WxMpConfigStorage map | |||||
* @param defaultMpId 设置一个{@link WxMpConfigStorage} 所对应的{@link String mpId}进行Http初始化 | |||||
*/ | |||||
void setMultiConfigStorages(Map<String, WxMpConfigStorage> configStorages, String defaultMpId); | |||||
/** | |||||
* 进行相应的公众号切换 | |||||
* @param mpId 公众号标识 | |||||
* @return 切换是否成功 | |||||
*/ | |||||
boolean switchover(String mpId); | |||||
/** | |||||
* 进行相应的公众号切换 | |||||
* @param mpId 公众号标识 | |||||
* @return 切换成功,则返回当前对象,方便链式调用,否则抛出异常 | |||||
*/ | |||||
WxMpService switchoverTo(String mpId); | |||||
/** | /** | ||||
* 返回客服接口方法实现类,以方便调用其各个接口. | * 返回客服接口方法实现类,以方便调用其各个接口. | ||||
* | * | ||||
@@ -411,6 +454,13 @@ public interface WxMpService { | |||||
*/ | */ | ||||
WxMpMemberCardService getMemberCardService(); | WxMpMemberCardService getMemberCardService(); | ||||
/** | |||||
* 返回营销相关接口方法的实现类对象,以方便调用其各个接口. | |||||
* | |||||
* @return WxMpMarketingService | |||||
*/ | |||||
WxMpMarketingService getMarketingService(); | |||||
/** | /** | ||||
* 初始化http请求对象. | * 初始化http请求对象. | ||||
*/ | */ | ||||
@@ -473,4 +523,6 @@ public interface WxMpService { | |||||
void setMassMessageService(WxMpMassMessageService massMessageService); | void setMassMessageService(WxMpMassMessageService massMessageService); | ||||
void setAiOpenService(WxMpAiOpenService aiOpenService); | void setAiOpenService(WxMpAiOpenService aiOpenService); | ||||
void setMarketingService(WxMpMarketingService marketingService); | |||||
} | } |
@@ -1,12 +1,7 @@ | |||||
package me.chanjar.weixin.mp.api.impl; | package me.chanjar.weixin.mp.api.impl; | ||||
import java.io.IOException; | |||||
import java.util.concurrent.locks.Lock; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import com.google.common.collect.ImmutableMap; | |||||
import com.google.common.collect.Maps; | |||||
import com.google.gson.JsonArray; | import com.google.gson.JsonArray; | ||||
import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||
import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
@@ -19,37 +14,22 @@ import me.chanjar.weixin.common.session.WxSessionManager; | |||||
import me.chanjar.weixin.common.util.DataUtils; | import me.chanjar.weixin.common.util.DataUtils; | ||||
import me.chanjar.weixin.common.util.RandomUtils; | import me.chanjar.weixin.common.util.RandomUtils; | ||||
import me.chanjar.weixin.common.util.crypto.SHA1; | import me.chanjar.weixin.common.util.crypto.SHA1; | ||||
import me.chanjar.weixin.common.util.http.RequestExecutor; | |||||
import me.chanjar.weixin.common.util.http.RequestHttp; | |||||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||||
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||||
import me.chanjar.weixin.common.util.http.URIUtil; | |||||
import me.chanjar.weixin.mp.api.WxMpAiOpenService; | |||||
import me.chanjar.weixin.mp.api.WxMpCardService; | |||||
import me.chanjar.weixin.mp.api.WxMpConfigStorage; | |||||
import me.chanjar.weixin.mp.api.WxMpDataCubeService; | |||||
import me.chanjar.weixin.mp.api.WxMpDeviceService; | |||||
import me.chanjar.weixin.mp.api.WxMpKefuService; | |||||
import me.chanjar.weixin.mp.api.WxMpMassMessageService; | |||||
import me.chanjar.weixin.mp.api.WxMpMaterialService; | |||||
import me.chanjar.weixin.mp.api.WxMpMemberCardService; | |||||
import me.chanjar.weixin.mp.api.WxMpMenuService; | |||||
import me.chanjar.weixin.mp.api.WxMpQrcodeService; | |||||
import me.chanjar.weixin.mp.api.WxMpService; | |||||
import me.chanjar.weixin.mp.api.WxMpShakeService; | |||||
import me.chanjar.weixin.mp.api.WxMpStoreService; | |||||
import me.chanjar.weixin.mp.api.WxMpSubscribeMsgService; | |||||
import me.chanjar.weixin.mp.api.WxMpTemplateMsgService; | |||||
import me.chanjar.weixin.mp.api.WxMpUserBlacklistService; | |||||
import me.chanjar.weixin.mp.api.WxMpUserService; | |||||
import me.chanjar.weixin.mp.api.WxMpUserTagService; | |||||
import me.chanjar.weixin.mp.api.WxMpWifiService; | |||||
import me.chanjar.weixin.common.util.http.*; | |||||
import me.chanjar.weixin.mp.api.*; | |||||
import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; | import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; | ||||
import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; | import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; | ||||
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | ||||
import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult; | import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult; | ||||
import me.chanjar.weixin.mp.bean.result.WxMpUser; | import me.chanjar.weixin.mp.bean.result.WxMpUser; | ||||
import me.chanjar.weixin.mp.enums.TicketType; | import me.chanjar.weixin.mp.enums.TicketType; | ||||
import me.chanjar.weixin.mp.util.WxMpConfigStorageHolder; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.io.IOException; | |||||
import java.util.Map; | |||||
import java.util.concurrent.locks.Lock; | |||||
/** | /** | ||||
* 基础实现类. | * 基础实现类. | ||||
@@ -62,7 +42,6 @@ public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestH | |||||
protected final Logger log = LoggerFactory.getLogger(this.getClass()); | protected final Logger log = LoggerFactory.getLogger(this.getClass()); | ||||
protected WxSessionManager sessionManager = new StandardSessionManager(); | protected WxSessionManager sessionManager = new StandardSessionManager(); | ||||
protected WxMpConfigStorage wxMpConfigStorage; | |||||
private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this); | private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this); | ||||
private WxMpMaterialService materialService = new WxMpMaterialServiceImpl(this); | private WxMpMaterialService materialService = new WxMpMaterialServiceImpl(this); | ||||
private WxMpMenuService menuService = new WxMpMenuServiceImpl(this); | private WxMpMenuService menuService = new WxMpMenuServiceImpl(this); | ||||
@@ -81,11 +60,13 @@ public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestH | |||||
private WxMpMassMessageService massMessageService = new WxMpMassMessageServiceImpl(this); | private WxMpMassMessageService massMessageService = new WxMpMassMessageServiceImpl(this); | ||||
private WxMpAiOpenService aiOpenService = new WxMpAiOpenServiceImpl(this); | private WxMpAiOpenService aiOpenService = new WxMpAiOpenServiceImpl(this); | ||||
private WxMpWifiService wifiService = new WxMpWifiServiceImpl(this); | private WxMpWifiService wifiService = new WxMpWifiServiceImpl(this); | ||||
private WxMpMarketingService marketingService = new WxMpMarketingServiceImpl(this); | |||||
private Map<String, WxMpConfigStorage> configStorageMap; | |||||
private int retrySleepMillis = 1000; | private int retrySleepMillis = 1000; | ||||
private int maxRetryTimes = 5; | private int maxRetryTimes = 5; | ||||
@Override | @Override | ||||
public boolean checkSignature(String timestamp, String nonce, String signature) { | public boolean checkSignature(String timestamp, String nonce, String signature) { | ||||
try { | try { | ||||
@@ -352,15 +333,70 @@ public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestH | |||||
@Override | @Override | ||||
public WxMpConfigStorage getWxMpConfigStorage() { | public WxMpConfigStorage getWxMpConfigStorage() { | ||||
return this.wxMpConfigStorage; | |||||
if (this.configStorageMap.size() == 1) { | |||||
// 只有一个公众号,直接返回其配置即可 | |||||
return this.configStorageMap.values().iterator().next(); | |||||
} | |||||
return this.configStorageMap.get(WxMpConfigStorageHolder.get()); | |||||
} | } | ||||
@Override | @Override | ||||
public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { | public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { | ||||
this.wxMpConfigStorage = wxConfigProvider; | |||||
final String defaultMpId = WxMpConfigStorageHolder.get(); | |||||
this.setMultiConfigStorages(ImmutableMap.of(defaultMpId, wxConfigProvider), defaultMpId); | |||||
} | |||||
@Override | |||||
public void setMultiConfigStorages(Map<String, WxMpConfigStorage> configStorages) { | |||||
this.setMultiConfigStorages(configStorages, configStorages.keySet().iterator().next()); | |||||
} | |||||
@Override | |||||
public void setMultiConfigStorages(Map<String, WxMpConfigStorage> configStorages, String defaultMpId) { | |||||
this.configStorageMap = Maps.newHashMap(configStorages); | |||||
WxMpConfigStorageHolder.set(defaultMpId); | |||||
this.initHttp(); | this.initHttp(); | ||||
} | } | ||||
@Override | |||||
public void addConfigStorage(String mpId, WxMpConfigStorage configStorages) { | |||||
synchronized (this) { | |||||
if (this.configStorageMap.containsKey(mpId)) { | |||||
throw new RuntimeException("该公众号标识已存在,请更换其他标识!"); | |||||
} | |||||
this.configStorageMap.put(mpId, configStorages); | |||||
} | |||||
} | |||||
@Override | |||||
public void removeConfigStorage(String mpId) { | |||||
synchronized (this) { | |||||
this.configStorageMap.remove(mpId); | |||||
} | |||||
} | |||||
@Override | |||||
public WxMpService switchoverTo(String mpId) { | |||||
if (this.configStorageMap.containsKey(mpId)) { | |||||
WxMpConfigStorageHolder.set(mpId); | |||||
return this; | |||||
} | |||||
throw new RuntimeException(String.format("无法找到对应【%s】的公众号配置信息,请核实!", mpId)); | |||||
} | |||||
@Override | |||||
public boolean switchover(String mpId) { | |||||
if (this.configStorageMap.containsKey(mpId)) { | |||||
WxMpConfigStorageHolder.set(mpId); | |||||
return true; | |||||
} | |||||
log.error("无法找到对应【{}】的公众号配置信息,请核实!", mpId); | |||||
return false; | |||||
} | |||||
@Override | @Override | ||||
public void setRetrySleepMillis(int retrySleepMillis) { | public void setRetrySleepMillis(int retrySleepMillis) { | ||||
this.retrySleepMillis = retrySleepMillis; | this.retrySleepMillis = retrySleepMillis; | ||||
@@ -545,4 +581,14 @@ public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestH | |||||
public WxMpWifiService getWifiService() { | public WxMpWifiService getWifiService() { | ||||
return this.wifiService; | return this.wifiService; | ||||
} | } | ||||
@Override | |||||
public WxMpMarketingService getMarketingService() { | |||||
return this.marketingService; | |||||
} | |||||
@Override | |||||
public void setMarketingService(WxMpMarketingService marketingService) { | |||||
this.marketingService = marketingService; | |||||
} | |||||
} | } |
@@ -1,17 +1,16 @@ | |||||
package me.chanjar.weixin.mp.api.impl; | package me.chanjar.weixin.mp.api.impl; | ||||
import com.google.gson.JsonObject; | |||||
import java.io.File; | |||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
import me.chanjar.weixin.common.WxType; | import me.chanjar.weixin.common.WxType; | ||||
import me.chanjar.weixin.common.error.WxError; | import me.chanjar.weixin.common.error.WxError; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.mp.enums.AiLangType; | |||||
import me.chanjar.weixin.mp.api.WxMpAiOpenService; | import me.chanjar.weixin.mp.api.WxMpAiOpenService; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
import me.chanjar.weixin.mp.enums.AiLangType; | |||||
import me.chanjar.weixin.mp.util.requestexecuter.voice.VoiceUploadRequestExecutor; | import me.chanjar.weixin.mp.util.requestexecuter.voice.VoiceUploadRequestExecutor; | ||||
import java.io.File; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* Created by BinaryWang on 2018/6/9. | * Created by BinaryWang on 2018/6/9. | ||||
@@ -20,9 +19,7 @@ import java.io.File; | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
public class WxMpAiOpenServiceImpl implements WxMpAiOpenService { | public class WxMpAiOpenServiceImpl implements WxMpAiOpenService { | ||||
private static final JsonParser JSON_PARSER = new JsonParser(); | private static final JsonParser JSON_PARSER = new JsonParser(); | ||||
public static final String TRANSLATE_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?lfrom=%s<o=%s"; | |||||
private WxMpService wxMpService; | private WxMpService wxMpService; | ||||
public WxMpAiOpenServiceImpl(WxMpService wxMpService) { | public WxMpAiOpenServiceImpl(WxMpService wxMpService) { | ||||
@@ -48,14 +45,14 @@ public class WxMpAiOpenServiceImpl implements WxMpAiOpenService { | |||||
@Override | @Override | ||||
public String translate(AiLangType langFrom, AiLangType langTo, String content) throws WxErrorException { | public String translate(AiLangType langFrom, AiLangType langTo, String content) throws WxErrorException { | ||||
final String responseContent = this.wxMpService.post(String.format(TRANSLATE_URL, langFrom.getCode(), langTo.getCode()), | |||||
content); | |||||
final JsonObject jsonObject = new JsonParser().parse(responseContent).getAsJsonObject(); | |||||
if (jsonObject.get("errcode") == null || jsonObject.get("errcode").getAsInt() == 0) { | |||||
return jsonObject.get("to_content").getAsString(); | |||||
String response = this.wxMpService.post(String.format(TRANSLATE_URL, langFrom.getCode(), langTo.getCode()), content); | |||||
WxError error = WxError.fromJson(response, WxType.MP); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | } | ||||
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP)); | |||||
return JSON_PARSER.parse(response).getAsJsonObject().get("to_content").getAsString(); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -64,13 +61,13 @@ public class WxMpAiOpenServiceImpl implements WxMpAiOpenService { | |||||
lang = AiLangType.zh_CN; | lang = AiLangType.zh_CN; | ||||
} | } | ||||
final String responseContent = this.wxMpService.get(VOICE_QUERY_RESULT_URL, | |||||
final String response = this.wxMpService.get(VOICE_QUERY_RESULT_URL, | |||||
String.format("voice_id=%s&lang=%s", voiceId, lang.getCode())); | String.format("voice_id=%s&lang=%s", voiceId, lang.getCode())); | ||||
final JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | |||||
if (jsonObject.get("errcode") == null || jsonObject.get("errcode").getAsInt() == 0) { | |||||
return jsonObject.get("result").getAsString(); | |||||
WxError error = WxError.fromJson(response, WxType.MP); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | } | ||||
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP)); | |||||
return JSON_PARSER.parse(response).getAsJsonObject().get("result").getAsString(); | |||||
} | } | ||||
} | } |
@@ -1,18 +1,6 @@ | |||||
package me.chanjar.weixin.mp.api.impl; | package me.chanjar.weixin.mp.api.impl; | ||||
import java.util.Arrays; | |||||
import java.util.concurrent.locks.Lock; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import com.google.gson.Gson; | |||||
import com.google.gson.JsonArray; | |||||
import com.google.gson.JsonElement; | |||||
import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonParser; | |||||
import com.google.gson.JsonPrimitive; | |||||
import com.google.gson.*; | |||||
import com.google.gson.reflect.TypeToken; | import com.google.gson.reflect.TypeToken; | ||||
import me.chanjar.weixin.common.bean.WxCardApiSignature; | import me.chanjar.weixin.common.bean.WxCardApiSignature; | ||||
import me.chanjar.weixin.common.error.WxError; | import me.chanjar.weixin.common.error.WxError; | ||||
@@ -22,12 +10,15 @@ import me.chanjar.weixin.common.util.crypto.SHA1; | |||||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | ||||
import me.chanjar.weixin.mp.api.WxMpCardService; | import me.chanjar.weixin.mp.api.WxMpCardService; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateRequest; | |||||
import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateResult; | |||||
import me.chanjar.weixin.mp.bean.card.WxMpCardQrcodeCreateResult; | |||||
import me.chanjar.weixin.mp.bean.result.WxMpCardResult; | |||||
import me.chanjar.weixin.mp.bean.card.*; | |||||
import me.chanjar.weixin.mp.enums.TicketType; | import me.chanjar.weixin.mp.enums.TicketType; | ||||
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.util.Arrays; | |||||
import java.util.concurrent.locks.Lock; | |||||
/** | /** | ||||
* Created by Binary Wang on 2016/7/27. | * Created by Binary Wang on 2016/7/27. | ||||
@@ -150,14 +141,6 @@ public class WxMpCardServiceImpl implements WxMpCardService { | |||||
return jsonPrimitive.getAsString(); | return jsonPrimitive.getAsString(); | ||||
} | } | ||||
/** | |||||
* 卡券Code查询. | |||||
* | |||||
* @param cardId 卡券ID代表一类卡券 | |||||
* @param code 单张卡券的唯一标准 | |||||
* @param checkConsume 是否校验code核销状态,填入true和false时的code异常状态返回数据不同 | |||||
* @return WxMpCardResult对象 | |||||
*/ | |||||
@Override | @Override | ||||
public WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) throws WxErrorException { | public WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) throws WxErrorException { | ||||
JsonObject param = new JsonObject(); | JsonObject param = new JsonObject(); | ||||
@@ -265,6 +248,13 @@ public class WxMpCardServiceImpl implements WxMpCardService { | |||||
return respone; | return respone; | ||||
} | } | ||||
@Override | |||||
public WxMpCardCreateResult createCard(WxMpCardCreateMessage cardCreateMessage) throws WxErrorException { | |||||
String response = this.wxMpService.post(CARD_CREATE, GSON.toJson(cardCreateMessage)); | |||||
return WxMpCardCreateResult.fromJson(response); | |||||
} | |||||
/** | /** | ||||
* 创建卡券二维码. | * 创建卡券二维码. | ||||
*/ | */ | ||||
@@ -324,4 +314,15 @@ public class WxMpCardServiceImpl implements WxMpCardService { | |||||
jsonRequest.addProperty("reason", reason); | jsonRequest.addProperty("reason", reason); | ||||
return this.wxMpService.post(CARD_CODE_UNAVAILABLE, GSON.toJson(jsonRequest)); | return this.wxMpService.post(CARD_CODE_UNAVAILABLE, GSON.toJson(jsonRequest)); | ||||
} | } | ||||
@Override | |||||
public WxMpCardDeleteResult deleteCard(String cardId) throws WxErrorException { | |||||
if (StringUtils.isEmpty(cardId)) { | |||||
throw new WxErrorException(WxError.builder().errorCode(41012).errorMsg("cardId不能为空").build()); | |||||
} | |||||
JsonObject param = new JsonObject(); | |||||
param.addProperty("card_id", cardId); | |||||
String response = this.wxMpService.post(CARD_DELETE, param.toString()); | |||||
return WxMpCardDeleteResult.fromJson(response); | |||||
} | |||||
} | } |
@@ -0,0 +1,92 @@ | |||||
package me.chanjar.weixin.mp.api.impl; | |||||
import com.google.gson.JsonArray; | |||||
import com.google.gson.JsonElement; | |||||
import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonParser; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
import me.chanjar.weixin.mp.api.WxMpMarketingService; | |||||
import me.chanjar.weixin.mp.api.WxMpService; | |||||
import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadFilter; | |||||
import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadResult; | |||||
import me.chanjar.weixin.mp.bean.marketing.WxMpUserAction; | |||||
import me.chanjar.weixin.mp.bean.marketing.WxMpUserActionSet; | |||||
import org.apache.commons.lang3.time.DateFormatUtils; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.io.IOException; | |||||
import java.net.URLEncoder; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.Date; | |||||
import java.util.List; | |||||
/** | |||||
* @author <a href="https://github.com/007gzs">007</a> | |||||
*/ | |||||
public class WxMpMarketingServiceImpl implements WxMpMarketingService { | |||||
protected final Logger log = LoggerFactory.getLogger(this.getClass()); | |||||
private WxMpService wxMpService; | |||||
public WxMpMarketingServiceImpl(WxMpService wxMpService) { | |||||
this.wxMpService = wxMpService; | |||||
} | |||||
@Override | |||||
public long addUserActionSets(String type, String name, String description) throws WxErrorException { | |||||
String url = API_URL_PREFIX + "user_action_sets/add?version=v1.0"; | |||||
JsonObject json = new JsonObject(); | |||||
json.addProperty("type", type); | |||||
json.addProperty("name", name); | |||||
json.addProperty("description", description); | |||||
String responseContent = wxMpService.post(url, json.toString()); | |||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||||
return tmpJsonElement.getAsJsonObject().get("data").getAsJsonObject().get("user_action_set_id").getAsLong(); | |||||
} | |||||
@Override | |||||
public List<WxMpUserActionSet> getUserActionSets(Long userActionSetId) throws WxErrorException { | |||||
String url = API_URL_PREFIX + "user_action_sets/get"; | |||||
String responseContent = wxMpService.get(url, "version=v1.0&user_action_set_id=" + userActionSetId); | |||||
return WxMpUserActionSet.fromJson(responseContent); | |||||
} | |||||
@Override | |||||
public void addUserAction(List<WxMpUserAction> actions) throws WxErrorException { | |||||
String url = API_URL_PREFIX + "user_actions/add?version=v1.0"; | |||||
JsonArray json = new JsonArray(); | |||||
for (WxMpUserAction action : actions) { | |||||
json.add(action.toJsonObject()); | |||||
} | |||||
wxMpService.post(url, json.toString()); | |||||
} | |||||
@Override | |||||
public WxMpAdLeadResult getAdLeads(Date beginDate, Date endDate, List<WxMpAdLeadFilter> filtering, Integer page, Integer page_size) throws WxErrorException, IOException { | |||||
Date today = new Date(); | |||||
if (beginDate == null) { | |||||
beginDate = today; | |||||
} | |||||
if (endDate == null) { | |||||
endDate = today; | |||||
} | |||||
String url = API_URL_PREFIX + "wechat_ad_leads/get"; | |||||
String params = "version=v1.0"; | |||||
JsonObject dateRange = new JsonObject(); | |||||
dateRange.addProperty("begin_date", DateFormatUtils.format(beginDate, "yyyy-MM-dd")); | |||||
dateRange.addProperty("end_date", DateFormatUtils.format(endDate, "yyyy-MM-dd")); | |||||
params += "&date_range=" + URLEncoder.encode(dateRange.toString(), StandardCharsets.UTF_8.name()); | |||||
params += "&page=" + page; | |||||
params += "&page_size=" + page_size; | |||||
if (filtering != null) { | |||||
JsonArray filterJson = new JsonArray(); | |||||
for (WxMpAdLeadFilter filter : filtering) { | |||||
filterJson.add(filter.toJsonObject()); | |||||
} | |||||
params += "&filtering=" + URLEncoder.encode(filterJson.toString(), StandardCharsets.UTF_8.name()); | |||||
; | |||||
} | |||||
String responseContent = wxMpService.get(url, params); | |||||
return WxMpAdLeadResult.fromJson(responseContent); | |||||
} | |||||
} |
@@ -5,6 +5,7 @@ import java.net.URLDecoder; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
import me.chanjar.weixin.mp.bean.membercard.*; | |||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
import com.google.gson.Gson; | import com.google.gson.Gson; | ||||
@@ -30,13 +31,6 @@ import me.chanjar.weixin.mp.bean.card.WxMpCardCreateResult; | |||||
import me.chanjar.weixin.mp.bean.card.enums.BusinessServiceType; | import me.chanjar.weixin.mp.bean.card.enums.BusinessServiceType; | ||||
import me.chanjar.weixin.mp.bean.card.enums.CardColor; | import me.chanjar.weixin.mp.bean.card.enums.CardColor; | ||||
import me.chanjar.weixin.mp.bean.card.enums.DateInfoType; | import me.chanjar.weixin.mp.bean.card.enums.DateInfoType; | ||||
import me.chanjar.weixin.mp.bean.membercard.ActivatePluginParam; | |||||
import me.chanjar.weixin.mp.bean.membercard.ActivatePluginParamResult; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardActivatedMessage; | |||||
import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardCreateMessage; | |||||
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 me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
/** | /** | ||||
@@ -51,7 +45,7 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService { | |||||
private static final Gson GSON = WxMpGsonBuilder.create(); | private static final Gson GSON = WxMpGsonBuilder.create(); | ||||
WxMpMemberCardServiceImpl(WxMpService wxMpService) { | |||||
public WxMpMemberCardServiceImpl(WxMpService wxMpService) { | |||||
this.wxMpService = wxMpService; | this.wxMpService = wxMpService; | ||||
} | } | ||||
@@ -76,7 +70,7 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService { | |||||
return validResult; | return validResult; | ||||
} | } | ||||
String response = this.wxMpService.post(MEMBER_CARD_CREAET, GSON.toJson(createMessageMessage)); | |||||
String response = this.wxMpService.post(MEMBER_CARD_CREATE, GSON.toJson(createMessageMessage)); | |||||
return WxMpCardCreateResult.fromJson(response); | return WxMpCardCreateResult.fromJson(response); | ||||
} | } | ||||
@@ -249,7 +243,7 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService { | |||||
@Override | @Override | ||||
public MemberCardActivateUserFormResult setActivateUserForm(MemberCardActivateUserFormRequest userFormRequest) throws WxErrorException { | public MemberCardActivateUserFormResult setActivateUserForm(MemberCardActivateUserFormRequest userFormRequest) throws WxErrorException { | ||||
String responseContent = this.getWxMpService().post(MEMBER_CARD_ACTIVATEUSERFORM, GSON.toJson(userFormRequest)); | |||||
String responseContent = this.getWxMpService().post(MEMBER_CARD_ACTIVATE_USER_FORM, GSON.toJson(userFormRequest)); | |||||
return MemberCardActivateUserFormResult.fromJson(responseContent); | return MemberCardActivateUserFormResult.fromJson(responseContent); | ||||
} | } | ||||
@@ -284,6 +278,15 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService { | |||||
return result; | return result; | ||||
} | } | ||||
@Override | |||||
public WxMpMemberCardActivateTempInfoResult getActivateTempInfo(String activateTicket) throws WxErrorException { | |||||
JsonObject params = new JsonObject(); | |||||
params.addProperty("activate_ticket", activateTicket); | |||||
String response = this.wxMpService.post(MEMBER_CARD_ACTIVATE_TEMP_INFO, GSON.toJson(params)); | |||||
WxMpMemberCardActivateTempInfoResult result = GSON.fromJson(response, WxMpMemberCardActivateTempInfoResult.class); | |||||
return result; | |||||
} | |||||
private static String truncateUrlPage(String strURL) { | private static String truncateUrlPage(String strURL) { | ||||
String strAllParam = null; | String strAllParam = null; | ||||
String[] arrSplit; | String[] arrSplit; | ||||