@@ -7,7 +7,7 @@ language: java | |||
# secure: "834110c7191f97ecb226970c46dcaff8e681da5a" | |||
jdk: | |||
- oraclejdk8 | |||
- openjdk8 | |||
script: "mvn clean package -DskipTests=true -Dcheckstyle.skip=true" | |||
#script: | |||
@@ -1,3 +0,0 @@ | |||
{ | |||
"java.configuration.updateBuildConfiguration": "interactive" | |||
} |
@@ -6,7 +6,7 @@ | |||
<modelVersion>4.0.0</modelVersion> | |||
<groupId>com.github.binarywang</groupId> | |||
<artifactId>wx-java</artifactId> | |||
<version>3.4.0</version> | |||
<version>3.5.0</version> | |||
<packaging>pom</packaging> | |||
<name>WxJava - Weixin/Wechat Java SDK</name> | |||
<description>微信开发Java SDK</description> | |||
@@ -105,8 +105,9 @@ | |||
<module>weixin-java-pay</module> | |||
<module>weixin-java-miniapp</module> | |||
<module>weixin-java-open</module> | |||
<module>starters/wx-java-pay-starter</module> | |||
<module>starters/wx-java-mp-starter</module> | |||
<module>spring-boot-starters/wx-java-pay-spring-boot-starter</module> | |||
<module>spring-boot-starters/wx-java-mp-spring-boot-starter</module> | |||
<module>spring-boot-starters/wx-java-miniapp-spring-boot-starter</module> | |||
<!--module>weixin-java-osgi</module--> | |||
</modules> | |||
@@ -246,7 +247,7 @@ | |||
<dependency> | |||
<groupId>org.projectlombok</groupId> | |||
<artifactId>lombok</artifactId> | |||
<version>1.16.18</version> | |||
<version>1.18.8</version> | |||
<scope>provided</scope> | |||
</dependency> | |||
</dependencies> | |||
@@ -370,6 +371,7 @@ | |||
<artifactId>maven-checkstyle-plugin</artifactId> | |||
<version>2.17</version> | |||
<configuration> | |||
<skip>true</skip> | |||
<configLocation>quality-checks/google_checks.xml</configLocation> | |||
<includeTestSourceDirectory>true</includeTestSourceDirectory> | |||
<consoleOutput>true</consoleOutput> | |||
@@ -0,0 +1,26 @@ | |||
# 使用说明 | |||
1. 在自己的Spring Boot项目里,引入maven依赖 | |||
```xml | |||
<dependency> | |||
<groupId>com.github.binarywang</groupId> | |||
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId> | |||
<version>${version}</version> | |||
</dependency> | |||
``` | |||
2. 添加配置(application.yml) | |||
```yml | |||
wx: | |||
miniapp: | |||
appid: 111 | |||
secret: 111 | |||
token: 111 | |||
aesKey: 111 | |||
msgDataFormat: JSON | |||
``` | |||
@@ -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.5.0</version> | |||
<relativePath>../../</relativePath> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId> | |||
<name>WxJava - Spring Boot Starter for MiniApp</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-miniapp</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,50 @@ | |||
package com.binarywang.spring.starter.wxjava.miniapp.config; | |||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; | |||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; | |||
import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties; | |||
import lombok.AllArgsConstructor; | |||
import org.apache.commons.lang3.StringUtils; | |||
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; | |||
/** | |||
* 自动配置. | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
* @date 2019-08-10 | |||
*/ | |||
@AllArgsConstructor | |||
@Configuration | |||
@ConditionalOnClass(WxMaService.class) | |||
@EnableConfigurationProperties(WxMaProperties.class) | |||
@ConditionalOnProperty(prefix = "wx.miniapp", value = "enabled", matchIfMissing = true) | |||
public class WxMaAutoConfiguration { | |||
private WxMaProperties properties; | |||
/** | |||
* 小程序service. | |||
* | |||
* @return 小程序service | |||
*/ | |||
@Bean | |||
@ConditionalOnMissingBean(WxMaService.class) | |||
public WxMaService service() { | |||
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl(); | |||
config.setAppid(StringUtils.trimToNull(this.properties.getAppid())); | |||
config.setSecret(StringUtils.trimToNull(this.properties.getSecret())); | |||
config.setToken(StringUtils.trimToNull(this.properties.getToken())); | |||
config.setAesKey(StringUtils.trimToNull(this.properties.getAesKey())); | |||
config.setMsgDataFormat(StringUtils.trimToNull(this.properties.getMsgDataFormat())); | |||
final WxMaServiceImpl service = new WxMaServiceImpl(); | |||
service.setWxMaConfig(config); | |||
return service; | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
package com.binarywang.spring.starter.wxjava.miniapp.properties; | |||
import lombok.Data; | |||
import org.springframework.boot.context.properties.ConfigurationProperties; | |||
/** | |||
* 属性配置类. | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
* @date 2019-08-10 | |||
*/ | |||
@Data | |||
@ConfigurationProperties(prefix = "wx.miniapp") | |||
public class WxMaProperties { | |||
/** | |||
* 设置微信小程序的appid. | |||
*/ | |||
private String appid; | |||
/** | |||
* 设置微信小程序的Secret. | |||
*/ | |||
private String secret; | |||
/** | |||
* 设置微信小程序消息服务器配置的token. | |||
*/ | |||
private String token; | |||
/** | |||
* 设置微信小程序消息服务器配置的EncodingAESKey. | |||
*/ | |||
private String aesKey; | |||
/** | |||
* 消息格式,XML或者JSON. | |||
*/ | |||
private String msgDataFormat; | |||
} |
@@ -0,0 +1 @@ | |||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.miniapp.config.WxMaAutoConfiguration |
@@ -4,7 +4,7 @@ | |||
```xml | |||
<dependency> | |||
<groupId>com.github.binarywang</groupId> | |||
<artifactId>wx-java-mp-starter</artifactId> | |||
<artifactId>wx-java-mp-spring-boot-starter</artifactId> | |||
<version>${version}</version> | |||
</dependency> | |||
``` |
@@ -6,13 +6,13 @@ | |||
<parent> | |||
<groupId>com.github.binarywang</groupId> | |||
<artifactId>wx-java</artifactId> | |||
<version>3.4.0</version> | |||
<version>3.5.0</version> | |||
<relativePath>../../</relativePath> | |||
</parent> | |||
<artifactId>wx-java-mp-starter</artifactId> | |||
<artifactId>wx-java-mp-spring-boot-starter</artifactId> | |||
<name>WxJava - Spring Boot Starter for MP</name> | |||
<description>微信公众号开发的Spring Boot Starter</description> | |||
<description>微信公众号开发的 Spring Boot Starter</description> | |||
<properties> | |||
<spring.boot.version>2.1.4.RELEASE</spring.boot.version> |
@@ -1,9 +1,15 @@ | |||
package com.binarywang.spring.starter.wxjava.mp; | |||
package com.binarywang.spring.starter.wxjava.mp.config; | |||
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; | |||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.context.annotation.Import; | |||
/** | |||
* . | |||
* | |||
* @author someone | |||
*/ | |||
@Configuration | |||
@EnableConfigurationProperties(WxMpProperties.class) | |||
@Import({WxMpStorageAutoConfiguration.class, WxMpServiceAutoConfiguration.class}) |
@@ -0,0 +1,55 @@ | |||
package com.binarywang.spring.starter.wxjava.mp.config; | |||
import me.chanjar.weixin.mp.config.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; | |||
/** | |||
* 微信公众号相关服务自动注册. | |||
* | |||
* @author someone | |||
*/ | |||
@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,88 @@ | |||
package com.binarywang.spring.starter.wxjava.mp.config; | |||
import com.binarywang.spring.starter.wxjava.mp.properties.RedisProperties; | |||
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; | |||
import lombok.RequiredArgsConstructor; | |||
import me.chanjar.weixin.mp.config.WxMpConfigStorage; | |||
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; | |||
import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl; | |||
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; | |||
/** | |||
* 微信公众号存储策略自动配置. | |||
* | |||
* @author someone | |||
*/ | |||
@Configuration | |||
@RequiredArgsConstructor | |||
public class WxMpStorageAutoConfiguration { | |||
private final 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 WxMpDefaultConfigImpl getWxMpInMemoryConfigStorage() { | |||
WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); | |||
setWxMpInfo(config); | |||
return config; | |||
} | |||
private WxMpRedisConfigImpl getWxMpInRedisConfigStorage() { | |||
JedisPool poolToUse = jedisPool; | |||
if (poolToUse == null) { | |||
poolToUse = getJedisPool(); | |||
} | |||
WxMpRedisConfigImpl config = new WxMpRedisConfigImpl(poolToUse); | |||
setWxMpInfo(config); | |||
return config; | |||
} | |||
private void setWxMpInfo(WxMpDefaultConfigImpl 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,45 @@ | |||
package com.binarywang.spring.starter.wxjava.mp.properties; | |||
import lombok.Data; | |||
import java.io.Serializable; | |||
/** | |||
* Redis配置. | |||
* | |||
* @author someone | |||
*/ | |||
@Data | |||
public class RedisProperties implements Serializable { | |||
private static final long serialVersionUID = -5924815351660074401L; | |||
/** | |||
* 主机地址. | |||
*/ | |||
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; | |||
} |
@@ -1,16 +1,18 @@ | |||
package com.binarywang.spring.starter.wxjava.mp; | |||
package com.binarywang.spring.starter.wxjava.mp.properties; | |||
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; | |||
import static com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties.PREFIX; | |||
import static com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties.StorageType.memory; | |||
/** | |||
* 微信接入相关配置属性 | |||
* 微信接入相关配置属性. | |||
* | |||
* @author someone | |||
*/ | |||
@Data | |||
@ConfigurationProperties(PREFIX) | |||
@@ -18,33 +20,34 @@ public class WxMpProperties { | |||
public static final String PREFIX = "wx.mp"; | |||
/** | |||
* 设置微信公众号的appid | |||
* 设置微信公众号的appid. | |||
*/ | |||
private String appId; | |||
/** | |||
* 设置微信公众号的app secret | |||
* 设置微信公众号的app secret. | |||
*/ | |||
private String secret; | |||
/** | |||
* 设置微信公众号的token | |||
* 设置微信公众号的token. | |||
*/ | |||
private String token; | |||
/** | |||
* 设置微信公众号的EncodingAESKey | |||
* 设置微信公众号的EncodingAESKey. | |||
*/ | |||
private String aesKey; | |||
/** | |||
* 存储策略, memory, redis | |||
* 存储策略, memory, redis. | |||
*/ | |||
private ConfigStorage configStorage = new ConfigStorage(); | |||
@Data | |||
public static class ConfigStorage implements Serializable { | |||
private static final long serialVersionUID = 4815731027000065434L; | |||
private StorageType type = memory; | |||
@@ -53,6 +56,13 @@ public class WxMpProperties { | |||
} | |||
public enum StorageType { | |||
memory, redis | |||
/** | |||
* 内存. | |||
*/ | |||
memory, | |||
/** | |||
* redis. | |||
*/ | |||
redis | |||
} | |||
} |
@@ -0,0 +1 @@ | |||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.mp.config.WxMpAutoConfiguration |
@@ -3,7 +3,7 @@ | |||
```xml | |||
<dependency> | |||
<groupId>com.github.binarywang</groupId> | |||
<artifactId>wx-java-pay-starter</artifactId> | |||
<artifactId>wx-java-pay-spring-boot-starter</artifactId> | |||
<version>${version}</version> | |||
</dependency> | |||
``` | |||
@@ -11,9 +11,9 @@ | |||
```yml | |||
wx: | |||
pay: | |||
appId: wx5b69c56ac01ed858 | |||
mchId: 1462547202 | |||
mchKey: OGL9fvig9y2HrXrQ86tM4jTwyv4ja6G5 | |||
appId: | |||
mchId: | |||
mchKey: | |||
subAppId: | |||
subMchId: | |||
keyPath: |
@@ -5,14 +5,14 @@ | |||
<parent> | |||
<artifactId>wx-java</artifactId> | |||
<groupId>com.github.binarywang</groupId> | |||
<version>3.4.0</version> | |||
<version>3.5.0</version> | |||
<relativePath>../../</relativePath> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>wx-java-pay-starter</artifactId> | |||
<artifactId>wx-java-pay-spring-boot-starter</artifactId> | |||
<name>WxJava - Spring Boot Starter for WxPay</name> | |||
<description>微信支付开发的Spring Boot Starter</description> | |||
<description>微信支付开发的 Spring Boot Starter</description> | |||
<properties> | |||
<spring.boot.version>2.1.4.RELEASE</spring.boot.version> |
@@ -1,42 +0,0 @@ | |||
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; | |||
} |
@@ -1,53 +0,0 @@ | |||
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; | |||
} | |||
} |
@@ -1,84 +0,0 @@ | |||
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; | |||
} | |||
} |
@@ -1 +0,0 @@ | |||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.mp.WxMpAutoConfiguration |
@@ -6,7 +6,7 @@ | |||
<parent> | |||
<groupId>com.github.binarywang</groupId> | |||
<artifactId>wx-java</artifactId> | |||
<version>3.4.0</version> | |||
<version>3.5.0</version> | |||
</parent> | |||
<artifactId>weixin-java-common</artifactId> | |||
@@ -85,14 +85,24 @@ public class WxConsts { | |||
public static final String TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | |||
/** | |||
* 小程序卡片(要求小程序与公众号已关联) | |||
* 小程序卡片(要求小程序与公众号已关联). | |||
*/ | |||
public static final String MINIPROGRAMPAGE = "miniprogrampage"; | |||
/** | |||
* 任务卡片消息 | |||
* 任务卡片消息. | |||
*/ | |||
public static final String TASKCARD = "taskcard"; | |||
/** | |||
* 菜单消息. | |||
*/ | |||
public static final String MSGMENU = "msgmenu"; | |||
/** | |||
* 小程序通知消息. | |||
*/ | |||
public static final String MINIPROGRAM_NOTICE = "miniprogram_notice"; | |||
} | |||
/** | |||
@@ -6,12 +6,14 @@ import org.apache.commons.lang3.StringUtils; | |||
import java.util.Arrays; | |||
/** | |||
* Created by Daniel Qian on 14/10/19. | |||
* | |||
* @author Daniel Qian | |||
* @date 14/10/19 | |||
*/ | |||
public class SHA1 { | |||
/** | |||
* 串接arr参数,生成sha1 digest | |||
* 串接arr参数,生成sha1 digest. | |||
*/ | |||
public static String gen(String... arr) { | |||
if (StringUtils.isAnyEmpty(arr)) { | |||
@@ -27,7 +29,7 @@ public class SHA1 { | |||
} | |||
/** | |||
* 用&串接arr参数,生成sha1 digest | |||
* 用&串接arr参数,生成sha1 digest. | |||
*/ | |||
public static String genWithAmple(String... arr) { | |||
if (StringUtils.isAnyEmpty(arr)) { | |||
@@ -55,20 +55,19 @@ public class WxCryptUtil { | |||
} | |||
/** | |||
* 构造函数 | |||
* 构造函数. | |||
* | |||
* @param token 公众平台上,开发者设置的token | |||
* @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey | |||
* @param appidOrCorpid 公众平台appid/corpid | |||
*/ | |||
public WxCryptUtil(String token, String encodingAesKey, | |||
String appidOrCorpid) { | |||
public WxCryptUtil(String token, String encodingAesKey, String appidOrCorpid) { | |||
this.token = token; | |||
this.appidOrCorpid = appidOrCorpid; | |||
this.aesKey = Base64.decodeBase64(encodingAesKey + "="); | |||
} | |||
static String extractEncryptPart(String xml) { | |||
private static String extractEncryptPart(String xml) { | |||
try { | |||
DocumentBuilder db = BUILDER_LOCAL.get(); | |||
Document document = db.parse(new InputSource(new StringReader(xml))); | |||
@@ -81,7 +80,7 @@ public class WxCryptUtil { | |||
} | |||
/** | |||
* 将一个数字转换成生成4个字节的网络字节序bytes数组 | |||
* 将一个数字转换成生成4个字节的网络字节序bytes数组. | |||
*/ | |||
private static byte[] number2BytesInNetworkOrder(int number) { | |||
byte[] orderBytes = new byte[4]; | |||
@@ -93,7 +92,7 @@ public class WxCryptUtil { | |||
} | |||
/** | |||
* 4个字节的网络字节序bytes数组还原成一个数字 | |||
* 4个字节的网络字节序bytes数组还原成一个数字. | |||
*/ | |||
private static int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) { | |||
int sourceNumber = 0; | |||
@@ -105,7 +104,7 @@ public class WxCryptUtil { | |||
} | |||
/** | |||
* 随机生成16位字符串 | |||
* 随机生成16位字符串. | |||
*/ | |||
private static String genRandomStr() { | |||
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | |||
@@ -119,7 +118,7 @@ public class WxCryptUtil { | |||
} | |||
/** | |||
* 生成xml消息 | |||
* 生成xml消息. | |||
* | |||
* @param encrypt 加密后的消息密文 | |||
* @param signature 安全签名 | |||
@@ -127,8 +126,7 @@ public class WxCryptUtil { | |||
* @param nonce 随机字符串 | |||
* @return 生成的xml字符串 | |||
*/ | |||
private static String generateXml(String encrypt, String signature, | |||
String timestamp, String nonce) { | |||
private static String generateXml(String encrypt, String signature, String timestamp, String nonce) { | |||
String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n" | |||
+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" | |||
+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" | |||
@@ -242,10 +240,9 @@ public class WxCryptUtil { | |||
try { | |||
// 设置解密模式为AES的CBC模式 | |||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | |||
SecretKeySpec key_spec = new SecretKeySpec(this.aesKey, "AES"); | |||
IvParameterSpec iv = new IvParameterSpec( | |||
Arrays.copyOfRange(this.aesKey, 0, 16)); | |||
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); | |||
SecretKeySpec keySpec = new SecretKeySpec(this.aesKey, "AES"); | |||
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(this.aesKey, 0, 16)); | |||
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); | |||
// 使用BASE64对密文进行解码 | |||
byte[] encrypted = Base64.decodeBase64(cipherText); | |||
@@ -256,7 +253,8 @@ public class WxCryptUtil { | |||
throw new RuntimeException(e); | |||
} | |||
String xmlContent, fromAppid; | |||
String xmlContent; | |||
String fromAppid; | |||
try { | |||
// 去除补位字符 | |||
byte[] bytes = PKCS7Encoder.decode(original); | |||
@@ -266,17 +264,15 @@ public class WxCryptUtil { | |||
int xmlLength = bytesNetworkOrder2Number(networkOrder); | |||
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), | |||
CHARSET); | |||
fromAppid = new String( | |||
Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); | |||
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); | |||
fromAppid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); | |||
} catch (Exception e) { | |||
throw new RuntimeException(e); | |||
} | |||
// appid不相同的情况 | |||
if (!fromAppid.equals(this.appidOrCorpid)) { | |||
throw new RuntimeException("AppID不正确"); | |||
throw new RuntimeException("AppID不正确,请核实!"); | |||
} | |||
return xmlContent; | |||
@@ -7,7 +7,7 @@ | |||
<parent> | |||
<groupId>com.github.binarywang</groupId> | |||
<artifactId>wx-java</artifactId> | |||
<version>3.4.0</version> | |||
<version>3.5.0</version> | |||
</parent> | |||
<artifactId>weixin-java-cp</artifactId> | |||
@@ -15,7 +15,6 @@ import java.util.List; | |||
* @author <a href="https://github.com/huansinho">huansinho</a> | |||
*/ | |||
public interface WxCpAgentService { | |||
/** | |||
* <pre> | |||
* 获取企业号应用信息 | |||
@@ -1,20 +1,20 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import java.util.List; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpChat; | |||
import java.util.List; | |||
/** | |||
* 群聊服务. | |||
* | |||
* @author gaigeshen | |||
*/ | |||
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="; | |||
@Deprecated | |||
String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException; | |||
/** | |||
* 创建群聊会话,注意:刚创建的群,如果没有下发消息,在企业微信不会出现该群. | |||
@@ -24,15 +24,13 @@ public interface WxCpChatService { | |||
* @param users 群成员id列表。至少2人,至多500人 | |||
* @param chatId 群聊的唯一标志,不能与已有的群重复;字符串类型,最长32个字符。只允许字符0-9及字母a-zA-Z。如果不填,系统会随机生成群id | |||
* @return 创建的群聊会话chatId | |||
* @throws WxErrorException 发生异常 | |||
*/ | |||
String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException; | |||
/** | |||
* chatCreate 同名方法 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
String create(String name, String owner, List<String> users, String chatId) throws WxErrorException; | |||
@Deprecated | |||
void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException; | |||
/** | |||
* 修改群聊会话. | |||
* | |||
@@ -41,26 +39,19 @@ public interface WxCpChatService { | |||
* @param owner 新群主的id。若不需更新,请忽略此参数(null or empty) | |||
* @param usersToAdd 添加成员的id列表,若不需要更新,则传递空对象或者空集合 | |||
* @param usersToDelete 踢出成员的id列表,若不需要更新,则传递空对象或者空集合 | |||
* @throws WxErrorException 发生异常 | |||
*/ | |||
void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException; | |||
/** | |||
* chatUpdate 同名方法 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
void update(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException; | |||
@Deprecated | |||
WxCpChat chatGet(String chatId) throws WxErrorException; | |||
/** | |||
* 获取群聊会话. | |||
* | |||
* @param chatId 群聊编号 | |||
* @return 群聊会话 | |||
* @throws WxErrorException 发生异常 | |||
*/ | |||
WxCpChat chatGet(String chatId) throws WxErrorException; | |||
/** | |||
* chatGet 同名方法 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
WxCpChat get(String chatId) throws WxErrorException; | |||
@@ -71,6 +62,7 @@ public interface WxCpChatService { | |||
* 文档地址:https://work.weixin.qq.com/api/doc#90000/90135/90248 | |||
* | |||
* @param message 要发送的消息内容对象 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
void sendMsg(WxCpAppChatMessage message) throws WxErrorException; | |||
@@ -1,10 +1,10 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import java.util.List; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.bean.WxCpDepart; | |||
import java.util.List; | |||
/** | |||
* <pre> | |||
* 部门管理接口 | |||
@@ -19,7 +19,7 @@ public interface WxCpDepartmentService { | |||
* <pre> | |||
* 部门管理接口 - 创建部门. | |||
* 最多支持创建500个部门 | |||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口 | |||
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90205 | |||
* </pre> | |||
* | |||
* @param depart 部门 | |||
@@ -30,19 +30,20 @@ public interface WxCpDepartmentService { | |||
/** | |||
* <pre> | |||
* 部门管理接口 - 查询部门. | |||
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E5.88.97.E8.A1.A8 | |||
* 部门管理接口 - 获取部门列表. | |||
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90208 | |||
* </pre> | |||
* | |||
* @param id 部门id。获取指定部门及其下的子部门。非必需,可为null | |||
* @return 获取的部门列表 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
List<WxCpDepart> list(Long id) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 部门管理接口 - 修改部门名. | |||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口 | |||
* 部门管理接口 - 更新部门. | |||
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90206 | |||
* 如果id为0(未部门),1(黑名单),2(星标组),或者不存在的id,微信会返回系统繁忙的错误 | |||
* </pre> | |||
* | |||
@@ -54,6 +55,8 @@ public interface WxCpDepartmentService { | |||
/** | |||
* <pre> | |||
* 部门管理接口 - 删除部门. | |||
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90207 | |||
* 应用须拥有指定部门的管理权限 | |||
* </pre> | |||
* | |||
* @param departId 部门id | |||
@@ -0,0 +1,58 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | |||
import java.util.List; | |||
/** | |||
* <pre> | |||
* 外部联系人管理接口,企业微信的外部联系人的接口和通讯录接口已经拆离 | |||
* Created by Joe Cao on 2019/6/14 | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/JoeCao">JoeCao</a> | |||
*/ | |||
public interface WxCpExternalContactService { | |||
/** | |||
* 获取外部联系人详情. | |||
* <pre> | |||
* 企业可通过此接口,根据外部联系人的userid,拉取外部联系人详情。权限说明: | |||
* 企业需要使用外部联系人管理secret所获取的accesstoken来调用 | |||
* 第三方应用需拥有“企业客户”权限。 | |||
* 第三方应用调用时,返回的跟进人follow_user仅包含应用可见范围之内的成员。 | |||
* </pre> | |||
* | |||
* @param userId 外部联系人的userid | |||
*/ | |||
WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException; | |||
/** | |||
* 获取外部联系人列表. | |||
* <pre> | |||
* 企业可通过此接口获取指定成员添加的客户列表。 | |||
* 客户是指配置了客户联系功能的成员所添加的外部联系人。 | |||
* 没有配置客户联系功能的成员,所添加的外部联系人将不会作为客户返回。 | |||
* 第三方应用需拥有“企业客户”权限。 | |||
* 第三方应用调用时,返回的跟进人follow_user仅包含应用可见范围之内的成员。 | |||
* </pre> | |||
* | |||
* @param userId 外部联系人的userid | |||
* @return List of External wx id | |||
*/ | |||
List<String> listExternalContacts(String userId) throws WxErrorException; | |||
/** | |||
* 企业和第三方服务商可通过此接口获取配置了客户联系功能的成员(Customer Contact)列表。 | |||
* <pre> | |||
* 企业需要使用外部联系人管理secret所获取的accesstoken来调用(accesstoken如何获取?); | |||
* 第三方应用需拥有“企业客户”权限。 | |||
* 第三方应用只能获取到可见范围内的配置了客户联系功能的成员 | |||
* </pre> | |||
* | |||
* @return List of CpUser id | |||
*/ | |||
List<String> listFollowUser() throws WxErrorException; | |||
} |
@@ -1,12 +1,12 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
/** | |||
* <pre> | |||
* 媒体管理接口. | |||
@@ -16,10 +16,6 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public interface WxCpMediaService { | |||
String MEDIA_GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; | |||
String MEDIA_UPLOAD_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type="; | |||
String IMG_UPLOAD_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/uploadimg"; | |||
String JSSDK_MEDIA_GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/get/jssdk"; | |||
/** | |||
* <pre> | |||
@@ -12,6 +12,7 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public interface WxCpMenuService { | |||
/** | |||
* <pre> | |||
* 自定义菜单创建接口 | |||
@@ -1,66 +0,0 @@ | |||
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; | |||
} |
@@ -1,20 +1,22 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo; | |||
import me.chanjar.weixin.cp.bean.WxCpUserDetail; | |||
/** | |||
* <pre> | |||
* OAuth2相关管理接口 | |||
* OAuth2相关管理接口. | |||
* Created by BinaryWang on 2017/6/24. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public interface WxCpOAuth2Service { | |||
/** | |||
* <pre> | |||
* 构造oauth2授权的url连接 | |||
* 构造oauth2授权的url连接. | |||
* </pre> | |||
* | |||
* @param state 状态码 | |||
@@ -24,7 +26,7 @@ public interface WxCpOAuth2Service { | |||
/** | |||
* <pre> | |||
* 构造oauth2授权的url连接 | |||
* 构造oauth2授权的url连接. | |||
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code | |||
* </pre> | |||
* | |||
@@ -57,16 +59,18 @@ public interface WxCpOAuth2Service { | |||
* </pre> | |||
* | |||
* @param code 微信oauth授权返回的代码 | |||
* @return [userid, deviceid] | |||
* @return WxCpOauth2UserInfo | |||
* @throws WxErrorException 异常 | |||
* @see #getUserInfo(Integer, String) | |||
*/ | |||
String[] getUserInfo(String code) throws WxErrorException; | |||
WxCpOauth2UserInfo getUserInfo(String code) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 根据code获取成员信息 | |||
* http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息 | |||
* https://work.weixin.qq.com/api/doc#10028/根据code获取成员信息 | |||
* https://work.weixin.qq.com/api/doc#90000/90135/91023 获取访问用户身份 | |||
* 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。 | |||
* | |||
* 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | |||
@@ -74,10 +78,11 @@ public interface WxCpOAuth2Service { | |||
* | |||
* @param agentId 企业号应用的id | |||
* @param code 通过成员授权获取到的code,最大为512字节。每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。 | |||
* @return [UserId, DeviceId, OpenId, user_ticket, expires_in] | |||
* @return WxCpOauth2UserInfo | |||
* @throws WxErrorException 异常 | |||
* @see #getUserInfo(String) | |||
*/ | |||
String[] getUserInfo(Integer agentId, String code) throws WxErrorException; | |||
WxCpOauth2UserInfo getUserInfo(Integer agentId, String code) throws WxErrorException; | |||
/** | |||
* <pre> | |||
@@ -92,6 +97,8 @@ public interface WxCpOAuth2Service { | |||
* </pre> | |||
* | |||
* @param userTicket 成员票据 | |||
* @return WxCpUserDetail | |||
* @throws WxErrorException 异常 | |||
*/ | |||
WxCpUserDetail getUserDetail(String userTicket) throws WxErrorException; | |||
} |
@@ -0,0 +1,64 @@ | |||
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; | |||
/** | |||
* 企业微信OA相关接口. | |||
* | |||
* @author Element | |||
* @date 2019-04-06 10:52 | |||
*/ | |||
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 需要获取打卡记录的用户列表 | |||
* @return 打卡数据列表 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
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 第一个拉取的审批单号,不填从该时间段的第一个审批单拉取 | |||
* @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; | |||
} |
@@ -13,19 +13,10 @@ import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
/** | |||
* 微信API的Service | |||
* 微信API的Service. | |||
* @author chanjaster | |||
*/ | |||
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> | |||
* 验证推送过来的消息的正确性 | |||
@@ -293,7 +284,9 @@ public interface WxCpService { | |||
* 获取用户相关接口的服务类对象 | |||
*/ | |||
WxCpUserService getUserService(); | |||
WxCpExternalContactService getExternalContactService(); | |||
/** | |||
* 获取群聊服务 | |||
* | |||
@@ -310,7 +303,7 @@ public interface WxCpService { | |||
WxCpAgentService getAgentService(); | |||
WxCpOAService getOAService(); | |||
WxCpOaService getOAService(); | |||
/** | |||
* http请求对象 | |||
@@ -17,6 +17,7 @@ import java.util.List; | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public interface WxCpTagService { | |||
/** | |||
* 创建标签. | |||
* | |||
@@ -51,6 +52,14 @@ public interface WxCpTagService { | |||
*/ | |||
List<WxCpUser> listUsersByTagId(String tagId) throws WxErrorException; | |||
/** | |||
* 获取标签成员. | |||
* 对应: http://qydev.weixin.qq.com/wiki/index.php?title=管理标签 中的get接口 | |||
* | |||
* @param tagId 标签id | |||
*/ | |||
WxCpTagGetResult get(String tagId) throws WxErrorException; | |||
/** | |||
* 增加标签成员. | |||
* | |||
@@ -69,13 +78,4 @@ public interface WxCpTagService { | |||
*/ | |||
WxCpTagAddOrRemoveUsersResult removeUsersFromTag(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException; | |||
/** | |||
* 获取标签成员. | |||
* 对应: http://qydev.weixin.qq.com/wiki/index.php?title=管理标签 中的get接口 | |||
* | |||
* @param tagId 标签id | |||
*/ | |||
WxCpTagGetResult get(String tagId) throws WxErrorException; | |||
} |
@@ -14,6 +14,7 @@ import java.util.List; | |||
* @date 2019-05-16 | |||
*/ | |||
public interface WxCpTaskCardService { | |||
/** | |||
* <pre> | |||
* 更新任务卡片消息状态 | |||
@@ -0,0 +1,169 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.RequestExecutor; | |||
import me.chanjar.weixin.common.util.http.RequestHttp; | |||
import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | |||
import me.chanjar.weixin.cp.bean.WxCpTpCorp; | |||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | |||
/** | |||
* 微信第三方应用API的Service. | |||
* | |||
* @author zhenjun cai | |||
*/ | |||
public interface WxCpTpService { | |||
/** | |||
* <pre> | |||
* 验证推送过来的消息的正确性 | |||
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90139/90968/消息体签名校验 | |||
* </pre> | |||
* | |||
* @param msgSignature 消息签名 | |||
* @param timestamp 时间戳 | |||
* @param nonce 随机数 | |||
* @param data 微信传输过来的数据,有可能是echoStr,有可能是xml消息 | |||
*/ | |||
boolean checkSignature(String msgSignature, String timestamp, String nonce, String data); | |||
/** | |||
* 获取suite_access_token, 不强制刷新suite_access_token | |||
* | |||
* @see #getSuiteAccessToken(boolean) | |||
*/ | |||
String getSuiteAccessToken() throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 获取suite_access_token,本方法线程安全 | |||
* 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限 | |||
* 另:本service的所有方法都会在suite_access_token过期是调用此方法 | |||
* 程序员在非必要情况下尽量不要主动调用此方法 | |||
* 详情请见: https://work.weixin.qq.com/api/doc#90001/90143/90600 | |||
* </pre> | |||
* | |||
* @param forceRefresh 强制刷新 | |||
*/ | |||
String getSuiteAccessToken(boolean forceRefresh) throws WxErrorException; | |||
/** | |||
* 获得suite_ticket,不强制刷新suite_ticket | |||
* | |||
* @see #getSuiteTicket(boolean) | |||
*/ | |||
String getSuiteTicket() throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 获得suite_ticket | |||
* 由于suite_ticket是微信服务器定时推送(每10分钟),不能主动获取,如果碰到过期只能抛异常 | |||
* | |||
* 详情请见:https://work.weixin.qq.com/api/doc#90001/90143/90628 | |||
* </pre> | |||
* | |||
* @param forceRefresh 强制刷新 | |||
*/ | |||
String getSuiteTicket(boolean forceRefresh) throws WxErrorException; | |||
/** | |||
* 小程序登录凭证校验 | |||
* | |||
* @param jsCode 登录时获取的 code | |||
*/ | |||
WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException; | |||
/** | |||
* 获取企业凭证 | |||
* | |||
* @param authCorpid 授权方corpid | |||
* @param permanentCode 永久授权码,通过get_permanent_code获取 | |||
*/ | |||
WxAccessToken getCorpToken(String authCorpid, String permanentCode) throws WxErrorException; | |||
/** | |||
* 获取企业永久授权码 | |||
* | |||
* @param authCode | |||
* @return | |||
*/ | |||
WxCpTpCorp getPermanentCode(String authCode) throws WxErrorException; | |||
/** | |||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | |||
* | |||
* @param url 接口地址 | |||
* @param queryParam 请求参数 | |||
*/ | |||
String get(String url, String queryParam) throws WxErrorException; | |||
/** | |||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 | |||
* | |||
* @param url 接口地址 | |||
* @param postData 请求body字符串 | |||
*/ | |||
String post(String url, String postData) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* Service没有实现某个API的时候,可以用这个, | |||
* 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。 | |||
* 可以参考,{@link MediaUploadRequestExecutor}的实现方法 | |||
* </pre> | |||
* | |||
* @param executor 执行器 | |||
* @param uri 请求地址 | |||
* @param data 参数 | |||
* @param <T> 请求值类型 | |||
* @param <E> 返回值类型 | |||
*/ | |||
<T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试 | |||
* 默认:1000ms | |||
* </pre> | |||
* | |||
* @param retrySleepMillis 重试休息时间 | |||
*/ | |||
void setRetrySleepMillis(int retrySleepMillis); | |||
/** | |||
* <pre> | |||
* 设置当微信系统响应系统繁忙时,最大重试次数 | |||
* 默认:5次 | |||
* </pre> | |||
* | |||
* @param maxRetryTimes 最大重试次数 | |||
*/ | |||
void setMaxRetryTimes(int maxRetryTimes); | |||
/** | |||
* 初始化http请求对象 | |||
*/ | |||
void initHttp(); | |||
/** | |||
* 获取WxMpConfigStorage 对象 | |||
* | |||
* @return WxMpConfigStorage | |||
*/ | |||
WxCpTpConfigStorage getWxCpTpConfigStorage(); | |||
/** | |||
* 注入 {@link WxCpTpConfigStorage} 的实现 | |||
* | |||
* @param wxConfigProvider 配置对象 | |||
*/ | |||
void setWxCpTpConfigStorage(WxCpTpConfigStorage wxConfigProvider); | |||
/** | |||
* http请求对象 | |||
*/ | |||
RequestHttp<?, ?> getRequestHttp(); | |||
} |
@@ -1,13 +1,13 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import java.util.List; | |||
import java.util.Map; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.bean.WxCpInviteResult; | |||
import me.chanjar.weixin.cp.bean.WxCpUser; | |||
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* <pre> | |||
* 用户管理接口 | |||
@@ -17,6 +17,7 @@ import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public interface WxCpUserService { | |||
/** | |||
* <pre> | |||
* 用在二次验证的时候. | |||
@@ -136,6 +137,23 @@ public interface WxCpUserService { | |||
*/ | |||
String openid2UserId(String openid) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* | |||
* 通过手机号获取其所对应的userid。 | |||
* | |||
* 请求方式:POST(HTTPS) | |||
* 请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/getuserid?access_token=ACCESS_TOKEN | |||
* | |||
* 文档地址:https://work.weixin.qq.com/api/doc#90001/90143/91693 | |||
* </pre> | |||
* | |||
* @param mobile 手机号码。长度为5~32个字节 | |||
* @return userid mobile对应的成员userid | |||
* @throws WxErrorException . | |||
*/ | |||
String getUserId(String mobile) throws WxErrorException; | |||
/** | |||
* 获取外部联系人详情. | |||
* <pre> | |||
@@ -146,6 +164,10 @@ public interface WxCpUserService { | |||
* </pre> | |||
* | |||
* @param userId 外部联系人的userid | |||
* @return 联系人详情 | |||
* @throws WxErrorException . | |||
*/ | |||
WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException; | |||
} |
@@ -5,6 +5,7 @@ import com.google.gson.JsonArray; | |||
import com.google.gson.JsonElement; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import lombok.extern.slf4j.Slf4j; | |||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
@@ -23,6 +24,7 @@ import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | |||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
@@ -31,22 +33,24 @@ import java.io.IOException; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.*; | |||
/** | |||
* @author chanjarster | |||
*/ | |||
@Slf4j | |||
public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestHttp<H, P> { | |||
protected final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
private WxCpUserService userService = new WxCpUserServiceImpl(this); | |||
private WxCpChatService chatService = new WxCpChatServiceImpl(this); | |||
private WxCpUserService userService = new WxCpUserServiceImpl(this); | |||
private WxCpChatService chatService = new WxCpChatServiceImpl(this); | |||
private WxCpDepartmentService departmentService = new WxCpDepartmentServiceImpl(this); | |||
private WxCpMediaService mediaService = new WxCpMediaServiceImpl(this); | |||
private WxCpMenuService menuService = new WxCpMenuServiceImpl(this); | |||
private WxCpOAuth2Service oauth2Service = new WxCpOAuth2ServiceImpl(this); | |||
private WxCpTagService tagService = new WxCpTagServiceImpl(this); | |||
private WxCpAgentService agentService = new WxCpAgentServiceImpl(this); | |||
private WxCpOAService oaService = new WxCpOAServiceImpl(this); | |||
private WxCpTaskCardService taskCardService = new WxCpTaskCardServiceImpl(this); | |||
private WxCpMediaService mediaService = new WxCpMediaServiceImpl(this); | |||
private WxCpMenuService menuService = new WxCpMenuServiceImpl(this); | |||
private WxCpOAuth2Service oauth2Service = new WxCpOAuth2ServiceImpl(this); | |||
private WxCpTagService tagService = new WxCpTagServiceImpl(this); | |||
private WxCpAgentService agentService = new WxCpAgentServiceImpl(this); | |||
private WxCpOaService oaService = new WxCpOaServiceImpl(this); | |||
private WxCpTaskCardService taskCardService = new WxCpTaskCardServiceImpl(this); | |||
private WxCpExternalContactService externalContactService = new WxCpExternalContactServiceImpl(this); | |||
/** | |||
* 全局的是否正在刷新access token的锁 | |||
@@ -80,7 +84,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data) | |||
.equals(msgSignature); | |||
} 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; | |||
} | |||
} | |||
@@ -104,7 +108,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
if (this.configStorage.isAgentJsapiTicketExpired()) { | |||
synchronized (this.globalAgentJsapiTicketRefreshLock) { | |||
if (this.configStorage.isAgentJsapiTicketExpired()) { | |||
String responseContent = this.get(WxCpService.GET_AGENT_CONFIG_TICKET, null); | |||
String responseContent = this.get(this.configStorage.getApiUrl(GET_AGENT_CONFIG_TICKET), null); | |||
JsonObject jsonObject = new JsonParser().parse(responseContent).getAsJsonObject(); | |||
this.configStorage.updateAgentJsapiTicket(jsonObject.get("ticket").getAsString(), | |||
jsonObject.get("expires_in").getAsInt()); | |||
@@ -129,7 +133,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
if (this.configStorage.isJsapiTicketExpired()) { | |||
synchronized (this.globalJsapiTicketRefreshLock) { | |||
if (this.configStorage.isJsapiTicketExpired()) { | |||
String responseContent = this.get(WxCpService.GET_JSAPI_TICKET, null); | |||
String responseContent = this.get(this.configStorage.getApiUrl(GET_JSAPI_TICKET), null); | |||
JsonObject tmpJsonObject = new JsonParser().parse(responseContent).getAsJsonObject(); | |||
this.configStorage.updateJsapiTicket(tmpJsonObject.get("ticket").getAsString(), | |||
tmpJsonObject.get("expires_in").getAsInt()); | |||
@@ -170,7 +174,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
message.setAgentId(this.getWxCpConfigStorage().getAgentId()); | |||
} | |||
return WxCpMessageSendResult.fromJson(this.post(WxCpService.MESSAGE_SEND, message.toJson())); | |||
return WxCpMessageSendResult.fromJson(this.post(this.configStorage.getApiUrl(MESSAGE_SEND), message.toJson())); | |||
} | |||
@Override | |||
@@ -179,13 +183,13 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
params.put("js_code", jsCode); | |||
params.put("grant_type", "authorization_code"); | |||
String result = this.get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); | |||
String result = this.get(this.configStorage.getApiUrl(JSCODE_TO_SESSION), Joiner.on("&").withKeyValueSeparator("=").join(params)); | |||
return WxCpMaJsCode2SessionResult.fromJson(result); | |||
} | |||
@Override | |||
public String[] getCallbackIp() throws WxErrorException { | |||
String responseContent = get(WxCpService.GET_CALLBACK_IP, null); | |||
String responseContent = get(this.configStorage.getApiUrl(GET_CALLBACK_IP), null); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); | |||
String[] ips = new String[jsonArray.size()]; | |||
@@ -216,7 +220,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
return this.executeInternal(executor, uri, data); | |||
} catch (WxErrorException e) { | |||
if (retryTimes + 1 > this.maxRetryTimes) { | |||
this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||
log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||
//最后一次重试失败后,直接抛出异常,不再等待 | |||
throw new RuntimeException("微信服务端异常,超出重试次数"); | |||
} | |||
@@ -228,7 +232,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
if (error.getErrorCode() == -1) { | |||
int sleepMillis = this.retrySleepMillis * (1 << retryTimes); | |||
try { | |||
this.log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); | |||
log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); | |||
Thread.sleep(sleepMillis); | |||
} catch (InterruptedException e1) { | |||
Thread.currentThread().interrupt(); | |||
@@ -239,7 +243,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
} | |||
} while (retryTimes++ < this.maxRetryTimes); | |||
this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||
log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||
throw new RuntimeException("微信服务端异常,超出重试次数"); | |||
} | |||
@@ -255,7 +259,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
try { | |||
T result = executor.execute(uriWithAccessToken, data); | |||
this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); | |||
log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); | |||
return result; | |||
} catch (WxErrorException e) { | |||
WxError error = e.getError(); | |||
@@ -272,12 +276,12 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
} | |||
if (error.getErrorCode() != 0) { | |||
this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); | |||
log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); | |||
throw new WxErrorException(error, e); | |||
} | |||
return null; | |||
} catch (IOException e) { | |||
this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); | |||
log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
@@ -329,19 +333,19 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
public String replaceParty(String mediaId) throws WxErrorException { | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("media_id", mediaId); | |||
return post(WxCpService.BATCH_REPLACE_PARTY, jsonObject.toString()); | |||
return post(this.configStorage.getApiUrl(BATCH_REPLACE_PARTY), jsonObject.toString()); | |||
} | |||
@Override | |||
public String replaceUser(String mediaId) throws WxErrorException { | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("media_id", mediaId); | |||
return post(WxCpService.BATCH_REPLACE_USER, jsonObject.toString()); | |||
return post(this.configStorage.getApiUrl(BATCH_REPLACE_USER), jsonObject.toString()); | |||
} | |||
@Override | |||
public String getTaskResult(String joinId) throws WxErrorException { | |||
String url = WxCpService.BATCH_GET_RESULT + joinId; | |||
String url = this.configStorage.getApiUrl(BATCH_GET_RESULT + joinId); | |||
return get(url, null); | |||
} | |||
@@ -383,13 +387,18 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
return userService; | |||
} | |||
@Override | |||
public WxCpExternalContactService getExternalContactService() { | |||
return externalContactService; | |||
} | |||
@Override | |||
public WxCpChatService getChatService() { | |||
return chatService; | |||
} | |||
@Override | |||
public WxCpOAService getOAService() { | |||
public WxCpOaService getOAService() { | |||
return oaService; | |||
} | |||
@@ -0,0 +1,239 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import com.google.common.base.Joiner; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import lombok.extern.slf4j.Slf4j; | |||
import me.chanjar.weixin.common.WxType; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.DataUtils; | |||
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.cp.api.WxCpTpService; | |||
import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | |||
import me.chanjar.weixin.cp.bean.WxCpTpCorp; | |||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
/** | |||
* @author zhenjun cai | |||
*/ | |||
@Slf4j | |||
public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, RequestHttp<H, P> { | |||
/** | |||
* 全局的是否正在刷新access token的锁. | |||
*/ | |||
protected final Object globalSuiteAccessTokenRefreshLock = new Object(); | |||
/** | |||
* 全局的是否正在刷新jsapi_ticket的锁. | |||
*/ | |||
protected final Object globalSuiteTicketRefreshLock = new Object(); | |||
protected WxCpTpConfigStorage configStorage; | |||
/** | |||
* 临时文件目录. | |||
*/ | |||
private File tmpDirFile; | |||
private int retrySleepMillis = 1000; | |||
private int maxRetryTimes = 5; | |||
@Override | |||
public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) { | |||
try { | |||
return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data) | |||
.equals(msgSignature); | |||
} catch (Exception e) { | |||
log.error("Checking signature failed, and the reason is :" + e.getMessage()); | |||
return false; | |||
} | |||
} | |||
@Override | |||
public String getSuiteAccessToken() throws WxErrorException { | |||
return getSuiteAccessToken(false); | |||
} | |||
@Override | |||
public String getSuiteTicket() throws WxErrorException { | |||
return getSuiteTicket(false); | |||
} | |||
@Override | |||
public String getSuiteTicket(boolean forceRefresh) throws WxErrorException { | |||
// suite ticket由微信服务器推送,不能强制刷新 | |||
// if (forceRefresh) { | |||
// this.configStorage.expireSuiteTicket(); | |||
// } | |||
if (this.configStorage.isSuiteTicketExpired()) { | |||
// 本地suite ticket 不存在或者过期 | |||
WxError wxError = WxError.fromJson("{\"errcode\":40085, \"errmsg\":\"invaild suite ticket\"}", WxType.CP); | |||
throw new WxErrorException(wxError); | |||
} | |||
return this.configStorage.getSuiteTicket(); | |||
} | |||
@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(configStorage.getApiUrl(WxCpApiPathConsts.Tp.JSCODE_TO_SESSION), Joiner.on("&").withKeyValueSeparator("=").join(params)); | |||
return WxCpMaJsCode2SessionResult.fromJson(result); | |||
} | |||
@Override | |||
public WxAccessToken getCorpToken(String authCorpid, String permanentCode) throws WxErrorException { | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("auth_corpid", authCorpid); | |||
jsonObject.addProperty("permanent_code", permanentCode); | |||
String result = post(configStorage.getApiUrl(WxCpApiPathConsts.Tp.GET_CORP_TOKEN), jsonObject.toString()); | |||
return WxAccessToken.fromJson(result); | |||
} | |||
@Override | |||
public WxCpTpCorp getPermanentCode(String authCode) throws WxErrorException { | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("auth_code", authCode); | |||
String result = post(configStorage.getApiUrl(WxCpApiPathConsts.Tp.GET_PERMANENT_CODE), jsonObject.toString()); | |||
jsonObject = new JsonParser().parse(result).getAsJsonObject(); | |||
WxCpTpCorp wxCpTpCorp = WxCpTpCorp.fromJson(jsonObject.get("auth_corp_info").getAsString()); | |||
wxCpTpCorp.setPermanentCode(jsonObject.get("permanent_code").getAsString()); | |||
return wxCpTpCorp; | |||
} | |||
@Override | |||
public String get(String url, String queryParam) throws WxErrorException { | |||
return execute(SimpleGetRequestExecutor.create(this), url, queryParam); | |||
} | |||
@Override | |||
public String post(String url, String postData) throws WxErrorException { | |||
return execute(SimplePostRequestExecutor.create(this), url, postData); | |||
} | |||
/** | |||
* 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求. | |||
*/ | |||
@Override | |||
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | |||
int retryTimes = 0; | |||
do { | |||
try { | |||
return this.executeInternal(executor, uri, data); | |||
} catch (WxErrorException e) { | |||
if (retryTimes + 1 > this.maxRetryTimes) { | |||
log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||
//最后一次重试失败后,直接抛出异常,不再等待 | |||
throw new RuntimeException("微信服务端异常,超出重试次数"); | |||
} | |||
WxError error = e.getError(); | |||
/* | |||
* -1 系统繁忙, 1000ms后重试 | |||
*/ | |||
if (error.getErrorCode() == -1) { | |||
int sleepMillis = this.retrySleepMillis * (1 << retryTimes); | |||
try { | |||
log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); | |||
Thread.sleep(sleepMillis); | |||
} catch (InterruptedException e1) { | |||
Thread.currentThread().interrupt(); | |||
} | |||
} else { | |||
throw e; | |||
} | |||
} | |||
} while (retryTimes++ < this.maxRetryTimes); | |||
log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||
throw new RuntimeException("微信服务端异常,超出重试次数"); | |||
} | |||
protected <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | |||
E dataForLog = DataUtils.handleDataWithSecret(data); | |||
if (uri.contains("suite_access_token=")) { | |||
throw new IllegalArgumentException("uri参数中不允许有suite_access_token: " + uri); | |||
} | |||
String suiteAccessToken = getSuiteAccessToken(false); | |||
String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "suite_access_token=" + suiteAccessToken; | |||
try { | |||
T result = executor.execute(uriWithAccessToken, data); | |||
log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); | |||
return result; | |||
} catch (WxErrorException e) { | |||
WxError error = e.getError(); | |||
/* | |||
* 发生以下情况时尝试刷新suite_access_token | |||
* 42009 suite_access_token已过期 | |||
*/ | |||
if (error.getErrorCode() == 42009) { | |||
// 强制设置wxCpTpConfigStorage它的suite access token过期了,这样在下一次请求里就会刷新suite access token | |||
this.configStorage.expireSuiteAccessToken(); | |||
return execute(executor, uri, data); | |||
} | |||
if (error.getErrorCode() != 0) { | |||
log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); | |||
throw new WxErrorException(error, e); | |||
} | |||
return null; | |||
} catch (IOException e) { | |||
log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
@Override | |||
public void setWxCpTpConfigStorage(WxCpTpConfigStorage wxConfigProvider) { | |||
this.configStorage = wxConfigProvider; | |||
this.initHttp(); | |||
} | |||
@Override | |||
public void setRetrySleepMillis(int retrySleepMillis) { | |||
this.retrySleepMillis = retrySleepMillis; | |||
} | |||
@Override | |||
public void setMaxRetryTimes(int maxRetryTimes) { | |||
this.maxRetryTimes = maxRetryTimes; | |||
} | |||
public File getTmpDirFile() { | |||
return this.tmpDirFile; | |||
} | |||
public void setTmpDirFile(File tmpDirFile) { | |||
this.tmpDirFile = tmpDirFile; | |||
} | |||
@Override | |||
public RequestHttp<?, ?> getRequestHttp() { | |||
return this; | |||
} | |||
} |
@@ -1,11 +1,9 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import java.util.List; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import com.google.gson.reflect.TypeToken; | |||
import lombok.RequiredArgsConstructor; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.api.WxCpAgentService; | |||
@@ -13,6 +11,10 @@ import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.bean.WxCpAgent; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.util.List; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Agent.*; | |||
/** | |||
* <pre> | |||
@@ -22,14 +24,11 @@ import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
* | |||
* @author <a href="https://github.com/huansinho">huansinho</a> | |||
*/ | |||
@RequiredArgsConstructor | |||
public class WxCpAgentServiceImpl implements WxCpAgentService { | |||
private static final JsonParser JSON_PARSER = new JsonParser(); | |||
private WxCpService mainService; | |||
public WxCpAgentServiceImpl(WxCpService mainService) { | |||
this.mainService = mainService; | |||
} | |||
private final WxCpService mainService; | |||
@Override | |||
public WxCpAgent get(Integer agentId) throws WxErrorException { | |||
@@ -37,14 +36,13 @@ public class WxCpAgentServiceImpl implements WxCpAgentService { | |||
throw new IllegalArgumentException("缺少agentid参数"); | |||
} | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/agent/get?agentid=" + agentId; | |||
String responseContent = this.mainService.get(url, null); | |||
String responseContent = this.mainService.get(String.format(this.mainService.getWxCpConfigStorage().getApiUrl(AGENT_GET), agentId), null); | |||
return WxCpAgent.fromJson(responseContent); | |||
} | |||
@Override | |||
public void set(WxCpAgent agentInfo) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/agent/set"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(AGENT_SET); | |||
String responseContent = this.mainService.post(url, agentInfo.toJson()); | |||
JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | |||
if (jsonObject.get("errcode").getAsInt() != 0) { | |||
@@ -54,7 +52,7 @@ public class WxCpAgentServiceImpl implements WxCpAgentService { | |||
@Override | |||
public List<WxCpAgent> list() throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/agent/list"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(AGENT_LIST); | |||
String responseContent = this.mainService.get(url, null); | |||
JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | |||
if (jsonObject.get("errcode").getAsInt() != 0) { | |||
@@ -1,12 +1,14 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import com.google.gson.JsonParser; | |||
import lombok.RequiredArgsConstructor; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
import me.chanjar.weixin.cp.api.WxCpChatService; | |||
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.constant.WxCpApiPathConsts; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import org.apache.commons.lang3.StringUtils; | |||
@@ -14,24 +16,18 @@ import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Chat.*; | |||
/** | |||
* 群聊服务实现. | |||
* | |||
* @author gaigeshen | |||
*/ | |||
@RequiredArgsConstructor | |||
public class WxCpChatServiceImpl implements WxCpChatService { | |||
private static final JsonParser JSON_PARSER = new JsonParser(); | |||
private final WxCpService cpService; | |||
/** | |||
* 创建群聊服务实现的实例. | |||
* | |||
* @param cpService 企业微信的服务 | |||
*/ | |||
WxCpChatServiceImpl(WxCpService cpService) { | |||
this.cpService = cpService; | |||
} | |||
@Override | |||
public String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException { | |||
Map<String, Object> data = new HashMap<>(4); | |||
@@ -47,7 +43,7 @@ public class WxCpChatServiceImpl implements WxCpChatService { | |||
if (StringUtils.isNotBlank(chatId)) { | |||
data.put("chatid", chatId); | |||
} | |||
String result = this.cpService.post(APPCHAT_CREATE, WxGsonBuilder.create().toJson(data)); | |||
String result = this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(APPCHAT_CREATE), WxGsonBuilder.create().toJson(data)); | |||
return new JsonParser().parse(result).getAsJsonObject().get("chatid").getAsString(); | |||
} | |||
@@ -76,7 +72,7 @@ public class WxCpChatServiceImpl implements WxCpChatService { | |||
data.put("del_user_list", usersToDelete); | |||
} | |||
this.cpService.post(APPCHAT_UPDATE, WxGsonBuilder.create().toJson(data)); | |||
this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(APPCHAT_UPDATE), WxGsonBuilder.create().toJson(data)); | |||
} | |||
@Override | |||
@@ -86,7 +82,7 @@ public class WxCpChatServiceImpl implements WxCpChatService { | |||
@Override | |||
public WxCpChat chatGet(String chatId) throws WxErrorException { | |||
String result = this.cpService.get(APPCHAT_GET_CHATID + chatId, null); | |||
String result = this.cpService.get(this.cpService.getWxCpConfigStorage().getApiUrl(APPCHAT_GET_CHATID + chatId), null); | |||
return WxCpGsonBuilder.create() | |||
.fromJson(JSON_PARSER.parse(result).getAsJsonObject().getAsJsonObject("chat_info").toString(), WxCpChat.class); | |||
} | |||
@@ -98,7 +94,7 @@ public class WxCpChatServiceImpl implements WxCpChatService { | |||
@Override | |||
public void sendMsg(WxCpAppChatMessage message) throws WxErrorException { | |||
this.cpService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/send", message.toJson()); | |||
this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(APPCHAT_SEND), message.toJson()); | |||
} | |||
} |
@@ -1,17 +1,21 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import java.util.List; | |||
import com.google.gson.JsonElement; | |||
import com.google.gson.JsonParser; | |||
import com.google.gson.reflect.TypeToken; | |||
import lombok.RequiredArgsConstructor; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.json.GsonHelper; | |||
import me.chanjar.weixin.cp.api.WxCpDepartmentService; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.bean.WxCpDepart; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.util.List; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Department.*; | |||
/** | |||
* <pre> | |||
* 部门管理接口 | |||
@@ -20,16 +24,13 @@ import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
@RequiredArgsConstructor | |||
public class WxCpDepartmentServiceImpl implements WxCpDepartmentService { | |||
private WxCpService mainService; | |||
public WxCpDepartmentServiceImpl(WxCpService mainService) { | |||
this.mainService = mainService; | |||
} | |||
private final WxCpService mainService; | |||
@Override | |||
public Long create(WxCpDepart depart) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_CREATE); | |||
String responseContent = this.mainService.post(url, depart.toJson()); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("id")); | |||
@@ -37,19 +38,19 @@ public class WxCpDepartmentServiceImpl implements WxCpDepartmentService { | |||
@Override | |||
public void update(WxCpDepart group) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/update"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_UPDATE); | |||
this.mainService.post(url, group.toJson()); | |||
} | |||
@Override | |||
public void delete(Long departId) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/delete?id=" + departId; | |||
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_DELETE), departId); | |||
this.mainService.get(url, null); | |||
} | |||
@Override | |||
public List<WxCpDepart> list(Long id) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/list"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_LIST); | |||
if (id != null) { | |||
url += "?id=" + id; | |||
} | |||
@@ -0,0 +1,41 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.api.WxCpExternalContactService; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | |||
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactList; | |||
import me.chanjar.weixin.cp.bean.WxCpUserWithExternalPermission; | |||
import java.util.List; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.ExternalContact.*; | |||
public class WxCpExternalContactServiceImpl implements WxCpExternalContactService { | |||
private WxCpService mainService; | |||
public WxCpExternalContactServiceImpl(WxCpService mainService) { | |||
this.mainService = mainService; | |||
} | |||
@Override | |||
public WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException { | |||
String responseContent = this.mainService.get(this.mainService.getWxCpConfigStorage().getApiUrl(GET_EXTERNAL_CONTACT + userId), null); | |||
return WxCpUserExternalContactInfo.fromJson(responseContent); | |||
} | |||
@Override | |||
public List<String> listExternalContacts(String userId) throws WxErrorException { | |||
String responseContent = this.mainService.get(this.mainService.getWxCpConfigStorage().getApiUrl(LIST_EXTERNAL_CONTACT + userId), null); | |||
WxCpUserExternalContactList list = WxCpUserExternalContactList.fromJson(responseContent); | |||
return list.getExternalUserId(); | |||
} | |||
@Override | |||
public List<String> listFollowUser() throws WxErrorException { | |||
String responseContent = this.mainService.get(this.mainService.getWxCpConfigStorage().getApiUrl(GET_FOLLOW_USER_LIST), null); | |||
WxCpUserWithExternalPermission list = WxCpUserWithExternalPermission.fromJson(responseContent); | |||
return list.getFollowUser(); | |||
} | |||
} |
@@ -1,10 +1,6 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.UUID; | |||
import lombok.RequiredArgsConstructor; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.fs.FileUtils; | |||
@@ -12,6 +8,14 @@ import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||
import me.chanjar.weixin.cp.api.WxCpMediaService; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.UUID; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.*; | |||
/** | |||
* <pre> | |||
@@ -21,12 +25,9 @@ import me.chanjar.weixin.cp.api.WxCpService; | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
@RequiredArgsConstructor | |||
public class WxCpMediaServiceImpl implements WxCpMediaService { | |||
private WxCpService mainService; | |||
public WxCpMediaServiceImpl(WxCpService mainService) { | |||
this.mainService = mainService; | |||
} | |||
private final WxCpService mainService; | |||
@Override | |||
public WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputStream) | |||
@@ -37,7 +38,7 @@ public class WxCpMediaServiceImpl implements WxCpMediaService { | |||
@Override | |||
public WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException { | |||
return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), | |||
MEDIA_UPLOAD_URL + mediaType, file); | |||
this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_UPLOAD + mediaType), file); | |||
} | |||
@Override | |||
@@ -45,7 +46,7 @@ public class WxCpMediaServiceImpl implements WxCpMediaService { | |||
return this.mainService.execute( | |||
BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(), | |||
this.mainService.getWxCpConfigStorage().getTmpDirFile()), | |||
MEDIA_GET_URL, "media_id=" + mediaId); | |||
this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_GET), "media_id=" + mediaId); | |||
} | |||
@Override | |||
@@ -53,13 +54,13 @@ public class WxCpMediaServiceImpl implements WxCpMediaService { | |||
return this.mainService.execute( | |||
BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(), | |||
this.mainService.getWxCpConfigStorage().getTmpDirFile()), | |||
JSSDK_MEDIA_GET_URL, "media_id=" + mediaId); | |||
this.mainService.getWxCpConfigStorage().getApiUrl(JSSDK_MEDIA_GET), "media_id=" + mediaId); | |||
} | |||
@Override | |||
public String uploadImg(File file) throws WxErrorException { | |||
final WxMediaUploadResult result = this.mainService | |||
.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), IMG_UPLOAD_URL, file); | |||
.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), this.mainService.getWxCpConfigStorage().getApiUrl(IMG_UPLOAD), file); | |||
return result.getUrl(); | |||
} | |||
} |
@@ -1,24 +1,26 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import lombok.RequiredArgsConstructor; | |||
import me.chanjar.weixin.common.bean.menu.WxMenu; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.api.WxCpMenuService; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Menu.*; | |||
/** | |||
* <pre> | |||
* 菜单管理相关接口 | |||
* 菜单管理相关接口. | |||
* Created by Binary Wang on 2017-6-25. | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
@RequiredArgsConstructor | |||
public class WxCpMenuServiceImpl implements WxCpMenuService { | |||
private WxCpService mainService; | |||
public WxCpMenuServiceImpl(WxCpService mainService) { | |||
this.mainService = mainService; | |||
} | |||
private final WxCpService mainService; | |||
@Override | |||
public void create(WxMenu menu) throws WxErrorException { | |||
@@ -27,7 +29,7 @@ public class WxCpMenuServiceImpl implements WxCpMenuService { | |||
@Override | |||
public void create(Integer agentId, WxMenu menu) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid=" + agentId; | |||
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(MENU_CREATE), agentId); | |||
this.mainService.post(url, menu.toJson()); | |||
} | |||
@@ -38,7 +40,7 @@ public class WxCpMenuServiceImpl implements WxCpMenuService { | |||
@Override | |||
public void delete(Integer agentId) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete?agentid=" + agentId; | |||
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(MENU_DELETE), agentId); | |||
this.mainService.get(url, null); | |||
} | |||
@@ -49,7 +51,7 @@ public class WxCpMenuServiceImpl implements WxCpMenuService { | |||
@Override | |||
public WxMenu get(Integer agentId) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/get?agentid=" + agentId; | |||
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(MENU_GET), agentId); | |||
try { | |||
String resultContent = this.mainService.get(url, null); | |||
return WxCpGsonBuilder.create().fromJson(resultContent, WxMenu.class); | |||
@@ -3,30 +3,35 @@ package me.chanjar.weixin.cp.api.impl; | |||
import com.google.gson.JsonElement; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import lombok.AllArgsConstructor; | |||
import lombok.RequiredArgsConstructor; | |||
import me.chanjar.weixin.common.api.WxConsts; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.URIUtil; | |||
import me.chanjar.weixin.common.util.json.GsonHelper; | |||
import me.chanjar.weixin.cp.api.WxCpOAuth2Service; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo; | |||
import me.chanjar.weixin.cp.bean.WxCpUserDetail; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import static me.chanjar.weixin.common.api.WxConsts.OAuth2Scope.*; | |||
import static me.chanjar.weixin.common.api.WxConsts.OAuth2Scope.SNSAPI_PRIVATEINFO; | |||
import static me.chanjar.weixin.common.api.WxConsts.OAuth2Scope.SNSAPI_USERINFO; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.OAuth2.*; | |||
/** | |||
* <pre> | |||
* | |||
* oauth2相关接口实现类. | |||
* Created by Binary Wang on 2017-6-25. | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
* | |||
* @author Binary Wang | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
@RequiredArgsConstructor | |||
public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { | |||
private WxCpService mainService; | |||
public WxCpOAuth2ServiceImpl(WxCpService mainService) { | |||
this.mainService = mainService; | |||
} | |||
private final WxCpService mainService; | |||
@Override | |||
public String buildAuthorizationUrl(String state) { | |||
@@ -38,55 +43,54 @@ public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { | |||
@Override | |||
public String buildAuthorizationUrl(String redirectUri, String state) { | |||
return this.buildAuthorizationUrl(redirectUri, state, WxConsts.OAuth2Scope.SNSAPI_BASE); | |||
return this.buildAuthorizationUrl(redirectUri, state, SNSAPI_BASE); | |||
} | |||
@Override | |||
public String buildAuthorizationUrl(String redirectUri, String state, String scope) { | |||
StringBuilder url = new StringBuilder("https://open.weixin.qq.com/connect/oauth2/authorize?"); | |||
url.append("appid=").append(this.mainService.getWxCpConfigStorage().getCorpId()); | |||
StringBuilder url = new StringBuilder(URL_OAUTH2_AUTHORIZE); | |||
url.append("?appid=").append(this.mainService.getWxCpConfigStorage().getCorpId()); | |||
url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectUri)); | |||
url.append("&response_type=code"); | |||
url.append("&scope=").append(scope); | |||
if (WxConsts.OAuth2Scope.SNSAPI_PRIVATEINFO.equals(scope) | |||
|| WxConsts.OAuth2Scope.SNSAPI_USERINFO.equals(scope)) { | |||
if (SNSAPI_PRIVATEINFO.equals(scope) || SNSAPI_USERINFO.equals(scope)) { | |||
url.append("&agentid=").append(this.mainService.getWxCpConfigStorage().getAgentId()); | |||
} | |||
if (state != null) { | |||
url.append("&state=").append(state); | |||
} | |||
url.append("#wechat_redirect"); | |||
return url.toString(); | |||
} | |||
@Override | |||
public String[] getUserInfo(String code) throws WxErrorException { | |||
public WxCpOauth2UserInfo getUserInfo(String code) throws WxErrorException { | |||
return this.getUserInfo(this.mainService.getWxCpConfigStorage().getAgentId(), code); | |||
} | |||
@Override | |||
public String[] getUserInfo(Integer agentId, String code) throws WxErrorException { | |||
String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?code=%s&agentid=%d", | |||
code, agentId); | |||
String responseText = this.mainService.get(url, null); | |||
public WxCpOauth2UserInfo getUserInfo(Integer agentId, String code) throws WxErrorException { | |||
String responseText = this.mainService.get(String.format(this.mainService.getWxCpConfigStorage().getApiUrl(GET_USER_INFO), code, agentId), null); | |||
JsonElement je = new JsonParser().parse(responseText); | |||
JsonObject jo = je.getAsJsonObject(); | |||
return new String[]{GsonHelper.getString(jo, "UserId"), | |||
GsonHelper.getString(jo, "DeviceId"), | |||
GsonHelper.getString(jo, "OpenId"), | |||
GsonHelper.getString(jo, "user_ticket"), | |||
GsonHelper.getString(jo, "expires_in") | |||
}; | |||
return WxCpOauth2UserInfo.builder() | |||
.userId(GsonHelper.getString(jo, "UserId")) | |||
.deviceId(GsonHelper.getString(jo, "DeviceId")) | |||
.openId(GsonHelper.getString(jo, "OpenId")) | |||
.userTicket(GsonHelper.getString(jo, "user_ticket")) | |||
.expiresIn(GsonHelper.getString(jo, "expires_in")) | |||
.build(); | |||
} | |||
@Override | |||
public WxCpUserDetail getUserDetail(String userTicket) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserdetail"; | |||
JsonObject param = new JsonObject(); | |||
param.addProperty("user_ticket", userTicket); | |||
String responseText = this.mainService.post(url, param.toString()); | |||
String responseText = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(GET_USER_DETAIL), param.toString()); | |||
return WxCpGsonBuilder.create().fromJson(responseText, WxCpUserDetail.class); | |||
} | |||
} |
@@ -5,36 +5,34 @@ import com.google.gson.JsonElement; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import com.google.gson.reflect.TypeToken; | |||
import lombok.RequiredArgsConstructor; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.api.WxCpOAService; | |||
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.constant.WxCpApiPathConsts; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.util.Date; | |||
import java.util.List; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Oa.*; | |||
/** | |||
* @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; | |||
} | |||
@RequiredArgsConstructor | |||
public class WxCpOaServiceImpl implements WxCpOaService { | |||
private final WxCpService mainService; | |||
@Override | |||
public List<WxCpCheckinData> getCheckinData(Integer openCheckinDataType, Date starttime, Date endtime, List<String> userIdList) throws WxErrorException { | |||
if (starttime == null || endtime == null) { | |||
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"); | |||
} | |||
@@ -42,15 +40,13 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||
throw new RuntimeException("用户列表不能为空,不超过100个,若用户超过100个,请分批获取"); | |||
} | |||
long endtimestamp = endtime.getTime() / 1000L; | |||
long starttimestamp = starttime.getTime() / 1000L; | |||
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(); | |||
@@ -64,7 +60,7 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||
jsonObject.add("useridlist", jsonArray); | |||
String responseContent = this.mainService.post(url, jsonObject.toString()); | |||
String responseContent = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(GET_CHECKIN_DATA), jsonObject.toString()); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return WxCpGsonBuilder.create() | |||
.fromJson( | |||
@@ -76,7 +72,6 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||
@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"); | |||
} | |||
@@ -94,7 +89,7 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||
jsonObject.addProperty("datetime", datetime.getTime() / 1000L); | |||
jsonObject.add("useridlist", jsonArray); | |||
String responseContent = this.mainService.post(url, jsonObject.toString()); | |||
String responseContent = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(GET_CHECKIN_OPTION), jsonObject.toString()); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return WxCpGsonBuilder.create() | |||
@@ -106,24 +101,20 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||
} | |||
@Override | |||
public WxCpApprovalDataResult getApprovalData(Date starttime, Date endtime, Long nextSpnum) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/corp/getapprovaldata"; | |||
public WxCpApprovalDataResult getApprovalData(Date startTime, Date endTime, Long nextSpnum) throws WxErrorException { | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("starttime", starttime.getTime() / 1000L); | |||
jsonObject.addProperty("endtime", endtime.getTime() / 1000L); | |||
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()); | |||
String responseContent = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(GET_APPROVAL_DATA), 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"; | |||
public List<WxCpDialRecord> getDialRecord(Date startTime, Date endTime, Integer offset, Integer limit) throws WxErrorException { | |||
JsonObject jsonObject = new JsonObject(); | |||
if (offset == null) { | |||
@@ -137,10 +128,10 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||
jsonObject.addProperty("offset", offset); | |||
jsonObject.addProperty("limit", limit); | |||
if (starttime != null && endtime != null) { | |||
if (startTime != null && endTime != null) { | |||
long endtimestamp = endtime.getTime() / 1000L; | |||
long starttimestamp = starttime.getTime() / 1000L; | |||
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天进行查询"); | |||
@@ -148,11 +139,9 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||
jsonObject.addProperty("start_time", starttimestamp); | |||
jsonObject.addProperty("end_time", endtimestamp); | |||
} | |||
String responseContent = this.mainService.post(url, jsonObject.toString()); | |||
String responseContent = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(GET_DIAL_RECORD), jsonObject.toString()); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return WxCpGsonBuilder.create() |
@@ -8,8 +8,8 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||
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.DefaultApacheHttpClientBuilder; | |||
import me.chanjar.weixin.cp.api.WxCpOAService; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import org.apache.http.HttpHost; | |||
import org.apache.http.client.config.RequestConfig; | |||
import org.apache.http.client.methods.CloseableHttpResponse; | |||
@@ -19,9 +19,12 @@ import org.apache.http.impl.client.CloseableHttpClient; | |||
import java.io.IOException; | |||
/** | |||
* @author someone | |||
*/ | |||
public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<CloseableHttpClient, HttpHost> { | |||
protected CloseableHttpClient httpClient; | |||
protected HttpHost httpProxy; | |||
private CloseableHttpClient httpClient; | |||
private HttpHost httpProxy; | |||
@Override | |||
public CloseableHttpClient getRequestHttpClient() { | |||
@@ -45,9 +48,8 @@ public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<Closeab | |||
} | |||
synchronized (this.globalAccessTokenRefreshLock) { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | |||
+ "&corpid=" + this.configStorage.getCorpId() | |||
+ "&corpsecret=" + this.configStorage.getCorpSecret(); | |||
String url = String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN), this.configStorage.getCorpId(), this.configStorage.getCorpSecret()); | |||
try { | |||
HttpGet httpGet = new HttpGet(url); | |||
if (this.httpProxy != null) { | |||
@@ -56,8 +58,8 @@ public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<Closeab | |||
httpGet.setConfig(config); | |||
} | |||
String resultContent; | |||
try (CloseableHttpClient httpclient = getRequestHttpClient(); | |||
CloseableHttpResponse response = httpclient.execute(httpGet)) { | |||
try (CloseableHttpClient httpClient = getRequestHttpClient(); | |||
CloseableHttpResponse response = httpClient.execute(httpGet)) { | |||
resultContent = new BasicResponseHandler().handleResponse(response); | |||
} finally { | |||
httpGet.releaseConnection(); | |||
@@ -1,17 +1,24 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import jodd.http.*; | |||
import jodd.http.HttpConnectionProvider; | |||
import jodd.http.HttpRequest; | |||
import jodd.http.HttpResponse; | |||
import jodd.http.JoddHttp; | |||
import jodd.http.ProxyInfo; | |||
import me.chanjar.weixin.common.WxType; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.HttpType; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
/** | |||
* @author someone | |||
*/ | |||
public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionProvider, ProxyInfo> { | |||
protected HttpConnectionProvider httpClient; | |||
protected ProxyInfo httpProxy; | |||
private HttpConnectionProvider httpClient; | |||
private ProxyInfo httpProxy; | |||
@Override | |||
public HttpConnectionProvider getRequestHttpClient() { | |||
@@ -35,11 +42,8 @@ public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionP | |||
} | |||
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); | |||
HttpRequest request = HttpRequest.get(String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN), | |||
this.configStorage.getCorpId(), this.configStorage.getCorpSecret())); | |||
if (this.httpProxy != null) { | |||
httpClient.useProxy(this.httpProxy); | |||
} | |||
@@ -60,7 +64,8 @@ public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionP | |||
@Override | |||
public void initHttp() { | |||
if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { | |||
httpProxy = new ProxyInfo(ProxyInfo.ProxyType.HTTP, configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort(), configStorage.getHttpProxyUsername(), configStorage.getHttpProxyPassword()); | |||
httpProxy = new ProxyInfo(ProxyInfo.ProxyType.HTTP, configStorage.getHttpProxyHost(), | |||
configStorage.getHttpProxyPort(), configStorage.getHttpProxyUsername(), configStorage.getHttpProxyPassword()); | |||
} | |||
httpClient = JoddHttp.httpConnectionProvider; | |||
@@ -1,5 +1,6 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import lombok.extern.slf4j.Slf4j; | |||
import me.chanjar.weixin.common.WxType; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.error.WxError; | |||
@@ -11,10 +12,15 @@ import okhttp3.*; | |||
import java.io.IOException; | |||
public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkHttpProxyInfo> { | |||
protected OkHttpClient httpClient; | |||
protected OkHttpProxyInfo httpProxy; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.GET_TOKEN; | |||
/** | |||
* @author someone | |||
*/ | |||
@Slf4j | |||
public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkHttpProxyInfo> { | |||
private OkHttpClient httpClient; | |||
private OkHttpProxyInfo httpProxy; | |||
@Override | |||
public OkHttpClient getRequestHttpClient() { | |||
@@ -38,35 +44,35 @@ public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkH | |||
} | |||
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); | |||
} | |||
//得到httpClient | |||
OkHttpClient client = getRequestHttpClient(); | |||
//请求的request | |||
Request request = new Request.Builder() | |||
.url(String.format(this.configStorage.getApiUrl(GET_TOKEN), this.configStorage.getCorpId(), this.configStorage.getCorpSecret())) | |||
.get() | |||
.build(); | |||
String resultContent = null; | |||
try { | |||
Response response = client.newCall(request).execute(); | |||
resultContent = response.body().string(); | |||
} catch (IOException e) { | |||
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(); | |||
} | |||
@Override | |||
public void initHttp() { | |||
this.log.debug("WxCpServiceOkHttpImpl initHttp"); | |||
log.debug("WxCpServiceOkHttpImpl initHttp"); | |||
//设置代理 | |||
if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { | |||
httpProxy = OkHttpProxyInfo.httpProxy(configStorage.getHttpProxyHost(), | |||
@@ -0,0 +1,33 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import lombok.RequiredArgsConstructor; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.api.WxCpTpService; | |||
/** | |||
* <pre> | |||
* 默认接口实现类,使用apache httpclient实现,配合第三方应用service使用 | |||
* Created by zhenjun cai. | |||
* </pre> | |||
* | |||
* @author zhenjun cai | |||
*/ | |||
@RequiredArgsConstructor | |||
public class WxCpServiceOnTpImpl extends WxCpServiceApacheHttpClientImpl { | |||
private final WxCpTpService wxCpTpService; | |||
@Override | |||
public String getAccessToken(boolean forceRefresh) throws WxErrorException { | |||
if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) { | |||
return this.configStorage.getAccessToken(); | |||
} | |||
//access token通过第三方应用service获取 | |||
//corpSecret对应企业永久授权码 | |||
WxAccessToken accessToken = wxCpTpService.getCorpToken(this.configStorage.getCorpId(), this.configStorage.getCorpSecret()); | |||
this.configStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||
return this.configStorage.getAccessToken(); | |||
} | |||
} |
@@ -1,13 +1,8 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import java.util.List; | |||
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 lombok.RequiredArgsConstructor; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.api.WxCpTagService; | |||
@@ -15,27 +10,32 @@ import me.chanjar.weixin.cp.bean.WxCpTag; | |||
import me.chanjar.weixin.cp.bean.WxCpTagAddOrRemoveUsersResult; | |||
import me.chanjar.weixin.cp.bean.WxCpTagGetResult; | |||
import me.chanjar.weixin.cp.bean.WxCpUser; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.util.List; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tag.*; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tag.TAG_CREATE; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tag.TAG_UPDATE; | |||
/** | |||
* <pre> | |||
* 标签管理接口 | |||
* 标签管理接口. | |||
* Created by Binary Wang on 2017-6-25. | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
@RequiredArgsConstructor | |||
public class WxCpTagServiceImpl implements WxCpTagService { | |||
private WxCpService mainService; | |||
public WxCpTagServiceImpl(WxCpService mainService) { | |||
this.mainService = mainService; | |||
} | |||
private final WxCpService mainService; | |||
@Override | |||
public String create(String tagName) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/create"; | |||
JsonObject o = new JsonObject(); | |||
o.addProperty("tagname", tagName); | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_CREATE); | |||
String responseContent = this.mainService.post(url, o.toString()); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return tmpJsonElement.getAsJsonObject().get("tagid").getAsString(); | |||
@@ -43,7 +43,7 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||
@Override | |||
public void update(String tagId, String tagName) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/update"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_UPDATE); | |||
JsonObject o = new JsonObject(); | |||
o.addProperty("tagid", tagId); | |||
o.addProperty("tagname", tagName); | |||
@@ -52,13 +52,13 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||
@Override | |||
public void delete(String tagId) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/delete?tagid=" + tagId; | |||
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(TAG_DELETE), tagId); | |||
this.mainService.get(url, null); | |||
} | |||
@Override | |||
public List<WxCpTag> listAll() throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/list"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_LIST); | |||
String responseContent = this.mainService.get(url, null); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return WxCpGsonBuilder.create() | |||
@@ -71,7 +71,7 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||
@Override | |||
public List<WxCpUser> listUsersByTagId(String tagId) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/get?tagid=" + tagId; | |||
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(TAG_GET), tagId); | |||
String responseContent = this.mainService.get(url, null); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return WxCpGsonBuilder.create() | |||
@@ -84,7 +84,7 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||
@Override | |||
public WxCpTagAddOrRemoveUsersResult addUsers2Tag(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/addtagusers"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_ADD_TAG_USERS); | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("tagid", tagId); | |||
this.addUserIdsAndPartyIdsToJson(userIds, partyIds, jsonObject); | |||
@@ -94,7 +94,7 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||
@Override | |||
public WxCpTagAddOrRemoveUsersResult removeUsersFromTag(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/deltagusers"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_DEL_TAG_USERS); | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("tagid", tagId); | |||
this.addUserIdsAndPartyIdsToJson(userIds, partyIds, jsonObject); | |||
@@ -122,15 +122,12 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||
@Override | |||
public WxCpTagGetResult get(String tagId) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/get"; | |||
if (tagId != null) { | |||
url += "?tagId=" + tagId; | |||
} else { | |||
if (tagId == null) { | |||
throw new IllegalArgumentException("缺少tagId参数"); | |||
} | |||
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(TAG_GET), tagId); | |||
String responseContent = this.mainService.get(url, null); | |||
return WxCpTagGetResult.fromJson(responseContent); | |||
} | |||
} |
@@ -5,11 +5,14 @@ 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 me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.TaskCard.*; | |||
/** | |||
* <pre> | |||
* 任务卡片管理接口. | |||
@@ -33,7 +36,7 @@ public class WxCpTaskCardServiceImpl implements WxCpTaskCardService { | |||
data.put("task_id", taskId); | |||
data.put("clicked_key", clickedKey); | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/update_taskcard"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(UPDATE_TASK_CARD); | |||
this.mainService.post(url, WxGsonBuilder.create().toJson(data)); | |||
} | |||
} |
@@ -0,0 +1,114 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import me.chanjar.weixin.common.WxType; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
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.DefaultApacheHttpClientBuilder; | |||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import org.apache.http.Consts; | |||
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.HttpPost; | |||
import org.apache.http.entity.StringEntity; | |||
import org.apache.http.impl.client.BasicResponseHandler; | |||
import org.apache.http.impl.client.CloseableHttpClient; | |||
import java.io.IOException; | |||
/** | |||
* @author someone | |||
*/ | |||
public class WxCpTpServiceApacheHttpClientImpl extends BaseWxCpTpServiceImpl<CloseableHttpClient, HttpHost> { | |||
private CloseableHttpClient httpClient; | |||
private HttpHost httpProxy; | |||
@Override | |||
public CloseableHttpClient getRequestHttpClient() { | |||
return httpClient; | |||
} | |||
@Override | |||
public HttpHost getRequestHttpProxy() { | |||
return httpProxy; | |||
} | |||
@Override | |||
public HttpType getRequestType() { | |||
return HttpType.APACHE_HTTP; | |||
} | |||
@Override | |||
public String getSuiteAccessToken(boolean forceRefresh) throws WxErrorException { | |||
if (!this.configStorage.isSuiteAccessTokenExpired() && !forceRefresh) { | |||
return this.configStorage.getSuiteAccessToken(); | |||
} | |||
synchronized (this.globalSuiteAccessTokenRefreshLock) { | |||
try { | |||
HttpPost httpPost = new HttpPost(configStorage.getApiUrl(WxCpApiPathConsts.Tp.GET_SUITE_TOKEN)); | |||
if (this.httpProxy != null) { | |||
RequestConfig config = RequestConfig.custom() | |||
.setProxy(this.httpProxy).build(); | |||
httpPost.setConfig(config); | |||
} | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("suite_id", this.configStorage.getSuiteId()); | |||
jsonObject.addProperty("suite_secret", this.configStorage.getSuiteSecret()); | |||
jsonObject.addProperty("suite_ticket", this.getSuiteTicket()); | |||
StringEntity entity = new StringEntity(jsonObject.toString(), Consts.UTF_8); | |||
httpPost.setEntity(entity); | |||
String resultContent; | |||
try (CloseableHttpClient httpclient = getRequestHttpClient(); | |||
CloseableHttpResponse response = httpclient.execute(httpPost)) { | |||
resultContent = new BasicResponseHandler().handleResponse(response); | |||
} finally { | |||
httpPost.releaseConnection(); | |||
} | |||
WxError error = WxError.fromJson(resultContent, WxType.CP); | |||
if (error.getErrorCode() != 0) { | |||
throw new WxErrorException(error); | |||
} | |||
jsonObject = new JsonParser().parse(resultContent).getAsJsonObject(); | |||
String suiteAccussToken = jsonObject.get("suite_access_token").getAsString(); | |||
Integer expiresIn = jsonObject.get("expires_in").getAsInt(); | |||
this.configStorage.updateSuiteAccessToken(suiteAccussToken, expiresIn); | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
return this.configStorage.getSuiteAccessToken(); | |||
} | |||
@Override | |||
public void initHttp() { | |||
ApacheHttpClientBuilder apacheHttpClientBuilder = this.configStorage.getApacheHttpClientBuilder(); | |||
if (null == apacheHttpClientBuilder) { | |||
apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); | |||
} | |||
apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost()) | |||
.httpProxyPort(this.configStorage.getHttpProxyPort()) | |||
.httpProxyUsername(this.configStorage.getHttpProxyUsername()) | |||
.httpProxyPassword(this.configStorage.getHttpProxyPassword()); | |||
if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { | |||
this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); | |||
} | |||
this.httpClient = apacheHttpClientBuilder.build(); | |||
} | |||
@Override | |||
public WxCpTpConfigStorage getWxCpTpConfigStorage() { | |||
return this.configStorage; | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
/** | |||
* <pre> | |||
* 默认接口实现类,使用apache httpclient实现 | |||
* Created by zhenjun cai. | |||
* </pre> | |||
* | |||
* @author zhenjun cai | |||
*/ | |||
public class WxCpTpServiceImpl extends WxCpTpServiceApacheHttpClientImpl { | |||
} |
@@ -1,23 +1,23 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import java.util.List; | |||
import java.util.Map; | |||
import com.google.common.collect.Maps; | |||
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 lombok.RequiredArgsConstructor; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.api.WxCpUserService; | |||
import me.chanjar.weixin.cp.bean.WxCpInviteResult; | |||
import me.chanjar.weixin.cp.bean.WxCpUser; | |||
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.util.List; | |||
import java.util.Map; | |||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.User.*; | |||
/** | |||
* <pre> | |||
* Created by BinaryWang on 2017/6/24. | |||
@@ -25,59 +25,54 @@ import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
@RequiredArgsConstructor | |||
public class WxCpUserServiceImpl implements WxCpUserService { | |||
private WxCpService mainService; | |||
public WxCpUserServiceImpl(WxCpService mainService) { | |||
this.mainService = mainService; | |||
} | |||
private final WxCpService mainService; | |||
@Override | |||
public void authenticate(String userId) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?userid=" + userId; | |||
this.mainService.get(url, null); | |||
this.mainService.get(this.mainService.getWxCpConfigStorage().getApiUrl(USER_AUTHENTICATE + userId), null); | |||
} | |||
@Override | |||
public void create(WxCpUser user) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/create"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_CREATE); | |||
this.mainService.post(url, user.toJson()); | |||
} | |||
@Override | |||
public void update(WxCpUser user) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/update"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_UPDATE); | |||
this.mainService.post(url, user.toJson()); | |||
} | |||
@Override | |||
public void delete(String... userIds) throws WxErrorException { | |||
if (userIds.length == 1) { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/delete?userid=" + userIds[0]; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_DELETE + userIds[0]); | |||
this.mainService.get(url, null); | |||
return; | |||
} | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete"; | |||
JsonObject jsonObject = new JsonObject(); | |||
JsonArray jsonArray = new JsonArray(); | |||
for (String userid : userIds) { | |||
jsonArray.add(new JsonPrimitive(userid)); | |||
for (String userId : userIds) { | |||
jsonArray.add(new JsonPrimitive(userId)); | |||
} | |||
jsonObject.add("useridlist", jsonArray); | |||
this.mainService.post(url, jsonObject.toString()); | |||
this.mainService.post(USER_BATCH_DELETE, jsonObject.toString()); | |||
} | |||
@Override | |||
public WxCpUser getById(String userid) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?userid=" + userid; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_GET + userid); | |||
String responseContent = this.mainService.get(url, null); | |||
return WxCpUser.fromJson(responseContent); | |||
} | |||
@Override | |||
public List<WxCpUser> listByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list?department_id=" + departId; | |||
String params = ""; | |||
if (fetchChild != null) { | |||
params += "&fetch_child=" + (fetchChild ? "1" : "0"); | |||
@@ -88,6 +83,7 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||
params += "&status=0"; | |||
} | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_LIST + departId); | |||
String responseContent = this.mainService.get(url, params); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return WxCpGsonBuilder.create() | |||
@@ -99,7 +95,6 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||
@Override | |||
public List<WxCpUser> listSimpleByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?department_id=" + departId; | |||
String params = ""; | |||
if (fetchChild != null) { | |||
params += "&fetch_child=" + (fetchChild ? "1" : "0"); | |||
@@ -110,6 +105,7 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||
params += "&status=0"; | |||
} | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_SIMPLE_LIST + departId); | |||
String responseContent = this.mainService.get(url, params); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return WxCpGsonBuilder.create() | |||
@@ -122,7 +118,6 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||
@Override | |||
public WxCpInviteResult invite(List<String> userIds, List<String> partyIds, List<String> tagIds) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/invite"; | |||
JsonObject jsonObject = new JsonObject(); | |||
if (userIds != null) { | |||
JsonArray jsonArray = new JsonArray(); | |||
@@ -148,12 +143,13 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||
jsonObject.add("tag", jsonArray); | |||
} | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(BATCH_INVITE); | |||
return WxCpInviteResult.fromJson(this.mainService.post(url, jsonObject.toString())); | |||
} | |||
@Override | |||
public Map<String, String> userId2Openid(String userId, Integer agentId) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_openid"; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_CONVERT_TO_OPENID); | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("userid", userId); | |||
if (agentId != null) { | |||
@@ -176,9 +172,19 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||
@Override | |||
public String openid2UserId(String openid) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_userid"; | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("openid", openid); | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_CONVERT_TO_USERID); | |||
String responseContent = this.mainService.post(url, jsonObject.toString()); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return tmpJsonElement.getAsJsonObject().get("userid").getAsString(); | |||
} | |||
@Override | |||
public String getUserId(String mobile) throws WxErrorException { | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("mobile", mobile); | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_USER_ID); | |||
String responseContent = this.mainService.post(url, jsonObject.toString()); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return tmpJsonElement.getAsJsonObject().get("userid").getAsString(); | |||
@@ -186,7 +192,7 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||
@Override | |||
public WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/crm/get_external_contact?external_userid=" + userId; | |||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_EXTERNAL_CONTACT + userId); | |||
String responseContent = this.mainService.get(url, null); | |||
return WxCpUserExternalContactInfo.fromJson(responseContent); | |||
} | |||
@@ -6,7 +6,7 @@ import lombok.AllArgsConstructor; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
import me.chanjar.weixin.cp.WxCpConsts.AppChatMsgType; | |||
import me.chanjar.weixin.cp.constant.WxCpConsts.AppChatMsgType; | |||
import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | |||
import me.chanjar.weixin.cp.bean.article.NewArticle; | |||
@@ -8,11 +8,15 @@ import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | |||
import me.chanjar.weixin.cp.bean.article.NewArticle; | |||
import me.chanjar.weixin.cp.bean.messagebuilder.*; | |||
import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; | |||
import me.chanjar.weixin.cp.constant.WxCpConsts; | |||
import org.apache.commons.lang3.StringUtils; | |||
import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import static me.chanjar.weixin.common.api.WxConsts.KefuMsgType.*; | |||
/** | |||
* 消息. | |||
@@ -40,9 +44,13 @@ public class WxCpMessage implements Serializable { | |||
private String btnTxt; | |||
private List<NewArticle> articles = new ArrayList<>(); | |||
private List<MpnewsArticle> mpnewsArticles = new ArrayList<>(); | |||
private String appId; | |||
private String page; | |||
private Boolean emphasisFirstItem; | |||
private Map<String, String> contentItems; | |||
/** | |||
* 任务卡片特有的属性 | |||
* 任务卡片特有的属性. | |||
*/ | |||
private String taskId; | |||
private List<TaskCardButton> taskButtons = new ArrayList<>(); | |||
@@ -117,10 +125,16 @@ public class WxCpMessage implements Serializable { | |||
return new TaskCardBuilder(); | |||
} | |||
/** | |||
* 获得小程序通知消息builder. | |||
*/ | |||
public static MiniProgramNoticeMsgBuilder newMiniProgramNoticeBuilder() { | |||
return new MiniProgramNoticeMsgBuilder(); | |||
} | |||
/** | |||
* <pre> | |||
* 请使用 | |||
* 请使用. | |||
* {@link KefuMsgType#TEXT} | |||
* {@link KefuMsgType#IMAGE} | |||
* {@link KefuMsgType#VOICE} | |||
@@ -130,6 +144,7 @@ public class WxCpMessage implements Serializable { | |||
* {@link KefuMsgType#MPNEWS} | |||
* {@link KefuMsgType#MARKDOWN} | |||
* {@link KefuMsgType#TASKCARD} | |||
* {@link KefuMsgType#MINIPROGRAM_NOTICE} | |||
* </pre> | |||
* | |||
* @param msgType 消息类型 | |||
@@ -169,19 +184,19 @@ public class WxCpMessage implements Serializable { | |||
private void handleMsgType(JsonObject messageJson) { | |||
switch (this.getMsgType()) { | |||
case KefuMsgType.TEXT: { | |||
case TEXT: { | |||
JsonObject text = new JsonObject(); | |||
text.addProperty("content", this.getContent()); | |||
messageJson.add("text", text); | |||
break; | |||
} | |||
case KefuMsgType.MARKDOWN: { | |||
case MARKDOWN: { | |||
JsonObject text = new JsonObject(); | |||
text.addProperty("content", this.getContent()); | |||
messageJson.add("markdown", text); | |||
break; | |||
} | |||
case KefuMsgType.TEXTCARD: { | |||
case TEXTCARD: { | |||
JsonObject text = new JsonObject(); | |||
text.addProperty("title", this.getTitle()); | |||
text.addProperty("description", this.getDescription()); | |||
@@ -190,25 +205,25 @@ public class WxCpMessage implements Serializable { | |||
messageJson.add("textcard", text); | |||
break; | |||
} | |||
case KefuMsgType.IMAGE: { | |||
case IMAGE: { | |||
JsonObject image = new JsonObject(); | |||
image.addProperty("media_id", this.getMediaId()); | |||
messageJson.add("image", image); | |||
break; | |||
} | |||
case KefuMsgType.FILE: { | |||
case FILE: { | |||
JsonObject image = new JsonObject(); | |||
image.addProperty("media_id", this.getMediaId()); | |||
messageJson.add("file", image); | |||
break; | |||
} | |||
case KefuMsgType.VOICE: { | |||
case VOICE: { | |||
JsonObject voice = new JsonObject(); | |||
voice.addProperty("media_id", this.getMediaId()); | |||
messageJson.add("voice", voice); | |||
break; | |||
} | |||
case KefuMsgType.VIDEO: { | |||
case VIDEO: { | |||
JsonObject video = new JsonObject(); | |||
video.addProperty("media_id", this.getMediaId()); | |||
video.addProperty("thumb_media_id", this.getThumbMediaId()); | |||
@@ -217,7 +232,7 @@ public class WxCpMessage implements Serializable { | |||
messageJson.add("video", video); | |||
break; | |||
} | |||
case KefuMsgType.NEWS: { | |||
case NEWS: { | |||
JsonObject newsJsonObject = new JsonObject(); | |||
JsonArray articleJsonArray = new JsonArray(); | |||
for (NewArticle article : this.getArticles()) { | |||
@@ -232,7 +247,7 @@ public class WxCpMessage implements Serializable { | |||
messageJson.add("news", newsJsonObject); | |||
break; | |||
} | |||
case KefuMsgType.MPNEWS: { | |||
case MPNEWS: { | |||
JsonObject newsJsonObject = new JsonObject(); | |||
if (this.getMediaId() != null) { | |||
newsJsonObject.addProperty("media_id", this.getMediaId()); | |||
@@ -255,7 +270,7 @@ public class WxCpMessage implements Serializable { | |||
messageJson.add("mpnews", newsJsonObject); | |||
break; | |||
} | |||
case KefuMsgType.TASKCARD: { | |||
case TASKCARD: { | |||
JsonObject text = new JsonObject(); | |||
text.addProperty("title", this.getTitle()); | |||
text.addProperty("description", this.getDescription()); | |||
@@ -291,6 +306,25 @@ public class WxCpMessage implements Serializable { | |||
messageJson.add("taskcard", text); | |||
break; | |||
} | |||
case MINIPROGRAM_NOTICE: { | |||
JsonObject notice = new JsonObject(); | |||
notice.addProperty("appid", this.getAppId()); | |||
notice.addProperty("page", this.getPage()); | |||
notice.addProperty("description", this.getDescription()); | |||
notice.addProperty("title", this.getTitle()); | |||
notice.addProperty("emphasis_first_item", this.getEmphasisFirstItem()); | |||
JsonArray content = new JsonArray(); | |||
for (Map.Entry<String, String> item : this.getContentItems().entrySet()) { | |||
JsonObject articleJson = new JsonObject(); | |||
articleJson.addProperty("key", item.getKey()); | |||
articleJson.addProperty("value", item.getValue()); | |||
content.add(articleJson); | |||
} | |||
notice.add("content_item", content); | |||
messageJson.add("miniprogram_notice", notice); | |||
break; | |||
} | |||
default: { | |||
// do nothing | |||
} | |||
@@ -0,0 +1,28 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
import lombok.experimental.Accessors; | |||
/** | |||
* <pre> | |||
* 用oauth2获取用户信息的结果类 | |||
* Created by BinaryWang on 2019/5/26. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
@Data | |||
@Accessors(chain = true) | |||
@NoArgsConstructor | |||
@AllArgsConstructor | |||
@Builder | |||
public class WxCpOauth2UserInfo { | |||
private String openId; | |||
private String deviceId; | |||
private String userId; | |||
private String userTicket; | |||
private String expiresIn; | |||
} |
@@ -29,19 +29,19 @@ public class WxCpTagGetResult implements Serializable { | |||
private String errmsg; | |||
/** | |||
* 用户列表 | |||
* 用户列表. | |||
*/ | |||
@SerializedName("userlist") | |||
private List<WxCpUser> userlist; | |||
/** | |||
* 部门列表 | |||
* 部门列表. | |||
*/ | |||
@SerializedName("partylist") | |||
private List<Integer> partylist; | |||
/** | |||
* 标签名称 | |||
* 标签名称. | |||
*/ | |||
@SerializedName("tagname") | |||
private String tagname; | |||
@@ -0,0 +1,42 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import java.io.Serializable; | |||
import com.google.gson.annotations.SerializedName; | |||
import lombok.Data; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
/** | |||
* 微信部门. | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
@Data | |||
public class WxCpTpCorp implements Serializable { | |||
private static final long serialVersionUID = -5028321625140879571L; | |||
@SerializedName("corpid") | |||
private String corpId; | |||
@SerializedName("corp_name") | |||
private String corpName; | |||
@SerializedName("corp_full_name") | |||
private String corpFullName; | |||
@SerializedName("corp_type") | |||
private String corpType; | |||
@SerializedName("corp_square_logo_url") | |||
private String corpSquareLogoUrl; | |||
@SerializedName("corp_user_max") | |||
private String corpUserMax; | |||
@SerializedName("permanent_code") | |||
private String permanentCode; | |||
public static WxCpTpCorp fromJson(String json) { | |||
return WxCpGsonBuilder.create().fromJson(json, WxCpTpCorp.class); | |||
} | |||
public String toJson() { | |||
return WxCpGsonBuilder.create().toJson(this); | |||
} | |||
} |
@@ -0,0 +1,68 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import java.io.Serializable; | |||
import java.util.Map; | |||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
import com.thoughtworks.xstream.annotations.XStreamConverter; | |||
import lombok.Data; | |||
import lombok.extern.slf4j.Slf4j; | |||
import me.chanjar.weixin.common.util.XmlUtils; | |||
import me.chanjar.weixin.common.util.crypto.WxCryptUtil; | |||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.ImageBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.NewsBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.TextBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.VideoBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.VoiceBuilder; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | |||
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | |||
import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | |||
/** | |||
* 回调推送的message | |||
* https://work.weixin.qq.com/api/doc#90001/90143/90612 | |||
* | |||
* @author zhenjun cai | |||
*/ | |||
@XStreamAlias("xml") | |||
@Slf4j | |||
@Data | |||
public class WxCpTpXmlMessage implements Serializable { | |||
private static final long serialVersionUID = 6031833682211475786L; | |||
/** | |||
* 使用dom4j解析的存放所有xml属性和值的map. | |||
*/ | |||
private Map<String, Object> allFieldsMap; | |||
@XStreamAlias("SuiteId") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String suiteId; | |||
@XStreamAlias("InfoType") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String infoType; | |||
@XStreamAlias("TimeStamp") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String timeStamp; | |||
@XStreamAlias("SuiteTicket") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String suiteTicket; | |||
@XStreamAlias("AuthCode") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String authCode; | |||
public static WxCpTpXmlMessage fromXml(String xml) { | |||
//修改微信变态的消息内容格式,方便解析 | |||
//xml = xml.replace("</PicList><PicList>", ""); | |||
final WxCpTpXmlMessage xmlPackage = XStreamTransformer.fromXml(WxCpTpXmlMessage.class, xml); | |||
xmlPackage.setAllFieldsMap(XmlUtils.xml2Map(xml)); | |||
return xmlPackage; | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import java.io.Serializable; | |||
import java.util.Map; | |||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
import com.thoughtworks.xstream.annotations.XStreamConverter; | |||
import lombok.Data; | |||
import me.chanjar.weixin.common.util.XmlUtils; | |||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.ImageBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.NewsBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.TextBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.VideoBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.VoiceBuilder; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | |||
import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | |||
/** | |||
* 回调消息包. | |||
* https://work.weixin.qq.com/api/doc#90001/90143/91116 | |||
* | |||
* @author zhenjun cai | |||
*/ | |||
@XStreamAlias("xml") | |||
@Data | |||
public class WxCpTpXmlPackage implements Serializable { | |||
private static final long serialVersionUID = 6031833682211475786L; | |||
/** | |||
* 使用dom4j解析的存放所有xml属性和值的map. | |||
*/ | |||
private Map<String, Object> allFieldsMap; | |||
@XStreamAlias("ToUserName") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String toUserName; | |||
@XStreamAlias("AgentID") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String agentId; | |||
@XStreamAlias("Encrypt") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String msgEncrypt; | |||
public static WxCpTpXmlPackage fromXml(String xml) { | |||
//修改微信变态的消息内容格式,方便解析 | |||
//xml = xml.replace("</PicList><PicList>", ""); | |||
final WxCpTpXmlPackage xmlPackage = XStreamTransformer.fromXml(WxCpTpXmlPackage.class, xml); | |||
xmlPackage.setAllFieldsMap(XmlUtils.xml2Map(xml)); | |||
return xmlPackage; | |||
} | |||
} |
@@ -31,6 +31,11 @@ public class WxCpUser implements Serializable { | |||
private Integer status; | |||
private Integer enable; | |||
private Integer isLeader; | |||
/** | |||
* is_leader_in_dept. | |||
* 个数必须和department一致,表示在所在的部门内是否为上级。1表示为上级,0表示非上级。在审批等应用里可以用来标识上级审批人 | |||
*/ | |||
private Integer[] isLeaderInDept; | |||
private final List<Attr> extAttrs = new ArrayList<>(); | |||
private Integer hideMobile; | |||
private String englishName; | |||
@@ -1,16 +1,11 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import java.util.List; | |||
import com.google.gson.annotations.SerializedName; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
import lombok.Getter; | |||
import lombok.NoArgsConstructor; | |||
import lombok.Setter; | |||
import lombok.*; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.util.List; | |||
/** | |||
* <pre> | |||
* 外部联系人详情 | |||
@@ -118,9 +113,26 @@ public class WxCpUserExternalContactInfo { | |||
private String description; | |||
@SerializedName("createtime") | |||
private Long createTime; | |||
private String state; | |||
@SerializedName("remark_company") | |||
private String remarkCompany; | |||
@SerializedName("remark_mobiles") | |||
private String[] remarkMobiles; | |||
private Tag[] tags; | |||
} | |||
public static WxCpUserExternalContactInfo fromJson(String json) { | |||
return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalContactInfo.class); | |||
} | |||
@Setter | |||
@Getter | |||
public static class Tag { | |||
@SerializedName("group_name") | |||
private String groupName; | |||
@SerializedName("tag_name") | |||
private String tagName; | |||
private int type; | |||
} | |||
} |
@@ -0,0 +1,58 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import com.google.gson.annotations.Expose; | |||
import com.google.gson.annotations.SerializedName; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.util.List; | |||
/** | |||
* <pre> | |||
* 外部联系人列表 | |||
* Created by Joe Cao on 2019/6/16. | |||
* 参考文档:https://work.weixin.qq.com/api/doc#90001/90143/91570 | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/JoeCao">Joe Cao</a> | |||
*/ | |||
public class WxCpUserExternalContactList { | |||
@SerializedName("errcode") | |||
@Expose | |||
private Long errcode; | |||
@SerializedName("errmsg") | |||
@Expose | |||
private String errmsg; | |||
@SerializedName("external_userid") | |||
@Expose | |||
private List<String> externalUserId = null; | |||
public Long getErrcode() { | |||
return errcode; | |||
} | |||
public void setErrcode(Long errcode) { | |||
this.errcode = errcode; | |||
} | |||
public String getErrmsg() { | |||
return errmsg; | |||
} | |||
public void setErrmsg(String errmsg) { | |||
this.errmsg = errmsg; | |||
} | |||
public List<String> getExternalUserId() { | |||
return externalUserId; | |||
} | |||
public void setExternalUserId(List<String> externalUserId) { | |||
this.externalUserId = externalUserId; | |||
} | |||
public static WxCpUserExternalContactList fromJson(String json) { | |||
return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalContactList.class); | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import com.google.gson.annotations.Expose; | |||
import com.google.gson.annotations.SerializedName; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.util.List; | |||
public class WxCpUserWithExternalPermission { | |||
@SerializedName("errcode") | |||
@Expose | |||
private Long errcode; | |||
@SerializedName("errmsg") | |||
@Expose | |||
private String errmsg; | |||
@SerializedName("follow_user") | |||
@Expose | |||
private List<String> followUser = null; | |||
public Long getErrcode() { | |||
return errcode; | |||
} | |||
public void setErrcode(Long errcode) { | |||
this.errcode = errcode; | |||
} | |||
public String getErrmsg() { | |||
return errmsg; | |||
} | |||
public void setErrmsg(String errmsg) { | |||
this.errmsg = errmsg; | |||
} | |||
public List<String> getFollowUser() { | |||
return followUser; | |||
} | |||
public void setFollowUser(List<String> followUser) { | |||
this.followUser = followUser; | |||
} | |||
public static WxCpUserWithExternalPermission fromJson(String json) { | |||
return WxCpGsonBuilder.create().fromJson(json, WxCpUserWithExternalPermission.class); | |||
} | |||
} |
@@ -162,7 +162,7 @@ public class WxCpXmlMessage implements Serializable { | |||
/** | |||
* 通讯录变更事件. | |||
* 请参考常量 me.chanjar.weixin.cp.WxCpConsts.ContactChangeType | |||
* 请参考常量 me.chanjar.weixin.cp.constant.WxCpConsts.ContactChangeType | |||
*/ | |||
@XStreamAlias("ChangeType") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
@@ -175,6 +175,26 @@ public class WxCpXmlMessage implements Serializable { | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
private String userId; | |||
/** | |||
* 变更信息的外部联系人的userid,注意不是企业成员的帐号. | |||
*/ | |||
@XStreamAlias("ExternalUserID") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
private String externalUserId; | |||
/** | |||
* 添加此用户的「联系我」方式配置的state参数,可用于识别添加此用户的渠道. | |||
*/ | |||
@XStreamAlias("State") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
private String state; | |||
/** | |||
* 欢迎语code,可用于发送欢迎语. | |||
*/ | |||
@XStreamAlias("WelcomeCode") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
private String welcomeCode; | |||
/** | |||
* 新的UserID,变更时推送(userid由系统生成时可更改一次). | |||
*/ | |||
@@ -191,11 +211,11 @@ public class WxCpXmlMessage implements Serializable { | |||
private String name; | |||
/** | |||
* 成员部门列表. | |||
* 成员部门列表,变更时推送,仅返回该应用有查看权限的部门id. | |||
*/ | |||
@XStreamAlias("Department") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
private String department; | |||
private Long[] departments; | |||
/** | |||
* 手机号码. | |||
@@ -244,6 +264,12 @@ public class WxCpXmlMessage implements Serializable { | |||
@XStreamAlias("IsLeader") | |||
private Integer isLeader; | |||
/** | |||
* 表示所在部门是否为上级,0-否,1-是,顺序与Department字段的部门逐一对应. | |||
*/ | |||
@XStreamAlias("IsLeaderInDept") | |||
private Integer[] isLeaderInDept; | |||
/** | |||
* 座机. | |||
*/ | |||
@@ -268,7 +294,7 @@ public class WxCpXmlMessage implements Serializable { | |||
* 部门Id. | |||
*/ | |||
@XStreamAlias("Id") | |||
private Integer id; | |||
private Long id; | |||
/** | |||
* 父部门id. | |||
@@ -0,0 +1,69 @@ | |||
package me.chanjar.weixin.cp.bean.messagebuilder; | |||
import me.chanjar.weixin.common.api.WxConsts; | |||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||
import java.util.Map; | |||
/** | |||
* <pre> | |||
* miniprogram_notice 类型的消息 builder | |||
* Created by Binary Wang on 2019/6/16. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public class MiniProgramNoticeMsgBuilder extends BaseBuilder<MiniProgramNoticeMsgBuilder> { | |||
private String title; | |||
private String description; | |||
private String appId; | |||
private String page; | |||
private Boolean emphasisFirstItem; | |||
private Map<String, String> contentItems; | |||
public MiniProgramNoticeMsgBuilder() { | |||
this.msgType = WxConsts.KefuMsgType.MINIPROGRAM_NOTICE; | |||
} | |||
public MiniProgramNoticeMsgBuilder appId(String appId) { | |||
this.appId = appId; | |||
return this; | |||
} | |||
public MiniProgramNoticeMsgBuilder page(String page) { | |||
this.page = page; | |||
return this; | |||
} | |||
public MiniProgramNoticeMsgBuilder title(String title) { | |||
this.title = title; | |||
return this; | |||
} | |||
public MiniProgramNoticeMsgBuilder description(String description) { | |||
this.description = description; | |||
return this; | |||
} | |||
public MiniProgramNoticeMsgBuilder contentItems(Map<String, String> contentItems) { | |||
this.contentItems = contentItems; | |||
return this; | |||
} | |||
public MiniProgramNoticeMsgBuilder emphasisFirstItem(Boolean emphasisFirstItem) { | |||
this.emphasisFirstItem = emphasisFirstItem; | |||
return this; | |||
} | |||
@Override | |||
public WxCpMessage build() { | |||
WxCpMessage m = super.build(); | |||
m.setContentItems(this.contentItems); | |||
m.setAppId(this.appId); | |||
m.setDescription(this.description); | |||
m.setTitle(this.title); | |||
m.setEmphasisFirstItem(this.emphasisFirstItem); | |||
m.setPage(this.page); | |||
return m; | |||
} | |||
} |
@@ -6,18 +6,32 @@ import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||
import java.io.File; | |||
/** | |||
* 微信客户端配置存储 | |||
* 微信客户端配置存储. | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
public interface WxCpConfigStorage { | |||
/** | |||
* 设置企业微信服务器 baseUrl. | |||
* 默认值是 https://qyapi.weixin.qq.com , 如果使用默认值,则不需要调用 setBaseApiUrl | |||
* | |||
* @param baseUrl 企业微信服务器 Url | |||
*/ | |||
void setBaseApiUrl(String baseUrl); | |||
/** | |||
* 读取企业微信 API Url. | |||
* 支持私有化企业微信服务器. | |||
*/ | |||
String getApiUrl(String path); | |||
String getAccessToken(); | |||
boolean isAccessTokenExpired(); | |||
/** | |||
* 强制将access token过期掉 | |||
* 强制将access token过期掉. | |||
*/ | |||
void expireAccessToken(); | |||
@@ -30,12 +44,12 @@ public interface WxCpConfigStorage { | |||
boolean isJsapiTicketExpired(); | |||
/** | |||
* 强制将jsapi ticket过期掉 | |||
* 强制将jsapi ticket过期掉. | |||
*/ | |||
void expireJsapiTicket(); | |||
/** | |||
* 应该是线程安全的 | |||
* 应该是线程安全的. | |||
*/ | |||
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | |||
@@ -44,12 +58,12 @@ public interface WxCpConfigStorage { | |||
boolean isAgentJsapiTicketExpired(); | |||
/** | |||
* 强制将jsapi ticket过期掉 | |||
* 强制将jsapi ticket过期掉. | |||
*/ | |||
void expireAgentJsapiTicket(); | |||
/** | |||
* 应该是线程安全的 | |||
* 应该是线程安全的. | |||
*/ | |||
void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds); | |||
@@ -78,7 +92,7 @@ public interface WxCpConfigStorage { | |||
File getTmpDirFile(); | |||
/** | |||
* http client builder | |||
* http client builder. | |||
* | |||
* @return ApacheHttpClientBuilder | |||
*/ | |||
@@ -0,0 +1,86 @@ | |||
package me.chanjar.weixin.cp.config; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||
import java.io.File; | |||
/** | |||
* 微信客户端(第三方应用)配置存储 | |||
* | |||
* @author zhenjun cai | |||
*/ | |||
public interface WxCpTpConfigStorage { | |||
/** | |||
* 设置企业微信服务器 baseUrl. | |||
* 默认值是 https://qyapi.weixin.qq.com , 如果使用默认值,则不需要调用 setBaseApiUrl | |||
* | |||
* @param baseUrl 企业微信服务器 Url | |||
*/ | |||
void setBaseApiUrl(String baseUrl); | |||
/** | |||
* 读取企业微信 API Url. | |||
* 支持私有化企业微信服务器. | |||
*/ | |||
String getApiUrl(String path); | |||
String getSuiteAccessToken(); | |||
boolean isSuiteAccessTokenExpired(); | |||
/** | |||
* 强制将suite access token过期掉. | |||
*/ | |||
void expireSuiteAccessToken(); | |||
void updateSuiteAccessToken(WxAccessToken suiteAccessToken); | |||
void updateSuiteAccessToken(String suiteAccessToken, int expiresIn); | |||
String getSuiteTicket(); | |||
boolean isSuiteTicketExpired(); | |||
/** | |||
* 强制将suite ticket过期掉. | |||
*/ | |||
void expireSuiteTicket(); | |||
/** | |||
* 应该是线程安全的. | |||
*/ | |||
void updateSuiteTicket(String suiteTicket, int expiresInSeconds); | |||
String getCorpId(); | |||
String getCorpSecret(); | |||
String getSuiteId(); | |||
String getSuiteSecret(); | |||
String getToken(); | |||
String getAesKey(); | |||
long getExpiresTime(); | |||
String getHttpProxyHost(); | |||
int getHttpProxyPort(); | |||
String getHttpProxyUsername(); | |||
String getHttpProxyPassword(); | |||
File getTmpDirFile(); | |||
/** | |||
* http client builder. | |||
* | |||
* @return ApacheHttpClientBuilder | |||
*/ | |||
ApacheHttpClientBuilder getApacheHttpClientBuilder(); | |||
} |
@@ -1,44 +1,63 @@ | |||
package me.chanjar.weixin.cp.config; | |||
package me.chanjar.weixin.cp.config.impl; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.io.File; | |||
import java.io.Serializable; | |||
/** | |||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | |||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化. | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||
public class WxCpDefaultConfigImpl implements WxCpConfigStorage, Serializable { | |||
private static final long serialVersionUID = 1154541446729462780L; | |||
protected volatile String corpId; | |||
protected volatile String corpSecret; | |||
private volatile String corpId; | |||
private volatile String corpSecret; | |||
protected volatile String token; | |||
private volatile String token; | |||
protected volatile String accessToken; | |||
protected volatile String aesKey; | |||
private volatile String aesKey; | |||
protected volatile Integer agentId; | |||
protected volatile long expiresTime; | |||
private volatile long expiresTime; | |||
protected volatile String oauth2redirectUri; | |||
private volatile String oauth2redirectUri; | |||
protected volatile String httpProxyHost; | |||
protected volatile int httpProxyPort; | |||
protected volatile String httpProxyUsername; | |||
protected volatile String httpProxyPassword; | |||
private volatile String httpProxyHost; | |||
private volatile int httpProxyPort; | |||
private volatile String httpProxyUsername; | |||
private volatile String httpProxyPassword; | |||
protected volatile String jsapiTicket; | |||
protected volatile long jsapiTicketExpiresTime; | |||
private volatile String jsapiTicket; | |||
private volatile long jsapiTicketExpiresTime; | |||
protected volatile String agentJsapiTicket; | |||
protected volatile long agentJsapiTicketExpiresTime; | |||
private volatile String agentJsapiTicket; | |||
private volatile long agentJsapiTicketExpiresTime; | |||
protected volatile File tmpDirFile; | |||
private volatile File tmpDirFile; | |||
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | |||
private volatile String baseApiUrl; | |||
@Override | |||
public void setBaseApiUrl(String baseUrl) { | |||
this.baseApiUrl = baseUrl; | |||
} | |||
@Override | |||
public String getApiUrl(String path) { | |||
if (baseApiUrl == null) { | |||
baseApiUrl = WxCpApiPathConsts.DEFAULT_CP_BASE_URL; | |||
} | |||
return baseApiUrl + path; | |||
} | |||
@Override | |||
public String getAccessToken() { | |||
return this.accessToken; |
@@ -1,7 +1,9 @@ | |||
package me.chanjar.weixin.cp.config; | |||
package me.chanjar.weixin.cp.config.impl; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import redis.clients.jedis.Jedis; | |||
import redis.clients.jedis.JedisPool; | |||
import redis.clients.jedis.JedisPoolConfig; | |||
@@ -16,7 +18,7 @@ import java.io.File; | |||
* | |||
* @author gaigeshen | |||
*/ | |||
public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||
public class WxCpRedisConfigImpl implements WxCpConfigStorage { | |||
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 JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET"; | |||
@@ -38,23 +40,38 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||
private volatile File tmpDirFile; | |||
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | |||
public WxCpJedisConfigStorage(JedisPool jedisPool) { | |||
protected volatile String baseApiUrl; | |||
@Override | |||
public void setBaseApiUrl(String baseUrl) { | |||
this.baseApiUrl = baseUrl; | |||
} | |||
@Override | |||
public String getApiUrl(String path) { | |||
if (baseApiUrl == null) { | |||
baseApiUrl = WxCpApiPathConsts.DEFAULT_CP_BASE_URL; | |||
} | |||
return baseApiUrl + path; | |||
} | |||
public WxCpRedisConfigImpl(JedisPool jedisPool) { | |||
this.jedisPool = jedisPool; | |||
} | |||
public WxCpJedisConfigStorage(String host, int port) { | |||
public WxCpRedisConfigImpl(String host, int port) { | |||
jedisPool = new JedisPool(host, port); | |||
} | |||
public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port) { | |||
public WxCpRedisConfigImpl(JedisPoolConfig poolConfig, String host, int port) { | |||
jedisPool = new JedisPool(poolConfig, host, port); | |||
} | |||
public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port, int timeout, String password) { | |||
public WxCpRedisConfigImpl(JedisPoolConfig poolConfig, String host, int port, int timeout, String password) { | |||
jedisPool = new JedisPool(poolConfig, host, port, timeout, password); | |||
} | |||
public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port, int timeout, String password, int database) { | |||
public WxCpRedisConfigImpl(JedisPoolConfig poolConfig, String host, int port, int timeout, String password, int database) { | |||
jedisPool = new JedisPool(poolConfig, host, port, timeout, password, database); | |||
} | |||
@@ -0,0 +1,248 @@ | |||
package me.chanjar.weixin.cp.config.impl; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.io.File; | |||
import java.io.Serializable; | |||
/** | |||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化. | |||
* | |||
* @author someone | |||
*/ | |||
public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializable { | |||
private static final long serialVersionUID = 6678780920621872824L; | |||
private volatile String corpId; | |||
private volatile String corpSecret; | |||
private volatile String suiteId; | |||
private volatile String suiteSecret; | |||
private volatile String token; | |||
private volatile String suiteAccessToken; | |||
private volatile String aesKey; | |||
private volatile long expiresTime; | |||
private volatile String oauth2redirectUri; | |||
private volatile String httpProxyHost; | |||
private volatile int httpProxyPort; | |||
private volatile String httpProxyUsername; | |||
private volatile String httpProxyPassword; | |||
private volatile String suiteTicket; | |||
private volatile long suiteTicketExpiresTime; | |||
private volatile File tmpDirFile; | |||
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | |||
private volatile String baseApiUrl; | |||
@Override | |||
public void setBaseApiUrl(String baseUrl) { | |||
this.baseApiUrl = baseUrl; | |||
} | |||
@Override | |||
public String getApiUrl(String path) { | |||
if (baseApiUrl == null) { | |||
baseApiUrl = "https://qyapi.weixin.qq.com"; | |||
} | |||
return baseApiUrl + path; | |||
} | |||
@Override | |||
public String getSuiteAccessToken() { | |||
return this.suiteAccessToken; | |||
} | |||
public void setSuiteAccessToken(String suiteAccessToken) { | |||
this.suiteAccessToken = suiteAccessToken; | |||
} | |||
@Override | |||
public boolean isSuiteAccessTokenExpired() { | |||
return System.currentTimeMillis() > this.expiresTime; | |||
} | |||
@Override | |||
public void expireSuiteAccessToken() { | |||
this.expiresTime = 0; | |||
} | |||
@Override | |||
public synchronized void updateSuiteAccessToken(WxAccessToken suiteAccessToken) { | |||
updateSuiteAccessToken(suiteAccessToken.getAccessToken(), suiteAccessToken.getExpiresIn()); | |||
} | |||
@Override | |||
public synchronized void updateSuiteAccessToken(String suiteAccessToken, int expiresInSeconds) { | |||
this.suiteAccessToken = suiteAccessToken; | |||
this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||
} | |||
@Override | |||
public String getCorpId() { | |||
return this.corpId; | |||
} | |||
public void setCorpId(String corpId) { | |||
this.corpId = corpId; | |||
} | |||
@Override | |||
public String getCorpSecret() { | |||
return this.corpSecret; | |||
} | |||
public void setCorpSecret(String corpSecret) { | |||
this.corpSecret = corpSecret; | |||
} | |||
@Override | |||
public String getSuiteTicket() { | |||
return this.suiteTicket; | |||
} | |||
public void setSuiteTicket(String suiteTicket) { | |||
this.suiteTicket = suiteTicket; | |||
} | |||
public long getSuiteTicketExpiresTime() { | |||
return this.suiteTicketExpiresTime; | |||
} | |||
public void setSuiteTicketExpiresTime(long suiteTicketExpiresTime) { | |||
this.suiteTicketExpiresTime = suiteTicketExpiresTime; | |||
} | |||
@Override | |||
public boolean isSuiteTicketExpired() { | |||
return System.currentTimeMillis() > this.suiteTicketExpiresTime; | |||
} | |||
@Override | |||
public synchronized void updateSuiteTicket(String suiteTicket, int expiresInSeconds) { | |||
this.suiteTicket = suiteTicket; | |||
// 预留200秒的时间 | |||
this.suiteTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||
} | |||
@Override | |||
public void expireSuiteTicket() { | |||
this.suiteTicketExpiresTime = 0; | |||
} | |||
@Override | |||
public String getSuiteId() { | |||
return this.suiteId; | |||
} | |||
public void setSuiteId(String corpId) { | |||
this.suiteId = corpId; | |||
} | |||
@Override | |||
public String getSuiteSecret() { | |||
return this.suiteSecret; | |||
} | |||
public void setSuiteSecret(String corpSecret) { | |||
this.suiteSecret = corpSecret; | |||
} | |||
@Override | |||
public String getToken() { | |||
return this.token; | |||
} | |||
public void setToken(String token) { | |||
this.token = token; | |||
} | |||
@Override | |||
public long getExpiresTime() { | |||
return this.expiresTime; | |||
} | |||
public void setExpiresTime(long expiresTime) { | |||
this.expiresTime = expiresTime; | |||
} | |||
@Override | |||
public String getAesKey() { | |||
return this.aesKey; | |||
} | |||
public void setAesKey(String aesKey) { | |||
this.aesKey = aesKey; | |||
} | |||
public void setOauth2redirectUri(String oauth2redirectUri) { | |||
this.oauth2redirectUri = oauth2redirectUri; | |||
} | |||
@Override | |||
public String getHttpProxyHost() { | |||
return this.httpProxyHost; | |||
} | |||
public void setHttpProxyHost(String httpProxyHost) { | |||
this.httpProxyHost = httpProxyHost; | |||
} | |||
@Override | |||
public int getHttpProxyPort() { | |||
return this.httpProxyPort; | |||
} | |||
public void setHttpProxyPort(int httpProxyPort) { | |||
this.httpProxyPort = httpProxyPort; | |||
} | |||
@Override | |||
public String getHttpProxyUsername() { | |||
return this.httpProxyUsername; | |||
} | |||
public void setHttpProxyUsername(String httpProxyUsername) { | |||
this.httpProxyUsername = httpProxyUsername; | |||
} | |||
@Override | |||
public String getHttpProxyPassword() { | |||
return this.httpProxyPassword; | |||
} | |||
public void setHttpProxyPassword(String httpProxyPassword) { | |||
this.httpProxyPassword = httpProxyPassword; | |||
} | |||
@Override | |||
public String toString() { | |||
return WxCpGsonBuilder.create().toJson(this); | |||
} | |||
@Override | |||
public File getTmpDirFile() { | |||
return this.tmpDirFile; | |||
} | |||
public void setTmpDirFile(File tmpDirFile) { | |||
this.tmpDirFile = tmpDirFile; | |||
} | |||
@Override | |||
public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | |||
return this.apacheHttpClientBuilder; | |||
} | |||
public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { | |||
this.apacheHttpClientBuilder = apacheHttpClientBuilder; | |||
} | |||
} |
@@ -0,0 +1,113 @@ | |||
package me.chanjar.weixin.cp.constant; | |||
/** | |||
* <pre> | |||
* 企业微信api地址常量类 | |||
* Created by BinaryWang on 2019-06-02. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public final class WxCpApiPathConsts { | |||
public static final String DEFAULT_CP_BASE_URL = "https://qyapi.weixin.qq.com"; | |||
public static final String GET_JSAPI_TICKET = "/cgi-bin/get_jsapi_ticket"; | |||
public static final String GET_AGENT_CONFIG_TICKET = "/cgi-bin/ticket/get?&type=agent_config"; | |||
public static final String MESSAGE_SEND = "/cgi-bin/message/send"; | |||
public static final String GET_CALLBACK_IP = "/cgi-bin/getcallbackip"; | |||
public static final String BATCH_REPLACE_PARTY = "/cgi-bin/batch/replaceparty"; | |||
public static final String BATCH_REPLACE_USER = "/cgi-bin/batch/replaceuser"; | |||
public static final String BATCH_GET_RESULT = "/cgi-bin/batch/getresult?jobid="; | |||
public static final String JSCODE_TO_SESSION = "/cgi-bin/miniprogram/jscode2session"; | |||
public static final String GET_TOKEN = "/cgi-bin/gettoken?&corpid=%s&corpsecret=%s"; | |||
public static class Agent { | |||
public static final String AGENT_GET = "/cgi-bin/agent/get?agentid=%d"; | |||
public static final String AGENT_SET = "/cgi-bin/agent/set"; | |||
public static final String AGENT_LIST = "/cgi-bin/agent/list"; | |||
} | |||
public static class OAuth2 { | |||
public static final String GET_USER_INFO = "/cgi-bin/user/getuserinfo?code=%s&agentid=%d"; | |||
public static final String GET_USER_DETAIL = "/cgi-bin/user/getuserdetail"; | |||
public static final String URL_OAUTH2_AUTHORIZE = "https://open.weixin.qq.com/connect/oauth2/authorize"; | |||
} | |||
public static class Chat { | |||
public static final String APPCHAT_CREATE = "/cgi-bin/appchat/create"; | |||
public static final String APPCHAT_UPDATE = "/cgi-bin/appchat/update"; | |||
public static final String APPCHAT_GET_CHATID = "/cgi-bin/appchat/get?chatid="; | |||
public static final String APPCHAT_SEND = "/cgi-bin/appchat/send"; | |||
} | |||
public static class Department { | |||
public static final String DEPARTMENT_CREATE = "/cgi-bin/department/create"; | |||
public static final String DEPARTMENT_UPDATE = "/cgi-bin/department/update"; | |||
public static final String DEPARTMENT_DELETE = "/cgi-bin/department/delete?id=%d"; | |||
public static final String DEPARTMENT_LIST = "/cgi-bin/department/list"; | |||
} | |||
public static class Media { | |||
public static final String MEDIA_GET = "/cgi-bin/media/get"; | |||
public static final String MEDIA_UPLOAD = "/cgi-bin/media/upload?type="; | |||
public static final String IMG_UPLOAD = "/cgi-bin/media/uploadimg"; | |||
public static final String JSSDK_MEDIA_GET = "/cgi-bin/media/get/jssdk"; | |||
} | |||
public static class Menu { | |||
public static final String MENU_CREATE = "/cgi-bin/menu/create?agentid=%d"; | |||
public static final String MENU_DELETE = "/cgi-bin/menu/delete?agentid=%d"; | |||
public static final String MENU_GET = "/cgi-bin/menu/get?agentid=%d"; | |||
} | |||
public static class Oa { | |||
public static final String GET_CHECKIN_DATA = "/cgi-bin/checkin/getcheckindata"; | |||
public static final String GET_CHECKIN_OPTION = "/cgi-bin/checkin/getcheckinoption"; | |||
public static final String GET_APPROVAL_DATA = "/cgi-bin/corp/getapprovaldata"; | |||
public static final String GET_DIAL_RECORD = "/cgi-bin/dial/get_dial_record"; | |||
} | |||
public static class Tag { | |||
public static final String TAG_CREATE = "/cgi-bin/tag/create"; | |||
public static final String TAG_UPDATE = "/cgi-bin/tag/update"; | |||
public static final String TAG_DELETE = "/cgi-bin/tag/delete?tagid=%s"; | |||
public static final String TAG_LIST = "/cgi-bin/tag/list"; | |||
public static final String TAG_GET = "/cgi-bin/tag/get?tagid=%s"; | |||
public static final String TAG_ADD_TAG_USERS = "/cgi-bin/tag/addtagusers"; | |||
public static final String TAG_DEL_TAG_USERS = "/cgi-bin/tag/deltagusers"; | |||
} | |||
public static class TaskCard { | |||
public static final String UPDATE_TASK_CARD = "/cgi-bin/message/update_taskcard"; | |||
} | |||
public static class Tp { | |||
public static final String JSCODE_TO_SESSION = "/cgi-bin/service/miniprogram/jscode2session"; | |||
public static final String GET_CORP_TOKEN = "/cgi-bin/service/get_corp_token"; | |||
public static final String GET_PERMANENT_CODE = "/cgi-bin/service/get_permanent_code"; | |||
public static final String GET_SUITE_TOKEN = "/cgi-bin/service/get_suite_token"; | |||
} | |||
public static class User { | |||
public static final String USER_AUTHENTICATE = "/cgi-bin/user/authsucc?userid="; | |||
public static final String USER_CREATE = "/cgi-bin/user/create"; | |||
public static final String USER_UPDATE = "/cgi-bin/user/update"; | |||
public static final String USER_DELETE = "/cgi-bin/user/delete?userid="; | |||
public static final String USER_BATCH_DELETE = "/cgi-bin/user/batchdelete"; | |||
public static final String USER_GET = "/cgi-bin/user/get?userid="; | |||
public static final String USER_LIST = "/cgi-bin/user/list?department_id="; | |||
public static final String USER_SIMPLE_LIST = "/cgi-bin/user/simplelist?department_id="; | |||
public static final String BATCH_INVITE = "/cgi-bin/batch/invite"; | |||
public static final String USER_CONVERT_TO_OPENID = "/cgi-bin/user/convert_to_openid"; | |||
public static final String USER_CONVERT_TO_USERID = "/cgi-bin/user/convert_to_userid"; | |||
public static final String GET_USER_ID = "/cgi-bin/user/getuserid"; | |||
public static final String GET_EXTERNAL_CONTACT = "/cgi-bin/crm/get_external_contact?external_userid="; | |||
} | |||
public static class ExternalContact { | |||
public static final String GET_EXTERNAL_CONTACT = "/cgi-bin/crm/get_external_contact?external_userid="; | |||
public static final String LIST_EXTERNAL_CONTACT = "/cgi-bin/externalcontact/list?userid="; | |||
public static final String GET_FOLLOW_USER_LIST = "/cgi-bin/externalcontact/get_follow_user_list"; | |||
} | |||
} |
@@ -1,4 +1,4 @@ | |||
package me.chanjar.weixin.cp; | |||
package me.chanjar.weixin.cp.constant; | |||
/** | |||
* <pre> | |||
@@ -89,6 +89,26 @@ public class WxCpConsts { | |||
*/ | |||
public static final String TASKCARD_CLICK = "taskcard_click"; | |||
/** | |||
* 企业成员添加外部联系人事件推送 | |||
*/ | |||
public static final String CHANGE_EXTERNAL_CONTACT = "change_external_contact"; | |||
} | |||
/** | |||
* 企业外部联系人变更事件的CHANGE_TYPE | |||
*/ | |||
public static class ExternalContactChangeType { | |||
/** | |||
* 新增外部联系人 | |||
*/ | |||
public static final String ADD_EXTERNAL_CONTACT = "add_external_contact"; | |||
/** | |||
* 删除外部联系人 | |||
*/ | |||
public static final String DEL_EXTERNAL_CONTACT = "del_external_contact"; | |||
} | |||
/** |
@@ -0,0 +1,52 @@ | |||
/** | |||
* 对公众平台发送给公众账号的消息加解密示例代码. | |||
* | |||
* @copyright Copyright (c) 1998-2014 Tencent Inc. | |||
* <p> | |||
* 针对org.apache.commons.codec.binary.Base64, | |||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||
* <p> | |||
* 针对org.apache.commons.codec.binary.Base64, | |||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||
*/ | |||
// ------------------------------------------------------------------------ | |||
/** | |||
* 针对org.apache.commons.codec.binary.Base64, | |||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||
*/ | |||
package me.chanjar.weixin.cp.util.crypto; | |||
import org.apache.commons.codec.binary.Base64; | |||
import me.chanjar.weixin.common.util.crypto.WxCryptUtil; | |||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | |||
public class WxCpTpCryptUtil extends WxCryptUtil { | |||
/** | |||
* 构造函数 | |||
* | |||
* @param wxCpConfigStorage | |||
*/ | |||
public WxCpTpCryptUtil(WxCpTpConfigStorage wxCpTpConfigStorage) { | |||
/* | |||
* @param token 公众平台上,开发者设置的token | |||
* @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey | |||
* @param appidOrCorpid 公众平台corpId | |||
*/ | |||
String encodingAesKey = wxCpTpConfigStorage.getAesKey(); | |||
String token = wxCpTpConfigStorage.getToken(); | |||
String corpId = wxCpTpConfigStorage.getCorpId(); | |||
this.token = token; | |||
this.appidOrCorpid = corpId; | |||
this.aesKey = Base64.decodeBase64(encodingAesKey + "="); | |||
} | |||
} |
@@ -70,6 +70,7 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri | |||
user.setStatus(GsonHelper.getInteger(o, "status")); | |||
user.setEnable(GsonHelper.getInteger(o, "enable")); | |||
user.setIsLeader(GsonHelper.getInteger(o, "isleader")); | |||
user.setIsLeaderInDept(GsonHelper.getIntArray(o, "is_leader_in_dept")); | |||
user.setHideMobile(GsonHelper.getInteger(o, "hide_mobile")); | |||
user.setEnglishName(GsonHelper.getString(o, "english_name")); | |||
user.setTelephone(GsonHelper.getString(o, "telephone")); | |||
@@ -197,6 +198,13 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri | |||
if (user.getIsLeader() != null) { | |||
o.addProperty("isleader", user.getIsLeader()); | |||
} | |||
if (user.getIsLeaderInDept() != null && user.getIsLeaderInDept().length > 0) { | |||
JsonArray ary = new JsonArray(); | |||
for (int item : user.getIsLeaderInDept()) { | |||
ary.add(item); | |||
} | |||
o.add("is_leader_in_dept", ary); | |||
} | |||
if (user.getHideMobile() != null) { | |||
o.addProperty("hide_mobile", user.getHideMobile()); | |||
} | |||
@@ -1,117 +1,135 @@ | |||
package me.chanjar.weixin.cp.util.xml; | |||
import java.io.InputStream; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import com.thoughtworks.xstream.XStream; | |||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutImageMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutVideoMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutVoiceMessage; | |||
public class XStreamTransformer { | |||
protected static final Map<Class, XStream> CLASS_2_XSTREAM_INSTANCE = configXStreamInstance(); | |||
/** | |||
* xml -> pojo | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public static <T> T fromXml(Class<T> clazz, String xml) { | |||
T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml); | |||
return object; | |||
} | |||
@SuppressWarnings("unchecked") | |||
public static <T> T fromXml(Class<T> clazz, InputStream is) { | |||
T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is); | |||
return object; | |||
} | |||
/** | |||
* 注册扩展消息的解析器. | |||
* | |||
* @param clz 类型 | |||
* @param xStream xml解析器 | |||
*/ | |||
public static void register(Class clz, XStream xStream) { | |||
CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); | |||
} | |||
/** | |||
* pojo -> xml. | |||
*/ | |||
public static <T> String toXml(Class<T> clazz, T object) { | |||
return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); | |||
} | |||
private static Map<Class, XStream> configXStreamInstance() { | |||
Map<Class, XStream> map = new HashMap<>(); | |||
map.put(WxCpXmlMessage.class, configWxCpXmlMessage()); | |||
map.put(WxCpXmlOutNewsMessage.class, configWxCpXmlOutNewsMessage()); | |||
map.put(WxCpXmlOutTextMessage.class, configWxCpXmlOutTextMessage()); | |||
map.put(WxCpXmlOutImageMessage.class, configWxCpXmlOutImageMessage()); | |||
map.put(WxCpXmlOutVideoMessage.class, configWxCpXmlOutVideoMessage()); | |||
map.put(WxCpXmlOutVoiceMessage.class, configWxCpXmlOutVoiceMessage()); | |||
return map; | |||
} | |||
private static XStream configWxCpXmlMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlMessage.class); | |||
xstream.processAnnotations(WxCpXmlMessage.ScanCodeInfo.class); | |||
xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.class); | |||
xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.Item.class); | |||
xstream.processAnnotations(WxCpXmlMessage.SendLocationInfo.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutImageMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutImageMessage.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutNewsMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutNewsMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutNewsMessage.Item.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutTextMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutTextMessage.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutVideoMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutVideoMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutVideoMessage.Video.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutVoiceMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutVoiceMessage.class); | |||
return xstream; | |||
} | |||
} | |||
package me.chanjar.weixin.cp.util.xml; | |||
import java.io.InputStream; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import com.thoughtworks.xstream.XStream; | |||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
import me.chanjar.weixin.cp.bean.WxCpTpXmlMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpTpXmlPackage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutImageMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutVideoMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutVoiceMessage; | |||
public class XStreamTransformer { | |||
protected static final Map<Class, XStream> CLASS_2_XSTREAM_INSTANCE = configXStreamInstance(); | |||
/** | |||
* xml -> pojo | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public static <T> T fromXml(Class<T> clazz, String xml) { | |||
T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml); | |||
return object; | |||
} | |||
@SuppressWarnings("unchecked") | |||
public static <T> T fromXml(Class<T> clazz, InputStream is) { | |||
T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is); | |||
return object; | |||
} | |||
/** | |||
* 注册扩展消息的解析器. | |||
* | |||
* @param clz 类型 | |||
* @param xStream xml解析器 | |||
*/ | |||
public static void register(Class clz, XStream xStream) { | |||
CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); | |||
} | |||
/** | |||
* pojo -> xml. | |||
*/ | |||
public static <T> String toXml(Class<T> clazz, T object) { | |||
return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); | |||
} | |||
private static Map<Class, XStream> configXStreamInstance() { | |||
Map<Class, XStream> map = new HashMap<>(); | |||
map.put(WxCpXmlMessage.class, configWxCpXmlMessage()); | |||
map.put(WxCpXmlOutNewsMessage.class, configWxCpXmlOutNewsMessage()); | |||
map.put(WxCpXmlOutTextMessage.class, configWxCpXmlOutTextMessage()); | |||
map.put(WxCpXmlOutImageMessage.class, configWxCpXmlOutImageMessage()); | |||
map.put(WxCpXmlOutVideoMessage.class, configWxCpXmlOutVideoMessage()); | |||
map.put(WxCpXmlOutVoiceMessage.class, configWxCpXmlOutVoiceMessage()); | |||
map.put(WxCpTpXmlPackage.class, configWxCpTpXmlPackage()); | |||
map.put(WxCpTpXmlMessage.class, configWxCpTpXmlMessage()); | |||
return map; | |||
} | |||
private static XStream configWxCpXmlMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlMessage.class); | |||
xstream.processAnnotations(WxCpXmlMessage.ScanCodeInfo.class); | |||
xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.class); | |||
xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.Item.class); | |||
xstream.processAnnotations(WxCpXmlMessage.SendLocationInfo.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutImageMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutImageMessage.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutNewsMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutNewsMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutNewsMessage.Item.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutTextMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutTextMessage.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutVideoMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutVideoMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutVideoMessage.Video.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutVoiceMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutVoiceMessage.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpTpXmlPackage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpTpXmlPackage.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpTpXmlMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpTpXmlMessage.class); | |||
return xstream; | |||
} | |||
} |
@@ -12,7 +12,7 @@ import com.thoughtworks.xstream.XStream; | |||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; | |||
import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage; | |||
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; | |||
public class ApiTestModule implements Module { | |||
private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
@@ -44,7 +44,7 @@ public class ApiTestModule implements Module { | |||
} | |||
@XStreamAlias("xml") | |||
public static class WxXmlCpInMemoryConfigStorage extends WxCpInMemoryConfigStorage { | |||
public static class WxXmlCpInMemoryConfigStorage extends WxCpDefaultConfigImpl { | |||
protected String userId; | |||
@@ -52,6 +52,8 @@ public class ApiTestModule implements Module { | |||
protected String tagId; | |||
protected String externalUserId; | |||
public String getUserId() { | |||
return this.userId; | |||
} | |||
@@ -76,12 +78,22 @@ public class ApiTestModule implements Module { | |||
this.tagId = tagId; | |||
} | |||
public String getExternalUserId() { | |||
return externalUserId; | |||
} | |||
public void setExternalUserId(String externalUserId) { | |||
this.externalUserId = externalUserId; | |||
} | |||
@Override | |||
public String toString() { | |||
return super.toString() + " > WxXmlCpConfigStorage{" + | |||
"userId='" + this.userId + '\'' + | |||
", departmentId='" + this.departmentId + '\'' + | |||
", tagId='" + this.tagId + '\'' + | |||
", externalUserId='" + this.externalUserId + '\'' + | |||
'}'; | |||
} | |||
} | |||
@@ -1,5 +1,6 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import lombok.extern.slf4j.Slf4j; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.RequestExecutor; | |||
@@ -13,8 +14,8 @@ import java.util.concurrent.Executors; | |||
import java.util.concurrent.Future; | |||
@Test | |||
@Slf4j | |||
public class WxCpBusyRetryTest { | |||
@DataProvider(name = "getService") | |||
public Object[][] getService() { | |||
WxCpService service = new WxCpServiceImpl() { | |||
@@ -23,7 +24,7 @@ public class WxCpBusyRetryTest { | |||
public synchronized <T, E> T executeInternal( | |||
RequestExecutor<T, E> executor, String uri, E data) | |||
throws WxErrorException { | |||
this.log.info("Executed"); | |||
log.info("Executed"); | |||
throw new WxErrorException(WxError.builder().errorCode(-1).build()); | |||
} | |||
}; | |||
@@ -1,5 +1,6 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import com.google.common.collect.ImmutableMap; | |||
import org.testng.annotations.*; | |||
import com.google.inject.Inject; | |||
@@ -107,4 +108,27 @@ public class WxCpMessageAPITest { | |||
System.out.println(messageSendResult.getInvalidUserList()); | |||
System.out.println(messageSendResult.getInvalidTagList()); | |||
} | |||
@Test | |||
public void testSendMessage_miniprogram_notice() throws WxErrorException { | |||
WxCpMessage message = WxCpMessage | |||
.newMiniProgramNoticeBuilder() | |||
.toUser(configStorage.getUserId()) | |||
.appId("wx123123123123123") | |||
.page("pages/index?userid=zhangsan&orderid=123123123") | |||
.title("会议室预订成功通知") | |||
.description("4月27日 16:16") | |||
.emphasisFirstItem(true) | |||
.contentItems(ImmutableMap.of("会议室","402", | |||
"会议地点","广州TIT-402会议室", | |||
"会议时间","2018年8月1日 09:00-09:30")) | |||
.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()); | |||
} | |||
} |
@@ -6,7 +6,7 @@ import me.chanjar.weixin.cp.api.ApiTestModule; | |||
import me.chanjar.weixin.cp.api.WxCpAgentService; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.bean.WxCpAgent; | |||
import org.testng.Assert; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import org.testng.annotations.Guice; | |||
import org.testng.annotations.Test; | |||
@@ -71,7 +71,7 @@ public class WxCpAgentServiceImplTest { | |||
@Test | |||
public void testGet() throws Exception { | |||
String returnJson = "{\"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\": \"\"}"; | |||
when(wxService.get("https://qyapi.weixin.qq.com/cgi-bin/agent/get?agentid=9", null)).thenReturn(returnJson); | |||
when(wxService.get(String.format(wxService.getWxCpConfigStorage().getApiUrl(WxCpApiPathConsts.Agent.AGENT_GET), 9), null)).thenReturn(returnJson); | |||
when(wxService.getAgentService()).thenReturn(new WxCpAgentServiceImpl(wxService)); | |||
WxCpAgentService wxAgentService = this.wxService.getAgentService(); | |||
@@ -8,7 +8,7 @@ import org.testng.annotations.*; | |||
import com.google.common.collect.Lists; | |||
import com.google.inject.Inject; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.WxCpConsts.AppChatMsgType; | |||
import me.chanjar.weixin.cp.constant.WxCpConsts.AppChatMsgType; | |||
import me.chanjar.weixin.cp.api.ApiTestModule; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; | |||
@@ -0,0 +1,45 @@ | |||
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.WxCpUserExternalContactInfo; | |||
import org.testng.annotations.Guice; | |||
import org.testng.annotations.Test; | |||
import java.util.List; | |||
import static org.testng.Assert.assertNotNull; | |||
@Guice(modules = ApiTestModule.class) | |||
public class WxCpExternalContactServiceImplTest { | |||
@Inject | |||
private WxCpService wxCpService; | |||
@Inject | |||
protected ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage; | |||
private String userId = "someone" + System.currentTimeMillis(); | |||
@Test | |||
public void testGetExternalContact() throws WxErrorException { | |||
String externalUserId = this.configStorage.getExternalUserId(); | |||
WxCpUserExternalContactInfo result = this.wxCpService.getExternalContactService().getExternalContact(externalUserId); | |||
System.out.println(result); | |||
assertNotNull(result); | |||
} | |||
@Test | |||
public void testListExternalContacts() throws WxErrorException { | |||
String userId = this.configStorage.getUserId(); | |||
List<String> ret = this.wxCpService.getExternalContactService().listExternalContacts(userId); | |||
System.out.println(ret); | |||
assertNotNull(ret); | |||
} | |||
@Test | |||
public void testListExternalWithPermission() throws WxErrorException { | |||
List<String> ret = this.wxCpService.getExternalContactService().listFollowUser(); | |||
System.out.println(ret); | |||
assertNotNull(ret); | |||
} | |||
} |
@@ -4,10 +4,13 @@ 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.WxCpOauth2UserInfo; | |||
import me.chanjar.weixin.cp.bean.WxCpUserDetail; | |||
import org.testng.annotations.Guice; | |||
import org.testng.annotations.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
/** | |||
* <pre> | |||
* Created by BinaryWang on 2018/4/22. | |||
@@ -28,6 +31,13 @@ public class WxCpOAuth2ServiceImplTest { | |||
@Test | |||
public void testGetUserInfo() throws WxErrorException { | |||
this.wxService.getOauth2Service().getUserInfo("abc"); | |||
final WxCpOauth2UserInfo result = this.wxService.getOauth2Service().getUserInfo("abc"); | |||
assertThat(result).isNotNull(); | |||
System.out.println(result); | |||
} | |||
@Test | |||
public void testBuildAuthorizationUrl() { | |||
} | |||
} |
@@ -24,7 +24,7 @@ import static org.assertj.core.api.Assertions.assertThat; | |||
* @date 2019-04-20 13:46 | |||
*/ | |||
@Guice(modules = ApiTestModule.class) | |||
public class WxCpOAServiceImplTest { | |||
public class WxCpOaServiceImplTest { | |||
@Inject | |||
protected WxCpService wxService; | |||
@@ -10,6 +10,7 @@ import me.chanjar.weixin.cp.bean.WxCpTag; | |||
import me.chanjar.weixin.cp.bean.WxCpTagAddOrRemoveUsersResult; | |||
import me.chanjar.weixin.cp.bean.WxCpTagGetResult; | |||
import me.chanjar.weixin.cp.bean.WxCpUser; | |||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||
import org.testng.annotations.Guice; | |||
import org.testng.annotations.Test; | |||
@@ -22,10 +23,10 @@ import static org.testng.Assert.assertNotEquals; | |||
/** | |||
* <pre> | |||
* | |||
* Created by Binary Wang on 2017-6-25. | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
@Guice(modules = ApiTestModule.class) | |||
public class WxCpTagServiceImplTest { | |||
@@ -35,7 +36,7 @@ public class WxCpTagServiceImplTest { | |||
@Inject | |||
protected ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage; | |||
protected String tagId; | |||
private String tagId; | |||
@Test | |||
public void testCreate() throws Exception { | |||
@@ -83,7 +84,7 @@ public class WxCpTagServiceImplTest { | |||
public void testGet() throws WxErrorException { | |||
String apiResultJson = "{\"errcode\": 0,\"errmsg\": \"ok\",\"userlist\": [{\"userid\": \"0124035\",\"name\": \"王五\"},{\"userid\": \"0114035\",\"name\": \"梦雪\"}],\"partylist\": [9576,9567,9566],\"tagname\": \"测试标签-001\"}"; | |||
WxCpService wxService = mock(WxCpService.class); | |||
when(wxService.get("https://qyapi.weixin.qq.com/cgi-bin/tag/get?tagId=150", null)).thenReturn(apiResultJson); | |||
when(wxService.get(String.format(wxService.getWxCpConfigStorage().getApiUrl(WxCpApiPathConsts.Tag.TAG_GET), 150), null)).thenReturn(apiResultJson); | |||
when(wxService.getTagService()).thenReturn(new WxCpTagServiceImpl(wxService)); | |||
WxCpTagService wxCpTagService = wxService.getTagService(); | |||
@@ -96,7 +97,6 @@ public class WxCpTagServiceImplTest { | |||
assertEquals(3, wxCpTagGetResult.getPartylist().size()); | |||
assertEquals("测试标签-001", wxCpTagGetResult.getTagname()); | |||
} | |||
} |
@@ -93,7 +93,7 @@ public class WxCpUserServiceImplTest { | |||
@Test | |||
public void testInvite() throws Exception { | |||
WxCpInviteResult result = this.wxCpService.getUserService().invite( | |||
Lists.newArrayList(userId), null,null); | |||
Lists.newArrayList(userId), null, null); | |||
System.out.println(result); | |||
} | |||
@@ -111,10 +111,15 @@ public class WxCpUserServiceImplTest { | |||
assertNotNull(result); | |||
} | |||
@Test | |||
public void testGetExternalContact() throws WxErrorException { | |||
WxCpUserExternalContactInfo result = this.wxCpService.getUserService().getExternalContact(userId); | |||
public void testGetUserId() throws WxErrorException { | |||
String result = this.wxCpService.getUserService().getUserId("xxx"); | |||
System.out.println(result); | |||
assertNotNull(result); | |||
} | |||
@Test | |||
public void testGetExternalContact() { | |||
} | |||
} |
@@ -1,9 +1,10 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import me.chanjar.weixin.common.api.WxConsts; | |||
import me.chanjar.weixin.cp.constant.WxCpConsts; | |||
import org.testng.annotations.Test; | |||
import static me.chanjar.weixin.cp.WxCpConsts.EventType.TASKCARD_CLICK; | |||
import static me.chanjar.weixin.cp.constant.WxCpConsts.EventType.TASKCARD_CLICK; | |||
import static org.testng.Assert.assertEquals; | |||
import static org.testng.Assert.assertNotNull; | |||
@@ -106,7 +107,7 @@ public class WxCpXmlMessageTest { | |||
"<Count>2</Count>" + | |||
"</SendPicsInfo>" + | |||
"</xml>"; | |||
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml.replace("</PicList><PicList>","")); | |||
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml.replace("</PicList><PicList>", "")); | |||
assertEquals(wxMessage.getToUserName(), "wx45a0972125658be9"); | |||
assertEquals(wxMessage.getFromUserName(), "xiaohe"); | |||
assertEquals(wxMessage.getCreateTime(), new Long(1502012364L)); | |||
@@ -173,4 +174,52 @@ public class WxCpXmlMessageTest { | |||
assertEquals(wxMessage.getEventKey(), "key111"); | |||
assertEquals(wxMessage.getTaskId(), "taskid111"); | |||
} | |||
public void testAddExternalUserEvent() { | |||
String xml = "<xml>" + | |||
"<ToUserName><![CDATA[toUser]]></ToUserName>" + | |||
"<FromUserName><![CDATA[sys]]></FromUserName>" + | |||
"<CreateTime>1403610513</CreateTime>" + | |||
"<MsgType><![CDATA[event]]></MsgType>" + | |||
"<Event><![CDATA[change_external_contact]]></Event>" + | |||
"<ChangeType><![CDATA[add_external_contact]]></ChangeType>" + | |||
"<UserID><![CDATA[zhangsan]]></UserID>" + | |||
"<ExternalUserID><![CDATA[woAJ2GCAAAXtWyujaWJHDDGi0mACH71w]]></ExternalUserID>" + | |||
"<State><![CDATA[teststate]]></State>" + | |||
"<WelcomeCode><![CDATA[WELCOMECODE]]></WelcomeCode>" + | |||
"</xml >"; | |||
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); | |||
assertEquals(wxMessage.getToUserName(), "toUser"); | |||
assertEquals(wxMessage.getFromUserName(), "sys"); | |||
assertEquals(wxMessage.getCreateTime(), Long.valueOf(1403610513L)); | |||
assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); | |||
assertEquals(wxMessage.getEvent(), WxCpConsts.EventType.CHANGE_EXTERNAL_CONTACT); | |||
assertEquals(wxMessage.getChangeType(), WxCpConsts.ExternalContactChangeType.ADD_EXTERNAL_CONTACT); | |||
assertEquals(wxMessage.getExternalUserId(), "woAJ2GCAAAXtWyujaWJHDDGi0mACH71w"); | |||
assertEquals(wxMessage.getState(), "teststate"); | |||
assertEquals(wxMessage.getWelcomeCode(), "WELCOMECODE"); | |||
} | |||
public void testDelExternalUserEvent() { | |||
String xml = "<xml>" + | |||
"<ToUserName><![CDATA[toUser]]></ToUserName>" + | |||
"<FromUserName><![CDATA[sys]]></FromUserName>" + | |||
"<CreateTime>1403610513</CreateTime>" + | |||
"<MsgType><![CDATA[event]]></MsgType>" + | |||
"<Event><![CDATA[change_external_contact]]></Event>" + | |||
"<ChangeType><![CDATA[del_external_contact]]></ChangeType>" + | |||
"<UserID><![CDATA[zhangsan]]></UserID>" + | |||
"<ExternalUserID><![CDATA[woAJ2GCAAAXtWyujaWJHDDGi0mACH71w]]></ExternalUserID>" + | |||
"</xml>"; | |||
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); | |||
assertEquals(wxMessage.getToUserName(), "toUser"); | |||
assertEquals(wxMessage.getFromUserName(), "sys"); | |||
assertEquals(wxMessage.getCreateTime(), Long.valueOf(1403610513L)); | |||
assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); | |||
assertEquals(wxMessage.getEvent(), WxCpConsts.EventType.CHANGE_EXTERNAL_CONTACT); | |||
assertEquals(wxMessage.getChangeType(), WxCpConsts.ExternalContactChangeType.DEL_EXTERNAL_CONTACT); | |||
assertEquals(wxMessage.getUserId(), "zhangsan"); | |||
assertEquals(wxMessage.getExternalUserId(), "woAJ2GCAAAXtWyujaWJHDDGi0mACH71w"); | |||
} | |||
} |
@@ -6,14 +6,14 @@ import com.thoughtworks.xstream.XStream; | |||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
import lombok.ToString; | |||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage; | |||
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; | |||
/** | |||
* @author Daniel Qian | |||
*/ | |||
@XStreamAlias("xml") | |||
@ToString | |||
public class WxCpDemoInMemoryConfigStorage extends WxCpInMemoryConfigStorage { | |||
public class WxCpDemoInMemoryConfigStorage extends WxCpDefaultConfigImpl { | |||
public static WxCpDemoInMemoryConfigStorage fromXml(InputStream is) { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpDemoInMemoryConfigStorage.class); | |||
@@ -10,7 +10,7 @@ import org.eclipse.jetty.servlet.ServletHolder; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.session.WxSessionManager; | |||
import me.chanjar.weixin.cp.WxCpConsts; | |||
import me.chanjar.weixin.cp.constant.WxCpConsts; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | |||
@@ -2,6 +2,7 @@ package me.chanjar.weixin.cp.demo; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo; | |||
import javax.servlet.http.HttpServlet; | |||
import javax.servlet.http.HttpServletRequest; | |||
@@ -30,9 +31,9 @@ public class WxCpOAuth2Servlet extends HttpServlet { | |||
response.getWriter().println("<h1>code</h1>"); | |||
response.getWriter().println(code); | |||
String[] res = this.wxCpService.getOauth2Service().getUserInfo(code); | |||
WxCpOauth2UserInfo res = this.wxCpService.getOauth2Service().getUserInfo(code); | |||
response.getWriter().println("<h1>result</h1>"); | |||
response.getWriter().println(Arrays.toString(res)); | |||
response.getWriter().println(res); | |||
} catch (WxErrorException e) { | |||
e.printStackTrace(); | |||
} | |||