Browse Source

Merge pull request #1151 from Wechat-Group/develop

合并 Develop 分支
master
Binary Wang 5 years ago
committed by GitHub
parent
commit
476b067392
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
100 changed files with 2923 additions and 822 deletions
  1. +1
    -1
      .travis.yml
  2. +0
    -3
      .vscode/settings.json
  3. +6
    -4
      pom.xml
  4. +26
    -0
      spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md
  5. +68
    -0
      spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml
  6. +50
    -0
      spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/config/WxMaAutoConfiguration.java
  7. +39
    -0
      spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaProperties.java
  8. +1
    -0
      spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/resources/META-INF/spring.factories
  9. +1
    -1
      spring-boot-starters/wx-java-mp-spring-boot-starter/README.md
  10. +3
    -3
      spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml
  11. +7
    -1
      spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpAutoConfiguration.java
  12. +55
    -0
      spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpServiceAutoConfiguration.java
  13. +88
    -0
      spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java
  14. +45
    -0
      spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/RedisProperties.java
  15. +20
    -10
      spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpProperties.java
  16. +1
    -0
      spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/resources/META-INF/spring.factories
  17. +4
    -4
      spring-boot-starters/wx-java-pay-spring-boot-starter/README.md
  18. +3
    -3
      spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml
  19. +0
    -0
      spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java
  20. +0
    -0
      spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java
  21. +0
    -0
      spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/resources/META-INF/spring.factories
  22. +0
    -42
      starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/RedisProperties.java
  23. +0
    -53
      starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpServiceAutoConfiguration.java
  24. +0
    -84
      starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpStorageAutoConfiguration.java
  25. +0
    -1
      starters/wx-java-mp-starter/src/main/resources/META-INF/spring.factories
  26. +1
    -1
      weixin-java-common/pom.xml
  27. +12
    -2
      weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
  28. +5
    -3
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java
  29. +16
    -20
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java
  30. +1
    -1
      weixin-java-cp/pom.xml
  31. +0
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentService.java
  32. +15
    -23
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpChatService.java
  33. +10
    -7
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java
  34. +58
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java
  35. +3
    -7
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java
  36. +1
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMenuService.java
  37. +0
    -66
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAService.java
  38. +14
    -7
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAuth2Service.java
  39. +64
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java
  40. +5
    -12
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
  41. +9
    -9
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTagService.java
  42. +1
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTaskCardService.java
  43. +169
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTpService.java
  44. +25
    -3
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java
  45. +36
    -27
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
  46. +239
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpTpServiceImpl.java
  47. +10
    -12
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java
  48. +9
    -13
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImpl.java
  49. +12
    -11
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java
  50. +41
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java
  51. +15
    -14
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java
  52. +12
    -10
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImpl.java
  53. +31
    -27
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImpl.java
  54. +24
    -35
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java
  55. +10
    -8
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java
  56. +15
    -10
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java
  57. +31
    -25
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java
  58. +33
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOnTpImpl.java
  59. +23
    -26
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImpl.java
  60. +4
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImpl.java
  61. +114
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTpServiceApacheHttpClientImpl.java
  62. +12
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTpServiceImpl.java
  63. +35
    -29
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImpl.java
  64. +1
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAppChatMessage.java
  65. +46
    -12
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java
  66. +28
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpOauth2UserInfo.java
  67. +3
    -3
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagGetResult.java
  68. +42
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpCorp.java
  69. +68
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlMessage.java
  70. +55
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlPackage.java
  71. +5
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java
  72. +20
    -8
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserExternalContactInfo.java
  73. +58
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserExternalContactList.java
  74. +49
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserWithExternalPermission.java
  75. +30
    -4
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java
  76. +69
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MiniProgramNoticeMsgBuilder.java
  77. +21
    -7
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java
  78. +86
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpTpConfigStorage.java
  79. +37
    -18
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java
  80. +24
    -7
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java
  81. +248
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpTpDefaultConfigImpl.java
  82. +113
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
  83. +21
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
  84. +52
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpTpCryptUtil.java
  85. +8
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java
  86. +135
    -117
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java
  87. +14
    -2
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java
  88. +3
    -2
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java
  89. +24
    -0
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java
  90. +2
    -2
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImplTest.java
  91. +1
    -1
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImplTest.java
  92. +45
    -0
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java
  93. +11
    -1
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImplTest.java
  94. +1
    -1
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java
  95. +5
    -5
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImplTest.java
  96. +8
    -3
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java
  97. +51
    -2
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java
  98. +2
    -2
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java
  99. +1
    -1
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java
  100. +3
    -2
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java

+ 1
- 1
.travis.yml View File

@@ -7,7 +7,7 @@ language: java
# secure: "834110c7191f97ecb226970c46dcaff8e681da5a"

jdk:
- oraclejdk8
- openjdk8
script: "mvn clean package -DskipTests=true -Dcheckstyle.skip=true"

#script:


+ 0
- 3
.vscode/settings.json View File

@@ -1,3 +0,0 @@
{
"java.configuration.updateBuildConfiguration": "interactive"
}

+ 6
- 4
pom.xml View File

@@ -6,7 +6,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>3.4.0</version>
<version>3.5.0</version>
<packaging>pom</packaging>
<name>WxJava - Weixin/Wechat Java SDK</name>
<description>微信开发Java SDK</description>
@@ -105,8 +105,9 @@
<module>weixin-java-pay</module>
<module>weixin-java-miniapp</module>
<module>weixin-java-open</module>
<module>starters/wx-java-pay-starter</module>
<module>starters/wx-java-mp-starter</module>
<module>spring-boot-starters/wx-java-pay-spring-boot-starter</module>
<module>spring-boot-starters/wx-java-mp-spring-boot-starter</module>
<module>spring-boot-starters/wx-java-miniapp-spring-boot-starter</module>
<!--module>weixin-java-osgi</module-->
</modules>

@@ -246,7 +247,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -370,6 +371,7 @@
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.17</version>
<configuration>
<skip>true</skip>
<configLocation>quality-checks/google_checks.xml</configLocation>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<consoleOutput>true</consoleOutput>


+ 26
- 0
spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md View File

@@ -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
```








+ 68
- 0
spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml View File

@@ -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>

+ 50
- 0
spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/config/WxMaAutoConfiguration.java View File

@@ -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;
}
}

+ 39
- 0
spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaProperties.java View File

@@ -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;
}

+ 1
- 0
spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/resources/META-INF/spring.factories View File

@@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.miniapp.config.WxMaAutoConfiguration

starters/wx-java-mp-starter/README.md → spring-boot-starters/wx-java-mp-spring-boot-starter/README.md View File

@@ -4,7 +4,7 @@
```xml
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-mp-starter</artifactId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
```

starters/wx-java-mp-starter/pom.xml → spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml View File

@@ -6,13 +6,13 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>3.4.0</version>
<version>3.5.0</version>
<relativePath>../../</relativePath>
</parent>

<artifactId>wx-java-mp-starter</artifactId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
<name>WxJava - Spring Boot Starter for MP</name>
<description>微信公众号开发的Spring Boot Starter</description>
<description>微信公众号开发的 Spring Boot Starter</description>

<properties>
<spring.boot.version>2.1.4.RELEASE</spring.boot.version>

starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpAutoConfiguration.java → spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpAutoConfiguration.java View File

@@ -1,9 +1,15 @@
package com.binarywang.spring.starter.wxjava.mp;
package com.binarywang.spring.starter.wxjava.mp.config;

import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
* .
*
* @author someone
*/
@Configuration
@EnableConfigurationProperties(WxMpProperties.class)
@Import({WxMpStorageAutoConfiguration.class, WxMpServiceAutoConfiguration.class})

+ 55
- 0
spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpServiceAutoConfiguration.java View File

@@ -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;
}

}

+ 88
- 0
spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java View File

@@ -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;
}
}

+ 45
- 0
spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/RedisProperties.java View File

@@ -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;
}

starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpProperties.java → spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpProperties.java View File

@@ -1,16 +1,18 @@
package com.binarywang.spring.starter.wxjava.mp;
package com.binarywang.spring.starter.wxjava.mp.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.io.Serializable;

import static com.binarywang.spring.starter.wxjava.mp.WxMpProperties.PREFIX;
import static com.binarywang.spring.starter.wxjava.mp.WxMpProperties.StorageType.memory;
import static com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties.PREFIX;
import static com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties.StorageType.memory;


/**
* 微信接入相关配置属性
* 微信接入相关配置属性.
*
* @author someone
*/
@Data
@ConfigurationProperties(PREFIX)
@@ -18,33 +20,34 @@ public class WxMpProperties {
public static final String PREFIX = "wx.mp";

/**
* 设置微信公众号的appid
* 设置微信公众号的appid.
*/
private String appId;

/**
* 设置微信公众号的app secret
* 设置微信公众号的app secret.
*/
private String secret;

/**
* 设置微信公众号的token
* 设置微信公众号的token.
*/
private String token;

/**
* 设置微信公众号的EncodingAESKey
* 设置微信公众号的EncodingAESKey.
*/
private String aesKey;

/**
* 存储策略, memory, redis
* 存储策略, memory, redis.
*/
private ConfigStorage configStorage = new ConfigStorage();


@Data
public static class ConfigStorage implements Serializable {
private static final long serialVersionUID = 4815731027000065434L;

private StorageType type = memory;

@@ -53,6 +56,13 @@ public class WxMpProperties {
}

public enum StorageType {
memory, redis
/**
* 内存.
*/
memory,
/**
* redis.
*/
redis
}
}

+ 1
- 0
spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/resources/META-INF/spring.factories View File

@@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.mp.config.WxMpAutoConfiguration

starters/wx-java-pay-starter/README.md → spring-boot-starters/wx-java-pay-spring-boot-starter/README.md View File

@@ -3,7 +3,7 @@
```xml
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-pay-starter</artifactId>
<artifactId>wx-java-pay-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
```
@@ -11,9 +11,9 @@
```yml
wx:
pay:
appId: wx5b69c56ac01ed858
mchId: 1462547202
mchKey: OGL9fvig9y2HrXrQ86tM4jTwyv4ja6G5
appId:
mchId:
mchKey:
subAppId:
subMchId:
keyPath:

starters/wx-java-pay-starter/pom.xml → spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml View File

@@ -5,14 +5,14 @@
<parent>
<artifactId>wx-java</artifactId>
<groupId>com.github.binarywang</groupId>
<version>3.4.0</version>
<version>3.5.0</version>
<relativePath>../../</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>wx-java-pay-starter</artifactId>
<artifactId>wx-java-pay-spring-boot-starter</artifactId>
<name>WxJava - Spring Boot Starter for WxPay</name>
<description>微信支付开发的Spring Boot Starter</description>
<description>微信支付开发的 Spring Boot Starter</description>

<properties>
<spring.boot.version>2.1.4.RELEASE</spring.boot.version>

starters/wx-java-pay-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java → spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java View File


starters/wx-java-pay-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java → spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java View File


starters/wx-java-pay-starter/src/main/resources/META-INF/spring.factories → spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/resources/META-INF/spring.factories View File


+ 0
- 42
starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/RedisProperties.java View File

@@ -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;
}

+ 0
- 53
starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpServiceAutoConfiguration.java View File

@@ -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;
}

}

+ 0
- 84
starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpStorageAutoConfiguration.java View File

@@ -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;
}
}

+ 0
- 1
starters/wx-java-mp-starter/src/main/resources/META-INF/spring.factories View File

@@ -1 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.mp.WxMpAutoConfiguration

+ 1
- 1
weixin-java-common/pom.xml View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>3.4.0</version>
<version>3.5.0</version>
</parent>

<artifactId>weixin-java-common</artifactId>


+ 12
- 2
weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java View File

@@ -85,14 +85,24 @@ public class WxConsts {
public static final String TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service";

/**
* 小程序卡片(要求小程序与公众号已关联)
* 小程序卡片(要求小程序与公众号已关联).
*/
public static final String MINIPROGRAMPAGE = "miniprogrampage";

/**
* 任务卡片消息
* 任务卡片消息.
*/
public static final String TASKCARD = "taskcard";

/**
* 菜单消息.
*/
public static final String MSGMENU = "msgmenu";

/**
* 小程序通知消息.
*/
public static final String MINIPROGRAM_NOTICE = "miniprogram_notice";
}

/**


+ 5
- 3
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java View File

@@ -6,12 +6,14 @@ import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;

/**
* Created by Daniel Qian on 14/10/19.
*
* @author Daniel Qian
* @date 14/10/19
*/
public class SHA1 {

/**
* 串接arr参数,生成sha1 digest
* 串接arr参数,生成sha1 digest.
*/
public static String gen(String... arr) {
if (StringUtils.isAnyEmpty(arr)) {
@@ -27,7 +29,7 @@ public class SHA1 {
}

/**
* 用&串接arr参数,生成sha1 digest
* 用&串接arr参数,生成sha1 digest.
*/
public static String genWithAmple(String... arr) {
if (StringUtils.isAnyEmpty(arr)) {


+ 16
- 20
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java View File

@@ -55,20 +55,19 @@ public class WxCryptUtil {
}

/**
* 构造函数
* 构造函数.
*
* @param token 公众平台上,开发者设置的token
* @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey
* @param appidOrCorpid 公众平台appid/corpid
*/
public WxCryptUtil(String token, String encodingAesKey,
String appidOrCorpid) {
public WxCryptUtil(String token, String encodingAesKey, String appidOrCorpid) {
this.token = token;
this.appidOrCorpid = appidOrCorpid;
this.aesKey = Base64.decodeBase64(encodingAesKey + "=");
}

static String extractEncryptPart(String xml) {
private static String extractEncryptPart(String xml) {
try {
DocumentBuilder db = BUILDER_LOCAL.get();
Document document = db.parse(new InputSource(new StringReader(xml)));
@@ -81,7 +80,7 @@ public class WxCryptUtil {
}

/**
* 将一个数字转换成生成4个字节的网络字节序bytes数组
* 将一个数字转换成生成4个字节的网络字节序bytes数组.
*/
private static byte[] number2BytesInNetworkOrder(int number) {
byte[] orderBytes = new byte[4];
@@ -93,7 +92,7 @@ public class WxCryptUtil {
}

/**
* 4个字节的网络字节序bytes数组还原成一个数字
* 4个字节的网络字节序bytes数组还原成一个数字.
*/
private static int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) {
int sourceNumber = 0;
@@ -105,7 +104,7 @@ public class WxCryptUtil {
}

/**
* 随机生成16位字符串
* 随机生成16位字符串.
*/
private static String genRandomStr() {
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
@@ -119,7 +118,7 @@ public class WxCryptUtil {
}

/**
* 生成xml消息
* 生成xml消息.
*
* @param encrypt 加密后的消息密文
* @param signature 安全签名
@@ -127,8 +126,7 @@ public class WxCryptUtil {
* @param nonce 随机字符串
* @return 生成的xml字符串
*/
private static String generateXml(String encrypt, String signature,
String timestamp, String nonce) {
private static String generateXml(String encrypt, String signature, String timestamp, String nonce) {
String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n"
+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n"
+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n"
@@ -242,10 +240,9 @@ public class WxCryptUtil {
try {
// 设置解密模式为AES的CBC模式
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec key_spec = new SecretKeySpec(this.aesKey, "AES");
IvParameterSpec iv = new IvParameterSpec(
Arrays.copyOfRange(this.aesKey, 0, 16));
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);
SecretKeySpec keySpec = new SecretKeySpec(this.aesKey, "AES");
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(this.aesKey, 0, 16));
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);

// 使用BASE64对密文进行解码
byte[] encrypted = Base64.decodeBase64(cipherText);
@@ -256,7 +253,8 @@ public class WxCryptUtil {
throw new RuntimeException(e);
}

String xmlContent, fromAppid;
String xmlContent;
String fromAppid;
try {
// 去除补位字符
byte[] bytes = PKCS7Encoder.decode(original);
@@ -266,17 +264,15 @@ public class WxCryptUtil {

int xmlLength = bytesNetworkOrder2Number(networkOrder);

xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength),
CHARSET);
fromAppid = new String(
Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET);
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);
fromAppid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET);
} catch (Exception e) {
throw new RuntimeException(e);
}

// appid不相同的情况
if (!fromAppid.equals(this.appidOrCorpid)) {
throw new RuntimeException("AppID不正确");
throw new RuntimeException("AppID不正确,请核实!");
}

return xmlContent;


+ 1
- 1
weixin-java-cp/pom.xml View File

@@ -7,7 +7,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>3.4.0</version>
<version>3.5.0</version>
</parent>

<artifactId>weixin-java-cp</artifactId>


+ 0
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentService.java View File

@@ -15,7 +15,6 @@ import java.util.List;
* @author <a href="https://github.com/huansinho">huansinho</a>
*/
public interface WxCpAgentService {

/**
* <pre>
* 获取企业号应用信息


+ 15
- 23
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpChatService.java View File

@@ -1,20 +1,20 @@
package me.chanjar.weixin.cp.api;

import java.util.List;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage;
import me.chanjar.weixin.cp.bean.WxCpChat;

import java.util.List;

/**
* 群聊服务.
*
* @author gaigeshen
*/
public interface WxCpChatService {
String APPCHAT_CREATE = "https://qyapi.weixin.qq.com/cgi-bin/appchat/create";
String APPCHAT_UPDATE = "https://qyapi.weixin.qq.com/cgi-bin/appchat/update";
String APPCHAT_GET_CHATID = "https://qyapi.weixin.qq.com/cgi-bin/appchat/get?chatid=";
@Deprecated
String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException;

/**
* 创建群聊会话,注意:刚创建的群,如果没有下发消息,在企业微信不会出现该群.
@@ -24,15 +24,13 @@ public interface WxCpChatService {
* @param users 群成员id列表。至少2人,至多500人
* @param chatId 群聊的唯一标志,不能与已有的群重复;字符串类型,最长32个字符。只允许字符0-9及字母a-zA-Z。如果不填,系统会随机生成群id
* @return 创建的群聊会话chatId
* @throws WxErrorException 发生异常
*/
String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException;

/**
* chatCreate 同名方法
* @throws WxErrorException 异常
*/
String create(String name, String owner, List<String> users, String chatId) throws WxErrorException;

@Deprecated
void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException;

/**
* 修改群聊会话.
*
@@ -41,26 +39,19 @@ public interface WxCpChatService {
* @param owner 新群主的id。若不需更新,请忽略此参数(null or empty)
* @param usersToAdd 添加成员的id列表,若不需要更新,则传递空对象或者空集合
* @param usersToDelete 踢出成员的id列表,若不需要更新,则传递空对象或者空集合
* @throws WxErrorException 发生异常
*/
void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException;

/**
* chatUpdate 同名方法
* @throws WxErrorException 异常
*/
void update(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException;

@Deprecated
WxCpChat chatGet(String chatId) throws WxErrorException;

/**
* 获取群聊会话.
*
* @param chatId 群聊编号
* @return 群聊会话
* @throws WxErrorException 发生异常
*/
WxCpChat chatGet(String chatId) throws WxErrorException;

/**
* chatGet 同名方法
* @throws WxErrorException 异常
*/
WxCpChat get(String chatId) throws WxErrorException;

@@ -71,6 +62,7 @@ public interface WxCpChatService {
* 文档地址:https://work.weixin.qq.com/api/doc#90000/90135/90248
*
* @param message 要发送的消息内容对象
* @throws WxErrorException 异常
*/
void sendMsg(WxCpAppChatMessage message) throws WxErrorException;



+ 10
- 7
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java View File

@@ -1,10 +1,10 @@
package me.chanjar.weixin.cp.api;

import java.util.List;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpDepart;

import java.util.List;

/**
* <pre>
* 部门管理接口
@@ -19,7 +19,7 @@ public interface WxCpDepartmentService {
* <pre>
* 部门管理接口 - 创建部门.
* 最多支持创建500个部门
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90205
* </pre>
*
* @param depart 部门
@@ -30,19 +30,20 @@ public interface WxCpDepartmentService {

/**
* <pre>
* 部门管理接口 - 查询部门.
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E5.88.97.E8.A1.A8
* 部门管理接口 - 获取部门列表.
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90208
* </pre>
*
* @param id 部门id。获取指定部门及其下的子部门。非必需,可为null
* @return 获取的部门列表
* @throws WxErrorException 异常
*/
List<WxCpDepart> list(Long id) throws WxErrorException;

/**
* <pre>
* 部门管理接口 - 修改部门名.
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口
* 部门管理接口 - 更新部门.
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90206
* 如果id为0(未部门),1(黑名单),2(星标组),或者不存在的id,微信会返回系统繁忙的错误
* </pre>
*
@@ -54,6 +55,8 @@ public interface WxCpDepartmentService {
/**
* <pre>
* 部门管理接口 - 删除部门.
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90207
* 应用须拥有指定部门的管理权限
* </pre>
*
* @param departId 部门id


+ 58
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java View File

@@ -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;

}

+ 3
- 7
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java View File

@@ -1,12 +1,12 @@
package me.chanjar.weixin.cp.api;

import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;

/**
* <pre>
* 媒体管理接口.
@@ -16,10 +16,6 @@ import me.chanjar.weixin.common.error.WxErrorException;
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxCpMediaService {
String MEDIA_GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/get";
String MEDIA_UPLOAD_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=";
String IMG_UPLOAD_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/uploadimg";
String JSSDK_MEDIA_GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/get/jssdk";

/**
* <pre>


+ 1
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMenuService.java View File

@@ -12,6 +12,7 @@ import me.chanjar.weixin.common.error.WxErrorException;
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxCpMenuService {

/**
* <pre>
* 自定义菜单创建接口


+ 0
- 66
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAService.java View File

@@ -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;

}

+ 14
- 7
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAuth2Service.java View File

@@ -1,20 +1,22 @@
package me.chanjar.weixin.cp.api;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo;
import me.chanjar.weixin.cp.bean.WxCpUserDetail;

/**
* <pre>
* OAuth2相关管理接口
* OAuth2相关管理接口.
* Created by BinaryWang on 2017/6/24.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxCpOAuth2Service {

/**
* <pre>
* 构造oauth2授权的url连接
* 构造oauth2授权的url连接.
* </pre>
*
* @param state 状态码
@@ -24,7 +26,7 @@ public interface WxCpOAuth2Service {

/**
* <pre>
* 构造oauth2授权的url连接
* 构造oauth2授权的url连接.
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code
* </pre>
*
@@ -57,16 +59,18 @@ public interface WxCpOAuth2Service {
* </pre>
*
* @param code 微信oauth授权返回的代码
* @return [userid, deviceid]
* @return WxCpOauth2UserInfo
* @throws WxErrorException 异常
* @see #getUserInfo(Integer, String)
*/
String[] getUserInfo(String code) throws WxErrorException;
WxCpOauth2UserInfo getUserInfo(String code) throws WxErrorException;

/**
* <pre>
* 根据code获取成员信息
* http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息
* https://work.weixin.qq.com/api/doc#10028/根据code获取成员信息
* https://work.weixin.qq.com/api/doc#90000/90135/91023 获取访问用户身份
* 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。
*
* 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
@@ -74,10 +78,11 @@ public interface WxCpOAuth2Service {
*
* @param agentId 企业号应用的id
* @param code 通过成员授权获取到的code,最大为512字节。每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
* @return [UserId, DeviceId, OpenId, user_ticket, expires_in]
* @return WxCpOauth2UserInfo
* @throws WxErrorException 异常
* @see #getUserInfo(String)
*/
String[] getUserInfo(Integer agentId, String code) throws WxErrorException;
WxCpOauth2UserInfo getUserInfo(Integer agentId, String code) throws WxErrorException;

/**
* <pre>
@@ -92,6 +97,8 @@ public interface WxCpOAuth2Service {
* </pre>
*
* @param userTicket 成员票据
* @return WxCpUserDetail
* @throws WxErrorException 异常
*/
WxCpUserDetail getUserDetail(String userTicket) throws WxErrorException;
}

+ 64
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java View File

@@ -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;

}

+ 5
- 12
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java View File

@@ -13,19 +13,10 @@ import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;

/**
* 微信API的Service
* 微信API的Service.
* @author chanjaster
*/
public interface WxCpService {
String GET_JSAPI_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket";
String GET_AGENT_CONFIG_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?&type=agent_config";
String MESSAGE_SEND = "https://qyapi.weixin.qq.com/cgi-bin/message/send";
String GET_CALLBACK_IP = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip";
String BATCH_REPLACE_PARTY = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty";
String BATCH_REPLACE_USER = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser";
String BATCH_GET_RESULT = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid=";
String JSCODE_TO_SESSION_URL = "https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session";

/**
* <pre>
* 验证推送过来的消息的正确性
@@ -293,7 +284,9 @@ public interface WxCpService {
* 获取用户相关接口的服务类对象
*/
WxCpUserService getUserService();

WxCpExternalContactService getExternalContactService();

/**
* 获取群聊服务
*
@@ -310,7 +303,7 @@ public interface WxCpService {

WxCpAgentService getAgentService();

WxCpOAService getOAService();
WxCpOaService getOAService();

/**
* http请求对象


+ 9
- 9
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTagService.java View File

@@ -17,6 +17,7 @@ import java.util.List;
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxCpTagService {

/**
* 创建标签.
*
@@ -51,6 +52,14 @@ public interface WxCpTagService {
*/
List<WxCpUser> listUsersByTagId(String tagId) throws WxErrorException;

/**
* 获取标签成员.
* 对应: http://qydev.weixin.qq.com/wiki/index.php?title=管理标签 中的get接口
*
* @param tagId 标签id
*/
WxCpTagGetResult get(String tagId) throws WxErrorException;

/**
* 增加标签成员.
*
@@ -69,13 +78,4 @@ public interface WxCpTagService {
*/
WxCpTagAddOrRemoveUsersResult removeUsersFromTag(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException;


/**
* 获取标签成员.
* 对应: http://qydev.weixin.qq.com/wiki/index.php?title=管理标签 中的get接口
*
* @param tagId 标签id
*/
WxCpTagGetResult get(String tagId) throws WxErrorException;

}

+ 1
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTaskCardService.java View File

@@ -14,6 +14,7 @@ import java.util.List;
* @date 2019-05-16
*/
public interface WxCpTaskCardService {

/**
* <pre>
* 更新任务卡片消息状态


+ 169
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTpService.java View File

@@ -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();

}

+ 25
- 3
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java View File

@@ -1,13 +1,13 @@
package me.chanjar.weixin.cp.api;

import java.util.List;
import java.util.Map;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpInviteResult;
import me.chanjar.weixin.cp.bean.WxCpUser;
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo;

import java.util.List;
import java.util.Map;

/**
* <pre>
* 用户管理接口
@@ -17,6 +17,7 @@ import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo;
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxCpUserService {

/**
* <pre>
* 用在二次验证的时候.
@@ -136,6 +137,23 @@ public interface WxCpUserService {
*/
String openid2UserId(String openid) throws WxErrorException;

/**
* <pre>
*
* 通过手机号获取其所对应的userid。
*
* 请求方式:POST(HTTPS)
* 请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/getuserid?access_token=ACCESS_TOKEN
*
* 文档地址:https://work.weixin.qq.com/api/doc#90001/90143/91693
* </pre>
*
* @param mobile 手机号码。长度为5~32个字节
* @return userid mobile对应的成员userid
* @throws WxErrorException .
*/
String getUserId(String mobile) throws WxErrorException;

/**
* 获取外部联系人详情.
* <pre>
@@ -146,6 +164,10 @@ public interface WxCpUserService {
* </pre>
*
* @param userId 外部联系人的userid
* @return 联系人详情
* @throws WxErrorException .
*/
WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException;


}

+ 36
- 27
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java View File

@@ -5,6 +5,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
@@ -23,6 +24,7 @@ import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult;
import me.chanjar.weixin.cp.bean.WxCpMessage;
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@@ -31,22 +33,24 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.*;

/**
* @author chanjarster
*/
@Slf4j
public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestHttp<H, P> {
protected final Logger log = LoggerFactory.getLogger(this.getClass());

private WxCpUserService userService = new WxCpUserServiceImpl(this);
private WxCpChatService chatService = new WxCpChatServiceImpl(this);
private WxCpUserService userService = new WxCpUserServiceImpl(this);
private WxCpChatService chatService = new WxCpChatServiceImpl(this);
private WxCpDepartmentService departmentService = new WxCpDepartmentServiceImpl(this);
private WxCpMediaService mediaService = new WxCpMediaServiceImpl(this);
private WxCpMenuService menuService = new WxCpMenuServiceImpl(this);
private WxCpOAuth2Service oauth2Service = new WxCpOAuth2ServiceImpl(this);
private WxCpTagService tagService = new WxCpTagServiceImpl(this);
private WxCpAgentService agentService = new WxCpAgentServiceImpl(this);
private WxCpOAService oaService = new WxCpOAServiceImpl(this);
private WxCpTaskCardService taskCardService = new WxCpTaskCardServiceImpl(this);
private WxCpMediaService mediaService = new WxCpMediaServiceImpl(this);
private WxCpMenuService menuService = new WxCpMenuServiceImpl(this);
private WxCpOAuth2Service oauth2Service = new WxCpOAuth2ServiceImpl(this);
private WxCpTagService tagService = new WxCpTagServiceImpl(this);
private WxCpAgentService agentService = new WxCpAgentServiceImpl(this);
private WxCpOaService oaService = new WxCpOaServiceImpl(this);
private WxCpTaskCardService taskCardService = new WxCpTaskCardServiceImpl(this);
private WxCpExternalContactService externalContactService = new WxCpExternalContactServiceImpl(this);

/**
* 全局的是否正在刷新access token的锁
@@ -80,7 +84,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data)
.equals(msgSignature);
} catch (Exception e) {
this.log.error("Checking signature failed, and the reason is :" + e.getMessage());
log.error("Checking signature failed, and the reason is :" + e.getMessage());
return false;
}
}
@@ -104,7 +108,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
if (this.configStorage.isAgentJsapiTicketExpired()) {
synchronized (this.globalAgentJsapiTicketRefreshLock) {
if (this.configStorage.isAgentJsapiTicketExpired()) {
String responseContent = this.get(WxCpService.GET_AGENT_CONFIG_TICKET, null);
String responseContent = this.get(this.configStorage.getApiUrl(GET_AGENT_CONFIG_TICKET), null);
JsonObject jsonObject = new JsonParser().parse(responseContent).getAsJsonObject();
this.configStorage.updateAgentJsapiTicket(jsonObject.get("ticket").getAsString(),
jsonObject.get("expires_in").getAsInt());
@@ -129,7 +133,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
if (this.configStorage.isJsapiTicketExpired()) {
synchronized (this.globalJsapiTicketRefreshLock) {
if (this.configStorage.isJsapiTicketExpired()) {
String responseContent = this.get(WxCpService.GET_JSAPI_TICKET, null);
String responseContent = this.get(this.configStorage.getApiUrl(GET_JSAPI_TICKET), null);
JsonObject tmpJsonObject = new JsonParser().parse(responseContent).getAsJsonObject();
this.configStorage.updateJsapiTicket(tmpJsonObject.get("ticket").getAsString(),
tmpJsonObject.get("expires_in").getAsInt());
@@ -170,7 +174,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
message.setAgentId(this.getWxCpConfigStorage().getAgentId());
}

return WxCpMessageSendResult.fromJson(this.post(WxCpService.MESSAGE_SEND, message.toJson()));
return WxCpMessageSendResult.fromJson(this.post(this.configStorage.getApiUrl(MESSAGE_SEND), message.toJson()));
}

@Override
@@ -179,13 +183,13 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
params.put("js_code", jsCode);
params.put("grant_type", "authorization_code");

String result = this.get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params));
String result = this.get(this.configStorage.getApiUrl(JSCODE_TO_SESSION), Joiner.on("&").withKeyValueSeparator("=").join(params));
return WxCpMaJsCode2SessionResult.fromJson(result);
}

@Override
public String[] getCallbackIp() throws WxErrorException {
String responseContent = get(WxCpService.GET_CALLBACK_IP, null);
String responseContent = get(this.configStorage.getApiUrl(GET_CALLBACK_IP), null);
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray();
String[] ips = new String[jsonArray.size()];
@@ -216,7 +220,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
return this.executeInternal(executor, uri, data);
} catch (WxErrorException e) {
if (retryTimes + 1 > this.maxRetryTimes) {
this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
//最后一次重试失败后,直接抛出异常,不再等待
throw new RuntimeException("微信服务端异常,超出重试次数");
}
@@ -228,7 +232,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
if (error.getErrorCode() == -1) {
int sleepMillis = this.retrySleepMillis * (1 << retryTimes);
try {
this.log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
Thread.sleep(sleepMillis);
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
@@ -239,7 +243,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
}
} while (retryTimes++ < this.maxRetryTimes);

this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
throw new RuntimeException("微信服务端异常,超出重试次数");
}

@@ -255,7 +259,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH

try {
T result = executor.execute(uriWithAccessToken, data);
this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result);
log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result);
return result;
} catch (WxErrorException e) {
WxError error = e.getError();
@@ -272,12 +276,12 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
}

if (error.getErrorCode() != 0) {
this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error);
log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error);
throw new WxErrorException(error, e);
}
return null;
} catch (IOException e) {
this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage());
log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage());
throw new RuntimeException(e);
}
}
@@ -329,19 +333,19 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
public String replaceParty(String mediaId) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("media_id", mediaId);
return post(WxCpService.BATCH_REPLACE_PARTY, jsonObject.toString());
return post(this.configStorage.getApiUrl(BATCH_REPLACE_PARTY), jsonObject.toString());
}

@Override
public String replaceUser(String mediaId) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("media_id", mediaId);
return post(WxCpService.BATCH_REPLACE_USER, jsonObject.toString());
return post(this.configStorage.getApiUrl(BATCH_REPLACE_USER), jsonObject.toString());
}

@Override
public String getTaskResult(String joinId) throws WxErrorException {
String url = WxCpService.BATCH_GET_RESULT + joinId;
String url = this.configStorage.getApiUrl(BATCH_GET_RESULT + joinId);
return get(url, null);
}

@@ -383,13 +387,18 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
return userService;
}

@Override
public WxCpExternalContactService getExternalContactService() {
return externalContactService;
}

@Override
public WxCpChatService getChatService() {
return chatService;
}

@Override
public WxCpOAService getOAService() {
public WxCpOaService getOAService() {
return oaService;
}



+ 239
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpTpServiceImpl.java View File

@@ -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;
}

}

+ 10
- 12
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java View File

@@ -1,11 +1,9 @@
package me.chanjar.weixin.cp.api.impl;

import java.util.List;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpAgentService;
@@ -13,6 +11,10 @@ import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpAgent;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;

import java.util.List;

import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Agent.*;


/**
* <pre>
@@ -22,14 +24,11 @@ import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
*
* @author <a href="https://github.com/huansinho">huansinho</a>
*/
@RequiredArgsConstructor
public class WxCpAgentServiceImpl implements WxCpAgentService {
private static final JsonParser JSON_PARSER = new JsonParser();

private WxCpService mainService;

public WxCpAgentServiceImpl(WxCpService mainService) {
this.mainService = mainService;
}
private final WxCpService mainService;

@Override
public WxCpAgent get(Integer agentId) throws WxErrorException {
@@ -37,14 +36,13 @@ public class WxCpAgentServiceImpl implements WxCpAgentService {
throw new IllegalArgumentException("缺少agentid参数");
}

String url = "https://qyapi.weixin.qq.com/cgi-bin/agent/get?agentid=" + agentId;
String responseContent = this.mainService.get(url, null);
String responseContent = this.mainService.get(String.format(this.mainService.getWxCpConfigStorage().getApiUrl(AGENT_GET), agentId), null);
return WxCpAgent.fromJson(responseContent);
}

@Override
public void set(WxCpAgent agentInfo) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/agent/set";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(AGENT_SET);
String responseContent = this.mainService.post(url, agentInfo.toJson());
JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject();
if (jsonObject.get("errcode").getAsInt() != 0) {
@@ -54,7 +52,7 @@ public class WxCpAgentServiceImpl implements WxCpAgentService {

@Override
public List<WxCpAgent> list() throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/agent/list";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(AGENT_LIST);
String responseContent = this.mainService.get(url, null);
JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject();
if (jsonObject.get("errcode").getAsInt() != 0) {


+ 9
- 13
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImpl.java View File

@@ -1,12 +1,14 @@
package me.chanjar.weixin.cp.api.impl;

import com.google.gson.JsonParser;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import me.chanjar.weixin.cp.api.WxCpChatService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage;
import me.chanjar.weixin.cp.bean.WxCpChat;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import org.apache.commons.lang3.StringUtils;

@@ -14,24 +16,18 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Chat.*;

/**
* 群聊服务实现.
*
* @author gaigeshen
*/
@RequiredArgsConstructor
public class WxCpChatServiceImpl implements WxCpChatService {
private static final JsonParser JSON_PARSER = new JsonParser();
private final WxCpService cpService;

/**
* 创建群聊服务实现的实例.
*
* @param cpService 企业微信的服务
*/
WxCpChatServiceImpl(WxCpService cpService) {
this.cpService = cpService;
}

@Override
public String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException {
Map<String, Object> data = new HashMap<>(4);
@@ -47,7 +43,7 @@ public class WxCpChatServiceImpl implements WxCpChatService {
if (StringUtils.isNotBlank(chatId)) {
data.put("chatid", chatId);
}
String result = this.cpService.post(APPCHAT_CREATE, WxGsonBuilder.create().toJson(data));
String result = this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(APPCHAT_CREATE), WxGsonBuilder.create().toJson(data));
return new JsonParser().parse(result).getAsJsonObject().get("chatid").getAsString();
}

@@ -76,7 +72,7 @@ public class WxCpChatServiceImpl implements WxCpChatService {
data.put("del_user_list", usersToDelete);
}

this.cpService.post(APPCHAT_UPDATE, WxGsonBuilder.create().toJson(data));
this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(APPCHAT_UPDATE), WxGsonBuilder.create().toJson(data));
}

@Override
@@ -86,7 +82,7 @@ public class WxCpChatServiceImpl implements WxCpChatService {

@Override
public WxCpChat chatGet(String chatId) throws WxErrorException {
String result = this.cpService.get(APPCHAT_GET_CHATID + chatId, null);
String result = this.cpService.get(this.cpService.getWxCpConfigStorage().getApiUrl(APPCHAT_GET_CHATID + chatId), null);
return WxCpGsonBuilder.create()
.fromJson(JSON_PARSER.parse(result).getAsJsonObject().getAsJsonObject("chat_info").toString(), WxCpChat.class);
}
@@ -98,7 +94,7 @@ public class WxCpChatServiceImpl implements WxCpChatService {

@Override
public void sendMsg(WxCpAppChatMessage message) throws WxErrorException {
this.cpService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/send", message.toJson());
this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(APPCHAT_SEND), message.toJson());
}

}

+ 12
- 11
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java View File

@@ -1,17 +1,21 @@
package me.chanjar.weixin.cp.api.impl;

import java.util.List;

import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.GsonHelper;
import me.chanjar.weixin.cp.api.WxCpDepartmentService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpDepart;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;

import java.util.List;

import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Department.*;

/**
* <pre>
* 部门管理接口
@@ -20,16 +24,13 @@ import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@RequiredArgsConstructor
public class WxCpDepartmentServiceImpl implements WxCpDepartmentService {
private WxCpService mainService;

public WxCpDepartmentServiceImpl(WxCpService mainService) {
this.mainService = mainService;
}
private final WxCpService mainService;

@Override
public Long create(WxCpDepart depart) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_CREATE);
String responseContent = this.mainService.post(url, depart.toJson());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("id"));
@@ -37,19 +38,19 @@ public class WxCpDepartmentServiceImpl implements WxCpDepartmentService {

@Override
public void update(WxCpDepart group) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/update";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_UPDATE);
this.mainService.post(url, group.toJson());
}

@Override
public void delete(Long departId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/delete?id=" + departId;
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_DELETE), departId);
this.mainService.get(url, null);
}

@Override
public List<WxCpDepart> list(Long id) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/list";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_LIST);
if (id != null) {
url += "?id=" + id;
}


+ 41
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java View File

@@ -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();
}
}

+ 15
- 14
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java View File

@@ -1,10 +1,6 @@
package me.chanjar.weixin.cp.api.impl;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.fs.FileUtils;
@@ -12,6 +8,14 @@ import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor;
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
import me.chanjar.weixin.cp.api.WxCpMediaService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.*;

/**
* <pre>
@@ -21,12 +25,9 @@ import me.chanjar.weixin.cp.api.WxCpService;
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@RequiredArgsConstructor
public class WxCpMediaServiceImpl implements WxCpMediaService {
private WxCpService mainService;

public WxCpMediaServiceImpl(WxCpService mainService) {
this.mainService = mainService;
}
private final WxCpService mainService;

@Override
public WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputStream)
@@ -37,7 +38,7 @@ public class WxCpMediaServiceImpl implements WxCpMediaService {
@Override
public WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException {
return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()),
MEDIA_UPLOAD_URL + mediaType, file);
this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_UPLOAD + mediaType), file);
}

@Override
@@ -45,7 +46,7 @@ public class WxCpMediaServiceImpl implements WxCpMediaService {
return this.mainService.execute(
BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(),
this.mainService.getWxCpConfigStorage().getTmpDirFile()),
MEDIA_GET_URL, "media_id=" + mediaId);
this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_GET), "media_id=" + mediaId);
}

@Override
@@ -53,13 +54,13 @@ public class WxCpMediaServiceImpl implements WxCpMediaService {
return this.mainService.execute(
BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(),
this.mainService.getWxCpConfigStorage().getTmpDirFile()),
JSSDK_MEDIA_GET_URL, "media_id=" + mediaId);
this.mainService.getWxCpConfigStorage().getApiUrl(JSSDK_MEDIA_GET), "media_id=" + mediaId);
}

@Override
public String uploadImg(File file) throws WxErrorException {
final WxMediaUploadResult result = this.mainService
.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), IMG_UPLOAD_URL, file);
.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), this.mainService.getWxCpConfigStorage().getApiUrl(IMG_UPLOAD), file);
return result.getUrl();
}
}

+ 12
- 10
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImpl.java View File

@@ -1,24 +1,26 @@
package me.chanjar.weixin.cp.api.impl;

import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.bean.menu.WxMenu;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpMenuService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;

import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Menu.*;

/**
* <pre>
* 菜单管理相关接口
* 菜单管理相关接口.
* Created by Binary Wang on 2017-6-25.
* @author <a href="https://github.com/binarywang">Binary Wang</a>
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@RequiredArgsConstructor
public class WxCpMenuServiceImpl implements WxCpMenuService {
private WxCpService mainService;

public WxCpMenuServiceImpl(WxCpService mainService) {
this.mainService = mainService;
}
private final WxCpService mainService;

@Override
public void create(WxMenu menu) throws WxErrorException {
@@ -27,7 +29,7 @@ public class WxCpMenuServiceImpl implements WxCpMenuService {

@Override
public void create(Integer agentId, WxMenu menu) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid=" + agentId;
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(MENU_CREATE), agentId);
this.mainService.post(url, menu.toJson());
}

@@ -38,7 +40,7 @@ public class WxCpMenuServiceImpl implements WxCpMenuService {

@Override
public void delete(Integer agentId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete?agentid=" + agentId;
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(MENU_DELETE), agentId);
this.mainService.get(url, null);
}

@@ -49,7 +51,7 @@ public class WxCpMenuServiceImpl implements WxCpMenuService {

@Override
public WxMenu get(Integer agentId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/get?agentid=" + agentId;
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(MENU_GET), agentId);
try {
String resultContent = this.mainService.get(url, null);
return WxCpGsonBuilder.create().fromJson(resultContent, WxMenu.class);


+ 31
- 27
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImpl.java View File

@@ -3,30 +3,35 @@ package me.chanjar.weixin.cp.api.impl;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.URIUtil;
import me.chanjar.weixin.common.util.json.GsonHelper;
import me.chanjar.weixin.cp.api.WxCpOAuth2Service;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo;
import me.chanjar.weixin.cp.bean.WxCpUserDetail;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;

import static me.chanjar.weixin.common.api.WxConsts.OAuth2Scope.*;
import static me.chanjar.weixin.common.api.WxConsts.OAuth2Scope.SNSAPI_PRIVATEINFO;
import static me.chanjar.weixin.common.api.WxConsts.OAuth2Scope.SNSAPI_USERINFO;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.OAuth2.*;

/**
* <pre>
*
* oauth2相关接口实现类.
* Created by Binary Wang on 2017-6-25.
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*
* @author Binary Wang
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@RequiredArgsConstructor
public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service {
private WxCpService mainService;

public WxCpOAuth2ServiceImpl(WxCpService mainService) {
this.mainService = mainService;
}
private final WxCpService mainService;

@Override
public String buildAuthorizationUrl(String state) {
@@ -38,55 +43,54 @@ public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service {

@Override
public String buildAuthorizationUrl(String redirectUri, String state) {
return this.buildAuthorizationUrl(redirectUri, state, WxConsts.OAuth2Scope.SNSAPI_BASE);
return this.buildAuthorizationUrl(redirectUri, state, SNSAPI_BASE);
}

@Override
public String buildAuthorizationUrl(String redirectUri, String state, String scope) {
StringBuilder url = new StringBuilder("https://open.weixin.qq.com/connect/oauth2/authorize?");
url.append("appid=").append(this.mainService.getWxCpConfigStorage().getCorpId());
StringBuilder url = new StringBuilder(URL_OAUTH2_AUTHORIZE);
url.append("?appid=").append(this.mainService.getWxCpConfigStorage().getCorpId());
url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectUri));
url.append("&response_type=code");
url.append("&scope=").append(scope);

if (WxConsts.OAuth2Scope.SNSAPI_PRIVATEINFO.equals(scope)
|| WxConsts.OAuth2Scope.SNSAPI_USERINFO.equals(scope)) {
if (SNSAPI_PRIVATEINFO.equals(scope) || SNSAPI_USERINFO.equals(scope)) {
url.append("&agentid=").append(this.mainService.getWxCpConfigStorage().getAgentId());
}

if (state != null) {
url.append("&state=").append(state);
}

url.append("#wechat_redirect");
return url.toString();
}

@Override
public String[] getUserInfo(String code) throws WxErrorException {
public WxCpOauth2UserInfo getUserInfo(String code) throws WxErrorException {
return this.getUserInfo(this.mainService.getWxCpConfigStorage().getAgentId(), code);
}

@Override
public String[] getUserInfo(Integer agentId, String code) throws WxErrorException {
String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?code=%s&agentid=%d",
code, agentId);
String responseText = this.mainService.get(url, null);
public WxCpOauth2UserInfo getUserInfo(Integer agentId, String code) throws WxErrorException {
String responseText = this.mainService.get(String.format(this.mainService.getWxCpConfigStorage().getApiUrl(GET_USER_INFO), code, agentId), null);
JsonElement je = new JsonParser().parse(responseText);
JsonObject jo = je.getAsJsonObject();
return new String[]{GsonHelper.getString(jo, "UserId"),
GsonHelper.getString(jo, "DeviceId"),
GsonHelper.getString(jo, "OpenId"),
GsonHelper.getString(jo, "user_ticket"),
GsonHelper.getString(jo, "expires_in")
};

return WxCpOauth2UserInfo.builder()
.userId(GsonHelper.getString(jo, "UserId"))
.deviceId(GsonHelper.getString(jo, "DeviceId"))
.openId(GsonHelper.getString(jo, "OpenId"))
.userTicket(GsonHelper.getString(jo, "user_ticket"))
.expiresIn(GsonHelper.getString(jo, "expires_in"))
.build();
}

@Override
public WxCpUserDetail getUserDetail(String userTicket) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserdetail";
JsonObject param = new JsonObject();
param.addProperty("user_ticket", userTicket);
String responseText = this.mainService.post(url, param.toString());
String responseText = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(GET_USER_DETAIL), param.toString());
return WxCpGsonBuilder.create().fromJson(responseText, WxCpUserDetail.class);
}
}

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAServiceImpl.java → weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java View File

@@ -5,36 +5,34 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpOAService;
import me.chanjar.weixin.cp.api.WxCpOaService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpApprovalDataResult;
import me.chanjar.weixin.cp.bean.WxCpCheckinData;
import me.chanjar.weixin.cp.bean.WxCpCheckinOption;
import me.chanjar.weixin.cp.bean.WxCpDialRecord;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;

import java.util.Date;
import java.util.List;

import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Oa.*;

/**
* @author Element
* @Package me.chanjar.weixin.cp.api.impl
* @date 2019-04-06 11:20
* @Description: TODO
*/
public class WxCpOAServiceImpl implements WxCpOAService {

private WxCpService mainService;

public WxCpOAServiceImpl(WxCpService mainService) {
this.mainService = mainService;
}
@RequiredArgsConstructor
public class WxCpOaServiceImpl implements WxCpOaService {
private final WxCpService mainService;

@Override
public List<WxCpCheckinData> getCheckinData(Integer openCheckinDataType, Date starttime, Date endtime, List<String> userIdList) throws WxErrorException {
if (starttime == null || endtime == null) {
public List<WxCpCheckinData> getCheckinData(Integer openCheckinDataType, Date startTime, Date endTime,
List<String> userIdList) throws WxErrorException {
if (startTime == null || endTime == null) {
throw new RuntimeException("starttime and endtime can't be null");
}

@@ -42,15 +40,13 @@ public class WxCpOAServiceImpl implements WxCpOAService {
throw new RuntimeException("用户列表不能为空,不超过100个,若用户超过100个,请分批获取");
}

long endtimestamp = endtime.getTime() / 1000L;
long starttimestamp = starttime.getTime() / 1000L;
long endtimestamp = endTime.getTime() / 1000L;
long starttimestamp = startTime.getTime() / 1000L;

if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= 30 * 24 * 60 * 60) {
throw new RuntimeException("获取记录时间跨度不超过一个月");
}

String url = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckindata";

JsonObject jsonObject = new JsonObject();
JsonArray jsonArray = new JsonArray();

@@ -64,7 +60,7 @@ public class WxCpOAServiceImpl implements WxCpOAService {

jsonObject.add("useridlist", jsonArray);

String responseContent = this.mainService.post(url, jsonObject.toString());
String responseContent = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(GET_CHECKIN_DATA), jsonObject.toString());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return WxCpGsonBuilder.create()
.fromJson(
@@ -76,7 +72,6 @@ public class WxCpOAServiceImpl implements WxCpOAService {

@Override
public List<WxCpCheckinOption> getCheckinOption(Date datetime, List<String> userIdList) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckinoption";
if (datetime == null) {
throw new RuntimeException("datetime can't be null");
}
@@ -94,7 +89,7 @@ public class WxCpOAServiceImpl implements WxCpOAService {
jsonObject.addProperty("datetime", datetime.getTime() / 1000L);
jsonObject.add("useridlist", jsonArray);

String responseContent = this.mainService.post(url, jsonObject.toString());
String responseContent = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(GET_CHECKIN_OPTION), jsonObject.toString());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);

return WxCpGsonBuilder.create()
@@ -106,24 +101,20 @@ public class WxCpOAServiceImpl implements WxCpOAService {
}

@Override
public WxCpApprovalDataResult getApprovalData(Date starttime, Date endtime, Long nextSpnum) throws WxErrorException {

String url = "https://qyapi.weixin.qq.com/cgi-bin/corp/getapprovaldata";
public WxCpApprovalDataResult getApprovalData(Date startTime, Date endTime, Long nextSpnum) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("starttime", starttime.getTime() / 1000L);
jsonObject.addProperty("endtime", endtime.getTime() / 1000L);
jsonObject.addProperty("starttime", startTime.getTime() / 1000L);
jsonObject.addProperty("endtime", endTime.getTime() / 1000L);
if (nextSpnum != null) {
jsonObject.addProperty("next_spnum", nextSpnum);
}

String responseContent = this.mainService.post(url, jsonObject.toString());
String responseContent = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(GET_APPROVAL_DATA), jsonObject.toString());
return WxCpGsonBuilder.create().fromJson(responseContent, WxCpApprovalDataResult.class);
}

@Override
public List<WxCpDialRecord> getDialRecord(Date starttime, Date endtime, Integer offset, Integer limit) throws WxErrorException {

String url = "https://qyapi.weixin.qq.com/cgi-bin/dial/get_dial_record";
public List<WxCpDialRecord> getDialRecord(Date startTime, Date endTime, Integer offset, Integer limit) throws WxErrorException {
JsonObject jsonObject = new JsonObject();

if (offset == null) {
@@ -137,10 +128,10 @@ public class WxCpOAServiceImpl implements WxCpOAService {
jsonObject.addProperty("offset", offset);
jsonObject.addProperty("limit", limit);

if (starttime != null && endtime != null) {
if (startTime != null && endTime != null) {

long endtimestamp = endtime.getTime() / 1000L;
long starttimestamp = starttime.getTime() / 1000L;
long endtimestamp = endTime.getTime() / 1000L;
long starttimestamp = startTime.getTime() / 1000L;

if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= 30 * 24 * 60 * 60) {
throw new RuntimeException("受限于网络传输,起止时间的最大跨度为30天,如超过30天,则以结束时间为基准向前取30天进行查询");
@@ -148,11 +139,9 @@ public class WxCpOAServiceImpl implements WxCpOAService {

jsonObject.addProperty("start_time", starttimestamp);
jsonObject.addProperty("end_time", endtimestamp);


}

String responseContent = this.mainService.post(url, jsonObject.toString());
String responseContent = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(GET_DIAL_RECORD), jsonObject.toString());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);

return WxCpGsonBuilder.create()

+ 10
- 8
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java View File

@@ -8,8 +8,8 @@ import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.HttpType;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
import me.chanjar.weixin.cp.api.WxCpOAService;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
@@ -19,9 +19,12 @@ import org.apache.http.impl.client.CloseableHttpClient;

import java.io.IOException;

/**
* @author someone
*/
public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<CloseableHttpClient, HttpHost> {
protected CloseableHttpClient httpClient;
protected HttpHost httpProxy;
private CloseableHttpClient httpClient;
private HttpHost httpProxy;

@Override
public CloseableHttpClient getRequestHttpClient() {
@@ -45,9 +48,8 @@ public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<Closeab
}

synchronized (this.globalAccessTokenRefreshLock) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
+ "&corpid=" + this.configStorage.getCorpId()
+ "&corpsecret=" + this.configStorage.getCorpSecret();
String url = String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN), this.configStorage.getCorpId(), this.configStorage.getCorpSecret());

try {
HttpGet httpGet = new HttpGet(url);
if (this.httpProxy != null) {
@@ -56,8 +58,8 @@ public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<Closeab
httpGet.setConfig(config);
}
String resultContent;
try (CloseableHttpClient httpclient = getRequestHttpClient();
CloseableHttpResponse response = httpclient.execute(httpGet)) {
try (CloseableHttpClient httpClient = getRequestHttpClient();
CloseableHttpResponse response = httpClient.execute(httpGet)) {
resultContent = new BasicResponseHandler().handleResponse(response);
} finally {
httpGet.releaseConnection();


+ 15
- 10
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java View File

@@ -1,17 +1,24 @@
package me.chanjar.weixin.cp.api.impl;

import jodd.http.*;
import jodd.http.HttpConnectionProvider;
import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import jodd.http.JoddHttp;
import jodd.http.ProxyInfo;
import me.chanjar.weixin.common.WxType;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.HttpType;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;

/**
* @author someone
*/
public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionProvider, ProxyInfo> {
protected HttpConnectionProvider httpClient;
protected ProxyInfo httpProxy;

private HttpConnectionProvider httpClient;
private ProxyInfo httpProxy;

@Override
public HttpConnectionProvider getRequestHttpClient() {
@@ -35,11 +42,8 @@ public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionP
}

synchronized (this.globalAccessTokenRefreshLock) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
+ "&corpid=" + this.configStorage.getCorpId()
+ "&corpsecret=" + this.configStorage.getCorpSecret();

HttpRequest request = HttpRequest.get(url);
HttpRequest request = HttpRequest.get(String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN),
this.configStorage.getCorpId(), this.configStorage.getCorpSecret()));
if (this.httpProxy != null) {
httpClient.useProxy(this.httpProxy);
}
@@ -60,7 +64,8 @@ public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionP
@Override
public void initHttp() {
if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) {
httpProxy = new ProxyInfo(ProxyInfo.ProxyType.HTTP, configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort(), configStorage.getHttpProxyUsername(), configStorage.getHttpProxyPassword());
httpProxy = new ProxyInfo(ProxyInfo.ProxyType.HTTP, configStorage.getHttpProxyHost(),
configStorage.getHttpProxyPort(), configStorage.getHttpProxyUsername(), configStorage.getHttpProxyPassword());
}

httpClient = JoddHttp.httpConnectionProvider;


+ 31
- 25
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java View File

@@ -1,5 +1,6 @@
package me.chanjar.weixin.cp.api.impl;

import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.WxType;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.error.WxError;
@@ -11,10 +12,15 @@ import okhttp3.*;

import java.io.IOException;

public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkHttpProxyInfo> {
protected OkHttpClient httpClient;
protected OkHttpProxyInfo httpProxy;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.GET_TOKEN;

/**
* @author someone
*/
@Slf4j
public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkHttpProxyInfo> {
private OkHttpClient httpClient;
private OkHttpProxyInfo httpProxy;

@Override
public OkHttpClient getRequestHttpClient() {
@@ -38,35 +44,35 @@ public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkH
}

synchronized (this.globalAccessTokenRefreshLock) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
+ "&corpid=" + this.configStorage.getCorpId()
+ "&corpsecret=" + this.configStorage.getCorpSecret();
//得到httpClient
OkHttpClient client = getRequestHttpClient();
//请求的request
Request request = new Request.Builder().url(url).get().build();
String resultContent = null;
try {
Response response = client.newCall(request).execute();
resultContent = response.body().string();
} catch (IOException e) {
this.log.error(e.getMessage(), e);
}
//得到httpClient
OkHttpClient client = getRequestHttpClient();
//请求的request
Request request = new Request.Builder()
.url(String.format(this.configStorage.getApiUrl(GET_TOKEN), this.configStorage.getCorpId(), this.configStorage.getCorpSecret()))
.get()
.build();
String resultContent = null;
try {
Response response = client.newCall(request).execute();
resultContent = response.body().string();
} catch (IOException e) {
log.error(e.getMessage(), e);
}

WxError error = WxError.fromJson(resultContent, WxType.CP);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
this.configStorage.updateAccessToken(accessToken.getAccessToken(),
accessToken.getExpiresIn());
WxError error = WxError.fromJson(resultContent, WxType.CP);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
this.configStorage.updateAccessToken(accessToken.getAccessToken(),
accessToken.getExpiresIn());
}
return this.configStorage.getAccessToken();
}

@Override
public void initHttp() {
this.log.debug("WxCpServiceOkHttpImpl initHttp");
log.debug("WxCpServiceOkHttpImpl initHttp");
//设置代理
if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) {
httpProxy = OkHttpProxyInfo.httpProxy(configStorage.getHttpProxyHost(),


+ 33
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOnTpImpl.java View File

@@ -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();
}

}

+ 23
- 26
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImpl.java View File

@@ -1,13 +1,8 @@
package me.chanjar.weixin.cp.api.impl;

import java.util.List;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.WxCpTagService;
@@ -15,27 +10,32 @@ import me.chanjar.weixin.cp.bean.WxCpTag;
import me.chanjar.weixin.cp.bean.WxCpTagAddOrRemoveUsersResult;
import me.chanjar.weixin.cp.bean.WxCpTagGetResult;
import me.chanjar.weixin.cp.bean.WxCpUser;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;

import java.util.List;

import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tag.*;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tag.TAG_CREATE;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tag.TAG_UPDATE;

/**
* <pre>
* 标签管理接口
* 标签管理接口.
* Created by Binary Wang on 2017-6-25.
* @author <a href="https://github.com/binarywang">Binary Wang</a>
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@RequiredArgsConstructor
public class WxCpTagServiceImpl implements WxCpTagService {
private WxCpService mainService;

public WxCpTagServiceImpl(WxCpService mainService) {
this.mainService = mainService;
}
private final WxCpService mainService;

@Override
public String create(String tagName) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/create";
JsonObject o = new JsonObject();
o.addProperty("tagname", tagName);
String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_CREATE);
String responseContent = this.mainService.post(url, o.toString());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return tmpJsonElement.getAsJsonObject().get("tagid").getAsString();
@@ -43,7 +43,7 @@ public class WxCpTagServiceImpl implements WxCpTagService {

@Override
public void update(String tagId, String tagName) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/update";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_UPDATE);
JsonObject o = new JsonObject();
o.addProperty("tagid", tagId);
o.addProperty("tagname", tagName);
@@ -52,13 +52,13 @@ public class WxCpTagServiceImpl implements WxCpTagService {

@Override
public void delete(String tagId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/delete?tagid=" + tagId;
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(TAG_DELETE), tagId);
this.mainService.get(url, null);
}

@Override
public List<WxCpTag> listAll() throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/list";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_LIST);
String responseContent = this.mainService.get(url, null);
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return WxCpGsonBuilder.create()
@@ -71,7 +71,7 @@ public class WxCpTagServiceImpl implements WxCpTagService {

@Override
public List<WxCpUser> listUsersByTagId(String tagId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/get?tagid=" + tagId;
String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(TAG_GET), tagId);
String responseContent = this.mainService.get(url, null);
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return WxCpGsonBuilder.create()
@@ -84,7 +84,7 @@ public class WxCpTagServiceImpl implements WxCpTagService {

@Override
public WxCpTagAddOrRemoveUsersResult addUsers2Tag(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/addtagusers";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_ADD_TAG_USERS);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("tagid", tagId);
this.addUserIdsAndPartyIdsToJson(userIds, partyIds, jsonObject);
@@ -94,7 +94,7 @@ public class WxCpTagServiceImpl implements WxCpTagService {

@Override
public WxCpTagAddOrRemoveUsersResult removeUsersFromTag(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/deltagusers";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_DEL_TAG_USERS);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("tagid", tagId);
this.addUserIdsAndPartyIdsToJson(userIds, partyIds, jsonObject);
@@ -122,15 +122,12 @@ public class WxCpTagServiceImpl implements WxCpTagService {

@Override
public WxCpTagGetResult get(String tagId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/get";
if (tagId != null) {
url += "?tagId=" + tagId;
} else {
if (tagId == null) {
throw new IllegalArgumentException("缺少tagId参数");
}

String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(TAG_GET), tagId);
String responseContent = this.mainService.get(url, null);

return WxCpTagGetResult.fromJson(responseContent);
}
}

+ 4
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImpl.java View File

@@ -5,11 +5,14 @@ import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.WxCpTaskCardService;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.TaskCard.*;

/**
* <pre>
* 任务卡片管理接口.
@@ -33,7 +36,7 @@ public class WxCpTaskCardServiceImpl implements WxCpTaskCardService {
data.put("task_id", taskId);
data.put("clicked_key", clickedKey);

String url = "https://qyapi.weixin.qq.com/cgi-bin/message/update_taskcard";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(UPDATE_TASK_CARD);
this.mainService.post(url, WxGsonBuilder.create().toJson(data));
}
}

+ 114
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTpServiceApacheHttpClientImpl.java View File

@@ -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;
}

}

+ 12
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTpServiceImpl.java View File

@@ -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 {
}

+ 35
- 29
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImpl.java View File

@@ -1,23 +1,23 @@
package me.chanjar.weixin.cp.api.impl;

import java.util.List;
import java.util.Map;

import com.google.common.collect.Maps;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.WxCpUserService;
import me.chanjar.weixin.cp.bean.WxCpInviteResult;
import me.chanjar.weixin.cp.bean.WxCpUser;
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;

import java.util.List;
import java.util.Map;

import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.User.*;

/**
* <pre>
* Created by BinaryWang on 2017/6/24.
@@ -25,59 +25,54 @@ import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@RequiredArgsConstructor
public class WxCpUserServiceImpl implements WxCpUserService {
private WxCpService mainService;

public WxCpUserServiceImpl(WxCpService mainService) {
this.mainService = mainService;
}
private final WxCpService mainService;

@Override
public void authenticate(String userId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?userid=" + userId;
this.mainService.get(url, null);
this.mainService.get(this.mainService.getWxCpConfigStorage().getApiUrl(USER_AUTHENTICATE + userId), null);
}

@Override
public void create(WxCpUser user) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/create";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_CREATE);
this.mainService.post(url, user.toJson());
}

@Override
public void update(WxCpUser user) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/update";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_UPDATE);
this.mainService.post(url, user.toJson());
}

@Override
public void delete(String... userIds) throws WxErrorException {
if (userIds.length == 1) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/delete?userid=" + userIds[0];
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_DELETE + userIds[0]);
this.mainService.get(url, null);
return;
}

String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete";
JsonObject jsonObject = new JsonObject();
JsonArray jsonArray = new JsonArray();
for (String userid : userIds) {
jsonArray.add(new JsonPrimitive(userid));
for (String userId : userIds) {
jsonArray.add(new JsonPrimitive(userId));
}

jsonObject.add("useridlist", jsonArray);
this.mainService.post(url, jsonObject.toString());
this.mainService.post(USER_BATCH_DELETE, jsonObject.toString());
}

@Override
public WxCpUser getById(String userid) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?userid=" + userid;
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_GET + userid);
String responseContent = this.mainService.get(url, null);
return WxCpUser.fromJson(responseContent);
}

@Override
public List<WxCpUser> listByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list?department_id=" + departId;
String params = "";
if (fetchChild != null) {
params += "&fetch_child=" + (fetchChild ? "1" : "0");
@@ -88,6 +83,7 @@ public class WxCpUserServiceImpl implements WxCpUserService {
params += "&status=0";
}

String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_LIST + departId);
String responseContent = this.mainService.get(url, params);
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return WxCpGsonBuilder.create()
@@ -99,7 +95,6 @@ public class WxCpUserServiceImpl implements WxCpUserService {

@Override
public List<WxCpUser> listSimpleByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?department_id=" + departId;
String params = "";
if (fetchChild != null) {
params += "&fetch_child=" + (fetchChild ? "1" : "0");
@@ -110,6 +105,7 @@ public class WxCpUserServiceImpl implements WxCpUserService {
params += "&status=0";
}

String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_SIMPLE_LIST + departId);
String responseContent = this.mainService.get(url, params);
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return WxCpGsonBuilder.create()
@@ -122,7 +118,6 @@ public class WxCpUserServiceImpl implements WxCpUserService {

@Override
public WxCpInviteResult invite(List<String> userIds, List<String> partyIds, List<String> tagIds) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/invite";
JsonObject jsonObject = new JsonObject();
if (userIds != null) {
JsonArray jsonArray = new JsonArray();
@@ -148,12 +143,13 @@ public class WxCpUserServiceImpl implements WxCpUserService {
jsonObject.add("tag", jsonArray);
}

String url = this.mainService.getWxCpConfigStorage().getApiUrl(BATCH_INVITE);
return WxCpInviteResult.fromJson(this.mainService.post(url, jsonObject.toString()));
}

@Override
public Map<String, String> userId2Openid(String userId, Integer agentId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_openid";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_CONVERT_TO_OPENID);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("userid", userId);
if (agentId != null) {
@@ -176,9 +172,19 @@ public class WxCpUserServiceImpl implements WxCpUserService {

@Override
public String openid2UserId(String openid) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_userid";
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("openid", openid);
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_CONVERT_TO_USERID);
String responseContent = this.mainService.post(url, jsonObject.toString());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return tmpJsonElement.getAsJsonObject().get("userid").getAsString();
}

@Override
public String getUserId(String mobile) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("mobile", mobile);
String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_USER_ID);
String responseContent = this.mainService.post(url, jsonObject.toString());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return tmpJsonElement.getAsJsonObject().get("userid").getAsString();
@@ -186,7 +192,7 @@ public class WxCpUserServiceImpl implements WxCpUserService {

@Override
public WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/crm/get_external_contact?external_userid=" + userId;
String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_EXTERNAL_CONTACT + userId);
String responseContent = this.mainService.get(url, null);
return WxCpUserExternalContactInfo.fromJson(responseContent);
}


+ 1
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAppChatMessage.java View File

@@ -6,7 +6,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.cp.WxCpConsts.AppChatMsgType;
import me.chanjar.weixin.cp.constant.WxCpConsts.AppChatMsgType;
import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;



+ 46
- 12
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java View File

@@ -8,11 +8,15 @@ import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;
import me.chanjar.weixin.cp.bean.messagebuilder.*;
import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton;
import me.chanjar.weixin.cp.constant.WxCpConsts;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static me.chanjar.weixin.common.api.WxConsts.KefuMsgType.*;

/**
* 消息.
@@ -40,9 +44,13 @@ public class WxCpMessage implements Serializable {
private String btnTxt;
private List<NewArticle> articles = new ArrayList<>();
private List<MpnewsArticle> mpnewsArticles = new ArrayList<>();
private String appId;
private String page;
private Boolean emphasisFirstItem;
private Map<String, String> contentItems;

/**
* 任务卡片特有的属性
* 任务卡片特有的属性.
*/
private String taskId;
private List<TaskCardButton> taskButtons = new ArrayList<>();
@@ -117,10 +125,16 @@ public class WxCpMessage implements Serializable {
return new TaskCardBuilder();
}

/**
* 获得小程序通知消息builder.
*/
public static MiniProgramNoticeMsgBuilder newMiniProgramNoticeBuilder() {
return new MiniProgramNoticeMsgBuilder();
}

/**
* <pre>
* 请使用
* 请使用.
* {@link KefuMsgType#TEXT}
* {@link KefuMsgType#IMAGE}
* {@link KefuMsgType#VOICE}
@@ -130,6 +144,7 @@ public class WxCpMessage implements Serializable {
* {@link KefuMsgType#MPNEWS}
* {@link KefuMsgType#MARKDOWN}
* {@link KefuMsgType#TASKCARD}
* {@link KefuMsgType#MINIPROGRAM_NOTICE}
* </pre>
*
* @param msgType 消息类型
@@ -169,19 +184,19 @@ public class WxCpMessage implements Serializable {

private void handleMsgType(JsonObject messageJson) {
switch (this.getMsgType()) {
case KefuMsgType.TEXT: {
case TEXT: {
JsonObject text = new JsonObject();
text.addProperty("content", this.getContent());
messageJson.add("text", text);
break;
}
case KefuMsgType.MARKDOWN: {
case MARKDOWN: {
JsonObject text = new JsonObject();
text.addProperty("content", this.getContent());
messageJson.add("markdown", text);
break;
}
case KefuMsgType.TEXTCARD: {
case TEXTCARD: {
JsonObject text = new JsonObject();
text.addProperty("title", this.getTitle());
text.addProperty("description", this.getDescription());
@@ -190,25 +205,25 @@ public class WxCpMessage implements Serializable {
messageJson.add("textcard", text);
break;
}
case KefuMsgType.IMAGE: {
case IMAGE: {
JsonObject image = new JsonObject();
image.addProperty("media_id", this.getMediaId());
messageJson.add("image", image);
break;
}
case KefuMsgType.FILE: {
case FILE: {
JsonObject image = new JsonObject();
image.addProperty("media_id", this.getMediaId());
messageJson.add("file", image);
break;
}
case KefuMsgType.VOICE: {
case VOICE: {
JsonObject voice = new JsonObject();
voice.addProperty("media_id", this.getMediaId());
messageJson.add("voice", voice);
break;
}
case KefuMsgType.VIDEO: {
case VIDEO: {
JsonObject video = new JsonObject();
video.addProperty("media_id", this.getMediaId());
video.addProperty("thumb_media_id", this.getThumbMediaId());
@@ -217,7 +232,7 @@ public class WxCpMessage implements Serializable {
messageJson.add("video", video);
break;
}
case KefuMsgType.NEWS: {
case NEWS: {
JsonObject newsJsonObject = new JsonObject();
JsonArray articleJsonArray = new JsonArray();
for (NewArticle article : this.getArticles()) {
@@ -232,7 +247,7 @@ public class WxCpMessage implements Serializable {
messageJson.add("news", newsJsonObject);
break;
}
case KefuMsgType.MPNEWS: {
case MPNEWS: {
JsonObject newsJsonObject = new JsonObject();
if (this.getMediaId() != null) {
newsJsonObject.addProperty("media_id", this.getMediaId());
@@ -255,7 +270,7 @@ public class WxCpMessage implements Serializable {
messageJson.add("mpnews", newsJsonObject);
break;
}
case KefuMsgType.TASKCARD: {
case TASKCARD: {
JsonObject text = new JsonObject();
text.addProperty("title", this.getTitle());
text.addProperty("description", this.getDescription());
@@ -291,6 +306,25 @@ public class WxCpMessage implements Serializable {
messageJson.add("taskcard", text);
break;
}
case MINIPROGRAM_NOTICE: {
JsonObject notice = new JsonObject();
notice.addProperty("appid", this.getAppId());
notice.addProperty("page", this.getPage());
notice.addProperty("description", this.getDescription());
notice.addProperty("title", this.getTitle());
notice.addProperty("emphasis_first_item", this.getEmphasisFirstItem());
JsonArray content = new JsonArray();
for (Map.Entry<String, String> item : this.getContentItems().entrySet()) {
JsonObject articleJson = new JsonObject();
articleJson.addProperty("key", item.getKey());
articleJson.addProperty("value", item.getValue());
content.add(articleJson);
}
notice.add("content_item", content);

messageJson.add("miniprogram_notice", notice);
break;
}
default: {
// do nothing
}


+ 28
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpOauth2UserInfo.java View File

@@ -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;
}

+ 3
- 3
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagGetResult.java View File

@@ -29,19 +29,19 @@ public class WxCpTagGetResult implements Serializable {
private String errmsg;

/**
* 用户列表
* 用户列表.
*/
@SerializedName("userlist")
private List<WxCpUser> userlist;

/**
* 部门列表
* 部门列表.
*/
@SerializedName("partylist")
private List<Integer> partylist;

/**
* 标签名称
* 标签名称.
*/
@SerializedName("tagname")
private String tagname;


+ 42
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpCorp.java View File

@@ -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);
}
}

+ 68
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlMessage.java View File

@@ -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;
}
}

+ 55
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlPackage.java View File

@@ -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;
}
}

+ 5
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java View File

@@ -31,6 +31,11 @@ public class WxCpUser implements Serializable {
private Integer status;
private Integer enable;
private Integer isLeader;
/**
* is_leader_in_dept.
* 个数必须和department一致,表示在所在的部门内是否为上级。1表示为上级,0表示非上级。在审批等应用里可以用来标识上级审批人
*/
private Integer[] isLeaderInDept;
private final List<Attr> extAttrs = new ArrayList<>();
private Integer hideMobile;
private String englishName;


+ 20
- 8
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserExternalContactInfo.java View File

@@ -1,16 +1,11 @@
package me.chanjar.weixin.cp.bean;

import java.util.List;

import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.*;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;

import java.util.List;

/**
* <pre>
* 外部联系人详情
@@ -118,9 +113,26 @@ public class WxCpUserExternalContactInfo {
private String description;
@SerializedName("createtime")
private Long createTime;
private String state;
@SerializedName("remark_company")
private String remarkCompany;
@SerializedName("remark_mobiles")
private String[] remarkMobiles;
private Tag[] tags;

}

public static WxCpUserExternalContactInfo fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalContactInfo.class);
}

@Setter
@Getter
public static class Tag {
@SerializedName("group_name")
private String groupName;
@SerializedName("tag_name")
private String tagName;
private int type;
}
}

+ 58
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserExternalContactList.java View File

@@ -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);
}
}

+ 49
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserWithExternalPermission.java View File

@@ -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);
}
}

+ 30
- 4
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java View File

@@ -162,7 +162,7 @@ public class WxCpXmlMessage implements Serializable {

/**
* 通讯录变更事件.
* 请参考常量 me.chanjar.weixin.cp.WxCpConsts.ContactChangeType
* 请参考常量 me.chanjar.weixin.cp.constant.WxCpConsts.ContactChangeType
*/
@XStreamAlias("ChangeType")
@XStreamConverter(value = XStreamCDataConverter.class)
@@ -175,6 +175,26 @@ public class WxCpXmlMessage implements Serializable {
@XStreamConverter(value = XStreamCDataConverter.class)
private String userId;

/**
* 变更信息的外部联系人的userid,注意不是企业成员的帐号.
*/
@XStreamAlias("ExternalUserID")
@XStreamConverter(value = XStreamCDataConverter.class)
private String externalUserId;

/**
* 添加此用户的「联系我」方式配置的state参数,可用于识别添加此用户的渠道.
*/
@XStreamAlias("State")
@XStreamConverter(value = XStreamCDataConverter.class)
private String state;

/**
* 欢迎语code,可用于发送欢迎语.
*/
@XStreamAlias("WelcomeCode")
@XStreamConverter(value = XStreamCDataConverter.class)
private String welcomeCode;
/**
* 新的UserID,变更时推送(userid由系统生成时可更改一次).
*/
@@ -191,11 +211,11 @@ public class WxCpXmlMessage implements Serializable {
private String name;

/**
* 成员部门列表.
* 成员部门列表,变更时推送,仅返回该应用有查看权限的部门id.
*/
@XStreamAlias("Department")
@XStreamConverter(value = XStreamCDataConverter.class)
private String department;
private Long[] departments;

/**
* 手机号码.
@@ -244,6 +264,12 @@ public class WxCpXmlMessage implements Serializable {
@XStreamAlias("IsLeader")
private Integer isLeader;

/**
* 表示所在部门是否为上级,0-否,1-是,顺序与Department字段的部门逐一对应.
*/
@XStreamAlias("IsLeaderInDept")
private Integer[] isLeaderInDept;

/**
* 座机.
*/
@@ -268,7 +294,7 @@ public class WxCpXmlMessage implements Serializable {
* 部门Id.
*/
@XStreamAlias("Id")
private Integer id;
private Long id;

/**
* 父部门id.


+ 69
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MiniProgramNoticeMsgBuilder.java View File

@@ -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;
}
}

+ 21
- 7
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java View File

@@ -6,18 +6,32 @@ import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import java.io.File;

/**
* 微信客户端配置存储
* 微信客户端配置存储.
*
* @author Daniel Qian
*/
public interface WxCpConfigStorage {

/**
* 设置企业微信服务器 baseUrl.
* 默认值是 https://qyapi.weixin.qq.com , 如果使用默认值,则不需要调用 setBaseApiUrl
*
* @param baseUrl 企业微信服务器 Url
*/
void setBaseApiUrl(String baseUrl);

/**
* 读取企业微信 API Url.
* 支持私有化企业微信服务器.
*/
String getApiUrl(String path);

String getAccessToken();

boolean isAccessTokenExpired();

/**
* 强制将access token过期掉
* 强制将access token过期掉.
*/
void expireAccessToken();

@@ -30,12 +44,12 @@ public interface WxCpConfigStorage {
boolean isJsapiTicketExpired();

/**
* 强制将jsapi ticket过期掉
* 强制将jsapi ticket过期掉.
*/
void expireJsapiTicket();

/**
* 应该是线程安全的
* 应该是线程安全的.
*/
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds);

@@ -44,12 +58,12 @@ public interface WxCpConfigStorage {
boolean isAgentJsapiTicketExpired();

/**
* 强制将jsapi ticket过期掉
* 强制将jsapi ticket过期掉.
*/
void expireAgentJsapiTicket();

/**
* 应该是线程安全的
* 应该是线程安全的.
*/
void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds);

@@ -78,7 +92,7 @@ public interface WxCpConfigStorage {
File getTmpDirFile();

/**
* http client builder
* http client builder.
*
* @return ApacheHttpClientBuilder
*/


+ 86
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpTpConfigStorage.java View File

@@ -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();
}

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpInMemoryConfigStorage.java → weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java View File

@@ -1,44 +1,63 @@
package me.chanjar.weixin.cp.config;
package me.chanjar.weixin.cp.config.impl;

import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;

import java.io.File;
import java.io.Serializable;

/**
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化.
*
* @author Daniel Qian
*/
public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
public class WxCpDefaultConfigImpl implements WxCpConfigStorage, Serializable {
private static final long serialVersionUID = 1154541446729462780L;

protected volatile String corpId;
protected volatile String corpSecret;
private volatile String corpId;
private volatile String corpSecret;

protected volatile String token;
private volatile String token;
protected volatile String accessToken;
protected volatile String aesKey;
private volatile String aesKey;
protected volatile Integer agentId;
protected volatile long expiresTime;
private volatile long expiresTime;

protected volatile String oauth2redirectUri;
private volatile String oauth2redirectUri;

protected volatile String httpProxyHost;
protected volatile int httpProxyPort;
protected volatile String httpProxyUsername;
protected volatile String httpProxyPassword;
private volatile String httpProxyHost;
private volatile int httpProxyPort;
private volatile String httpProxyUsername;
private volatile String httpProxyPassword;

protected volatile String jsapiTicket;
protected volatile long jsapiTicketExpiresTime;
private volatile String jsapiTicket;
private volatile long jsapiTicketExpiresTime;

protected volatile String agentJsapiTicket;
protected volatile long agentJsapiTicketExpiresTime;
private volatile String agentJsapiTicket;
private volatile long agentJsapiTicketExpiresTime;

protected volatile File tmpDirFile;
private volatile File tmpDirFile;

private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;

private volatile String baseApiUrl;

@Override
public void setBaseApiUrl(String baseUrl) {
this.baseApiUrl = baseUrl;
}

@Override
public String getApiUrl(String path) {
if (baseApiUrl == null) {
baseApiUrl = WxCpApiPathConsts.DEFAULT_CP_BASE_URL;
}
return baseApiUrl + path;
}

@Override
public String getAccessToken() {
return this.accessToken;

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpJedisConfigStorage.java → weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java View File

@@ -1,7 +1,9 @@
package me.chanjar.weixin.cp.config;
package me.chanjar.weixin.cp.config.impl;

import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@@ -16,7 +18,7 @@ import java.io.File;
*
* @author gaigeshen
*/
public class WxCpJedisConfigStorage implements WxCpConfigStorage {
public class WxCpRedisConfigImpl implements WxCpConfigStorage {
private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN";
private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME";
private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET";
@@ -38,23 +40,38 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage {
private volatile File tmpDirFile;
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;

public WxCpJedisConfigStorage(JedisPool jedisPool) {
protected volatile String baseApiUrl;

@Override
public void setBaseApiUrl(String baseUrl) {
this.baseApiUrl = baseUrl;
}

@Override
public String getApiUrl(String path) {
if (baseApiUrl == null) {
baseApiUrl = WxCpApiPathConsts.DEFAULT_CP_BASE_URL;
}
return baseApiUrl + path;
}

public WxCpRedisConfigImpl(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}

public WxCpJedisConfigStorage(String host, int port) {
public WxCpRedisConfigImpl(String host, int port) {
jedisPool = new JedisPool(host, port);
}

public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port) {
public WxCpRedisConfigImpl(JedisPoolConfig poolConfig, String host, int port) {
jedisPool = new JedisPool(poolConfig, host, port);
}

public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port, int timeout, String password) {
public WxCpRedisConfigImpl(JedisPoolConfig poolConfig, String host, int port, int timeout, String password) {
jedisPool = new JedisPool(poolConfig, host, port, timeout, password);
}

public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port, int timeout, String password, int database) {
public WxCpRedisConfigImpl(JedisPoolConfig poolConfig, String host, int port, int timeout, String password, int database) {
jedisPool = new JedisPool(poolConfig, host, port, timeout, password, database);
}


+ 248
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpTpDefaultConfigImpl.java View File

@@ -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;
}
}

+ 113
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java View File

@@ -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";
}
}

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/WxCpConsts.java → weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java View File

@@ -1,4 +1,4 @@
package me.chanjar.weixin.cp;
package me.chanjar.weixin.cp.constant;

/**
* <pre>
@@ -89,6 +89,26 @@ public class WxCpConsts {
*/
public static final String TASKCARD_CLICK = "taskcard_click";

/**
* 企业成员添加外部联系人事件推送
*/
public static final String CHANGE_EXTERNAL_CONTACT = "change_external_contact";


}

/**
* 企业外部联系人变更事件的CHANGE_TYPE
*/
public static class ExternalContactChangeType {
/**
* 新增外部联系人
*/
public static final String ADD_EXTERNAL_CONTACT = "add_external_contact";
/**
* 删除外部联系人
*/
public static final String DEL_EXTERNAL_CONTACT = "del_external_contact";
}

/**

+ 52
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpTpCryptUtil.java View File

@@ -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 + "=");
}
}

+ 8
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java View File

@@ -70,6 +70,7 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri
user.setStatus(GsonHelper.getInteger(o, "status"));
user.setEnable(GsonHelper.getInteger(o, "enable"));
user.setIsLeader(GsonHelper.getInteger(o, "isleader"));
user.setIsLeaderInDept(GsonHelper.getIntArray(o, "is_leader_in_dept"));
user.setHideMobile(GsonHelper.getInteger(o, "hide_mobile"));
user.setEnglishName(GsonHelper.getString(o, "english_name"));
user.setTelephone(GsonHelper.getString(o, "telephone"));
@@ -197,6 +198,13 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri
if (user.getIsLeader() != null) {
o.addProperty("isleader", user.getIsLeader());
}
if (user.getIsLeaderInDept() != null && user.getIsLeaderInDept().length > 0) {
JsonArray ary = new JsonArray();
for (int item : user.getIsLeaderInDept()) {
ary.add(item);
}
o.add("is_leader_in_dept", ary);
}
if (user.getHideMobile() != null) {
o.addProperty("hide_mobile", user.getHideMobile());
}


+ 135
- 117
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java View File

@@ -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;
}
}

+ 14
- 2
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java View File

@@ -12,7 +12,7 @@ import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage;
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;

public class ApiTestModule implements Module {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@@ -44,7 +44,7 @@ public class ApiTestModule implements Module {
}

@XStreamAlias("xml")
public static class WxXmlCpInMemoryConfigStorage extends WxCpInMemoryConfigStorage {
public static class WxXmlCpInMemoryConfigStorage extends WxCpDefaultConfigImpl {

protected String userId;

@@ -52,6 +52,8 @@ public class ApiTestModule implements Module {

protected String tagId;

protected String externalUserId;

public String getUserId() {
return this.userId;
}
@@ -76,12 +78,22 @@ public class ApiTestModule implements Module {
this.tagId = tagId;
}

public String getExternalUserId() {
return externalUserId;
}

public void setExternalUserId(String externalUserId) {
this.externalUserId = externalUserId;
}

@Override
public String toString() {
return super.toString() + " > WxXmlCpConfigStorage{" +
"userId='" + this.userId + '\'' +
", departmentId='" + this.departmentId + '\'' +
", tagId='" + this.tagId + '\'' +
", externalUserId='" + this.externalUserId + '\'' +

'}';
}
}


+ 3
- 2
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java View File

@@ -1,5 +1,6 @@
package me.chanjar.weixin.cp.api;

import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.RequestExecutor;
@@ -13,8 +14,8 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;

@Test
@Slf4j
public class WxCpBusyRetryTest {

@DataProvider(name = "getService")
public Object[][] getService() {
WxCpService service = new WxCpServiceImpl() {
@@ -23,7 +24,7 @@ public class WxCpBusyRetryTest {
public synchronized <T, E> T executeInternal(
RequestExecutor<T, E> executor, String uri, E data)
throws WxErrorException {
this.log.info("Executed");
log.info("Executed");
throw new WxErrorException(WxError.builder().errorCode(-1).build());
}
};


+ 24
- 0
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java View File

@@ -1,5 +1,6 @@
package me.chanjar.weixin.cp.api;

import com.google.common.collect.ImmutableMap;
import org.testng.annotations.*;

import com.google.inject.Inject;
@@ -107,4 +108,27 @@ public class WxCpMessageAPITest {
System.out.println(messageSendResult.getInvalidUserList());
System.out.println(messageSendResult.getInvalidTagList());
}

@Test
public void testSendMessage_miniprogram_notice() throws WxErrorException {
WxCpMessage message = WxCpMessage
.newMiniProgramNoticeBuilder()
.toUser(configStorage.getUserId())
.appId("wx123123123123123")
.page("pages/index?userid=zhangsan&orderid=123123123")
.title("会议室预订成功通知")
.description("4月27日 16:16")
.emphasisFirstItem(true)
.contentItems(ImmutableMap.of("会议室","402",
"会议地点","广州TIT-402会议室",
"会议时间","2018年8月1日 09:00-09:30"))
.build();

WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message);
assertNotNull(messageSendResult);
System.out.println(messageSendResult);
System.out.println(messageSendResult.getInvalidPartyList());
System.out.println(messageSendResult.getInvalidUserList());
System.out.println(messageSendResult.getInvalidTagList());
}
}

+ 2
- 2
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImplTest.java View File

@@ -6,7 +6,7 @@ import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.WxCpAgentService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpAgent;
import org.testng.Assert;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;

@@ -71,7 +71,7 @@ public class WxCpAgentServiceImplTest {
@Test
public void testGet() throws Exception {
String returnJson = "{\"errcode\": 0,\"errmsg\": \"ok\",\"agentid\": 9,\"name\": \"测试应用\",\"square_logo_url\": \"http://wx.qlogo.cn/mmhead/alksjf;lasdjf;lasjfuodiuj3rj2o34j/0\",\"description\": \"这是一个企业号应用\",\"allow_userinfos\": {\"user\": [{\"userid\": \"0009854\"}, {\"userid\": \"1723\"}, {\"userid\": \"5625\"}]},\"allow_partys\": {\"partyid\": [42762742]},\"allow_tags\": {\"tagid\": [23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7]},\"close\": 0,\"redirect_domain\": \"weixin.com.cn\",\"report_location_flag\": 0,\"isreportenter\": 0,\"home_url\": \"\"}";
when(wxService.get("https://qyapi.weixin.qq.com/cgi-bin/agent/get?agentid=9", null)).thenReturn(returnJson);
when(wxService.get(String.format(wxService.getWxCpConfigStorage().getApiUrl(WxCpApiPathConsts.Agent.AGENT_GET), 9), null)).thenReturn(returnJson);
when(wxService.getAgentService()).thenReturn(new WxCpAgentServiceImpl(wxService));

WxCpAgentService wxAgentService = this.wxService.getAgentService();


+ 1
- 1
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImplTest.java View File

@@ -8,7 +8,7 @@ import org.testng.annotations.*;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.WxCpConsts.AppChatMsgType;
import me.chanjar.weixin.cp.constant.WxCpConsts.AppChatMsgType;
import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage;


+ 45
- 0
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java View File

@@ -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);
}
}

+ 11
- 1
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImplTest.java View File

@@ -4,10 +4,13 @@ import com.google.inject.Inject;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo;
import me.chanjar.weixin.cp.bean.WxCpUserDetail;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;

import static org.assertj.core.api.Assertions.assertThat;

/**
* <pre>
* Created by BinaryWang on 2018/4/22.
@@ -28,6 +31,13 @@ public class WxCpOAuth2ServiceImplTest {

@Test
public void testGetUserInfo() throws WxErrorException {
this.wxService.getOauth2Service().getUserInfo("abc");
final WxCpOauth2UserInfo result = this.wxService.getOauth2Service().getUserInfo("abc");
assertThat(result).isNotNull();
System.out.println(result);
}

@Test
public void testBuildAuthorizationUrl() {
}

}

weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOAServiceImplTest.java → weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java View File

@@ -24,7 +24,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @date 2019-04-20 13:46
*/
@Guice(modules = ApiTestModule.class)
public class WxCpOAServiceImplTest {
public class WxCpOaServiceImplTest {
@Inject
protected WxCpService wxService;


+ 5
- 5
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImplTest.java View File

@@ -10,6 +10,7 @@ import me.chanjar.weixin.cp.bean.WxCpTag;
import me.chanjar.weixin.cp.bean.WxCpTagAddOrRemoveUsersResult;
import me.chanjar.weixin.cp.bean.WxCpTagGetResult;
import me.chanjar.weixin.cp.bean.WxCpUser;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;

@@ -22,10 +23,10 @@ import static org.testng.Assert.assertNotEquals;

/**
* <pre>
*
* Created by Binary Wang on 2017-6-25.
* @author <a href="https://github.com/binarywang">Binary Wang</a>
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Guice(modules = ApiTestModule.class)
public class WxCpTagServiceImplTest {
@@ -35,7 +36,7 @@ public class WxCpTagServiceImplTest {
@Inject
protected ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage;

protected String tagId;
private String tagId;

@Test
public void testCreate() throws Exception {
@@ -83,7 +84,7 @@ public class WxCpTagServiceImplTest {
public void testGet() throws WxErrorException {
String apiResultJson = "{\"errcode\": 0,\"errmsg\": \"ok\",\"userlist\": [{\"userid\": \"0124035\",\"name\": \"王五\"},{\"userid\": \"0114035\",\"name\": \"梦雪\"}],\"partylist\": [9576,9567,9566],\"tagname\": \"测试标签-001\"}";
WxCpService wxService = mock(WxCpService.class);
when(wxService.get("https://qyapi.weixin.qq.com/cgi-bin/tag/get?tagId=150", null)).thenReturn(apiResultJson);
when(wxService.get(String.format(wxService.getWxCpConfigStorage().getApiUrl(WxCpApiPathConsts.Tag.TAG_GET), 150), null)).thenReturn(apiResultJson);
when(wxService.getTagService()).thenReturn(new WxCpTagServiceImpl(wxService));

WxCpTagService wxCpTagService = wxService.getTagService();
@@ -96,7 +97,6 @@ public class WxCpTagServiceImplTest {
assertEquals(3, wxCpTagGetResult.getPartylist().size());
assertEquals("测试标签-001", wxCpTagGetResult.getTagname());


}

}

+ 8
- 3
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java View File

@@ -93,7 +93,7 @@ public class WxCpUserServiceImplTest {
@Test
public void testInvite() throws Exception {
WxCpInviteResult result = this.wxCpService.getUserService().invite(
Lists.newArrayList(userId), null,null);
Lists.newArrayList(userId), null, null);
System.out.println(result);
}

@@ -111,10 +111,15 @@ public class WxCpUserServiceImplTest {
assertNotNull(result);
}


@Test
public void testGetExternalContact() throws WxErrorException {
WxCpUserExternalContactInfo result = this.wxCpService.getUserService().getExternalContact(userId);
public void testGetUserId() throws WxErrorException {
String result = this.wxCpService.getUserService().getUserId("xxx");
System.out.println(result);
assertNotNull(result);
}

@Test
public void testGetExternalContact() {
}
}

+ 51
- 2
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java View File

@@ -1,9 +1,10 @@
package me.chanjar.weixin.cp.bean;

import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.cp.constant.WxCpConsts;
import org.testng.annotations.Test;

import static me.chanjar.weixin.cp.WxCpConsts.EventType.TASKCARD_CLICK;
import static me.chanjar.weixin.cp.constant.WxCpConsts.EventType.TASKCARD_CLICK;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;

@@ -106,7 +107,7 @@ public class WxCpXmlMessageTest {
"<Count>2</Count>" +
"</SendPicsInfo>" +
"</xml>";
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml.replace("</PicList><PicList>",""));
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml.replace("</PicList><PicList>", ""));
assertEquals(wxMessage.getToUserName(), "wx45a0972125658be9");
assertEquals(wxMessage.getFromUserName(), "xiaohe");
assertEquals(wxMessage.getCreateTime(), new Long(1502012364L));
@@ -173,4 +174,52 @@ public class WxCpXmlMessageTest {
assertEquals(wxMessage.getEventKey(), "key111");
assertEquals(wxMessage.getTaskId(), "taskid111");
}

public void testAddExternalUserEvent() {
String xml = "<xml>" +
"<ToUserName><![CDATA[toUser]]></ToUserName>" +
"<FromUserName><![CDATA[sys]]></FromUserName>" +
"<CreateTime>1403610513</CreateTime>" +
"<MsgType><![CDATA[event]]></MsgType>" +
"<Event><![CDATA[change_external_contact]]></Event>" +
"<ChangeType><![CDATA[add_external_contact]]></ChangeType>" +
"<UserID><![CDATA[zhangsan]]></UserID>" +
"<ExternalUserID><![CDATA[woAJ2GCAAAXtWyujaWJHDDGi0mACH71w]]></ExternalUserID>" +
"<State><![CDATA[teststate]]></State>" +
"<WelcomeCode><![CDATA[WELCOMECODE]]></WelcomeCode>" +
"</xml >";
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml);
assertEquals(wxMessage.getToUserName(), "toUser");
assertEquals(wxMessage.getFromUserName(), "sys");
assertEquals(wxMessage.getCreateTime(), Long.valueOf(1403610513L));
assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT);
assertEquals(wxMessage.getEvent(), WxCpConsts.EventType.CHANGE_EXTERNAL_CONTACT);
assertEquals(wxMessage.getChangeType(), WxCpConsts.ExternalContactChangeType.ADD_EXTERNAL_CONTACT);
assertEquals(wxMessage.getExternalUserId(), "woAJ2GCAAAXtWyujaWJHDDGi0mACH71w");
assertEquals(wxMessage.getState(), "teststate");
assertEquals(wxMessage.getWelcomeCode(), "WELCOMECODE");

}

public void testDelExternalUserEvent() {
String xml = "<xml>" +
"<ToUserName><![CDATA[toUser]]></ToUserName>" +
"<FromUserName><![CDATA[sys]]></FromUserName>" +
"<CreateTime>1403610513</CreateTime>" +
"<MsgType><![CDATA[event]]></MsgType>" +
"<Event><![CDATA[change_external_contact]]></Event>" +
"<ChangeType><![CDATA[del_external_contact]]></ChangeType>" +
"<UserID><![CDATA[zhangsan]]></UserID>" +
"<ExternalUserID><![CDATA[woAJ2GCAAAXtWyujaWJHDDGi0mACH71w]]></ExternalUserID>" +
"</xml>";
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml);
assertEquals(wxMessage.getToUserName(), "toUser");
assertEquals(wxMessage.getFromUserName(), "sys");
assertEquals(wxMessage.getCreateTime(), Long.valueOf(1403610513L));
assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT);
assertEquals(wxMessage.getEvent(), WxCpConsts.EventType.CHANGE_EXTERNAL_CONTACT);
assertEquals(wxMessage.getChangeType(), WxCpConsts.ExternalContactChangeType.DEL_EXTERNAL_CONTACT);
assertEquals(wxMessage.getUserId(), "zhangsan");
assertEquals(wxMessage.getExternalUserId(), "woAJ2GCAAAXtWyujaWJHDDGi0mACH71w");
}
}

+ 2
- 2
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java View File

@@ -6,14 +6,14 @@ import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.ToString;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage;
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;

/**
* @author Daniel Qian
*/
@XStreamAlias("xml")
@ToString
public class WxCpDemoInMemoryConfigStorage extends WxCpInMemoryConfigStorage {
public class WxCpDemoInMemoryConfigStorage extends WxCpDefaultConfigImpl {
public static WxCpDemoInMemoryConfigStorage fromXml(InputStream is) {
XStream xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(WxCpDemoInMemoryConfigStorage.class);


+ 1
- 1
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java View File

@@ -10,7 +10,7 @@ import org.eclipse.jetty.servlet.ServletHolder;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.WxCpConsts;
import me.chanjar.weixin.cp.constant.WxCpConsts;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;


+ 3
- 2
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java View File

@@ -2,6 +2,7 @@ package me.chanjar.weixin.cp.demo;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -30,9 +31,9 @@ public class WxCpOAuth2Servlet extends HttpServlet {
response.getWriter().println("<h1>code</h1>");
response.getWriter().println(code);

String[] res = this.wxCpService.getOauth2Service().getUserInfo(code);
WxCpOauth2UserInfo res = this.wxCpService.getOauth2Service().getUserInfo(code);
response.getWriter().println("<h1>result</h1>");
response.getWriter().println(Arrays.toString(res));
response.getWriter().println(res);
} catch (WxErrorException e) {
e.printStackTrace();
}


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save