@@ -7,7 +7,7 @@ language: java | |||||
# secure: "834110c7191f97ecb226970c46dcaff8e681da5a" | # secure: "834110c7191f97ecb226970c46dcaff8e681da5a" | ||||
jdk: | jdk: | ||||
- oraclejdk8 | |||||
- openjdk8 | |||||
script: "mvn clean package -DskipTests=true -Dcheckstyle.skip=true" | script: "mvn clean package -DskipTests=true -Dcheckstyle.skip=true" | ||||
#script: | #script: | ||||
@@ -1,3 +0,0 @@ | |||||
{ | |||||
"java.configuration.updateBuildConfiguration": "interactive" | |||||
} |
@@ -6,7 +6,7 @@ | |||||
<modelVersion>4.0.0</modelVersion> | <modelVersion>4.0.0</modelVersion> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>wx-java</artifactId> | <artifactId>wx-java</artifactId> | ||||
<version>3.4.0</version> | |||||
<version>3.5.0</version> | |||||
<packaging>pom</packaging> | <packaging>pom</packaging> | ||||
<name>WxJava - Weixin/Wechat Java SDK</name> | <name>WxJava - Weixin/Wechat Java SDK</name> | ||||
<description>微信开发Java SDK</description> | <description>微信开发Java SDK</description> | ||||
@@ -105,8 +105,9 @@ | |||||
<module>weixin-java-pay</module> | <module>weixin-java-pay</module> | ||||
<module>weixin-java-miniapp</module> | <module>weixin-java-miniapp</module> | ||||
<module>weixin-java-open</module> | <module>weixin-java-open</module> | ||||
<module>starters/wx-java-pay-starter</module> | |||||
<module>starters/wx-java-mp-starter</module> | |||||
<module>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--> | <!--module>weixin-java-osgi</module--> | ||||
</modules> | </modules> | ||||
@@ -246,7 +247,7 @@ | |||||
<dependency> | <dependency> | ||||
<groupId>org.projectlombok</groupId> | <groupId>org.projectlombok</groupId> | ||||
<artifactId>lombok</artifactId> | <artifactId>lombok</artifactId> | ||||
<version>1.16.18</version> | |||||
<version>1.18.8</version> | |||||
<scope>provided</scope> | <scope>provided</scope> | ||||
</dependency> | </dependency> | ||||
</dependencies> | </dependencies> | ||||
@@ -370,6 +371,7 @@ | |||||
<artifactId>maven-checkstyle-plugin</artifactId> | <artifactId>maven-checkstyle-plugin</artifactId> | ||||
<version>2.17</version> | <version>2.17</version> | ||||
<configuration> | <configuration> | ||||
<skip>true</skip> | |||||
<configLocation>quality-checks/google_checks.xml</configLocation> | <configLocation>quality-checks/google_checks.xml</configLocation> | ||||
<includeTestSourceDirectory>true</includeTestSourceDirectory> | <includeTestSourceDirectory>true</includeTestSourceDirectory> | ||||
<consoleOutput>true</consoleOutput> | <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 | ```xml | ||||
<dependency> | <dependency> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>wx-java-mp-starter</artifactId> | |||||
<artifactId>wx-java-mp-spring-boot-starter</artifactId> | |||||
<version>${version}</version> | <version>${version}</version> | ||||
</dependency> | </dependency> | ||||
``` | ``` |
@@ -6,13 +6,13 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>wx-java</artifactId> | <artifactId>wx-java</artifactId> | ||||
<version>3.4.0</version> | |||||
<version>3.5.0</version> | |||||
<relativePath>../../</relativePath> | <relativePath>../../</relativePath> | ||||
</parent> | </parent> | ||||
<artifactId>wx-java-mp-starter</artifactId> | |||||
<artifactId>wx-java-mp-spring-boot-starter</artifactId> | |||||
<name>WxJava - Spring Boot Starter for MP</name> | <name>WxJava - Spring Boot Starter for MP</name> | ||||
<description>微信公众号开发的Spring Boot Starter</description> | |||||
<description>微信公众号开发的 Spring Boot Starter</description> | |||||
<properties> | <properties> | ||||
<spring.boot.version>2.1.4.RELEASE</spring.boot.version> | <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.boot.context.properties.EnableConfigurationProperties; | ||||
import org.springframework.context.annotation.Configuration; | import org.springframework.context.annotation.Configuration; | ||||
import org.springframework.context.annotation.Import; | import org.springframework.context.annotation.Import; | ||||
/** | |||||
* . | |||||
* | |||||
* @author someone | |||||
*/ | |||||
@Configuration | @Configuration | ||||
@EnableConfigurationProperties(WxMpProperties.class) | @EnableConfigurationProperties(WxMpProperties.class) | ||||
@Import({WxMpStorageAutoConfiguration.class, WxMpServiceAutoConfiguration.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 lombok.Data; | ||||
import org.springframework.boot.context.properties.ConfigurationProperties; | import org.springframework.boot.context.properties.ConfigurationProperties; | ||||
import java.io.Serializable; | 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 | @Data | ||||
@ConfigurationProperties(PREFIX) | @ConfigurationProperties(PREFIX) | ||||
@@ -18,33 +20,34 @@ public class WxMpProperties { | |||||
public static final String PREFIX = "wx.mp"; | public static final String PREFIX = "wx.mp"; | ||||
/** | /** | ||||
* 设置微信公众号的appid | |||||
* 设置微信公众号的appid. | |||||
*/ | */ | ||||
private String appId; | private String appId; | ||||
/** | /** | ||||
* 设置微信公众号的app secret | |||||
* 设置微信公众号的app secret. | |||||
*/ | */ | ||||
private String secret; | private String secret; | ||||
/** | /** | ||||
* 设置微信公众号的token | |||||
* 设置微信公众号的token. | |||||
*/ | */ | ||||
private String token; | private String token; | ||||
/** | /** | ||||
* 设置微信公众号的EncodingAESKey | |||||
* 设置微信公众号的EncodingAESKey. | |||||
*/ | */ | ||||
private String aesKey; | private String aesKey; | ||||
/** | /** | ||||
* 存储策略, memory, redis | |||||
* 存储策略, memory, redis. | |||||
*/ | */ | ||||
private ConfigStorage configStorage = new ConfigStorage(); | private ConfigStorage configStorage = new ConfigStorage(); | ||||
@Data | @Data | ||||
public static class ConfigStorage implements Serializable { | public static class ConfigStorage implements Serializable { | ||||
private static final long serialVersionUID = 4815731027000065434L; | |||||
private StorageType type = memory; | private StorageType type = memory; | ||||
@@ -53,6 +56,13 @@ public class WxMpProperties { | |||||
} | } | ||||
public enum StorageType { | 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 | ```xml | ||||
<dependency> | <dependency> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>wx-java-pay-starter</artifactId> | |||||
<artifactId>wx-java-pay-spring-boot-starter</artifactId> | |||||
<version>${version}</version> | <version>${version}</version> | ||||
</dependency> | </dependency> | ||||
``` | ``` | ||||
@@ -11,9 +11,9 @@ | |||||
```yml | ```yml | ||||
wx: | wx: | ||||
pay: | pay: | ||||
appId: wx5b69c56ac01ed858 | |||||
mchId: 1462547202 | |||||
mchKey: OGL9fvig9y2HrXrQ86tM4jTwyv4ja6G5 | |||||
appId: | |||||
mchId: | |||||
mchKey: | |||||
subAppId: | subAppId: | ||||
subMchId: | subMchId: | ||||
keyPath: | keyPath: |
@@ -5,14 +5,14 @@ | |||||
<parent> | <parent> | ||||
<artifactId>wx-java</artifactId> | <artifactId>wx-java</artifactId> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<version>3.4.0</version> | |||||
<version>3.5.0</version> | |||||
<relativePath>../../</relativePath> | <relativePath>../../</relativePath> | ||||
</parent> | </parent> | ||||
<modelVersion>4.0.0</modelVersion> | <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> | <name>WxJava - Spring Boot Starter for WxPay</name> | ||||
<description>微信支付开发的Spring Boot Starter</description> | |||||
<description>微信支付开发的 Spring Boot Starter</description> | |||||
<properties> | <properties> | ||||
<spring.boot.version>2.1.4.RELEASE</spring.boot.version> | <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> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>wx-java</artifactId> | <artifactId>wx-java</artifactId> | ||||
<version>3.4.0</version> | |||||
<version>3.5.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-common</artifactId> | <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 TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | ||||
/** | /** | ||||
* 小程序卡片(要求小程序与公众号已关联) | |||||
* 小程序卡片(要求小程序与公众号已关联). | |||||
*/ | */ | ||||
public static final String MINIPROGRAMPAGE = "miniprogrampage"; | public static final String MINIPROGRAMPAGE = "miniprogrampage"; | ||||
/** | /** | ||||
* 任务卡片消息 | |||||
* 任务卡片消息. | |||||
*/ | */ | ||||
public static final String TASKCARD = "taskcard"; | 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; | import java.util.Arrays; | ||||
/** | /** | ||||
* Created by Daniel Qian on 14/10/19. | |||||
* | |||||
* @author Daniel Qian | |||||
* @date 14/10/19 | |||||
*/ | */ | ||||
public class SHA1 { | public class SHA1 { | ||||
/** | /** | ||||
* 串接arr参数,生成sha1 digest | |||||
* 串接arr参数,生成sha1 digest. | |||||
*/ | */ | ||||
public static String gen(String... arr) { | public static String gen(String... arr) { | ||||
if (StringUtils.isAnyEmpty(arr)) { | if (StringUtils.isAnyEmpty(arr)) { | ||||
@@ -27,7 +29,7 @@ public class SHA1 { | |||||
} | } | ||||
/** | /** | ||||
* 用&串接arr参数,生成sha1 digest | |||||
* 用&串接arr参数,生成sha1 digest. | |||||
*/ | */ | ||||
public static String genWithAmple(String... arr) { | public static String genWithAmple(String... arr) { | ||||
if (StringUtils.isAnyEmpty(arr)) { | if (StringUtils.isAnyEmpty(arr)) { | ||||
@@ -55,20 +55,19 @@ public class WxCryptUtil { | |||||
} | } | ||||
/** | /** | ||||
* 构造函数 | |||||
* 构造函数. | |||||
* | * | ||||
* @param token 公众平台上,开发者设置的token | * @param token 公众平台上,开发者设置的token | ||||
* @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey | * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey | ||||
* @param appidOrCorpid 公众平台appid/corpid | * @param appidOrCorpid 公众平台appid/corpid | ||||
*/ | */ | ||||
public WxCryptUtil(String token, String encodingAesKey, | |||||
String appidOrCorpid) { | |||||
public WxCryptUtil(String token, String encodingAesKey, String appidOrCorpid) { | |||||
this.token = token; | this.token = token; | ||||
this.appidOrCorpid = appidOrCorpid; | this.appidOrCorpid = appidOrCorpid; | ||||
this.aesKey = Base64.decodeBase64(encodingAesKey + "="); | this.aesKey = Base64.decodeBase64(encodingAesKey + "="); | ||||
} | } | ||||
static String extractEncryptPart(String xml) { | |||||
private static String extractEncryptPart(String xml) { | |||||
try { | try { | ||||
DocumentBuilder db = BUILDER_LOCAL.get(); | DocumentBuilder db = BUILDER_LOCAL.get(); | ||||
Document document = db.parse(new InputSource(new StringReader(xml))); | 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) { | private static byte[] number2BytesInNetworkOrder(int number) { | ||||
byte[] orderBytes = new byte[4]; | byte[] orderBytes = new byte[4]; | ||||
@@ -93,7 +92,7 @@ public class WxCryptUtil { | |||||
} | } | ||||
/** | /** | ||||
* 4个字节的网络字节序bytes数组还原成一个数字 | |||||
* 4个字节的网络字节序bytes数组还原成一个数字. | |||||
*/ | */ | ||||
private static int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) { | private static int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) { | ||||
int sourceNumber = 0; | int sourceNumber = 0; | ||||
@@ -105,7 +104,7 @@ public class WxCryptUtil { | |||||
} | } | ||||
/** | /** | ||||
* 随机生成16位字符串 | |||||
* 随机生成16位字符串. | |||||
*/ | */ | ||||
private static String genRandomStr() { | private static String genRandomStr() { | ||||
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||||
@@ -119,7 +118,7 @@ public class WxCryptUtil { | |||||
} | } | ||||
/** | /** | ||||
* 生成xml消息 | |||||
* 生成xml消息. | |||||
* | * | ||||
* @param encrypt 加密后的消息密文 | * @param encrypt 加密后的消息密文 | ||||
* @param signature 安全签名 | * @param signature 安全签名 | ||||
@@ -127,8 +126,7 @@ public class WxCryptUtil { | |||||
* @param nonce 随机字符串 | * @param nonce 随机字符串 | ||||
* @return 生成的xml字符串 | * @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" | String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n" | ||||
+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" | + "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" | ||||
+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" | + "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" | ||||
@@ -242,10 +240,9 @@ public class WxCryptUtil { | |||||
try { | try { | ||||
// 设置解密模式为AES的CBC模式 | // 设置解密模式为AES的CBC模式 | ||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | 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对密文进行解码 | // 使用BASE64对密文进行解码 | ||||
byte[] encrypted = Base64.decodeBase64(cipherText); | byte[] encrypted = Base64.decodeBase64(cipherText); | ||||
@@ -256,7 +253,8 @@ public class WxCryptUtil { | |||||
throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
} | } | ||||
String xmlContent, fromAppid; | |||||
String xmlContent; | |||||
String fromAppid; | |||||
try { | try { | ||||
// 去除补位字符 | // 去除补位字符 | ||||
byte[] bytes = PKCS7Encoder.decode(original); | byte[] bytes = PKCS7Encoder.decode(original); | ||||
@@ -266,17 +264,15 @@ public class WxCryptUtil { | |||||
int xmlLength = bytesNetworkOrder2Number(networkOrder); | 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) { | } catch (Exception e) { | ||||
throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
} | } | ||||
// appid不相同的情况 | // appid不相同的情况 | ||||
if (!fromAppid.equals(this.appidOrCorpid)) { | if (!fromAppid.equals(this.appidOrCorpid)) { | ||||
throw new RuntimeException("AppID不正确"); | |||||
throw new RuntimeException("AppID不正确,请核实!"); | |||||
} | } | ||||
return xmlContent; | return xmlContent; | ||||
@@ -7,7 +7,7 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>wx-java</artifactId> | <artifactId>wx-java</artifactId> | ||||
<version>3.4.0</version> | |||||
<version>3.5.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-cp</artifactId> | <artifactId>weixin-java-cp</artifactId> | ||||
@@ -15,7 +15,6 @@ import java.util.List; | |||||
* @author <a href="https://github.com/huansinho">huansinho</a> | * @author <a href="https://github.com/huansinho">huansinho</a> | ||||
*/ | */ | ||||
public interface WxCpAgentService { | public interface WxCpAgentService { | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 获取企业号应用信息 | * 获取企业号应用信息 | ||||
@@ -1,20 +1,20 @@ | |||||
package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
import java.util.List; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; | import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; | ||||
import me.chanjar.weixin.cp.bean.WxCpChat; | import me.chanjar.weixin.cp.bean.WxCpChat; | ||||
import java.util.List; | |||||
/** | /** | ||||
* 群聊服务. | * 群聊服务. | ||||
* | * | ||||
* @author gaigeshen | * @author gaigeshen | ||||
*/ | */ | ||||
public interface WxCpChatService { | public interface WxCpChatService { | ||||
String APPCHAT_CREATE = "https://qyapi.weixin.qq.com/cgi-bin/appchat/create"; | |||||
String APPCHAT_UPDATE = "https://qyapi.weixin.qq.com/cgi-bin/appchat/update"; | |||||
String APPCHAT_GET_CHATID = "https://qyapi.weixin.qq.com/cgi-bin/appchat/get?chatid="; | |||||
@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 users 群成员id列表。至少2人,至多500人 | ||||
* @param chatId 群聊的唯一标志,不能与已有的群重复;字符串类型,最长32个字符。只允许字符0-9及字母a-zA-Z。如果不填,系统会随机生成群id | * @param chatId 群聊的唯一标志,不能与已有的群重复;字符串类型,最长32个字符。只允许字符0-9及字母a-zA-Z。如果不填,系统会随机生成群id | ||||
* @return 创建的群聊会话chatId | * @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; | 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 owner 新群主的id。若不需更新,请忽略此参数(null or empty) | ||||
* @param usersToAdd 添加成员的id列表,若不需要更新,则传递空对象或者空集合 | * @param usersToAdd 添加成员的id列表,若不需要更新,则传递空对象或者空集合 | ||||
* @param usersToDelete 踢出成员的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; | 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 群聊编号 | * @param chatId 群聊编号 | ||||
* @return 群聊会话 | * @return 群聊会话 | ||||
* @throws WxErrorException 发生异常 | |||||
*/ | |||||
WxCpChat chatGet(String chatId) throws WxErrorException; | |||||
/** | |||||
* chatGet 同名方法 | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
WxCpChat get(String chatId) 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 | * 文档地址:https://work.weixin.qq.com/api/doc#90000/90135/90248 | ||||
* | * | ||||
* @param message 要发送的消息内容对象 | * @param message 要发送的消息内容对象 | ||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
void sendMsg(WxCpAppChatMessage message) throws WxErrorException; | void sendMsg(WxCpAppChatMessage message) throws WxErrorException; | ||||
@@ -1,10 +1,10 @@ | |||||
package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
import java.util.List; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.bean.WxCpDepart; | import me.chanjar.weixin.cp.bean.WxCpDepart; | ||||
import java.util.List; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 部门管理接口 | * 部门管理接口 | ||||
@@ -19,7 +19,7 @@ public interface WxCpDepartmentService { | |||||
* <pre> | * <pre> | ||||
* 部门管理接口 - 创建部门. | * 部门管理接口 - 创建部门. | ||||
* 最多支持创建500个部门 | * 最多支持创建500个部门 | ||||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口 | |||||
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90205 | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param depart 部门 | * @param depart 部门 | ||||
@@ -30,19 +30,20 @@ public interface WxCpDepartmentService { | |||||
/** | /** | ||||
* <pre> | * <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> | * </pre> | ||||
* | * | ||||
* @param id 部门id。获取指定部门及其下的子部门。非必需,可为null | * @param id 部门id。获取指定部门及其下的子部门。非必需,可为null | ||||
* @return 获取的部门列表 | |||||
* @throws WxErrorException 异常 | * @throws WxErrorException 异常 | ||||
*/ | */ | ||||
List<WxCpDepart> list(Long id) throws WxErrorException; | List<WxCpDepart> list(Long id) throws WxErrorException; | ||||
/** | /** | ||||
* <pre> | * <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,微信会返回系统繁忙的错误 | * 如果id为0(未部门),1(黑名单),2(星标组),或者不存在的id,微信会返回系统繁忙的错误 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
@@ -54,6 +55,8 @@ public interface WxCpDepartmentService { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 部门管理接口 - 删除部门. | * 部门管理接口 - 删除部门. | ||||
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90207 | |||||
* 应用须拥有指定部门的管理权限 | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param departId 部门id | * @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; | 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.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 媒体管理接口. | * 媒体管理接口. | ||||
@@ -16,10 +16,6 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
public interface WxCpMediaService { | 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> | * <pre> | ||||
@@ -12,6 +12,7 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
public interface WxCpMenuService { | public interface WxCpMenuService { | ||||
/** | /** | ||||
* <pre> | * <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; | package me.chanjar.weixin.cp.api; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo; | |||||
import me.chanjar.weixin.cp.bean.WxCpUserDetail; | import me.chanjar.weixin.cp.bean.WxCpUserDetail; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* OAuth2相关管理接口 | |||||
* OAuth2相关管理接口. | |||||
* Created by BinaryWang on 2017/6/24. | * Created by BinaryWang on 2017/6/24. | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
public interface WxCpOAuth2Service { | public interface WxCpOAuth2Service { | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 构造oauth2授权的url连接 | |||||
* 构造oauth2授权的url连接. | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param state 状态码 | * @param state 状态码 | ||||
@@ -24,7 +26,7 @@ public interface WxCpOAuth2Service { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 构造oauth2授权的url连接 | |||||
* 构造oauth2授权的url连接. | |||||
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code | * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code | ||||
* </pre> | * </pre> | ||||
* | * | ||||
@@ -57,16 +59,18 @@ public interface WxCpOAuth2Service { | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param code 微信oauth授权返回的代码 | * @param code 微信oauth授权返回的代码 | ||||
* @return [userid, deviceid] | |||||
* @return WxCpOauth2UserInfo | |||||
* @throws WxErrorException 异常 | |||||
* @see #getUserInfo(Integer, String) | * @see #getUserInfo(Integer, String) | ||||
*/ | */ | ||||
String[] getUserInfo(String code) throws WxErrorException; | |||||
WxCpOauth2UserInfo getUserInfo(String code) throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 根据code获取成员信息 | * 根据code获取成员信息 | ||||
* http://qydev.weixin.qq.com/wiki/index.php?title=根据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#10028/根据code获取成员信息 | ||||
* https://work.weixin.qq.com/api/doc#90000/90135/91023 获取访问用户身份 | |||||
* 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。 | * 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。 | ||||
* | * | ||||
* 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | ||||
@@ -74,10 +78,11 @@ public interface WxCpOAuth2Service { | |||||
* | * | ||||
* @param agentId 企业号应用的id | * @param agentId 企业号应用的id | ||||
* @param code 通过成员授权获取到的code,最大为512字节。每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。 | * @param code 通过成员授权获取到的code,最大为512字节。每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。 | ||||
* @return [UserId, DeviceId, OpenId, user_ticket, expires_in] | |||||
* @return WxCpOauth2UserInfo | |||||
* @throws WxErrorException 异常 | |||||
* @see #getUserInfo(String) | * @see #getUserInfo(String) | ||||
*/ | */ | ||||
String[] getUserInfo(Integer agentId, String code) throws WxErrorException; | |||||
WxCpOauth2UserInfo getUserInfo(Integer agentId, String code) throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -92,6 +97,8 @@ public interface WxCpOAuth2Service { | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param userTicket 成员票据 | * @param userTicket 成员票据 | ||||
* @return WxCpUserDetail | |||||
* @throws WxErrorException 异常 | |||||
*/ | */ | ||||
WxCpUserDetail getUserDetail(String userTicket) 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; | import me.chanjar.weixin.cp.config.WxCpConfigStorage; | ||||
/** | /** | ||||
* 微信API的Service | |||||
* 微信API的Service. | |||||
* @author chanjaster | * @author chanjaster | ||||
*/ | */ | ||||
public interface WxCpService { | public interface WxCpService { | ||||
String GET_JSAPI_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket"; | |||||
String GET_AGENT_CONFIG_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?&type=agent_config"; | |||||
String MESSAGE_SEND = "https://qyapi.weixin.qq.com/cgi-bin/message/send"; | |||||
String GET_CALLBACK_IP = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip"; | |||||
String BATCH_REPLACE_PARTY = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty"; | |||||
String BATCH_REPLACE_USER = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser"; | |||||
String BATCH_GET_RESULT = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid="; | |||||
String JSCODE_TO_SESSION_URL = "https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session"; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 验证推送过来的消息的正确性 | * 验证推送过来的消息的正确性 | ||||
@@ -293,7 +284,9 @@ public interface WxCpService { | |||||
* 获取用户相关接口的服务类对象 | * 获取用户相关接口的服务类对象 | ||||
*/ | */ | ||||
WxCpUserService getUserService(); | WxCpUserService getUserService(); | ||||
WxCpExternalContactService getExternalContactService(); | |||||
/** | /** | ||||
* 获取群聊服务 | * 获取群聊服务 | ||||
* | * | ||||
@@ -310,7 +303,7 @@ public interface WxCpService { | |||||
WxCpAgentService getAgentService(); | WxCpAgentService getAgentService(); | ||||
WxCpOAService getOAService(); | |||||
WxCpOaService getOAService(); | |||||
/** | /** | ||||
* http请求对象 | * http请求对象 | ||||
@@ -17,6 +17,7 @@ import java.util.List; | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
public interface WxCpTagService { | public interface WxCpTagService { | ||||
/** | /** | ||||
* 创建标签. | * 创建标签. | ||||
* | * | ||||
@@ -51,6 +52,14 @@ public interface WxCpTagService { | |||||
*/ | */ | ||||
List<WxCpUser> listUsersByTagId(String tagId) throws WxErrorException; | 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; | 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 | * @date 2019-05-16 | ||||
*/ | */ | ||||
public interface WxCpTaskCardService { | public interface WxCpTaskCardService { | ||||
/** | /** | ||||
* <pre> | * <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; | 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.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.bean.WxCpInviteResult; | import me.chanjar.weixin.cp.bean.WxCpInviteResult; | ||||
import me.chanjar.weixin.cp.bean.WxCpUser; | import me.chanjar.weixin.cp.bean.WxCpUser; | ||||
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | ||||
import java.util.List; | |||||
import java.util.Map; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 用户管理接口 | * 用户管理接口 | ||||
@@ -17,6 +17,7 @@ import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
public interface WxCpUserService { | public interface WxCpUserService { | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 用在二次验证的时候. | * 用在二次验证的时候. | ||||
@@ -136,6 +137,23 @@ public interface WxCpUserService { | |||||
*/ | */ | ||||
String openid2UserId(String openid) throws WxErrorException; | 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> | * <pre> | ||||
@@ -146,6 +164,10 @@ public interface WxCpUserService { | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param userId 外部联系人的userid | * @param userId 外部联系人的userid | ||||
* @return 联系人详情 | |||||
* @throws WxErrorException . | |||||
*/ | */ | ||||
WxCpUserExternalContactInfo getExternalContact(String userId) 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.JsonElement; | ||||
import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
import lombok.extern.slf4j.Slf4j; | |||||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | import me.chanjar.weixin.common.bean.WxJsapiSignature; | ||||
import me.chanjar.weixin.common.error.WxError; | import me.chanjar.weixin.common.error.WxError; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
@@ -23,6 +24,7 @@ import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | |||||
import me.chanjar.weixin.cp.bean.WxCpMessage; | import me.chanjar.weixin.cp.bean.WxCpMessage; | ||||
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | ||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpConfigStorage; | ||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
@@ -31,22 +33,24 @@ import java.io.IOException; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.*; | |||||
/** | /** | ||||
* @author chanjarster | * @author chanjarster | ||||
*/ | */ | ||||
@Slf4j | |||||
public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestHttp<H, P> { | 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 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的锁 | * 全局的是否正在刷新access token的锁 | ||||
@@ -80,7 +84,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||||
return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data) | return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data) | ||||
.equals(msgSignature); | .equals(msgSignature); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
this.log.error("Checking signature failed, and the reason is :" + e.getMessage()); | |||||
log.error("Checking signature failed, and the reason is :" + e.getMessage()); | |||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
@@ -104,7 +108,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||||
if (this.configStorage.isAgentJsapiTicketExpired()) { | if (this.configStorage.isAgentJsapiTicketExpired()) { | ||||
synchronized (this.globalAgentJsapiTicketRefreshLock) { | synchronized (this.globalAgentJsapiTicketRefreshLock) { | ||||
if (this.configStorage.isAgentJsapiTicketExpired()) { | 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(); | JsonObject jsonObject = new JsonParser().parse(responseContent).getAsJsonObject(); | ||||
this.configStorage.updateAgentJsapiTicket(jsonObject.get("ticket").getAsString(), | this.configStorage.updateAgentJsapiTicket(jsonObject.get("ticket").getAsString(), | ||||
jsonObject.get("expires_in").getAsInt()); | jsonObject.get("expires_in").getAsInt()); | ||||
@@ -129,7 +133,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||||
if (this.configStorage.isJsapiTicketExpired()) { | if (this.configStorage.isJsapiTicketExpired()) { | ||||
synchronized (this.globalJsapiTicketRefreshLock) { | synchronized (this.globalJsapiTicketRefreshLock) { | ||||
if (this.configStorage.isJsapiTicketExpired()) { | 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(); | JsonObject tmpJsonObject = new JsonParser().parse(responseContent).getAsJsonObject(); | ||||
this.configStorage.updateJsapiTicket(tmpJsonObject.get("ticket").getAsString(), | this.configStorage.updateJsapiTicket(tmpJsonObject.get("ticket").getAsString(), | ||||
tmpJsonObject.get("expires_in").getAsInt()); | tmpJsonObject.get("expires_in").getAsInt()); | ||||
@@ -170,7 +174,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||||
message.setAgentId(this.getWxCpConfigStorage().getAgentId()); | 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 | @Override | ||||
@@ -179,13 +183,13 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||||
params.put("js_code", jsCode); | params.put("js_code", jsCode); | ||||
params.put("grant_type", "authorization_code"); | 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); | return WxCpMaJsCode2SessionResult.fromJson(result); | ||||
} | } | ||||
@Override | @Override | ||||
public String[] getCallbackIp() throws WxErrorException { | 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); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); | JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); | ||||
String[] ips = new String[jsonArray.size()]; | String[] ips = new String[jsonArray.size()]; | ||||
@@ -216,7 +220,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||||
return this.executeInternal(executor, uri, data); | return this.executeInternal(executor, uri, data); | ||||
} catch (WxErrorException e) { | } catch (WxErrorException e) { | ||||
if (retryTimes + 1 > this.maxRetryTimes) { | if (retryTimes + 1 > this.maxRetryTimes) { | ||||
this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||||
log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||||
//最后一次重试失败后,直接抛出异常,不再等待 | //最后一次重试失败后,直接抛出异常,不再等待 | ||||
throw new RuntimeException("微信服务端异常,超出重试次数"); | throw new RuntimeException("微信服务端异常,超出重试次数"); | ||||
} | } | ||||
@@ -228,7 +232,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||||
if (error.getErrorCode() == -1) { | if (error.getErrorCode() == -1) { | ||||
int sleepMillis = this.retrySleepMillis * (1 << retryTimes); | int sleepMillis = this.retrySleepMillis * (1 << retryTimes); | ||||
try { | try { | ||||
this.log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); | |||||
log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); | |||||
Thread.sleep(sleepMillis); | Thread.sleep(sleepMillis); | ||||
} catch (InterruptedException e1) { | } catch (InterruptedException e1) { | ||||
Thread.currentThread().interrupt(); | Thread.currentThread().interrupt(); | ||||
@@ -239,7 +243,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||||
} | } | ||||
} while (retryTimes++ < this.maxRetryTimes); | } while (retryTimes++ < this.maxRetryTimes); | ||||
this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||||
log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||||
throw new RuntimeException("微信服务端异常,超出重试次数"); | throw new RuntimeException("微信服务端异常,超出重试次数"); | ||||
} | } | ||||
@@ -255,7 +259,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||||
try { | try { | ||||
T result = executor.execute(uriWithAccessToken, data); | 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; | return result; | ||||
} catch (WxErrorException e) { | } catch (WxErrorException e) { | ||||
WxError error = e.getError(); | WxError error = e.getError(); | ||||
@@ -272,12 +276,12 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||||
} | } | ||||
if (error.getErrorCode() != 0) { | 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); | throw new WxErrorException(error, e); | ||||
} | } | ||||
return null; | return null; | ||||
} catch (IOException e) { | } 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); | throw new RuntimeException(e); | ||||
} | } | ||||
} | } | ||||
@@ -329,19 +333,19 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||||
public String replaceParty(String mediaId) throws WxErrorException { | public String replaceParty(String mediaId) throws WxErrorException { | ||||
JsonObject jsonObject = new JsonObject(); | JsonObject jsonObject = new JsonObject(); | ||||
jsonObject.addProperty("media_id", mediaId); | jsonObject.addProperty("media_id", mediaId); | ||||
return post(WxCpService.BATCH_REPLACE_PARTY, jsonObject.toString()); | |||||
return post(this.configStorage.getApiUrl(BATCH_REPLACE_PARTY), jsonObject.toString()); | |||||
} | } | ||||
@Override | @Override | ||||
public String replaceUser(String mediaId) throws WxErrorException { | public String replaceUser(String mediaId) throws WxErrorException { | ||||
JsonObject jsonObject = new JsonObject(); | JsonObject jsonObject = new JsonObject(); | ||||
jsonObject.addProperty("media_id", mediaId); | jsonObject.addProperty("media_id", mediaId); | ||||
return post(WxCpService.BATCH_REPLACE_USER, jsonObject.toString()); | |||||
return post(this.configStorage.getApiUrl(BATCH_REPLACE_USER), jsonObject.toString()); | |||||
} | } | ||||
@Override | @Override | ||||
public String getTaskResult(String joinId) throws WxErrorException { | 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); | return get(url, null); | ||||
} | } | ||||
@@ -383,13 +387,18 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||||
return userService; | return userService; | ||||
} | } | ||||
@Override | |||||
public WxCpExternalContactService getExternalContactService() { | |||||
return externalContactService; | |||||
} | |||||
@Override | @Override | ||||
public WxCpChatService getChatService() { | public WxCpChatService getChatService() { | ||||
return chatService; | return chatService; | ||||
} | } | ||||
@Override | @Override | ||||
public WxCpOAService getOAService() { | |||||
public WxCpOaService getOAService() { | |||||
return oaService; | 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; | package me.chanjar.weixin.cp.api.impl; | ||||
import java.util.List; | |||||
import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
import com.google.gson.reflect.TypeToken; | import com.google.gson.reflect.TypeToken; | ||||
import lombok.RequiredArgsConstructor; | |||||
import me.chanjar.weixin.common.error.WxError; | import me.chanjar.weixin.common.error.WxError; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.api.WxCpAgentService; | 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.bean.WxCpAgent; | ||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import java.util.List; | |||||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Agent.*; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -22,14 +24,11 @@ import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
* | * | ||||
* @author <a href="https://github.com/huansinho">huansinho</a> | * @author <a href="https://github.com/huansinho">huansinho</a> | ||||
*/ | */ | ||||
@RequiredArgsConstructor | |||||
public class WxCpAgentServiceImpl implements WxCpAgentService { | public class WxCpAgentServiceImpl implements WxCpAgentService { | ||||
private static final JsonParser JSON_PARSER = new JsonParser(); | private static final JsonParser JSON_PARSER = new JsonParser(); | ||||
private WxCpService mainService; | |||||
public WxCpAgentServiceImpl(WxCpService mainService) { | |||||
this.mainService = mainService; | |||||
} | |||||
private final WxCpService mainService; | |||||
@Override | @Override | ||||
public WxCpAgent get(Integer agentId) throws WxErrorException { | public WxCpAgent get(Integer agentId) throws WxErrorException { | ||||
@@ -37,14 +36,13 @@ public class WxCpAgentServiceImpl implements WxCpAgentService { | |||||
throw new IllegalArgumentException("缺少agentid参数"); | 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); | return WxCpAgent.fromJson(responseContent); | ||||
} | } | ||||
@Override | @Override | ||||
public void set(WxCpAgent agentInfo) throws WxErrorException { | 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()); | String responseContent = this.mainService.post(url, agentInfo.toJson()); | ||||
JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | ||||
if (jsonObject.get("errcode").getAsInt() != 0) { | if (jsonObject.get("errcode").getAsInt() != 0) { | ||||
@@ -54,7 +52,7 @@ public class WxCpAgentServiceImpl implements WxCpAgentService { | |||||
@Override | @Override | ||||
public List<WxCpAgent> list() throws WxErrorException { | 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); | String responseContent = this.mainService.get(url, null); | ||||
JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | ||||
if (jsonObject.get("errcode").getAsInt() != 0) { | if (jsonObject.get("errcode").getAsInt() != 0) { | ||||
@@ -1,12 +1,14 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | package me.chanjar.weixin.cp.api.impl; | ||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
import lombok.RequiredArgsConstructor; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | import me.chanjar.weixin.common.util.json.WxGsonBuilder; | ||||
import me.chanjar.weixin.cp.api.WxCpChatService; | import me.chanjar.weixin.cp.api.WxCpChatService; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; | import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; | ||||
import me.chanjar.weixin.cp.bean.WxCpChat; | import me.chanjar.weixin.cp.bean.WxCpChat; | ||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
@@ -14,24 +16,18 @@ import java.util.HashMap; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Chat.*; | |||||
/** | /** | ||||
* 群聊服务实现. | * 群聊服务实现. | ||||
* | * | ||||
* @author gaigeshen | * @author gaigeshen | ||||
*/ | */ | ||||
@RequiredArgsConstructor | |||||
public class WxCpChatServiceImpl implements WxCpChatService { | public class WxCpChatServiceImpl implements WxCpChatService { | ||||
private static final JsonParser JSON_PARSER = new JsonParser(); | private static final JsonParser JSON_PARSER = new JsonParser(); | ||||
private final WxCpService cpService; | private final WxCpService cpService; | ||||
/** | |||||
* 创建群聊服务实现的实例. | |||||
* | |||||
* @param cpService 企业微信的服务 | |||||
*/ | |||||
WxCpChatServiceImpl(WxCpService cpService) { | |||||
this.cpService = cpService; | |||||
} | |||||
@Override | @Override | ||||
public String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException { | public String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException { | ||||
Map<String, Object> data = new HashMap<>(4); | Map<String, Object> data = new HashMap<>(4); | ||||
@@ -47,7 +43,7 @@ public class WxCpChatServiceImpl implements WxCpChatService { | |||||
if (StringUtils.isNotBlank(chatId)) { | if (StringUtils.isNotBlank(chatId)) { | ||||
data.put("chatid", 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(); | return new JsonParser().parse(result).getAsJsonObject().get("chatid").getAsString(); | ||||
} | } | ||||
@@ -76,7 +72,7 @@ public class WxCpChatServiceImpl implements WxCpChatService { | |||||
data.put("del_user_list", usersToDelete); | 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 | @Override | ||||
@@ -86,7 +82,7 @@ public class WxCpChatServiceImpl implements WxCpChatService { | |||||
@Override | @Override | ||||
public WxCpChat chatGet(String chatId) throws WxErrorException { | 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() | return WxCpGsonBuilder.create() | ||||
.fromJson(JSON_PARSER.parse(result).getAsJsonObject().getAsJsonObject("chat_info").toString(), WxCpChat.class); | .fromJson(JSON_PARSER.parse(result).getAsJsonObject().getAsJsonObject("chat_info").toString(), WxCpChat.class); | ||||
} | } | ||||
@@ -98,7 +94,7 @@ public class WxCpChatServiceImpl implements WxCpChatService { | |||||
@Override | @Override | ||||
public void sendMsg(WxCpAppChatMessage message) throws WxErrorException { | 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; | package me.chanjar.weixin.cp.api.impl; | ||||
import java.util.List; | |||||
import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
import com.google.gson.reflect.TypeToken; | import com.google.gson.reflect.TypeToken; | ||||
import lombok.RequiredArgsConstructor; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.json.GsonHelper; | import me.chanjar.weixin.common.util.json.GsonHelper; | ||||
import me.chanjar.weixin.cp.api.WxCpDepartmentService; | import me.chanjar.weixin.cp.api.WxCpDepartmentService; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.bean.WxCpDepart; | import me.chanjar.weixin.cp.bean.WxCpDepart; | ||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import java.util.List; | |||||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Department.*; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 部门管理接口 | * 部门管理接口 | ||||
@@ -20,16 +24,13 @@ import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||||
* | * | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
@RequiredArgsConstructor | |||||
public class WxCpDepartmentServiceImpl implements WxCpDepartmentService { | public class WxCpDepartmentServiceImpl implements WxCpDepartmentService { | ||||
private WxCpService mainService; | |||||
public WxCpDepartmentServiceImpl(WxCpService mainService) { | |||||
this.mainService = mainService; | |||||
} | |||||
private final WxCpService mainService; | |||||
@Override | @Override | ||||
public Long create(WxCpDepart depart) throws WxErrorException { | public Long create(WxCpDepart depart) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; | |||||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_CREATE); | |||||
String responseContent = this.mainService.post(url, depart.toJson()); | String responseContent = this.mainService.post(url, depart.toJson()); | ||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("id")); | return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("id")); | ||||
@@ -37,19 +38,19 @@ public class WxCpDepartmentServiceImpl implements WxCpDepartmentService { | |||||
@Override | @Override | ||||
public void update(WxCpDepart group) throws WxErrorException { | 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()); | this.mainService.post(url, group.toJson()); | ||||
} | } | ||||
@Override | @Override | ||||
public void delete(Long departId) throws WxErrorException { | 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); | this.mainService.get(url, null); | ||||
} | } | ||||
@Override | @Override | ||||
public List<WxCpDepart> list(Long id) throws WxErrorException { | 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) { | if (id != null) { | ||||
url += "?id=" + id; | 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; | 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.bean.result.WxMediaUploadResult; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.fs.FileUtils; | 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.common.util.http.MediaUploadRequestExecutor; | ||||
import me.chanjar.weixin.cp.api.WxCpMediaService; | import me.chanjar.weixin.cp.api.WxCpMediaService; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | 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> | * <pre> | ||||
@@ -21,12 +25,9 @@ import me.chanjar.weixin.cp.api.WxCpService; | |||||
* | * | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
@RequiredArgsConstructor | |||||
public class WxCpMediaServiceImpl implements WxCpMediaService { | public class WxCpMediaServiceImpl implements WxCpMediaService { | ||||
private WxCpService mainService; | |||||
public WxCpMediaServiceImpl(WxCpService mainService) { | |||||
this.mainService = mainService; | |||||
} | |||||
private final WxCpService mainService; | |||||
@Override | @Override | ||||
public WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputStream) | public WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputStream) | ||||
@@ -37,7 +38,7 @@ public class WxCpMediaServiceImpl implements WxCpMediaService { | |||||
@Override | @Override | ||||
public WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException { | public WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException { | ||||
return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), | return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), | ||||
MEDIA_UPLOAD_URL + mediaType, file); | |||||
this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_UPLOAD + mediaType), file); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -45,7 +46,7 @@ public class WxCpMediaServiceImpl implements WxCpMediaService { | |||||
return this.mainService.execute( | return this.mainService.execute( | ||||
BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(), | BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(), | ||||
this.mainService.getWxCpConfigStorage().getTmpDirFile()), | this.mainService.getWxCpConfigStorage().getTmpDirFile()), | ||||
MEDIA_GET_URL, "media_id=" + mediaId); | |||||
this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_GET), "media_id=" + mediaId); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -53,13 +54,13 @@ public class WxCpMediaServiceImpl implements WxCpMediaService { | |||||
return this.mainService.execute( | return this.mainService.execute( | ||||
BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(), | BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(), | ||||
this.mainService.getWxCpConfigStorage().getTmpDirFile()), | this.mainService.getWxCpConfigStorage().getTmpDirFile()), | ||||
JSSDK_MEDIA_GET_URL, "media_id=" + mediaId); | |||||
this.mainService.getWxCpConfigStorage().getApiUrl(JSSDK_MEDIA_GET), "media_id=" + mediaId); | |||||
} | } | ||||
@Override | @Override | ||||
public String uploadImg(File file) throws WxErrorException { | public String uploadImg(File file) throws WxErrorException { | ||||
final WxMediaUploadResult result = this.mainService | 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(); | return result.getUrl(); | ||||
} | } | ||||
} | } |
@@ -1,24 +1,26 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | package me.chanjar.weixin.cp.api.impl; | ||||
import lombok.RequiredArgsConstructor; | |||||
import me.chanjar.weixin.common.bean.menu.WxMenu; | import me.chanjar.weixin.common.bean.menu.WxMenu; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.api.WxCpMenuService; | import me.chanjar.weixin.cp.api.WxCpMenuService; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Menu.*; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 菜单管理相关接口 | |||||
* 菜单管理相关接口. | |||||
* Created by Binary Wang on 2017-6-25. | * Created by Binary Wang on 2017-6-25. | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
* </pre> | * </pre> | ||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | */ | ||||
@RequiredArgsConstructor | |||||
public class WxCpMenuServiceImpl implements WxCpMenuService { | public class WxCpMenuServiceImpl implements WxCpMenuService { | ||||
private WxCpService mainService; | |||||
public WxCpMenuServiceImpl(WxCpService mainService) { | |||||
this.mainService = mainService; | |||||
} | |||||
private final WxCpService mainService; | |||||
@Override | @Override | ||||
public void create(WxMenu menu) throws WxErrorException { | public void create(WxMenu menu) throws WxErrorException { | ||||
@@ -27,7 +29,7 @@ public class WxCpMenuServiceImpl implements WxCpMenuService { | |||||
@Override | @Override | ||||
public void create(Integer agentId, WxMenu menu) throws WxErrorException { | 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()); | this.mainService.post(url, menu.toJson()); | ||||
} | } | ||||
@@ -38,7 +40,7 @@ public class WxCpMenuServiceImpl implements WxCpMenuService { | |||||
@Override | @Override | ||||
public void delete(Integer agentId) throws WxErrorException { | 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); | this.mainService.get(url, null); | ||||
} | } | ||||
@@ -49,7 +51,7 @@ public class WxCpMenuServiceImpl implements WxCpMenuService { | |||||
@Override | @Override | ||||
public WxMenu get(Integer agentId) throws WxErrorException { | 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 { | try { | ||||
String resultContent = this.mainService.get(url, null); | String resultContent = this.mainService.get(url, null); | ||||
return WxCpGsonBuilder.create().fromJson(resultContent, WxMenu.class); | 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.JsonElement; | ||||
import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
import lombok.AllArgsConstructor; | |||||
import lombok.RequiredArgsConstructor; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.http.URIUtil; | import me.chanjar.weixin.common.util.http.URIUtil; | ||||
import me.chanjar.weixin.common.util.json.GsonHelper; | import me.chanjar.weixin.common.util.json.GsonHelper; | ||||
import me.chanjar.weixin.cp.api.WxCpOAuth2Service; | import me.chanjar.weixin.cp.api.WxCpOAuth2Service; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | 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.bean.WxCpUserDetail; | ||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | 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> | * <pre> | ||||
* | |||||
* oauth2相关接口实现类. | |||||
* Created by Binary Wang on 2017-6-25. | * Created by Binary Wang on 2017-6-25. | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
* | |||||
* @author Binary Wang | |||||
* </pre> | * </pre> | ||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | */ | ||||
@RequiredArgsConstructor | |||||
public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { | public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { | ||||
private WxCpService mainService; | |||||
public WxCpOAuth2ServiceImpl(WxCpService mainService) { | |||||
this.mainService = mainService; | |||||
} | |||||
private final WxCpService mainService; | |||||
@Override | @Override | ||||
public String buildAuthorizationUrl(String state) { | public String buildAuthorizationUrl(String state) { | ||||
@@ -38,55 +43,54 @@ public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { | |||||
@Override | @Override | ||||
public String buildAuthorizationUrl(String redirectUri, String state) { | public String buildAuthorizationUrl(String redirectUri, String state) { | ||||
return this.buildAuthorizationUrl(redirectUri, state, WxConsts.OAuth2Scope.SNSAPI_BASE); | |||||
return this.buildAuthorizationUrl(redirectUri, state, SNSAPI_BASE); | |||||
} | } | ||||
@Override | @Override | ||||
public String buildAuthorizationUrl(String redirectUri, String state, String scope) { | 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("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectUri)); | ||||
url.append("&response_type=code"); | url.append("&response_type=code"); | ||||
url.append("&scope=").append(scope); | 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()); | url.append("&agentid=").append(this.mainService.getWxCpConfigStorage().getAgentId()); | ||||
} | } | ||||
if (state != null) { | if (state != null) { | ||||
url.append("&state=").append(state); | url.append("&state=").append(state); | ||||
} | } | ||||
url.append("#wechat_redirect"); | url.append("#wechat_redirect"); | ||||
return url.toString(); | return url.toString(); | ||||
} | } | ||||
@Override | @Override | ||||
public String[] getUserInfo(String code) throws WxErrorException { | |||||
public WxCpOauth2UserInfo getUserInfo(String code) throws WxErrorException { | |||||
return this.getUserInfo(this.mainService.getWxCpConfigStorage().getAgentId(), code); | return this.getUserInfo(this.mainService.getWxCpConfigStorage().getAgentId(), code); | ||||
} | } | ||||
@Override | @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); | JsonElement je = new JsonParser().parse(responseText); | ||||
JsonObject jo = je.getAsJsonObject(); | 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 | @Override | ||||
public WxCpUserDetail getUserDetail(String userTicket) throws WxErrorException { | public WxCpUserDetail getUserDetail(String userTicket) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserdetail"; | |||||
JsonObject param = new JsonObject(); | JsonObject param = new JsonObject(); | ||||
param.addProperty("user_ticket", userTicket); | 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); | 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.JsonObject; | ||||
import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||
import com.google.gson.reflect.TypeToken; | import com.google.gson.reflect.TypeToken; | ||||
import lombok.RequiredArgsConstructor; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | 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.api.WxCpService; | ||||
import me.chanjar.weixin.cp.bean.WxCpApprovalDataResult; | import me.chanjar.weixin.cp.bean.WxCpApprovalDataResult; | ||||
import me.chanjar.weixin.cp.bean.WxCpCheckinData; | import me.chanjar.weixin.cp.bean.WxCpCheckinData; | ||||
import me.chanjar.weixin.cp.bean.WxCpCheckinOption; | import me.chanjar.weixin.cp.bean.WxCpCheckinOption; | ||||
import me.chanjar.weixin.cp.bean.WxCpDialRecord; | import me.chanjar.weixin.cp.bean.WxCpDialRecord; | ||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.List; | import java.util.List; | ||||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Oa.*; | |||||
/** | /** | ||||
* @author Element | * @author Element | ||||
* @Package me.chanjar.weixin.cp.api.impl | |||||
* @date 2019-04-06 11:20 | * @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 | @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"); | throw new RuntimeException("starttime and endtime can't be null"); | ||||
} | } | ||||
@@ -42,15 +40,13 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||||
throw new RuntimeException("用户列表不能为空,不超过100个,若用户超过100个,请分批获取"); | 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) { | if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= 30 * 24 * 60 * 60) { | ||||
throw new RuntimeException("获取记录时间跨度不超过一个月"); | throw new RuntimeException("获取记录时间跨度不超过一个月"); | ||||
} | } | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckindata"; | |||||
JsonObject jsonObject = new JsonObject(); | JsonObject jsonObject = new JsonObject(); | ||||
JsonArray jsonArray = new JsonArray(); | JsonArray jsonArray = new JsonArray(); | ||||
@@ -64,7 +60,7 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||||
jsonObject.add("useridlist", jsonArray); | 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); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return WxCpGsonBuilder.create() | return WxCpGsonBuilder.create() | ||||
.fromJson( | .fromJson( | ||||
@@ -76,7 +72,6 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||||
@Override | @Override | ||||
public List<WxCpCheckinOption> getCheckinOption(Date datetime, List<String> userIdList) throws WxErrorException { | 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) { | if (datetime == null) { | ||||
throw new RuntimeException("datetime can't be 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.addProperty("datetime", datetime.getTime() / 1000L); | ||||
jsonObject.add("useridlist", jsonArray); | 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); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return WxCpGsonBuilder.create() | return WxCpGsonBuilder.create() | ||||
@@ -106,24 +101,20 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||||
} | } | ||||
@Override | @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 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) { | if (nextSpnum != null) { | ||||
jsonObject.addProperty("next_spnum", nextSpnum); | 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); | return WxCpGsonBuilder.create().fromJson(responseContent, WxCpApprovalDataResult.class); | ||||
} | } | ||||
@Override | @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(); | JsonObject jsonObject = new JsonObject(); | ||||
if (offset == null) { | if (offset == null) { | ||||
@@ -137,10 +128,10 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||||
jsonObject.addProperty("offset", offset); | jsonObject.addProperty("offset", offset); | ||||
jsonObject.addProperty("limit", limit); | 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) { | if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= 30 * 24 * 60 * 60) { | ||||
throw new RuntimeException("受限于网络传输,起止时间的最大跨度为30天,如超过30天,则以结束时间为基准向前取30天进行查询"); | throw new RuntimeException("受限于网络传输,起止时间的最大跨度为30天,如超过30天,则以结束时间为基准向前取30天进行查询"); | ||||
@@ -148,11 +139,9 @@ public class WxCpOAServiceImpl implements WxCpOAService { | |||||
jsonObject.addProperty("start_time", starttimestamp); | jsonObject.addProperty("start_time", starttimestamp); | ||||
jsonObject.addProperty("end_time", endtimestamp); | 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); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return WxCpGsonBuilder.create() | 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.HttpType; | ||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | ||||
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; | import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; | ||||
import me.chanjar.weixin.cp.api.WxCpOAService; | |||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpConfigStorage; | ||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import org.apache.http.HttpHost; | import org.apache.http.HttpHost; | ||||
import org.apache.http.client.config.RequestConfig; | import org.apache.http.client.config.RequestConfig; | ||||
import org.apache.http.client.methods.CloseableHttpResponse; | import org.apache.http.client.methods.CloseableHttpResponse; | ||||
@@ -19,9 +19,12 @@ import org.apache.http.impl.client.CloseableHttpClient; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
/** | |||||
* @author someone | |||||
*/ | |||||
public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<CloseableHttpClient, HttpHost> { | public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<CloseableHttpClient, HttpHost> { | ||||
protected CloseableHttpClient httpClient; | |||||
protected HttpHost httpProxy; | |||||
private CloseableHttpClient httpClient; | |||||
private HttpHost httpProxy; | |||||
@Override | @Override | ||||
public CloseableHttpClient getRequestHttpClient() { | public CloseableHttpClient getRequestHttpClient() { | ||||
@@ -45,9 +48,8 @@ public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<Closeab | |||||
} | } | ||||
synchronized (this.globalAccessTokenRefreshLock) { | 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 { | try { | ||||
HttpGet httpGet = new HttpGet(url); | HttpGet httpGet = new HttpGet(url); | ||||
if (this.httpProxy != null) { | if (this.httpProxy != null) { | ||||
@@ -56,8 +58,8 @@ public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<Closeab | |||||
httpGet.setConfig(config); | httpGet.setConfig(config); | ||||
} | } | ||||
String resultContent; | 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); | resultContent = new BasicResponseHandler().handleResponse(response); | ||||
} finally { | } finally { | ||||
httpGet.releaseConnection(); | httpGet.releaseConnection(); | ||||
@@ -1,17 +1,24 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | 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.WxType; | ||||
import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
import me.chanjar.weixin.common.error.WxError; | import me.chanjar.weixin.common.error.WxError; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.http.HttpType; | import me.chanjar.weixin.common.util.http.HttpType; | ||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | import me.chanjar.weixin.cp.config.WxCpConfigStorage; | ||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
/** | |||||
* @author someone | |||||
*/ | |||||
public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionProvider, ProxyInfo> { | public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionProvider, ProxyInfo> { | ||||
protected HttpConnectionProvider httpClient; | |||||
protected ProxyInfo httpProxy; | |||||
private HttpConnectionProvider httpClient; | |||||
private ProxyInfo httpProxy; | |||||
@Override | @Override | ||||
public HttpConnectionProvider getRequestHttpClient() { | public HttpConnectionProvider getRequestHttpClient() { | ||||
@@ -35,11 +42,8 @@ public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionP | |||||
} | } | ||||
synchronized (this.globalAccessTokenRefreshLock) { | 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) { | if (this.httpProxy != null) { | ||||
httpClient.useProxy(this.httpProxy); | httpClient.useProxy(this.httpProxy); | ||||
} | } | ||||
@@ -60,7 +64,8 @@ public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionP | |||||
@Override | @Override | ||||
public void initHttp() { | public void initHttp() { | ||||
if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { | 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; | httpClient = JoddHttp.httpConnectionProvider; | ||||
@@ -1,5 +1,6 @@ | |||||
package me.chanjar.weixin.cp.api.impl; | package me.chanjar.weixin.cp.api.impl; | ||||
import lombok.extern.slf4j.Slf4j; | |||||
import me.chanjar.weixin.common.WxType; | import me.chanjar.weixin.common.WxType; | ||||
import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
import me.chanjar.weixin.common.error.WxError; | import me.chanjar.weixin.common.error.WxError; | ||||
@@ -11,10 +12,15 @@ import okhttp3.*; | |||||
import java.io.IOException; | 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 | @Override | ||||
public OkHttpClient getRequestHttpClient() { | public OkHttpClient getRequestHttpClient() { | ||||
@@ -38,35 +44,35 @@ public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkH | |||||
} | } | ||||
synchronized (this.globalAccessTokenRefreshLock) { | 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(); | return this.configStorage.getAccessToken(); | ||||
} | } | ||||
@Override | @Override | ||||
public void initHttp() { | public void initHttp() { | ||||
this.log.debug("WxCpServiceOkHttpImpl initHttp"); | |||||
log.debug("WxCpServiceOkHttpImpl initHttp"); | |||||
//设置代理 | //设置代理 | ||||
if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { | if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { | ||||
httpProxy = OkHttpProxyInfo.httpProxy(configStorage.getHttpProxyHost(), | 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; | 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 com.google.gson.reflect.TypeToken; | ||||
import lombok.RequiredArgsConstructor; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.api.WxCpTagService; | 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.WxCpTagAddOrRemoveUsersResult; | ||||
import me.chanjar.weixin.cp.bean.WxCpTagGetResult; | import me.chanjar.weixin.cp.bean.WxCpTagGetResult; | ||||
import me.chanjar.weixin.cp.bean.WxCpUser; | import me.chanjar.weixin.cp.bean.WxCpUser; | ||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | 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> | * <pre> | ||||
* 标签管理接口 | |||||
* 标签管理接口. | |||||
* Created by Binary Wang on 2017-6-25. | * Created by Binary Wang on 2017-6-25. | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
* </pre> | * </pre> | ||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | */ | ||||
@RequiredArgsConstructor | |||||
public class WxCpTagServiceImpl implements WxCpTagService { | public class WxCpTagServiceImpl implements WxCpTagService { | ||||
private WxCpService mainService; | |||||
public WxCpTagServiceImpl(WxCpService mainService) { | |||||
this.mainService = mainService; | |||||
} | |||||
private final WxCpService mainService; | |||||
@Override | @Override | ||||
public String create(String tagName) throws WxErrorException { | public String create(String tagName) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/create"; | |||||
JsonObject o = new JsonObject(); | JsonObject o = new JsonObject(); | ||||
o.addProperty("tagname", tagName); | o.addProperty("tagname", tagName); | ||||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_CREATE); | |||||
String responseContent = this.mainService.post(url, o.toString()); | String responseContent = this.mainService.post(url, o.toString()); | ||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return tmpJsonElement.getAsJsonObject().get("tagid").getAsString(); | return tmpJsonElement.getAsJsonObject().get("tagid").getAsString(); | ||||
@@ -43,7 +43,7 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||||
@Override | @Override | ||||
public void update(String tagId, String tagName) throws WxErrorException { | 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(); | JsonObject o = new JsonObject(); | ||||
o.addProperty("tagid", tagId); | o.addProperty("tagid", tagId); | ||||
o.addProperty("tagname", tagName); | o.addProperty("tagname", tagName); | ||||
@@ -52,13 +52,13 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||||
@Override | @Override | ||||
public void delete(String tagId) throws WxErrorException { | 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); | this.mainService.get(url, null); | ||||
} | } | ||||
@Override | @Override | ||||
public List<WxCpTag> listAll() throws WxErrorException { | 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); | String responseContent = this.mainService.get(url, null); | ||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return WxCpGsonBuilder.create() | return WxCpGsonBuilder.create() | ||||
@@ -71,7 +71,7 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||||
@Override | @Override | ||||
public List<WxCpUser> listUsersByTagId(String tagId) throws WxErrorException { | 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); | String responseContent = this.mainService.get(url, null); | ||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return WxCpGsonBuilder.create() | return WxCpGsonBuilder.create() | ||||
@@ -84,7 +84,7 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||||
@Override | @Override | ||||
public WxCpTagAddOrRemoveUsersResult addUsers2Tag(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException { | 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 jsonObject = new JsonObject(); | ||||
jsonObject.addProperty("tagid", tagId); | jsonObject.addProperty("tagid", tagId); | ||||
this.addUserIdsAndPartyIdsToJson(userIds, partyIds, jsonObject); | this.addUserIdsAndPartyIdsToJson(userIds, partyIds, jsonObject); | ||||
@@ -94,7 +94,7 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||||
@Override | @Override | ||||
public WxCpTagAddOrRemoveUsersResult removeUsersFromTag(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException { | 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 jsonObject = new JsonObject(); | ||||
jsonObject.addProperty("tagid", tagId); | jsonObject.addProperty("tagid", tagId); | ||||
this.addUserIdsAndPartyIdsToJson(userIds, partyIds, jsonObject); | this.addUserIdsAndPartyIdsToJson(userIds, partyIds, jsonObject); | ||||
@@ -122,15 +122,12 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||||
@Override | @Override | ||||
public WxCpTagGetResult get(String tagId) throws WxErrorException { | 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参数"); | throw new IllegalArgumentException("缺少tagId参数"); | ||||
} | } | ||||
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(TAG_GET), tagId); | |||||
String responseContent = this.mainService.get(url, null); | String responseContent = this.mainService.get(url, null); | ||||
return WxCpTagGetResult.fromJson(responseContent); | 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.common.util.json.WxGsonBuilder; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.api.WxCpTaskCardService; | import me.chanjar.weixin.cp.api.WxCpTaskCardService; | ||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.TaskCard.*; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 任务卡片管理接口. | * 任务卡片管理接口. | ||||
@@ -33,7 +36,7 @@ public class WxCpTaskCardServiceImpl implements WxCpTaskCardService { | |||||
data.put("task_id", taskId); | data.put("task_id", taskId); | ||||
data.put("clicked_key", clickedKey); | 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)); | 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; | package me.chanjar.weixin.cp.api.impl; | ||||
import java.util.List; | |||||
import java.util.Map; | |||||
import com.google.common.collect.Maps; | 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 com.google.gson.reflect.TypeToken; | ||||
import lombok.RequiredArgsConstructor; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.api.WxCpUserService; | import me.chanjar.weixin.cp.api.WxCpUserService; | ||||
import me.chanjar.weixin.cp.bean.WxCpInviteResult; | import me.chanjar.weixin.cp.bean.WxCpInviteResult; | ||||
import me.chanjar.weixin.cp.bean.WxCpUser; | import me.chanjar.weixin.cp.bean.WxCpUser; | ||||
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | ||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | 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> | * <pre> | ||||
* Created by BinaryWang on 2017/6/24. | * 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> | * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
*/ | */ | ||||
@RequiredArgsConstructor | |||||
public class WxCpUserServiceImpl implements WxCpUserService { | public class WxCpUserServiceImpl implements WxCpUserService { | ||||
private WxCpService mainService; | |||||
public WxCpUserServiceImpl(WxCpService mainService) { | |||||
this.mainService = mainService; | |||||
} | |||||
private final WxCpService mainService; | |||||
@Override | @Override | ||||
public void authenticate(String userId) throws WxErrorException { | 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 | @Override | ||||
public void create(WxCpUser user) throws WxErrorException { | 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()); | this.mainService.post(url, user.toJson()); | ||||
} | } | ||||
@Override | @Override | ||||
public void update(WxCpUser user) throws WxErrorException { | 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()); | this.mainService.post(url, user.toJson()); | ||||
} | } | ||||
@Override | @Override | ||||
public void delete(String... userIds) throws WxErrorException { | public void delete(String... userIds) throws WxErrorException { | ||||
if (userIds.length == 1) { | 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); | this.mainService.get(url, null); | ||||
return; | return; | ||||
} | } | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete"; | |||||
JsonObject jsonObject = new JsonObject(); | JsonObject jsonObject = new JsonObject(); | ||||
JsonArray jsonArray = new JsonArray(); | 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); | jsonObject.add("useridlist", jsonArray); | ||||
this.mainService.post(url, jsonObject.toString()); | |||||
this.mainService.post(USER_BATCH_DELETE, jsonObject.toString()); | |||||
} | } | ||||
@Override | @Override | ||||
public WxCpUser getById(String userid) throws WxErrorException { | 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); | String responseContent = this.mainService.get(url, null); | ||||
return WxCpUser.fromJson(responseContent); | return WxCpUser.fromJson(responseContent); | ||||
} | } | ||||
@Override | @Override | ||||
public List<WxCpUser> listByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException { | 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 = ""; | String params = ""; | ||||
if (fetchChild != null) { | if (fetchChild != null) { | ||||
params += "&fetch_child=" + (fetchChild ? "1" : "0"); | params += "&fetch_child=" + (fetchChild ? "1" : "0"); | ||||
@@ -88,6 +83,7 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||||
params += "&status=0"; | params += "&status=0"; | ||||
} | } | ||||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_LIST + departId); | |||||
String responseContent = this.mainService.get(url, params); | String responseContent = this.mainService.get(url, params); | ||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return WxCpGsonBuilder.create() | return WxCpGsonBuilder.create() | ||||
@@ -99,7 +95,6 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||||
@Override | @Override | ||||
public List<WxCpUser> listSimpleByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException { | 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 = ""; | String params = ""; | ||||
if (fetchChild != null) { | if (fetchChild != null) { | ||||
params += "&fetch_child=" + (fetchChild ? "1" : "0"); | params += "&fetch_child=" + (fetchChild ? "1" : "0"); | ||||
@@ -110,6 +105,7 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||||
params += "&status=0"; | params += "&status=0"; | ||||
} | } | ||||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_SIMPLE_LIST + departId); | |||||
String responseContent = this.mainService.get(url, params); | String responseContent = this.mainService.get(url, params); | ||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return WxCpGsonBuilder.create() | return WxCpGsonBuilder.create() | ||||
@@ -122,7 +118,6 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||||
@Override | @Override | ||||
public WxCpInviteResult invite(List<String> userIds, List<String> partyIds, List<String> tagIds) throws WxErrorException { | 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(); | JsonObject jsonObject = new JsonObject(); | ||||
if (userIds != null) { | if (userIds != null) { | ||||
JsonArray jsonArray = new JsonArray(); | JsonArray jsonArray = new JsonArray(); | ||||
@@ -148,12 +143,13 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||||
jsonObject.add("tag", jsonArray); | jsonObject.add("tag", jsonArray); | ||||
} | } | ||||
String url = this.mainService.getWxCpConfigStorage().getApiUrl(BATCH_INVITE); | |||||
return WxCpInviteResult.fromJson(this.mainService.post(url, jsonObject.toString())); | return WxCpInviteResult.fromJson(this.mainService.post(url, jsonObject.toString())); | ||||
} | } | ||||
@Override | @Override | ||||
public Map<String, String> userId2Openid(String userId, Integer agentId) throws WxErrorException { | 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 jsonObject = new JsonObject(); | ||||
jsonObject.addProperty("userid", userId); | jsonObject.addProperty("userid", userId); | ||||
if (agentId != null) { | if (agentId != null) { | ||||
@@ -176,9 +172,19 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||||
@Override | @Override | ||||
public String openid2UserId(String openid) throws WxErrorException { | 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 jsonObject = new JsonObject(); | ||||
jsonObject.addProperty("openid", openid); | 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()); | String responseContent = this.mainService.post(url, jsonObject.toString()); | ||||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | ||||
return tmpJsonElement.getAsJsonObject().get("userid").getAsString(); | return tmpJsonElement.getAsJsonObject().get("userid").getAsString(); | ||||
@@ -186,7 +192,7 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||||
@Override | @Override | ||||
public WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException { | 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); | String responseContent = this.mainService.get(url, null); | ||||
return WxCpUserExternalContactInfo.fromJson(responseContent); | return WxCpUserExternalContactInfo.fromJson(responseContent); | ||||
} | } | ||||
@@ -6,7 +6,7 @@ import lombok.AllArgsConstructor; | |||||
import lombok.Builder; | import lombok.Builder; | ||||
import lombok.Data; | import lombok.Data; | ||||
import lombok.NoArgsConstructor; | 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.MpnewsArticle; | ||||
import me.chanjar.weixin.cp.bean.article.NewArticle; | 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.article.NewArticle; | ||||
import me.chanjar.weixin.cp.bean.messagebuilder.*; | import me.chanjar.weixin.cp.bean.messagebuilder.*; | ||||
import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; | import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; | ||||
import me.chanjar.weixin.cp.constant.WxCpConsts; | |||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
import java.io.Serializable; | import java.io.Serializable; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | 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 String btnTxt; | ||||
private List<NewArticle> articles = new ArrayList<>(); | private List<NewArticle> articles = new ArrayList<>(); | ||||
private List<MpnewsArticle> mpnewsArticles = new ArrayList<>(); | private List<MpnewsArticle> mpnewsArticles = new ArrayList<>(); | ||||
private String appId; | |||||
private String page; | |||||
private Boolean emphasisFirstItem; | |||||
private Map<String, String> contentItems; | |||||
/** | /** | ||||
* 任务卡片特有的属性 | |||||
* 任务卡片特有的属性. | |||||
*/ | */ | ||||
private String taskId; | private String taskId; | ||||
private List<TaskCardButton> taskButtons = new ArrayList<>(); | private List<TaskCardButton> taskButtons = new ArrayList<>(); | ||||
@@ -117,10 +125,16 @@ public class WxCpMessage implements Serializable { | |||||
return new TaskCardBuilder(); | return new TaskCardBuilder(); | ||||
} | } | ||||
/** | |||||
* 获得小程序通知消息builder. | |||||
*/ | |||||
public static MiniProgramNoticeMsgBuilder newMiniProgramNoticeBuilder() { | |||||
return new MiniProgramNoticeMsgBuilder(); | |||||
} | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 请使用 | |||||
* 请使用. | |||||
* {@link KefuMsgType#TEXT} | * {@link KefuMsgType#TEXT} | ||||
* {@link KefuMsgType#IMAGE} | * {@link KefuMsgType#IMAGE} | ||||
* {@link KefuMsgType#VOICE} | * {@link KefuMsgType#VOICE} | ||||
@@ -130,6 +144,7 @@ public class WxCpMessage implements Serializable { | |||||
* {@link KefuMsgType#MPNEWS} | * {@link KefuMsgType#MPNEWS} | ||||
* {@link KefuMsgType#MARKDOWN} | * {@link KefuMsgType#MARKDOWN} | ||||
* {@link KefuMsgType#TASKCARD} | * {@link KefuMsgType#TASKCARD} | ||||
* {@link KefuMsgType#MINIPROGRAM_NOTICE} | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param msgType 消息类型 | * @param msgType 消息类型 | ||||
@@ -169,19 +184,19 @@ public class WxCpMessage implements Serializable { | |||||
private void handleMsgType(JsonObject messageJson) { | private void handleMsgType(JsonObject messageJson) { | ||||
switch (this.getMsgType()) { | switch (this.getMsgType()) { | ||||
case KefuMsgType.TEXT: { | |||||
case TEXT: { | |||||
JsonObject text = new JsonObject(); | JsonObject text = new JsonObject(); | ||||
text.addProperty("content", this.getContent()); | text.addProperty("content", this.getContent()); | ||||
messageJson.add("text", text); | messageJson.add("text", text); | ||||
break; | break; | ||||
} | } | ||||
case KefuMsgType.MARKDOWN: { | |||||
case MARKDOWN: { | |||||
JsonObject text = new JsonObject(); | JsonObject text = new JsonObject(); | ||||
text.addProperty("content", this.getContent()); | text.addProperty("content", this.getContent()); | ||||
messageJson.add("markdown", text); | messageJson.add("markdown", text); | ||||
break; | break; | ||||
} | } | ||||
case KefuMsgType.TEXTCARD: { | |||||
case TEXTCARD: { | |||||
JsonObject text = new JsonObject(); | JsonObject text = new JsonObject(); | ||||
text.addProperty("title", this.getTitle()); | text.addProperty("title", this.getTitle()); | ||||
text.addProperty("description", this.getDescription()); | text.addProperty("description", this.getDescription()); | ||||
@@ -190,25 +205,25 @@ public class WxCpMessage implements Serializable { | |||||
messageJson.add("textcard", text); | messageJson.add("textcard", text); | ||||
break; | break; | ||||
} | } | ||||
case KefuMsgType.IMAGE: { | |||||
case IMAGE: { | |||||
JsonObject image = new JsonObject(); | JsonObject image = new JsonObject(); | ||||
image.addProperty("media_id", this.getMediaId()); | image.addProperty("media_id", this.getMediaId()); | ||||
messageJson.add("image", image); | messageJson.add("image", image); | ||||
break; | break; | ||||
} | } | ||||
case KefuMsgType.FILE: { | |||||
case FILE: { | |||||
JsonObject image = new JsonObject(); | JsonObject image = new JsonObject(); | ||||
image.addProperty("media_id", this.getMediaId()); | image.addProperty("media_id", this.getMediaId()); | ||||
messageJson.add("file", image); | messageJson.add("file", image); | ||||
break; | break; | ||||
} | } | ||||
case KefuMsgType.VOICE: { | |||||
case VOICE: { | |||||
JsonObject voice = new JsonObject(); | JsonObject voice = new JsonObject(); | ||||
voice.addProperty("media_id", this.getMediaId()); | voice.addProperty("media_id", this.getMediaId()); | ||||
messageJson.add("voice", voice); | messageJson.add("voice", voice); | ||||
break; | break; | ||||
} | } | ||||
case KefuMsgType.VIDEO: { | |||||
case VIDEO: { | |||||
JsonObject video = new JsonObject(); | JsonObject video = new JsonObject(); | ||||
video.addProperty("media_id", this.getMediaId()); | video.addProperty("media_id", this.getMediaId()); | ||||
video.addProperty("thumb_media_id", this.getThumbMediaId()); | video.addProperty("thumb_media_id", this.getThumbMediaId()); | ||||
@@ -217,7 +232,7 @@ public class WxCpMessage implements Serializable { | |||||
messageJson.add("video", video); | messageJson.add("video", video); | ||||
break; | break; | ||||
} | } | ||||
case KefuMsgType.NEWS: { | |||||
case NEWS: { | |||||
JsonObject newsJsonObject = new JsonObject(); | JsonObject newsJsonObject = new JsonObject(); | ||||
JsonArray articleJsonArray = new JsonArray(); | JsonArray articleJsonArray = new JsonArray(); | ||||
for (NewArticle article : this.getArticles()) { | for (NewArticle article : this.getArticles()) { | ||||
@@ -232,7 +247,7 @@ public class WxCpMessage implements Serializable { | |||||
messageJson.add("news", newsJsonObject); | messageJson.add("news", newsJsonObject); | ||||
break; | break; | ||||
} | } | ||||
case KefuMsgType.MPNEWS: { | |||||
case MPNEWS: { | |||||
JsonObject newsJsonObject = new JsonObject(); | JsonObject newsJsonObject = new JsonObject(); | ||||
if (this.getMediaId() != null) { | if (this.getMediaId() != null) { | ||||
newsJsonObject.addProperty("media_id", this.getMediaId()); | newsJsonObject.addProperty("media_id", this.getMediaId()); | ||||
@@ -255,7 +270,7 @@ public class WxCpMessage implements Serializable { | |||||
messageJson.add("mpnews", newsJsonObject); | messageJson.add("mpnews", newsJsonObject); | ||||
break; | break; | ||||
} | } | ||||
case KefuMsgType.TASKCARD: { | |||||
case TASKCARD: { | |||||
JsonObject text = new JsonObject(); | JsonObject text = new JsonObject(); | ||||
text.addProperty("title", this.getTitle()); | text.addProperty("title", this.getTitle()); | ||||
text.addProperty("description", this.getDescription()); | text.addProperty("description", this.getDescription()); | ||||
@@ -291,6 +306,25 @@ public class WxCpMessage implements Serializable { | |||||
messageJson.add("taskcard", text); | messageJson.add("taskcard", text); | ||||
break; | 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: { | default: { | ||||
// do nothing | // 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; | private String errmsg; | ||||
/** | /** | ||||
* 用户列表 | |||||
* 用户列表. | |||||
*/ | */ | ||||
@SerializedName("userlist") | @SerializedName("userlist") | ||||
private List<WxCpUser> userlist; | private List<WxCpUser> userlist; | ||||
/** | /** | ||||
* 部门列表 | |||||
* 部门列表. | |||||
*/ | */ | ||||
@SerializedName("partylist") | @SerializedName("partylist") | ||||
private List<Integer> partylist; | private List<Integer> partylist; | ||||
/** | /** | ||||
* 标签名称 | |||||
* 标签名称. | |||||
*/ | */ | ||||
@SerializedName("tagname") | @SerializedName("tagname") | ||||
private String 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 status; | ||||
private Integer enable; | private Integer enable; | ||||
private Integer isLeader; | private Integer isLeader; | ||||
/** | |||||
* is_leader_in_dept. | |||||
* 个数必须和department一致,表示在所在的部门内是否为上级。1表示为上级,0表示非上级。在审批等应用里可以用来标识上级审批人 | |||||
*/ | |||||
private Integer[] isLeaderInDept; | |||||
private final List<Attr> extAttrs = new ArrayList<>(); | private final List<Attr> extAttrs = new ArrayList<>(); | ||||
private Integer hideMobile; | private Integer hideMobile; | ||||
private String englishName; | private String englishName; | ||||
@@ -1,16 +1,11 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import java.util.List; | |||||
import com.google.gson.annotations.SerializedName; | 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 me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import java.util.List; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 外部联系人详情 | * 外部联系人详情 | ||||
@@ -118,9 +113,26 @@ public class WxCpUserExternalContactInfo { | |||||
private String description; | private String description; | ||||
@SerializedName("createtime") | @SerializedName("createtime") | ||||
private Long 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) { | public static WxCpUserExternalContactInfo fromJson(String json) { | ||||
return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalContactInfo.class); | 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") | @XStreamAlias("ChangeType") | ||||
@XStreamConverter(value = XStreamCDataConverter.class) | @XStreamConverter(value = XStreamCDataConverter.class) | ||||
@@ -175,6 +175,26 @@ public class WxCpXmlMessage implements Serializable { | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | @XStreamConverter(value = XStreamCDataConverter.class) | ||||
private String userId; | 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由系统生成时可更改一次). | * 新的UserID,变更时推送(userid由系统生成时可更改一次). | ||||
*/ | */ | ||||
@@ -191,11 +211,11 @@ public class WxCpXmlMessage implements Serializable { | |||||
private String name; | private String name; | ||||
/** | /** | ||||
* 成员部门列表. | |||||
* 成员部门列表,变更时推送,仅返回该应用有查看权限的部门id. | |||||
*/ | */ | ||||
@XStreamAlias("Department") | @XStreamAlias("Department") | ||||
@XStreamConverter(value = XStreamCDataConverter.class) | @XStreamConverter(value = XStreamCDataConverter.class) | ||||
private String department; | |||||
private Long[] departments; | |||||
/** | /** | ||||
* 手机号码. | * 手机号码. | ||||
@@ -244,6 +264,12 @@ public class WxCpXmlMessage implements Serializable { | |||||
@XStreamAlias("IsLeader") | @XStreamAlias("IsLeader") | ||||
private Integer isLeader; | private Integer isLeader; | ||||
/** | |||||
* 表示所在部门是否为上级,0-否,1-是,顺序与Department字段的部门逐一对应. | |||||
*/ | |||||
@XStreamAlias("IsLeaderInDept") | |||||
private Integer[] isLeaderInDept; | |||||
/** | /** | ||||
* 座机. | * 座机. | ||||
*/ | */ | ||||
@@ -268,7 +294,7 @@ public class WxCpXmlMessage implements Serializable { | |||||
* 部门Id. | * 部门Id. | ||||
*/ | */ | ||||
@XStreamAlias("Id") | @XStreamAlias("Id") | ||||
private Integer id; | |||||
private Long id; | |||||
/** | /** | ||||
* 父部门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; | import java.io.File; | ||||
/** | /** | ||||
* 微信客户端配置存储 | |||||
* 微信客户端配置存储. | |||||
* | * | ||||
* @author Daniel Qian | * @author Daniel Qian | ||||
*/ | */ | ||||
public interface WxCpConfigStorage { | 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(); | String getAccessToken(); | ||||
boolean isAccessTokenExpired(); | boolean isAccessTokenExpired(); | ||||
/** | /** | ||||
* 强制将access token过期掉 | |||||
* 强制将access token过期掉. | |||||
*/ | */ | ||||
void expireAccessToken(); | void expireAccessToken(); | ||||
@@ -30,12 +44,12 @@ public interface WxCpConfigStorage { | |||||
boolean isJsapiTicketExpired(); | boolean isJsapiTicketExpired(); | ||||
/** | /** | ||||
* 强制将jsapi ticket过期掉 | |||||
* 强制将jsapi ticket过期掉. | |||||
*/ | */ | ||||
void expireJsapiTicket(); | void expireJsapiTicket(); | ||||
/** | /** | ||||
* 应该是线程安全的 | |||||
* 应该是线程安全的. | |||||
*/ | */ | ||||
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | ||||
@@ -44,12 +58,12 @@ public interface WxCpConfigStorage { | |||||
boolean isAgentJsapiTicketExpired(); | boolean isAgentJsapiTicketExpired(); | ||||
/** | /** | ||||
* 强制将jsapi ticket过期掉 | |||||
* 强制将jsapi ticket过期掉. | |||||
*/ | */ | ||||
void expireAgentJsapiTicket(); | void expireAgentJsapiTicket(); | ||||
/** | /** | ||||
* 应该是线程安全的 | |||||
* 应该是线程安全的. | |||||
*/ | */ | ||||
void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds); | void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds); | ||||
@@ -78,7 +92,7 @@ public interface WxCpConfigStorage { | |||||
File getTmpDirFile(); | File getTmpDirFile(); | ||||
/** | /** | ||||
* http client builder | |||||
* http client builder. | |||||
* | * | ||||
* @return ApacheHttpClientBuilder | * @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.bean.WxAccessToken; | ||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | ||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.Serializable; | |||||
/** | /** | ||||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | |||||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化. | |||||
* | * | ||||
* @author Daniel Qian | * @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 accessToken; | ||||
protected volatile String aesKey; | |||||
private volatile String aesKey; | |||||
protected volatile Integer agentId; | 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 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 | @Override | ||||
public String getAccessToken() { | public String getAccessToken() { | ||||
return this.accessToken; | 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.bean.WxAccessToken; | ||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | ||||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import redis.clients.jedis.Jedis; | import redis.clients.jedis.Jedis; | ||||
import redis.clients.jedis.JedisPool; | import redis.clients.jedis.JedisPool; | ||||
import redis.clients.jedis.JedisPoolConfig; | import redis.clients.jedis.JedisPoolConfig; | ||||
@@ -16,7 +18,7 @@ import java.io.File; | |||||
* | * | ||||
* @author gaigeshen | * @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_KEY = "WX_CP_ACCESS_TOKEN"; | ||||
private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME"; | private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME"; | ||||
private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET"; | private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET"; | ||||
@@ -38,23 +40,38 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||||
private volatile File tmpDirFile; | private volatile File tmpDirFile; | ||||
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | 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; | this.jedisPool = jedisPool; | ||||
} | } | ||||
public WxCpJedisConfigStorage(String host, int port) { | |||||
public WxCpRedisConfigImpl(String host, int port) { | |||||
jedisPool = new JedisPool(host, 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); | 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); | 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); | 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> | * <pre> | ||||
@@ -89,6 +89,26 @@ public class WxCpConsts { | |||||
*/ | */ | ||||
public static final String TASKCARD_CLICK = "taskcard_click"; | 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.setStatus(GsonHelper.getInteger(o, "status")); | ||||
user.setEnable(GsonHelper.getInteger(o, "enable")); | user.setEnable(GsonHelper.getInteger(o, "enable")); | ||||
user.setIsLeader(GsonHelper.getInteger(o, "isleader")); | user.setIsLeader(GsonHelper.getInteger(o, "isleader")); | ||||
user.setIsLeaderInDept(GsonHelper.getIntArray(o, "is_leader_in_dept")); | |||||
user.setHideMobile(GsonHelper.getInteger(o, "hide_mobile")); | user.setHideMobile(GsonHelper.getInteger(o, "hide_mobile")); | ||||
user.setEnglishName(GsonHelper.getString(o, "english_name")); | user.setEnglishName(GsonHelper.getString(o, "english_name")); | ||||
user.setTelephone(GsonHelper.getString(o, "telephone")); | user.setTelephone(GsonHelper.getString(o, "telephone")); | ||||
@@ -197,6 +198,13 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri | |||||
if (user.getIsLeader() != null) { | if (user.getIsLeader() != null) { | ||||
o.addProperty("isleader", user.getIsLeader()); | 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) { | if (user.getHideMobile() != null) { | ||||
o.addProperty("hide_mobile", user.getHideMobile()); | 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 com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | import me.chanjar.weixin.common.util.xml.XStreamInitializer; | ||||
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; | 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 { | public class ApiTestModule implements Module { | ||||
private final Logger log = LoggerFactory.getLogger(this.getClass()); | private final Logger log = LoggerFactory.getLogger(this.getClass()); | ||||
@@ -44,7 +44,7 @@ public class ApiTestModule implements Module { | |||||
} | } | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public static class WxXmlCpInMemoryConfigStorage extends WxCpInMemoryConfigStorage { | |||||
public static class WxXmlCpInMemoryConfigStorage extends WxCpDefaultConfigImpl { | |||||
protected String userId; | protected String userId; | ||||
@@ -52,6 +52,8 @@ public class ApiTestModule implements Module { | |||||
protected String tagId; | protected String tagId; | ||||
protected String externalUserId; | |||||
public String getUserId() { | public String getUserId() { | ||||
return this.userId; | return this.userId; | ||||
} | } | ||||
@@ -76,12 +78,22 @@ public class ApiTestModule implements Module { | |||||
this.tagId = tagId; | this.tagId = tagId; | ||||
} | } | ||||
public String getExternalUserId() { | |||||
return externalUserId; | |||||
} | |||||
public void setExternalUserId(String externalUserId) { | |||||
this.externalUserId = externalUserId; | |||||
} | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return super.toString() + " > WxXmlCpConfigStorage{" + | return super.toString() + " > WxXmlCpConfigStorage{" + | ||||
"userId='" + this.userId + '\'' + | "userId='" + this.userId + '\'' + | ||||
", departmentId='" + this.departmentId + '\'' + | ", departmentId='" + this.departmentId + '\'' + | ||||
", tagId='" + this.tagId + '\'' + | ", tagId='" + this.tagId + '\'' + | ||||
", externalUserId='" + this.externalUserId + '\'' + | |||||
'}'; | '}'; | ||||
} | } | ||||
} | } | ||||
@@ -1,5 +1,6 @@ | |||||
package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
import lombok.extern.slf4j.Slf4j; | |||||
import me.chanjar.weixin.common.error.WxError; | import me.chanjar.weixin.common.error.WxError; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.util.http.RequestExecutor; | import me.chanjar.weixin.common.util.http.RequestExecutor; | ||||
@@ -13,8 +14,8 @@ import java.util.concurrent.Executors; | |||||
import java.util.concurrent.Future; | import java.util.concurrent.Future; | ||||
@Test | @Test | ||||
@Slf4j | |||||
public class WxCpBusyRetryTest { | public class WxCpBusyRetryTest { | ||||
@DataProvider(name = "getService") | @DataProvider(name = "getService") | ||||
public Object[][] getService() { | public Object[][] getService() { | ||||
WxCpService service = new WxCpServiceImpl() { | WxCpService service = new WxCpServiceImpl() { | ||||
@@ -23,7 +24,7 @@ public class WxCpBusyRetryTest { | |||||
public synchronized <T, E> T executeInternal( | public synchronized <T, E> T executeInternal( | ||||
RequestExecutor<T, E> executor, String uri, E data) | RequestExecutor<T, E> executor, String uri, E data) | ||||
throws WxErrorException { | throws WxErrorException { | ||||
this.log.info("Executed"); | |||||
log.info("Executed"); | |||||
throw new WxErrorException(WxError.builder().errorCode(-1).build()); | throw new WxErrorException(WxError.builder().errorCode(-1).build()); | ||||
} | } | ||||
}; | }; | ||||
@@ -1,5 +1,6 @@ | |||||
package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
import com.google.common.collect.ImmutableMap; | |||||
import org.testng.annotations.*; | import org.testng.annotations.*; | ||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
@@ -107,4 +108,27 @@ public class WxCpMessageAPITest { | |||||
System.out.println(messageSendResult.getInvalidUserList()); | System.out.println(messageSendResult.getInvalidUserList()); | ||||
System.out.println(messageSendResult.getInvalidTagList()); | System.out.println(messageSendResult.getInvalidTagList()); | ||||
} | } | ||||
@Test | |||||
public void testSendMessage_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.WxCpAgentService; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.bean.WxCpAgent; | 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.Guice; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
@@ -71,7 +71,7 @@ public class WxCpAgentServiceImplTest { | |||||
@Test | @Test | ||||
public void testGet() throws Exception { | 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\": \"\"}"; | 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)); | when(wxService.getAgentService()).thenReturn(new WxCpAgentServiceImpl(wxService)); | ||||
WxCpAgentService wxAgentService = this.wxService.getAgentService(); | WxCpAgentService wxAgentService = this.wxService.getAgentService(); | ||||
@@ -8,7 +8,7 @@ import org.testng.annotations.*; | |||||
import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||
import com.google.inject.Inject; | import com.google.inject.Inject; | ||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.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.ApiTestModule; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; | import me.chanjar.weixin.cp.bean.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.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.api.ApiTestModule; | import me.chanjar.weixin.cp.api.ApiTestModule; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo; | |||||
import me.chanjar.weixin.cp.bean.WxCpUserDetail; | import me.chanjar.weixin.cp.bean.WxCpUserDetail; | ||||
import org.testng.annotations.Guice; | import org.testng.annotations.Guice; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* Created by BinaryWang on 2018/4/22. | * Created by BinaryWang on 2018/4/22. | ||||
@@ -28,6 +31,13 @@ public class WxCpOAuth2ServiceImplTest { | |||||
@Test | @Test | ||||
public void testGetUserInfo() throws WxErrorException { | 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 | * @date 2019-04-20 13:46 | ||||
*/ | */ | ||||
@Guice(modules = ApiTestModule.class) | @Guice(modules = ApiTestModule.class) | ||||
public class WxCpOAServiceImplTest { | |||||
public class WxCpOaServiceImplTest { | |||||
@Inject | @Inject | ||||
protected WxCpService wxService; | 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.WxCpTagAddOrRemoveUsersResult; | ||||
import me.chanjar.weixin.cp.bean.WxCpTagGetResult; | import me.chanjar.weixin.cp.bean.WxCpTagGetResult; | ||||
import me.chanjar.weixin.cp.bean.WxCpUser; | import me.chanjar.weixin.cp.bean.WxCpUser; | ||||
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; | |||||
import org.testng.annotations.Guice; | import org.testng.annotations.Guice; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
@@ -22,10 +23,10 @@ import static org.testng.Assert.assertNotEquals; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* | |||||
* Created by Binary Wang on 2017-6-25. | * Created by Binary Wang on 2017-6-25. | ||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
* </pre> | * </pre> | ||||
* | |||||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
*/ | */ | ||||
@Guice(modules = ApiTestModule.class) | @Guice(modules = ApiTestModule.class) | ||||
public class WxCpTagServiceImplTest { | public class WxCpTagServiceImplTest { | ||||
@@ -35,7 +36,7 @@ public class WxCpTagServiceImplTest { | |||||
@Inject | @Inject | ||||
protected ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage; | protected ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage; | ||||
protected String tagId; | |||||
private String tagId; | |||||
@Test | @Test | ||||
public void testCreate() throws Exception { | public void testCreate() throws Exception { | ||||
@@ -83,7 +84,7 @@ public class WxCpTagServiceImplTest { | |||||
public void testGet() throws WxErrorException { | public void testGet() throws WxErrorException { | ||||
String apiResultJson = "{\"errcode\": 0,\"errmsg\": \"ok\",\"userlist\": [{\"userid\": \"0124035\",\"name\": \"王五\"},{\"userid\": \"0114035\",\"name\": \"梦雪\"}],\"partylist\": [9576,9567,9566],\"tagname\": \"测试标签-001\"}"; | String apiResultJson = "{\"errcode\": 0,\"errmsg\": \"ok\",\"userlist\": [{\"userid\": \"0124035\",\"name\": \"王五\"},{\"userid\": \"0114035\",\"name\": \"梦雪\"}],\"partylist\": [9576,9567,9566],\"tagname\": \"测试标签-001\"}"; | ||||
WxCpService wxService = mock(WxCpService.class); | 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)); | when(wxService.getTagService()).thenReturn(new WxCpTagServiceImpl(wxService)); | ||||
WxCpTagService wxCpTagService = wxService.getTagService(); | WxCpTagService wxCpTagService = wxService.getTagService(); | ||||
@@ -96,7 +97,6 @@ public class WxCpTagServiceImplTest { | |||||
assertEquals(3, wxCpTagGetResult.getPartylist().size()); | assertEquals(3, wxCpTagGetResult.getPartylist().size()); | ||||
assertEquals("测试标签-001", wxCpTagGetResult.getTagname()); | assertEquals("测试标签-001", wxCpTagGetResult.getTagname()); | ||||
} | } | ||||
} | } |
@@ -93,7 +93,7 @@ public class WxCpUserServiceImplTest { | |||||
@Test | @Test | ||||
public void testInvite() throws Exception { | public void testInvite() throws Exception { | ||||
WxCpInviteResult result = this.wxCpService.getUserService().invite( | WxCpInviteResult result = this.wxCpService.getUserService().invite( | ||||
Lists.newArrayList(userId), null,null); | |||||
Lists.newArrayList(userId), null, null); | |||||
System.out.println(result); | System.out.println(result); | ||||
} | } | ||||
@@ -111,10 +111,15 @@ public class WxCpUserServiceImplTest { | |||||
assertNotNull(result); | assertNotNull(result); | ||||
} | } | ||||
@Test | @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); | System.out.println(result); | ||||
assertNotNull(result); | assertNotNull(result); | ||||
} | } | ||||
@Test | |||||
public void testGetExternalContact() { | |||||
} | |||||
} | } |
@@ -1,9 +1,10 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.cp.constant.WxCpConsts; | |||||
import org.testng.annotations.Test; | 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.assertEquals; | ||||
import static org.testng.Assert.assertNotNull; | import static org.testng.Assert.assertNotNull; | ||||
@@ -106,7 +107,7 @@ public class WxCpXmlMessageTest { | |||||
"<Count>2</Count>" + | "<Count>2</Count>" + | ||||
"</SendPicsInfo>" + | "</SendPicsInfo>" + | ||||
"</xml>"; | "</xml>"; | ||||
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml.replace("</PicList><PicList>","")); | |||||
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml.replace("</PicList><PicList>", "")); | |||||
assertEquals(wxMessage.getToUserName(), "wx45a0972125658be9"); | assertEquals(wxMessage.getToUserName(), "wx45a0972125658be9"); | ||||
assertEquals(wxMessage.getFromUserName(), "xiaohe"); | assertEquals(wxMessage.getFromUserName(), "xiaohe"); | ||||
assertEquals(wxMessage.getCreateTime(), new Long(1502012364L)); | assertEquals(wxMessage.getCreateTime(), new Long(1502012364L)); | ||||
@@ -173,4 +174,52 @@ public class WxCpXmlMessageTest { | |||||
assertEquals(wxMessage.getEventKey(), "key111"); | assertEquals(wxMessage.getEventKey(), "key111"); | ||||
assertEquals(wxMessage.getTaskId(), "taskid111"); | 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 com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import lombok.ToString; | import lombok.ToString; | ||||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | import me.chanjar.weixin.common.util.xml.XStreamInitializer; | ||||
import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage; | |||||
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; | |||||
/** | /** | ||||
* @author Daniel Qian | * @author Daniel Qian | ||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
@ToString | @ToString | ||||
public class WxCpDemoInMemoryConfigStorage extends WxCpInMemoryConfigStorage { | |||||
public class WxCpDemoInMemoryConfigStorage extends WxCpDefaultConfigImpl { | |||||
public static WxCpDemoInMemoryConfigStorage fromXml(InputStream is) { | public static WxCpDemoInMemoryConfigStorage fromXml(InputStream is) { | ||||
XStream xstream = XStreamInitializer.getInstance(); | XStream xstream = XStreamInitializer.getInstance(); | ||||
xstream.processAnnotations(WxCpDemoInMemoryConfigStorage.class); | xstream.processAnnotations(WxCpDemoInMemoryConfigStorage.class); | ||||
@@ -10,7 +10,7 @@ import org.eclipse.jetty.servlet.ServletHolder; | |||||
import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||
import me.chanjar.weixin.common.session.WxSessionManager; | 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.WxCpService; | ||||
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; | import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; | ||||
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | 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.common.error.WxErrorException; | ||||
import me.chanjar.weixin.cp.api.WxCpService; | import me.chanjar.weixin.cp.api.WxCpService; | ||||
import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo; | |||||
import javax.servlet.http.HttpServlet; | import javax.servlet.http.HttpServlet; | ||||
import javax.servlet.http.HttpServletRequest; | import javax.servlet.http.HttpServletRequest; | ||||
@@ -30,9 +31,9 @@ public class WxCpOAuth2Servlet extends HttpServlet { | |||||
response.getWriter().println("<h1>code</h1>"); | response.getWriter().println("<h1>code</h1>"); | ||||
response.getWriter().println(code); | 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("<h1>result</h1>"); | ||||
response.getWriter().println(Arrays.toString(res)); | |||||
response.getWriter().println(res); | |||||
} catch (WxErrorException e) { | } catch (WxErrorException e) { | ||||
e.printStackTrace(); | e.printStackTrace(); | ||||
} | } | ||||